From 7ac0883970b1a8f3f2dc478fdaa9ae69dd7a9090 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sat, 14 Dec 2024 13:33:47 +0100 Subject: [PATCH 001/174] Generate and test .deb package for Debian and Ubuntu --- .gitignore | 1 + flake.lock | 24 ++++++++++++++++++ flake.nix | 21 +++++++++++++--- pkgs/example.toml | 9 +++++++ pkgs/package-deb.nix | 30 ++++++++++++++++++++++ tests/packaging/deb.nix | 44 +++++++++++++++++++++++++++++++++ tests/packaging/prepare-test.sh | 30 ++++++++++++++++++++++ 7 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 pkgs/example.toml create mode 100644 pkgs/package-deb.nix create mode 100644 tests/packaging/deb.nix create mode 100644 tests/packaging/prepare-test.sh diff --git a/.gitignore b/.gitignore index 4270d4ea..966c47cb 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ _markdown_* .vscode /output +.nixos-test-history diff --git a/flake.lock b/flake.lock index 06183ae0..2b21eb74 100644 --- a/flake.lock +++ b/flake.lock @@ -39,6 +39,29 @@ "type": "github" } }, + "nix-vm-test": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1734178503, + "narHash": "sha256-R2HSewN6ekutGwBA1RY5EKT1eV3idY/KjSUvPpQT+Gg=", + "owner": "tfc", + "repo": "nix-vm-test", + "rev": "7216720e54ff058744d84dba3a6057e52ebb4fbc", + "type": "github" + }, + "original": { + "owner": "tfc", + "repo": "nix-vm-test", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1728193676, @@ -59,6 +82,7 @@ "inputs": { "fenix": "fenix", "flake-utils": "flake-utils", + "nix-vm-test": "nix-vm-test", "nixpkgs": "nixpkgs" } }, diff --git a/flake.nix b/flake.nix index 658b06ea..ddec834b 100644 --- a/flake.nix +++ b/flake.nix @@ -6,9 +6,15 @@ # for rust nightly with llvm-tools-preview fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; + + # TODO: Switch to github:numtide/nix-vm-tests when pull request + # https://github.com/numtide/nix-vm-test/pull/71 is through + nix-vm-test.url = "github:tfc/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 +83,16 @@ 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; + }; # ### Reading materials ### @@ -151,7 +163,10 @@ { nativeBuildInputs = [ pkgs.nodePackages.prettier ]; } '' cd ${./.} && prettier --check . && touch $out ''; - }; + } // pkgs.lib.optionalAttrs (system == "x86_64-linux") (import ./tests/packaging/deb.nix { + inherit pkgs; + rosenpass-deb = self.packages.${system}.package-deb; + }); formatter = pkgs.nixpkgs-fmt; })) diff --git a/pkgs/example.toml b/pkgs/example.toml new file mode 100644 index 00000000..659a90a9 --- /dev/null +++ b/pkgs/example.toml @@ -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" diff --git a/pkgs/package-deb.nix b/pkgs/package-deb.nix new file mode 100644 index 00000000..d7194d6f --- /dev/null +++ b/pkgs/package-deb.nix @@ -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 + 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 +'' diff --git a/tests/packaging/deb.nix b/tests/packaging/deb.nix new file mode 100644 index 00000000..994d93ca --- /dev/null +++ b/tests/packaging/deb.nix @@ -0,0 +1,44 @@ +{ pkgs, rosenpass-deb }: + +let + wg-deb = pkgs.fetchurl { + url = "http://ftp.de.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1.1_amd64.deb"; + hash = "sha256-s/hCUisQLR19kEbV6d8JXzzTAWUPM+NV0APgHizRGA4="; + }; + pkgsDir = pkgs.runCommand "packages" {} '' + mkdir $out + cp ${rosenpass-deb} $out/rosenpass.deb + cp ${wg-deb} $out/wireguard.deb + cp ${./prepare-test.sh} $out/prepare-test.sh + ''; + + testAttrs = { + sharedDirs.share = { + source = pkgsDir; + target = "/mnt/share"; + }; + testScript = '' + vm.wait_for_unit("multi-user.target") + vm.succeed("dpkg --install /mnt/share/wireguard.deb") + vm.succeed("dpkg --install /mnt/share/rosenpass.deb") + vm.succeed("bash /mnt/share/prepare-test.sh") + + vm.succeed(f"systemctl start rp@server") + vm.succeed(f"systemctl start rp@client") + + vm.wait_for_unit("rp@server.service") + vm.wait_for_unit("rp@client.service") + + vm.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + + psk_server = vm.succeed("wg show rp-server preshared-keys").strip().split()[-1] + psk_client = vm.succeed("wg show rp-client preshared-keys").strip().split()[-1] + + assert psk_server == psk_client, "preshared-key exchange must be successful" + ''; + }; +in +{ + debian-13 = (pkgs.testers.legacyDistros.debian."13" testAttrs).sandboxed; + ubuntu-23_10 = (pkgs.testers.legacyDistros.ubuntu."23_10" testAttrs).sandboxed; +} diff --git a/tests/packaging/prepare-test.sh b/tests/packaging/prepare-test.sh new file mode 100644 index 00000000..a10c97f9 --- /dev/null +++ b/tests/packaging/prepare-test.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +< /etc/rosenpass/example.toml \ + sed 's@example@server@' > /etc/rosenpass/server.toml + +< /etc/rosenpass/example.toml \ + sed 's@listen.*@@' | + sed 's@client@server@' | + sed 's@example@client@' | + sed 's@fc00::2@fc00::1@' | + sed 's@fc00::1/64@fc00::2/64@' > /etc/rosenpass/client.toml + +echo 'endpoint = "[::1]:51821"' >> /etc/rosenpass/client.toml + +rp genkey server-sk +rp pubkey server-sk server-pk + +rp genkey client-sk +rp pubkey client-sk client-pk + +mkdir -p /etc/rosenpass/server/peers/client +mkdir -p /etc/rosenpass/client/peers/server + +cp server-sk/{pqpk,pqsk,wgsk} /etc/rosenpass/server/ +cp client-sk/{pqpk,pqsk,wgsk} /etc/rosenpass/client/ + +cp client-pk/{pqpk,wgpk} /etc/rosenpass/server/peers/client +cp server-pk/{pqpk,wgpk} /etc/rosenpass/client/peers/server From eadf70ee381c0690443aa6085c970b72943619af Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sat, 14 Dec 2024 14:46:00 +0100 Subject: [PATCH 002/174] Generate and test RPM package for Fedora --- flake.nix | 6 ++- pkgs/package-rpm.nix | 57 +++++++++++++++++++++ tests/legacy-distro-packaging.nix | 71 +++++++++++++++++++++++++++ tests/packaging/deb.nix | 44 ----------------- tests/{packaging => }/prepare-test.sh | 0 5 files changed, 133 insertions(+), 45 deletions(-) create mode 100644 pkgs/package-rpm.nix create mode 100644 tests/legacy-distro-packaging.nix delete mode 100644 tests/packaging/deb.nix rename tests/{packaging => }/prepare-test.sh (100%) diff --git a/flake.nix b/flake.nix index ddec834b..851b5563 100644 --- a/flake.nix +++ b/flake.nix @@ -93,6 +93,9 @@ 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 ### @@ -163,9 +166,10 @@ { nativeBuildInputs = [ pkgs.nodePackages.prettier ]; } '' cd ${./.} && prettier --check . && touch $out ''; - } // pkgs.lib.optionalAttrs (system == "x86_64-linux") (import ./tests/packaging/deb.nix { + } // 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; diff --git a/pkgs/package-rpm.nix b/pkgs/package-rpm.nix new file mode 100644 index 00000000..aaf6de37 --- /dev/null +++ b/pkgs/package-rpm.nix @@ -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}.deb" { } '' + 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 +'' diff --git a/tests/legacy-distro-packaging.nix b/tests/legacy-distro-packaging.nix new file mode 100644 index 00000000..5c09b2ce --- /dev/null +++ b/tests/legacy-distro-packaging.nix @@ -0,0 +1,71 @@ +{ pkgs, rosenpass-deb, rosenpass-rpm }: + +let + wg-deb = pkgs.fetchurl { + url = "http://ftp.de.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1.1_amd64.deb"; + hash = "sha256-s/hCUisQLR19kEbV6d8JXzzTAWUPM+NV0APgHizRGA4="; + }; + wg-rpm = pkgs.fetchurl { + url = "https://mirrors.n-ix.net/fedora/linux/releases/40/Everything/x86_64/os/Packages/w/wireguard-tools-1.0.20210914-6.fc40.x86_64.rpm"; + hash = "sha256-lh6kCW5gh9bfuOwzjPv96ol1d6u1JTIr/oKH5QbAlK0="; + }; + + pkgsDirDeb = pkgs.runCommand "packages" { } '' + mkdir $out + cp ${rosenpass-deb} $out/rosenpass.deb + cp ${wg-deb} $out/wireguard.deb + cp ${./prepare-test.sh} $out/prepare-test.sh + ''; + pkgsDirRpm = pkgs.runCommand "packages" { } '' + mkdir $out + cp ${rosenpass-rpm} $out/rosenpass.rpm + cp ${wg-rpm} $out/wireguard.rpm + cp ${./prepare-test.sh} $out/prepare-test.sh + ''; + + test = { tester, installPrefix, suffix, source }: (tester { + sharedDirs.share = { + inherit source; + target = "/mnt/share"; + }; + testScript = '' + vm.wait_for_unit("multi-user.target") + vm.succeed("${installPrefix} /mnt/share/wireguard.${suffix}") + vm.succeed("${installPrefix} /mnt/share/rosenpass.${suffix}") + vm.succeed("bash /mnt/share/prepare-test.sh") + + vm.succeed(f"systemctl start rp@server") + vm.succeed(f"systemctl start rp@client") + + vm.wait_for_unit("rp@server.service") + vm.wait_for_unit("rp@client.service") + + vm.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + + psk_server = vm.succeed("wg show rp-server preshared-keys").strip().split()[-1] + psk_client = vm.succeed("wg show rp-client preshared-keys").strip().split()[-1] + + assert psk_server == psk_client, "preshared-key exchange must be successful" + ''; + }).sandboxed; +in +{ + package-deb-debian-13 = test { + tester = pkgs.testers.legacyDistros.debian."13"; + installPrefix = "dpkg --install"; + suffix = "deb"; + source = pkgsDirDeb; + }; + package-deb-ubuntu-23_10 = test { + tester = pkgs.testers.legacyDistros.ubuntu."23_10"; + installPrefix = "dpkg --install"; + suffix = "deb"; + source = pkgsDirDeb; + }; + package-rpm-fedora_40 = test { + tester = pkgs.testers.legacyDistros.fedora."40"; + installPrefix = "rpm -i"; + suffix = "rpm"; + source = pkgsDirRpm; + }; +} diff --git a/tests/packaging/deb.nix b/tests/packaging/deb.nix deleted file mode 100644 index 994d93ca..00000000 --- a/tests/packaging/deb.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ pkgs, rosenpass-deb }: - -let - wg-deb = pkgs.fetchurl { - url = "http://ftp.de.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1.1_amd64.deb"; - hash = "sha256-s/hCUisQLR19kEbV6d8JXzzTAWUPM+NV0APgHizRGA4="; - }; - pkgsDir = pkgs.runCommand "packages" {} '' - mkdir $out - cp ${rosenpass-deb} $out/rosenpass.deb - cp ${wg-deb} $out/wireguard.deb - cp ${./prepare-test.sh} $out/prepare-test.sh - ''; - - testAttrs = { - sharedDirs.share = { - source = pkgsDir; - target = "/mnt/share"; - }; - testScript = '' - vm.wait_for_unit("multi-user.target") - vm.succeed("dpkg --install /mnt/share/wireguard.deb") - vm.succeed("dpkg --install /mnt/share/rosenpass.deb") - vm.succeed("bash /mnt/share/prepare-test.sh") - - vm.succeed(f"systemctl start rp@server") - vm.succeed(f"systemctl start rp@client") - - vm.wait_for_unit("rp@server.service") - vm.wait_for_unit("rp@client.service") - - vm.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); - - psk_server = vm.succeed("wg show rp-server preshared-keys").strip().split()[-1] - psk_client = vm.succeed("wg show rp-client preshared-keys").strip().split()[-1] - - assert psk_server == psk_client, "preshared-key exchange must be successful" - ''; - }; -in -{ - debian-13 = (pkgs.testers.legacyDistros.debian."13" testAttrs).sandboxed; - ubuntu-23_10 = (pkgs.testers.legacyDistros.ubuntu."23_10" testAttrs).sandboxed; -} diff --git a/tests/packaging/prepare-test.sh b/tests/prepare-test.sh similarity index 100% rename from tests/packaging/prepare-test.sh rename to tests/prepare-test.sh From f093406c340ff18856e9a7b0b1b992164ec8122b Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sun, 15 Dec 2024 12:50:59 +0000 Subject: [PATCH 003/174] Use upstream nix-vm-test after PR was merged --- flake.lock | 10 +++++----- flake.nix | 4 +--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index 2b21eb74..ad97849e 100644 --- a/flake.lock +++ b/flake.lock @@ -49,15 +49,15 @@ ] }, "locked": { - "lastModified": 1734178503, - "narHash": "sha256-R2HSewN6ekutGwBA1RY5EKT1eV3idY/KjSUvPpQT+Gg=", - "owner": "tfc", + "lastModified": 1734206535, + "narHash": "sha256-e/OcfpGhVDc400m0LvBu22MCcdIJsEBoCVhpcgDuSv4=", + "owner": "numtide", "repo": "nix-vm-test", - "rev": "7216720e54ff058744d84dba3a6057e52ebb4fbc", + "rev": "b9630e7d64325b636943157c41b486bc66413766", "type": "github" }, "original": { - "owner": "tfc", + "owner": "numtide", "repo": "nix-vm-test", "type": "github" } diff --git a/flake.nix b/flake.nix index 851b5563..d232bfa8 100644 --- a/flake.nix +++ b/flake.nix @@ -7,9 +7,7 @@ fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; - # TODO: Switch to github:numtide/nix-vm-tests when pull request - # https://github.com/numtide/nix-vm-test/pull/71 is through - nix-vm-test.url = "github:tfc/nix-vm-test"; + 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"; }; From 436c6e6f8747278c9e6ffe027b0d309798d43ee9 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sun, 15 Dec 2024 16:27:06 +0000 Subject: [PATCH 004/174] use https --- tests/legacy-distro-packaging.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/legacy-distro-packaging.nix b/tests/legacy-distro-packaging.nix index 5c09b2ce..6f8c819f 100644 --- a/tests/legacy-distro-packaging.nix +++ b/tests/legacy-distro-packaging.nix @@ -2,7 +2,7 @@ let wg-deb = pkgs.fetchurl { - url = "http://ftp.de.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1.1_amd64.deb"; + url = "https://ftp.de.debian.org/debian/pool/main/w/wireguard/wireguard-tools_1.0.20210914-1.1_amd64.deb"; hash = "sha256-s/hCUisQLR19kEbV6d8JXzzTAWUPM+NV0APgHizRGA4="; }; wg-rpm = pkgs.fetchurl { From 771dce3ac7e8f3cfd8f71ab4edc10ece22aef061 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Thu, 19 Dec 2024 11:23:47 +0000 Subject: [PATCH 005/174] Use latest naming scheme of upstream flake --- flake.lock | 9 +++------ tests/legacy-distro-packaging.nix | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index ad97849e..e8d4a2e1 100644 --- a/flake.lock +++ b/flake.lock @@ -41,19 +41,16 @@ }, "nix-vm-test": { "inputs": { - "flake-utils": [ - "flake-utils" - ], "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1734206535, - "narHash": "sha256-e/OcfpGhVDc400m0LvBu22MCcdIJsEBoCVhpcgDuSv4=", + "lastModified": 1734355073, + "narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=", "owner": "numtide", "repo": "nix-vm-test", - "rev": "b9630e7d64325b636943157c41b486bc66413766", + "rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c", "type": "github" }, "original": { diff --git a/tests/legacy-distro-packaging.nix b/tests/legacy-distro-packaging.nix index 6f8c819f..1d358069 100644 --- a/tests/legacy-distro-packaging.nix +++ b/tests/legacy-distro-packaging.nix @@ -51,19 +51,19 @@ let in { package-deb-debian-13 = test { - tester = pkgs.testers.legacyDistros.debian."13"; + tester = pkgs.testers.nonNixOSDistros.debian."13"; installPrefix = "dpkg --install"; suffix = "deb"; source = pkgsDirDeb; }; package-deb-ubuntu-23_10 = test { - tester = pkgs.testers.legacyDistros.ubuntu."23_10"; + tester = pkgs.testers.nonNixOSDistros.ubuntu."23_10"; installPrefix = "dpkg --install"; suffix = "deb"; source = pkgsDirDeb; }; package-rpm-fedora_40 = test { - tester = pkgs.testers.legacyDistros.fedora."40"; + tester = pkgs.testers.nonNixOSDistros.fedora."40"; installPrefix = "rpm -i"; suffix = "rpm"; source = pkgsDirRpm; From 0bfe47e5b8adc340f7cab1e6a7b8b96980f36bd8 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Thu, 19 Dec 2024 11:24:00 +0000 Subject: [PATCH 006/174] fix naming typo --- pkgs/package-rpm.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/package-rpm.nix b/pkgs/package-rpm.nix index aaf6de37..f446bfc9 100644 --- a/pkgs/package-rpm.nix +++ b/pkgs/package-rpm.nix @@ -10,7 +10,7 @@ let arch = builtins.head (builtins.split "-" system); in -runCommand "rosenpass-${version}.deb" { } '' +runCommand "rosenpass-${version}.rpm" { } '' mkdir -p rpmbuild/SPECS cat << EOF > rpmbuild/SPECS/rosenpass.spec From 9fdba31b32bdf4aa54369a6ffad9730d2e6b57ba Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sun, 9 Feb 2025 21:36:10 +0700 Subject: [PATCH 007/174] Build and upload DEB and RPM artefacts --- .github/workflows/release.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 90cc1176..7ee95f27 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -69,3 +69,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 package + run: nix build .#package-deb --print-build-logs + - name: Build RPM package + run: nix build .#package-rpm --print-build-logs + - 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: | + result-*/* From 87587399edd09ba0031f131073e8069304d289a0 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sun, 9 Feb 2025 21:38:41 +0700 Subject: [PATCH 008/174] Drop nix channels as we're not using channels anyway. --- .github/workflows/release.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7ee95f27..a8e91bb3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -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 From e35955f99c8e1c56b1494833bbb1d6e242449f6a Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Sun, 9 Feb 2025 15:19:55 +0000 Subject: [PATCH 009/174] fix release workflow --- .github/workflows/release.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a8e91bb3..8fb64135 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -75,14 +75,14 @@ jobs: with: name: rosenpass authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - - name: Build DEB package - run: nix build .#package-deb --print-build-logs - - name: Build RPM package - run: nix build .#package-rpm --print-build-logs + - 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: | - result-*/* + packages/* From b40b7f4f2f692784666521977d5a21950cee68cb Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sat, 22 Feb 2025 15:03:58 +0100 Subject: [PATCH 010/174] 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. https://github.com/clap-rs/clap/commit/d96cc71626c5291718b7db697d4aca2d03ef496f - 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"` --- Cargo.lock | 443 ++++++++++-------- Cargo.toml | 2 +- deny.toml | 4 +- rosenpass/tests/main-fn-generates-manpages.rs | 7 +- supply-chain/config.toml | 312 ++++++++++++ util/src/fd.rs | 4 +- 6 files changed, 574 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 486d8840..4364fa0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -75,43 +75,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" dependencies = [ "backtrace", ] @@ -177,7 +178,7 @@ version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "lazy_static", @@ -190,7 +191,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.87", + "syn 2.0.98", "which", ] @@ -202,9 +203,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "blake2" @@ -235,9 +236,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -247,9 +248,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cast" @@ -259,9 +260,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.30" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "jobserver", "libc", @@ -358,9 +359,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -368,9 +369,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -380,23 +381,23 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.40" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" +checksum = "1e3040c8291884ddf39445dc033c70abc2bc44a42f0a3a00571a0f483a83f0cd" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -417,9 +418,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.51" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] @@ -432,9 +433,9 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "command-fds" @@ -443,14 +444,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f190f3c954f7bca3c6296d0ec561c739bdbe6c7e990294ed168d415f6e1b5b01" dependencies = [ "nix 0.27.1", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -499,18 +500,18 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -527,15 +528,15 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -581,7 +582,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -629,7 +630,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -651,7 +652,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -662,7 +663,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -704,7 +705,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -724,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core 0.20.2", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -777,9 +778,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -793,9 +794,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fiat-crypto" @@ -877,7 +878,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -932,7 +933,7 @@ dependencies = [ "netlink-packet-generic", "netlink-packet-utils", "netlink-proto", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -945,10 +946,22 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "gimli" version = "0.31.1" @@ -957,9 +970,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "half" @@ -1047,9 +1060,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -1057,9 +1070,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] @@ -1085,13 +1098,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1111,9 +1124,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -1126,10 +1139,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -1147,9 +1161,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.168" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libcrux" @@ -1157,7 +1171,7 @@ version = "0.0.2-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31d9dcd435758db03438089760c55a45e6bcab7e4e299ee261f75225ab29d482" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libcrux-hacl", "libcrux-platform", "libjade-sys", @@ -1185,9 +1199,9 @@ dependencies = [ [[package]] name = "libfuzzer-sys" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" dependencies = [ "arbitrary", "cc", @@ -1205,9 +1219,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -1215,9 +1229,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "lock_api" @@ -1231,9 +1245,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -1264,7 +1278,7 @@ name = "memsec" version = "0.6.3" source = "git+https://github.com/rosenpass/memsec.git?rev=aceb9baee8aec6844125bd6612f92e9a281373df#aceb9baee8aec6844125bd6612f92e9a281373df" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libc", "windows-sys 0.45.0", ] @@ -1277,9 +1291,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -1292,7 +1306,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1310,9 +1324,9 @@ dependencies = [ [[package]] name = "neli-proc-macros" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4" +checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" dependencies = [ "either", "proc-macro2", @@ -1367,7 +1381,7 @@ dependencies = [ "anyhow", "byteorder", "paste", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1386,24 +1400,23 @@ dependencies = [ [[package]] name = "netlink-proto" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b33524dc0968bfad349684447bfce6db937a9ac3332a1fe60c0c5a5ce63f21" +checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" dependencies = [ "bytes", "futures", "log", "netlink-packet-core", "netlink-sys", - "thiserror", - "tokio", + "thiserror 2.0.11", ] [[package]] name = "netlink-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" dependencies = [ "bytes", "futures", @@ -1431,7 +1444,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "libc", ] @@ -1457,18 +1470,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "oorandom" @@ -1532,9 +1545,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1611,19 +1624,19 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -1645,18 +1658,18 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" dependencies = [ "cc", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -1688,7 +1701,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1713,18 +1726,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -1734,9 +1747,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -1792,7 +1805,7 @@ dependencies = [ "static_assertions", "tempfile", "test_bin", - "thiserror", + "thiserror 1.0.69", "toml", "uds", "zerocopy", @@ -1895,7 +1908,7 @@ dependencies = [ "rustix", "static_assertions", "tempfile", - "thiserror", + "thiserror 1.0.69", "typenum", "uds", "zerocopy", @@ -1920,7 +1933,7 @@ dependencies = [ "rosenpass-to", "rosenpass-util", "rustix", - "thiserror", + "thiserror 1.0.69", "tokio", "wireguard-uapi", "zerocopy", @@ -1969,7 +1982,7 @@ dependencies = [ "netlink-proto", "netlink-sys", "nix 0.27.1", - "thiserror", + "thiserror 1.0.69", "tokio", ] @@ -1996,11 +2009,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -2008,10 +2021,16 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.18" +name = "rustversion" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -2024,9 +2043,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.1" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553f8299af7450cda9a52d3a370199904e7a46b5ffd1bef187c4a6af3bb6db69" +checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" dependencies = [ "sdd", ] @@ -2039,41 +2058,41 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" +checksum = "b07779b9b918cc05650cb30f404d4d7835d26df37c235eded8a6832e2fb82cca" [[package]] name = "semver" -version = "1.0.23" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "itoa", "memchr", @@ -2112,7 +2131,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -2157,15 +2176,15 @@ checksum = "88414a5ca1f85d82cc34471e975f0f74f6aa54c40f062efa42c0080e7f763f81" [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2188,9 +2207,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stacker" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +checksum = "d9156ebd5870ef293bfb43f91c7a74528d363ec0d424afe24160ed5a4343d08a" dependencies = [ "cc", "cfg-if", @@ -2236,9 +2255,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2253,12 +2272,13 @@ checksum = "b4e17d8598067a8c134af59cd33c1c263470e089924a11ab61cf61690919fe3b" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2285,7 +2305,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -2296,7 +2325,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -2311,9 +2351,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -2329,13 +2369,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -2374,9 +2414,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "uds" @@ -2389,9 +2429,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "universal-hash" @@ -2411,11 +2451,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] [[package]] @@ -2441,36 +2481,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.95" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2478,28 +2527,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -2579,7 +2631,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -2590,7 +2642,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -2846,7 +2898,16 @@ dependencies = [ "libc", "neli", "take-until", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", ] [[package]] @@ -2879,7 +2940,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] [[package]] @@ -2899,5 +2960,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.98", ] diff --git a/Cargo.toml b/Cargo.toml index d1f2c13b..1d7f3d4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "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"] } diff --git a/deny.toml b/deny.toml index 273109f1..295d685d 100644 --- a/deny.toml +++ b/deny.toml @@ -56,7 +56,9 @@ confidence-threshold = 0.8 exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list - { allow = ["Unicode-DFS-2016"], crate = "unicode-ident" }, + { allow = ["Unicode-DFS-2016", "Unicode-3.0"], crate = "unicode-ident" }, + { allow = ["NCSA"], crate = "libfuzzer-sys" }, + ] [licenses.private] diff --git a/rosenpass/tests/main-fn-generates-manpages.rs b/rosenpass/tests/main-fn-generates-manpages.rs index 41124c95..30dfdb79 100644 --- a/rosenpass/tests/main-fn-generates-manpages.rs +++ b/rosenpass/tests/main-fn-generates-manpages.rs @@ -46,7 +46,6 @@ fn main_fn_generates_manpages() -> anyhow::Result<()> { "rosenpass-exchange-config.1", "rosenpass-gen-config.1", "rosenpass-gen-keys.1", - "rosenpass-keygen.1", "rosenpass-validate.1", ]; @@ -56,7 +55,10 @@ fn main_fn_generates_manpages() -> anyhow::Result<()> { .map(|name| (name, dir.path().join(name))) .map(|(name, path)| { let res = std::process::Command::new("man").arg(path).output()?; - assert!(res.status.success()); + assert!( + res.status.success(), + "Error rendering manpage {name} using man" + ); let body = res .stdout .apply(String::from_utf8)? @@ -64,7 +66,6 @@ fn main_fn_generates_manpages() -> anyhow::Result<()> { Ok((name, body)) }) .collect::>()?; - for (name, body) in man_texts.iter() { expect_sections(body, &["NAME", "SYNOPSIS", "OPTIONS"])?; diff --git a/supply-chain/config.toml b/supply-chain/config.toml index c8673d10..78233367 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -57,26 +57,50 @@ criteria = "safe-to-run" version = "0.6.15" criteria = "safe-to-deploy" +[[exemptions.anstream]] +version = "0.6.18" +criteria = "safe-to-deploy" + [[exemptions.anstyle]] version = "1.0.8" criteria = "safe-to-deploy" +[[exemptions.anstyle]] +version = "1.0.10" +criteria = "safe-to-deploy" + [[exemptions.anstyle-parse]] version = "0.2.5" criteria = "safe-to-deploy" +[[exemptions.anstyle-parse]] +version = "0.2.6" +criteria = "safe-to-deploy" + [[exemptions.anstyle-query]] version = "1.1.1" criteria = "safe-to-deploy" +[[exemptions.anstyle-query]] +version = "1.1.2" +criteria = "safe-to-deploy" + [[exemptions.anstyle-wincon]] version = "3.0.4" criteria = "safe-to-deploy" +[[exemptions.anstyle-wincon]] +version = "3.0.7" +criteria = "safe-to-deploy" + [[exemptions.anyhow]] version = "1.0.95" criteria = "safe-to-deploy" +[[exemptions.anyhow]] +version = "1.0.96" +criteria = "safe-to-deploy" + [[exemptions.atomic-polyfill]] version = "1.0.3" criteria = "safe-to-deploy" @@ -93,6 +117,10 @@ criteria = "safe-to-deploy" version = "1.3.3" criteria = "safe-to-run" +[[exemptions.bitflags]] +version = "2.8.0" +criteria = "safe-to-deploy" + [[exemptions.blake2]] version = "0.10.6" criteria = "safe-to-deploy" @@ -101,14 +129,26 @@ criteria = "safe-to-deploy" version = "0.1.4" criteria = "safe-to-deploy" +[[exemptions.bumpalo]] +version = "3.17.0" +criteria = "safe-to-deploy" + [[exemptions.bytes]] version = "1.7.2" criteria = "safe-to-deploy" +[[exemptions.bytes]] +version = "1.10.0" +criteria = "safe-to-deploy" + [[exemptions.cc]] version = "1.1.30" criteria = "safe-to-deploy" +[[exemptions.cc]] +version = "1.2.15" +criteria = "safe-to-deploy" + [[exemptions.chacha20]] version = "0.9.1" criteria = "safe-to-deploy" @@ -137,18 +177,34 @@ criteria = "safe-to-deploy" version = "4.5.23" criteria = "safe-to-deploy" +[[exemptions.clap]] +version = "4.5.30" +criteria = "safe-to-deploy" + [[exemptions.clap_builder]] version = "4.5.23" criteria = "safe-to-deploy" +[[exemptions.clap_builder]] +version = "4.5.30" +criteria = "safe-to-deploy" + [[exemptions.clap_complete]] version = "4.5.40" criteria = "safe-to-deploy" +[[exemptions.clap_complete]] +version = "4.5.45" +criteria = "safe-to-deploy" + [[exemptions.clap_derive]] version = "4.5.18" criteria = "safe-to-deploy" +[[exemptions.clap_derive]] +version = "4.5.28" +criteria = "safe-to-deploy" + [[exemptions.clap_lex]] version = "0.7.4" criteria = "safe-to-deploy" @@ -161,10 +217,18 @@ criteria = "safe-to-deploy" version = "0.1.51" criteria = "safe-to-deploy" +[[exemptions.cmake]] +version = "0.1.54" +criteria = "safe-to-deploy" + [[exemptions.colorchoice]] version = "1.0.2" criteria = "safe-to-deploy" +[[exemptions.colorchoice]] +version = "1.0.3" +criteria = "safe-to-deploy" + [[exemptions.command-fds]] version = "0.2.3" criteria = "safe-to-deploy" @@ -173,6 +237,10 @@ criteria = "safe-to-deploy" version = "0.2.14" criteria = "safe-to-deploy" +[[exemptions.cpufeatures]] +version = "0.2.17" +criteria = "safe-to-deploy" + [[exemptions.criterion]] version = "0.5.1" criteria = "safe-to-run" @@ -185,10 +253,26 @@ criteria = "safe-to-run" version = "1.2.0" criteria = "safe-to-deploy" +[[exemptions.crossbeam-channel]] +version = "0.5.14" +criteria = "safe-to-deploy" + +[[exemptions.crossbeam-deque]] +version = "0.8.6" +criteria = "safe-to-deploy" + [[exemptions.crossbeam-utils]] version = "0.8.20" criteria = "safe-to-run" +[[exemptions.crossbeam-utils]] +version = "0.8.21" +criteria = "safe-to-deploy" + +[[exemptions.crunchy]] +version = "0.2.3" +criteria = "safe-to-deploy" + [[exemptions.ctrlc-async]] version = "3.2.2" criteria = "safe-to-deploy" @@ -265,6 +349,14 @@ criteria = "safe-to-deploy" version = "0.10.2" criteria = "safe-to-deploy" +[[exemptions.equivalent]] +version = "1.0.2" +criteria = "safe-to-deploy" + +[[exemptions.fastrand]] +version = "2.3.0" +criteria = "safe-to-deploy" + [[exemptions.findshlibs]] version = "0.10.2" criteria = "safe-to-run" @@ -289,10 +381,18 @@ criteria = "safe-to-deploy" version = "0.2.15" criteria = "safe-to-deploy" +[[exemptions.getrandom]] +version = "0.3.1" +criteria = "safe-to-deploy" + [[exemptions.gimli]] version = "0.31.1" criteria = "safe-to-deploy" +[[exemptions.glob]] +version = "0.3.2" +criteria = "safe-to-deploy" + [[exemptions.half]] version = "2.4.1" criteria = "safe-to-run" @@ -329,6 +429,14 @@ criteria = "safe-to-deploy" version = "2.6.0" criteria = "safe-to-deploy" +[[exemptions.indexmap]] +version = "2.7.1" +criteria = "safe-to-deploy" + +[[exemptions.inout]] +version = "0.1.4" +criteria = "safe-to-deploy" + [[exemptions.ipc-channel]] version = "0.18.3" criteria = "safe-to-run" @@ -337,10 +445,18 @@ criteria = "safe-to-run" version = "0.4.13" criteria = "safe-to-deploy" +[[exemptions.is-terminal]] +version = "0.4.15" +criteria = "safe-to-deploy" + [[exemptions.is_terminal_polyfill]] version = "1.70.1" criteria = "safe-to-deploy" +[[exemptions.itoa]] +version = "1.0.14" +criteria = "safe-to-deploy" + [[exemptions.jobserver]] version = "0.1.32" criteria = "safe-to-deploy" @@ -349,6 +465,10 @@ criteria = "safe-to-deploy" version = "0.3.72" criteria = "safe-to-deploy" +[[exemptions.js-sys]] +version = "0.3.77" +criteria = "safe-to-deploy" + [[exemptions.lazycell]] version = "1.3.0" criteria = "safe-to-deploy" @@ -357,6 +477,10 @@ criteria = "safe-to-deploy" version = "0.2.168" criteria = "safe-to-deploy" +[[exemptions.libc]] +version = "0.2.169" +criteria = "safe-to-deploy" + [[exemptions.libcrux]] version = "0.0.2-pre.2" criteria = "safe-to-deploy" @@ -373,6 +497,10 @@ criteria = "safe-to-deploy" version = "0.4.8" criteria = "safe-to-deploy" +[[exemptions.libfuzzer-sys]] +version = "0.4.9" +criteria = "safe-to-deploy" + [[exemptions.libjade-sys]] version = "0.0.2-pre.2" criteria = "safe-to-deploy" @@ -381,14 +509,26 @@ criteria = "safe-to-deploy" version = "0.8.5" criteria = "safe-to-deploy" +[[exemptions.libloading]] +version = "0.8.6" +criteria = "safe-to-deploy" + [[exemptions.linux-raw-sys]] version = "0.4.14" criteria = "safe-to-deploy" +[[exemptions.linux-raw-sys]] +version = "0.4.15" +criteria = "safe-to-deploy" + [[exemptions.lock_api]] version = "0.4.12" criteria = "safe-to-deploy" +[[exemptions.log]] +version = "0.4.26" +criteria = "safe-to-deploy" + [[exemptions.memchr]] version = "2.7.4" criteria = "safe-to-deploy" @@ -409,6 +549,10 @@ criteria = "safe-to-deploy" version = "0.2.1" criteria = "safe-to-deploy" +[[exemptions.miniz_oxide]] +version = "0.8.5" +criteria = "safe-to-deploy" + [[exemptions.mio]] version = "1.0.3" criteria = "safe-to-deploy" @@ -421,6 +565,10 @@ criteria = "safe-to-deploy" version = "0.1.3" criteria = "safe-to-deploy" +[[exemptions.neli-proc-macros]] +version = "0.1.4" +criteria = "safe-to-deploy" + [[exemptions.netlink-packet-core]] version = "0.7.0" criteria = "safe-to-deploy" @@ -445,10 +593,18 @@ criteria = "safe-to-deploy" version = "0.11.3" criteria = "safe-to-deploy" +[[exemptions.netlink-proto]] +version = "0.11.5" +criteria = "safe-to-deploy" + [[exemptions.netlink-sys]] version = "0.8.6" criteria = "safe-to-deploy" +[[exemptions.netlink-sys]] +version = "0.8.7" +criteria = "safe-to-deploy" + [[exemptions.nix]] version = "0.23.2" criteria = "safe-to-deploy" @@ -461,10 +617,18 @@ criteria = "safe-to-deploy" version = "0.36.5" criteria = "safe-to-deploy" +[[exemptions.object]] +version = "0.36.7" +criteria = "safe-to-deploy" + [[exemptions.once_cell]] version = "1.20.2" criteria = "safe-to-deploy" +[[exemptions.once_cell]] +version = "1.20.3" +criteria = "safe-to-deploy" + [[exemptions.oqs-sys]] version = "0.9.1+liboqs-0.9.0" criteria = "safe-to-deploy" @@ -481,6 +645,10 @@ criteria = "safe-to-deploy" version = "1.0.15" criteria = "safe-to-deploy" +[[exemptions.pin-project-lite]] +version = "0.2.16" +criteria = "safe-to-deploy" + [[exemptions.pkg-config]] version = "0.3.31" criteria = "safe-to-deploy" @@ -513,6 +681,14 @@ criteria = "safe-to-deploy" version = "0.2.22" criteria = "safe-to-deploy" +[[exemptions.prettyplease]] +version = "0.2.29" +criteria = "safe-to-deploy" + +[[exemptions.proc-macro2]] +version = "1.0.93" +criteria = "safe-to-deploy" + [[exemptions.procspawn]] version = "1.0.1" criteria = "safe-to-run" @@ -521,6 +697,14 @@ criteria = "safe-to-run" version = "0.1.23" criteria = "safe-to-deploy" +[[exemptions.psm]] +version = "0.1.25" +criteria = "safe-to-deploy" + +[[exemptions.quote]] +version = "1.0.38" +criteria = "safe-to-deploy" + [[exemptions.rand]] version = "0.8.5" criteria = "safe-to-deploy" @@ -529,14 +713,26 @@ criteria = "safe-to-deploy" version = "0.5.7" criteria = "safe-to-deploy" +[[exemptions.redox_syscall]] +version = "0.5.9" +criteria = "safe-to-deploy" + [[exemptions.regex]] version = "1.11.0" criteria = "safe-to-deploy" +[[exemptions.regex]] +version = "1.11.1" +criteria = "safe-to-deploy" + [[exemptions.regex-automata]] version = "0.4.8" criteria = "safe-to-deploy" +[[exemptions.regex-automata]] +version = "0.4.9" +criteria = "safe-to-deploy" + [[exemptions.roff]] version = "0.2.2" criteria = "safe-to-deploy" @@ -549,14 +745,30 @@ criteria = "safe-to-deploy" version = "0.38.42" criteria = "safe-to-deploy" +[[exemptions.rustix]] +version = "0.38.44" +criteria = "safe-to-deploy" + +[[exemptions.rustversion]] +version = "1.0.19" +criteria = "safe-to-deploy" + [[exemptions.ryu]] version = "1.0.18" criteria = "safe-to-run" +[[exemptions.ryu]] +version = "1.0.19" +criteria = "safe-to-deploy" + [[exemptions.scc]] version = "2.2.1" criteria = "safe-to-run" +[[exemptions.scc]] +version = "2.3.3" +criteria = "safe-to-deploy" + [[exemptions.scopeguard]] version = "1.2.0" criteria = "safe-to-deploy" @@ -565,6 +777,26 @@ criteria = "safe-to-deploy" version = "3.0.4" criteria = "safe-to-run" +[[exemptions.sdd]] +version = "3.0.7" +criteria = "safe-to-deploy" + +[[exemptions.semver]] +version = "1.0.25" +criteria = "safe-to-deploy" + +[[exemptions.serde]] +version = "1.0.218" +criteria = "safe-to-deploy" + +[[exemptions.serde_derive]] +version = "1.0.218" +criteria = "safe-to-deploy" + +[[exemptions.serde_json]] +version = "1.0.139" +criteria = "safe-to-deploy" + [[exemptions.serde_spanned]] version = "0.6.8" criteria = "safe-to-deploy" @@ -589,10 +821,18 @@ criteria = "safe-to-deploy" version = "0.4.9" criteria = "safe-to-deploy" +[[exemptions.smallvec]] +version = "1.14.0" +criteria = "safe-to-deploy" + [[exemptions.socket2]] version = "0.5.7" criteria = "safe-to-deploy" +[[exemptions.socket2]] +version = "0.5.8" +criteria = "safe-to-deploy" + [[exemptions.spin]] version = "0.9.8" criteria = "safe-to-deploy" @@ -601,6 +841,10 @@ criteria = "safe-to-deploy" version = "0.1.17" criteria = "safe-to-deploy" +[[exemptions.stacker]] +version = "0.1.19" +criteria = "safe-to-deploy" + [[exemptions.syn]] version = "1.0.109" criteria = "safe-to-deploy" @@ -609,6 +853,10 @@ criteria = "safe-to-deploy" version = "2.0.87" criteria = "safe-to-deploy" +[[exemptions.syn]] +version = "2.0.98" +criteria = "safe-to-deploy" + [[exemptions.take-until]] version = "0.1.0" criteria = "safe-to-deploy" @@ -617,6 +865,10 @@ criteria = "safe-to-deploy" version = "3.14.0" criteria = "safe-to-deploy" +[[exemptions.tempfile]] +version = "3.17.1" +criteria = "safe-to-deploy" + [[exemptions.termcolor]] version = "1.4.1" criteria = "safe-to-deploy" @@ -629,18 +881,34 @@ criteria = "safe-to-run" version = "1.0.69" criteria = "safe-to-deploy" +[[exemptions.thiserror]] +version = "2.0.11" +criteria = "safe-to-deploy" + [[exemptions.thiserror-impl]] version = "1.0.69" criteria = "safe-to-deploy" +[[exemptions.thiserror-impl]] +version = "2.0.11" +criteria = "safe-to-deploy" + [[exemptions.tokio]] version = "1.42.0" criteria = "safe-to-deploy" +[[exemptions.tokio]] +version = "1.43.0" +criteria = "safe-to-deploy" + [[exemptions.tokio-macros]] version = "2.4.0" criteria = "safe-to-deploy" +[[exemptions.tokio-macros]] +version = "2.5.0" +criteria = "safe-to-deploy" + [[exemptions.toml]] version = "0.7.8" criteria = "safe-to-deploy" @@ -657,10 +925,18 @@ criteria = "safe-to-deploy" version = "1.17.0" criteria = "safe-to-deploy" +[[exemptions.typenum]] +version = "1.18.0" +criteria = "safe-to-deploy" + [[exemptions.uds]] version = "0.4.2@git:b47934fe52422e559f7278938875f9105f91c5a2" criteria = "safe-to-deploy" +[[exemptions.unicode-ident]] +version = "1.0.17" +criteria = "safe-to-deploy" + [[exemptions.utf8parse]] version = "0.2.2" criteria = "safe-to-deploy" @@ -669,6 +945,10 @@ criteria = "safe-to-deploy" version = "1.10.0" criteria = "safe-to-run" +[[exemptions.uuid]] +version = "1.14.0" +criteria = "safe-to-deploy" + [[exemptions.version_check]] version = "0.9.5" criteria = "safe-to-deploy" @@ -681,30 +961,58 @@ criteria = "safe-to-run" version = "0.11.0+wasi-snapshot-preview1" criteria = "safe-to-deploy" +[[exemptions.wasi]] +version = "0.13.3+wasi-0.2.2" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen]] version = "0.2.95" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen]] +version = "0.2.100" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-backend]] version = "0.2.95" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-backend]] +version = "0.2.100" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-macro]] version = "0.2.95" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-macro]] +version = "0.2.100" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-macro-support]] version = "0.2.95" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-macro-support]] +version = "0.2.100" +criteria = "safe-to-deploy" + [[exemptions.wasm-bindgen-shared]] version = "0.2.95" criteria = "safe-to-deploy" +[[exemptions.wasm-bindgen-shared]] +version = "0.2.100" +criteria = "safe-to-deploy" + [[exemptions.web-sys]] version = "0.3.72" criteria = "safe-to-run" +[[exemptions.web-sys]] +version = "0.3.77" +criteria = "safe-to-deploy" + [[exemptions.which]] version = "4.4.2" criteria = "safe-to-deploy" @@ -873,6 +1181,10 @@ criteria = "safe-to-deploy" version = "3.0.0" criteria = "safe-to-deploy" +[[exemptions.wit-bindgen-rt]] +version = "0.33.0" +criteria = "safe-to-deploy" + [[exemptions.x25519-dalek]] version = "2.0.1" criteria = "safe-to-deploy" diff --git a/util/src/fd.rs b/util/src/fd.rs index f6966e92..658246ff 100644 --- a/util/src/fd.rs +++ b/util/src/fd.rs @@ -521,13 +521,13 @@ mod tests { use std::io::{Read, Write}; #[test] - #[should_panic(expected = "fd != u32::MAX as RawFd")] + #[should_panic] fn test_claim_fd_invalid_neg() { let _ = claim_fd(-1); } #[test] - #[should_panic(expected = "fd != u32::MAX as RawFd")] + #[should_panic] fn test_claim_fd_invalid_max() { let _ = claim_fd(i64::MAX as RawFd); } From fe60cea9590d94f54e084c7b77da08ebd81aae00 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Mon, 24 Feb 2025 13:48:31 +0200 Subject: [PATCH 011/174] fix: avoid duplicate crates --- Cargo.toml | 3 +++ constant-time/Cargo.toml | 2 +- rp/Cargo.toml | 6 +++--- wireguard-broker/Cargo.toml | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1d7f3d4b..56b76e5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,3 +91,6 @@ procspawn = { version = "1.0.1", features = ["test-support"] } wireguard-uapi = { version = "3.0.0", features = ["xplatform"] } command-fds = "0.2.3" rustix = { version = "0.38.42", features = ["net", "fs", "process"] } +futures = "0.3" +futures-util = "0.3" +x25519-dalek = "2" diff --git a/constant-time/Cargo.toml b/constant-time/Cargo.toml index b012a41b..9c85e0d3 100644 --- a/constant-time/Cargo.toml +++ b/constant-time/Cargo.toml @@ -19,7 +19,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)'] } diff --git a/rp/Cargo.toml b/rp/Cargo.toml index 22a8e90a..f31c34b3 100644 --- a/rp/Cargo.toml +++ b/rp/Cargo.toml @@ -14,7 +14,7 @@ anyhow = { workspace = true } base64ct = { workspace = true } serde = { workspace = true } toml = { workspace = true } -x25519-dalek = { version = "2", features = ["static_secrets"] } +x25519-dalek = { workspace = true, features = ["static_secrets"] } zeroize = { workspace = true } rosenpass = { workspace = true } @@ -26,8 +26,8 @@ rosenpass-wireguard-broker = { workspace = true } tokio = { workspace = true } -futures = "0.3" -futures-util = "0.3" +futures = { workspace = true } +futures-util = { workspace = true } [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] ctrlc-async = "3.2" diff --git a/wireguard-broker/Cargo.toml b/wireguard-broker/Cargo.toml index ae987f73..cfc74e9f 100644 --- a/wireguard-broker/Cargo.toml +++ b/wireguard-broker/Cargo.toml @@ -19,7 +19,7 @@ wireguard-uapi = { workspace = true } # Socket handler only rosenpass-to = { workspace = true } -tokio = { version = "1.42.0", features = ["sync", "full", "mio"] } +tokio = { workspace = true, features = ["sync", "full", "mio"] } anyhow = { workspace = true } clap = { workspace = true } env_logger = { workspace = true } @@ -28,8 +28,8 @@ derive_builder = { workspace = true } postcard = { workspace = true } # Problem in CI, unknown reasons: dependency (libc) specified without providing a local path, Git repository, version, or workspace dependency to use # Maybe something about the combination of features and optional crates? -rustix = { version = "0.38.42", optional = true } -libc = { version = "0.2", optional = true } +rustix = { workspace = true, optional = true } +libc = { workspace = true, optional = true } # Mio broker client mio = { workspace = true } From b5f6d076509af63b89aa7983a45703973f4b915a Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Wed, 12 Feb 2025 17:08:58 +0100 Subject: [PATCH 012/174] feat(docker): add .docker/Dockerfile, .docker/README.md and workflow building and publishing docker images --- .docker/Dockerfile | 75 +++++++++++ .docker/README.md | 236 ++++++++++++++++++++++++++++++++++ .dockerignore | 1 + .github/workflows/docker.yaml | 103 +++++++++++++++ 4 files changed, 415 insertions(+) create mode 100644 .docker/Dockerfile create mode 100644 .docker/README.md create mode 120000 .dockerignore create mode 100644 .github/workflows/docker.yaml diff --git a/.docker/Dockerfile b/.docker/Dockerfile new file mode 100644 index 00000000..b01727d3 --- /dev/null +++ b/.docker/Dockerfile @@ -0,0 +1,75 @@ +# 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: Annotate the base image with OCI Image Annotations, also install runtime-dependencies +FROM ${BASE_IMAGE} AS annotated_base_image + +RUN apt-get update && apt-get install -y iproute2 && rm -rf /var/lib/apt/lists/* + +ARG VERSION +ARG REF_NAME +ARG BUILD_DATE +ARG VCS_REF +ARG AUTHORS="Karolin Varner , wucke13 " +ARG URL="https://rosenpass.eu/" +ARG DOCUMENTATION="https://rosenpass.eu/docs/" +ARG SOURCE="https://github.com/rosenpass/rosenpass" +ARG VENDOR="Rosenpass e.V." +ARG LICENSES="MIT OR Apache-2.0" +ARG TITLE="Rosenpass" +ARG DESCRIPTION +ARG BASE_DIGEST +ARG BASE_IMAGE +LABEL org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.authors=${AUTHORS} \ + org.opencontainers.image.url=${URL} \ + org.opencontainers.image.documentation=${DOCUMENTATION} \ + org.opencontainers.image.source=${SOURCE} \ + org.opencontainers.image.version=${VERSION} \ + org.opencontainers.image.revision=${VCS_REF} \ + org.opencontainers.image.vendor=${VENDOR} \ + org.opencontainers.image.licenses=${LICENSES} \ + org.opencontainers.image.ref.name=${REF_NAME} \ + org.opencontainers.image.title=${TITLE} \ + org.opencontainers.image.description=${DESCRIPTION} \ + org.opencontainers.image.base.digest=${BASE_DIGEST} \ + org.opencontainers.image.base.name=${BASE_IMAGE} + + +# Final Stage (rosenpass): Copy the rosenpass binary +FROM annotated_base_image 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 annotated_base_image 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" ] diff --git a/.docker/README.md b/.docker/README.md new file mode 100644 index 00000000..7292f2c9 --- /dev/null +++ b/.docker/README.md @@ -0,0 +1,236 @@ +# Rosenpass + +Rosenpass is used to create post-quantum-secure VPNs. Rosenpass computes a shared key, [Wireguard](https://www.wireguard.com/papers/wireguard.pdf) uses the shared key to establish a secure connection. Rosenpass can also be used without WireGuard, deriving post-quantum-secure symmetric keys for another application. +The Rosenpass protocol builds on “Post-quantum WireGuard” ([PQWG](https://eprint.iacr.org/2020/379)) and improves it by using a cookie mechanism to provide security against state disruption attacks. + +The rosenpass tool is written in Rust and uses 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. + +As with any application a small risk of critical security issues (such as buffer overflows, remote code execution) exists; the Rosenpass application is written in the Rust programming language which is much less prone to such issues. Rosenpass can also write keys to files instead of supplying them to WireGuard With a bit of scripting the stand alone mode of the implementation can be used to run the application in a Container, VM or on another host. This mode can also be used to integrate tools other than WireGuard with Rosenpass. + +The `rp` tool written in Rust makes it easy to create a VPN using WireGuard and Rosenpass. + +`rp` is easy to get started with but has a few drawbacks; it runs as root, demanding access to both WireGuard +and Rosenpass private keys, takes control of the interface and works with exactly one interface. If you do not feel confident about running Rosenpass as root, you should use the stand-alone mode to create a more secure setup using containers, jails, or virtual machines. + +## Building the Docker Image + +Clone the Rosenpass repository: + +``` +git clone https://github.com/rosenpass/rosenpass +cd rosenpass +``` + +Use the `docker-buildscript.sh` script to build images from the source. + +```bash +bash docker-buildscript.sh +docker images + +| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE | +|------------------------------|------------------|----------------|-----------------|--------| +| ghcr.io/rosenpass/rp | commit-aeb0671 | dc2997662d2c | 9 hours ago | 93.2MB | +| ghcr.io/rosenpass/rosenpass | commit-aeb0671 | 65ccc5e5b9fb | 9 hours ago | 93.6MB | +``` + +Set environment variable `TAG_AS_RELEASE=true` to tag the built images with the current versions. + +Set environment variable `TAG_AS_LATEST=true` to tag the built images as latest. + +```bash +export TAG_AS_RELEASE=true +export TAG_AS_LATEST=true +bash docker-buildscript.sh +docker images + +| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE | +|-----------------------------|----------------|--------------|-------------|--------| +| ghcr.io/rosenpass/rp | 0.2.1 | 253338c948ab | 9 hours ago | 93.2MB | +| ghcr.io/rosenpass/rp | commit-05f0ac0 | 253338c948ab | 9 hours ago | 93.2MB | +| ghcr.io/rosenpass/rp | latest | 253338c948ab | 9 hours ago | 93.2MB | +| ghcr.io/rosenpass/rosenpass | 0.3.0-dev | 6958e24fd240 | 9 hours ago | 93.6MB | +| ghcr.io/rosenpass/rosenpass | commit-05f0ac0 | 6958e24fd240 | 9 hours ago | 93.6MB | +| ghcr.io/rosenpass/rosenpass | latest | 6958e24fd240 | 9 hours ago | 93.6MB | +``` + +## 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-sharedkey workdir/client-sharedkey +``` + +It is now possible to set add these keys as pre-shared keys within a wireguard interface. + +```bash +PREKEY=$(cat workdir/client-sharedkey) +wg set peer 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 workdir-client/client.rosenpass-public workdir-server/client.rosenpass-public +cp 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 +``` + +## Contributing + +The rosenpass project is maintained on [Github](https://github.com/rosenpass/rosenpass). + +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](https://github.com/rosenpass/rosenpass/blob/main/CONTRIBUTING.md). + +## Acknowledgements + +Funded through NLNet with financial support for the European Commission's NGI Assure program. diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 00000000..3e4e48b0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 00000000..e5cb759c --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,103 @@ +name: ci + +on: + push: + branches: + - 'main' + tags: + - 'v*' + pull_request: + branches: + - 'main' + +jobs: + docker-image-rp: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/utilacy/rp + tags: | + type=edge,branch=main + type=semver,pattern={{version}} + labels: | + maintainer=Karolin Varner , wucke13 + org.opencontainers.image.authors=Karolin Varner , wucke13 + 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.actor }} --password-stdin + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: .docker/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + target: rp + platforms: linux/amd64 #,linux/arm64,linux/arm/v7 + docker-image-rosenpass: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/utilacy/rosenpass + tags: | + type=edge,branch=main + type=semver,pattern={{version}} + labels: | + maintainer=Karolin Varner , wucke13 + org.opencontainers.image.authors=Karolin Varner , wucke13 + 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.actor }} --password-stdin + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: .docker/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + target: rosenpass + platforms: linux/amd64 #,linux/arm64,linux/arm/v7 From 43a930d3f7131197f51e144f7d1544237e166c81 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Fri, 21 Feb 2025 15:25:12 +0100 Subject: [PATCH 013/174] feat(docker): fix docker image names feat(docker): add tag based on commit hash, feat(docker): add arm64 platform for docker images --- .github/workflows/docker.yaml | 52 ++++++++++++++--------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index e5cb759c..c61376d8 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -3,28 +3,27 @@ name: ci on: push: branches: - - 'main' + - "main" tags: - - 'v*' + - "v*" pull_request: branches: - - 'main' + - "main" jobs: docker-image-rp: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v4 - - - name: Docker meta + - name: Docker meta id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/utilacy/rp + images: ghcr.io/rosenpass/rp tags: | type=edge,branch=main + type=sha,branch=main type=semver,pattern={{version}} labels: | maintainer=Karolin Varner , wucke13 @@ -36,17 +35,13 @@ jobs: 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 + - name: Log in to registry run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - - name: Set up QEMU + - name: Set up QEMU uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - - name: Build and push + - name: Build and push uses: docker/build-push-action@v6 with: context: . @@ -55,21 +50,20 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} target: rp - platforms: linux/amd64 #,linux/arm64,linux/arm/v7 + platforms: amd64,arm64 docker-image-rosenpass: runs-on: ubuntu-latest steps: - - - name: Checkout + - name: Checkout uses: actions/checkout@v4 - - - name: Docker meta + - name: Docker meta id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/utilacy/rosenpass + images: ghcr.io/rosenpass/rosenpass tags: | type=edge,branch=main + type=sha,branch=main type=semver,pattern={{version}} labels: | maintainer=Karolin Varner , wucke13 @@ -81,17 +75,13 @@ jobs: 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 + - name: Log in to registry run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - - - name: Set up QEMU + - name: Set up QEMU uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - - name: Build and push + - name: Build and push uses: docker/build-push-action@v6 with: context: . @@ -100,4 +90,4 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} target: rosenpass - platforms: linux/amd64 #,linux/arm64,linux/arm/v7 + platforms: amd64,arm64 From 88e7d1d1cb4a85e7a5f6c5df8757a9a2dc6e66cb Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 11:29:48 +0100 Subject: [PATCH 014/174] 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 --- .docker/Dockerfile | 38 ++------------ .docker/{README.md => USAGE.md} | 89 +++++++++++---------------------- .github/workflows/docker.yaml | 22 +++++--- readme.md | 9 ++++ 4 files changed, 56 insertions(+), 102 deletions(-) rename .docker/{README.md => USAGE.md} (56%) diff --git a/.docker/Dockerfile b/.docker/Dockerfile index b01727d3..838c2d99 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -26,48 +26,18 @@ WORKDIR /app COPY . . RUN cargo build --release -# Stage 5: Annotate the base image with OCI Image Annotations, also install runtime-dependencies -FROM ${BASE_IMAGE} AS annotated_base_image +# 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/* -ARG VERSION -ARG REF_NAME -ARG BUILD_DATE -ARG VCS_REF -ARG AUTHORS="Karolin Varner , wucke13 " -ARG URL="https://rosenpass.eu/" -ARG DOCUMENTATION="https://rosenpass.eu/docs/" -ARG SOURCE="https://github.com/rosenpass/rosenpass" -ARG VENDOR="Rosenpass e.V." -ARG LICENSES="MIT OR Apache-2.0" -ARG TITLE="Rosenpass" -ARG DESCRIPTION -ARG BASE_DIGEST -ARG BASE_IMAGE -LABEL org.opencontainers.image.created=${BUILD_DATE} \ - org.opencontainers.image.authors=${AUTHORS} \ - org.opencontainers.image.url=${URL} \ - org.opencontainers.image.documentation=${DOCUMENTATION} \ - org.opencontainers.image.source=${SOURCE} \ - org.opencontainers.image.version=${VERSION} \ - org.opencontainers.image.revision=${VCS_REF} \ - org.opencontainers.image.vendor=${VENDOR} \ - org.opencontainers.image.licenses=${LICENSES} \ - org.opencontainers.image.ref.name=${REF_NAME} \ - org.opencontainers.image.title=${TITLE} \ - org.opencontainers.image.description=${DESCRIPTION} \ - org.opencontainers.image.base.digest=${BASE_DIGEST} \ - org.opencontainers.image.base.name=${BASE_IMAGE} - - # Final Stage (rosenpass): Copy the rosenpass binary -FROM annotated_base_image AS rosenpass +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 annotated_base_image AS rp +FROM base_image_with_dependencies AS rp RUN apt-get update && apt-get install -y wireguard && rm -rf /var/lib/apt/lists/* diff --git a/.docker/README.md b/.docker/USAGE.md similarity index 56% rename from .docker/README.md rename to .docker/USAGE.md index 7292f2c9..8999051d 100644 --- a/.docker/README.md +++ b/.docker/USAGE.md @@ -1,57 +1,14 @@ -# Rosenpass +# Rosenpass in Docker -Rosenpass is used to create post-quantum-secure VPNs. Rosenpass computes a shared key, [Wireguard](https://www.wireguard.com/papers/wireguard.pdf) uses the shared key to establish a secure connection. Rosenpass can also be used without WireGuard, deriving post-quantum-secure symmetric keys for another application. -The Rosenpass protocol builds on “Post-quantum WireGuard” ([PQWG](https://eprint.iacr.org/2020/379)) and improves it by using a cookie mechanism to provide security against state disruption attacks. +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. -The rosenpass tool is written in Rust and uses 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. +Prebuilt Docker images are available for easy deployment: -As with any application a small risk of critical security issues (such as buffer overflows, remote code execution) exists; the Rosenpass application is written in the Rust programming language which is much less prone to such issues. Rosenpass can also write keys to files instead of supplying them to WireGuard With a bit of scripting the stand alone mode of the implementation can be used to run the application in a Container, VM or on another host. This mode can also be used to integrate tools other than WireGuard with Rosenpass. +- [`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 `rp` tool written in Rust makes it easy to create a VPN using WireGuard and Rosenpass. - -`rp` is easy to get started with but has a few drawbacks; it runs as root, demanding access to both WireGuard -and Rosenpass private keys, takes control of the interface and works with exactly one interface. If you do not feel confident about running Rosenpass as root, you should use the stand-alone mode to create a more secure setup using containers, jails, or virtual machines. - -## Building the Docker Image - -Clone the Rosenpass repository: - -``` -git clone https://github.com/rosenpass/rosenpass -cd rosenpass -``` - -Use the `docker-buildscript.sh` script to build images from the source. - -```bash -bash docker-buildscript.sh -docker images - -| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE | -|------------------------------|------------------|----------------|-----------------|--------| -| ghcr.io/rosenpass/rp | commit-aeb0671 | dc2997662d2c | 9 hours ago | 93.2MB | -| ghcr.io/rosenpass/rosenpass | commit-aeb0671 | 65ccc5e5b9fb | 9 hours ago | 93.6MB | -``` - -Set environment variable `TAG_AS_RELEASE=true` to tag the built images with the current versions. - -Set environment variable `TAG_AS_LATEST=true` to tag the built images as latest. - -```bash -export TAG_AS_RELEASE=true -export TAG_AS_LATEST=true -bash docker-buildscript.sh -docker images - -| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE | -|-----------------------------|----------------|--------------|-------------|--------| -| ghcr.io/rosenpass/rp | 0.2.1 | 253338c948ab | 9 hours ago | 93.2MB | -| ghcr.io/rosenpass/rp | commit-05f0ac0 | 253338c948ab | 9 hours ago | 93.2MB | -| ghcr.io/rosenpass/rp | latest | 253338c948ab | 9 hours ago | 93.2MB | -| ghcr.io/rosenpass/rosenpass | 0.3.0-dev | 6958e24fd240 | 9 hours ago | 93.6MB | -| ghcr.io/rosenpass/rosenpass | commit-05f0ac0 | 6958e24fd240 | 9 hours ago | 93.6MB | -| ghcr.io/rosenpass/rosenpass | latest | 6958e24fd240 | 9 hours ago | 93.6MB | -``` +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 @@ -116,14 +73,15 @@ Now the containers will exchange shared keys and each put them into their respec Comparing the outfiles shows that these shared keys equal: ```bash -cmp workdir/server-sharedkey workdir/client-sharedkey +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/client-sharedkey) -wg set peer preshared-key <(echo "$PREKEY") +PREKEY=$(cat workdir-server/server-sharedkey) +wg set peer preshared-key <(echo "$PREKEY") ``` ## Usage - Combined with wireguard @@ -158,8 +116,8 @@ 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 workdir-client/client.rosenpass-public workdir-server/client.rosenpass-public -cp workdir-server/server.rosenpass-public workdir-client/server.rosenpass-public +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. @@ -223,14 +181,23 @@ While the ping is running, you may stop the server container, and verify that th docker stop -t 1 rpserver ``` -## Contributing +## Building the Docker Images Locally -The rosenpass project is maintained on [Github](https://github.com/rosenpass/rosenpass). +Clone the Rosenpass repository: -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. +``` +git clone https://github.com/rosenpass/rosenpass +cd rosenpass +``` -We also have a – as of now, very minimal – [contributors guide](https://github.com/rosenpass/rosenpass/blob/main/CONTRIBUTING.md). +Build the rp image from the root of the repository as follows: -## Acknowledgements +``` +docker build -f .docker/Dockerfile -t ghcr.io/rosenpass/rp --target rp . +``` -Funded through NLNet with financial support for the European Commission's NGI Assure program. +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 . +``` diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index c61376d8..88bf15b0 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -1,4 +1,4 @@ -name: ci +name: Build Docker Images on: push: @@ -12,7 +12,12 @@ on: jobs: docker-image-rp: - runs-on: ubuntu-latest + # 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-latest-arm64' || 'ubuntu-latest' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -37,8 +42,6 @@ jobs: 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.actor }} --password-stdin - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push @@ -50,9 +53,14 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} target: rp - platforms: amd64,arm64 + platforms: linux/${{ matrix.arch }} docker-image-rosenpass: - runs-on: ubuntu-latest + # 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-latest-arm64' || 'ubuntu-latest' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -90,4 +98,4 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} target: rosenpass - platforms: amd64,arm64 + platforms: linux/${{ matrix.arch }} diff --git a/readme.md b/readme.md index 8975ce5e..bfaf7ff8 100644 --- a/readme.md +++ b/readme.md @@ -78,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: From 5e2c72ef99dfbdd583c922e00fc229a99cc35e37 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 12:19:45 +0100 Subject: [PATCH 015/174] feat(docker): add integration test to the build docker images workflow --- .github/workflows/docker.yaml | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 88bf15b0..bbba2fb6 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -11,7 +11,95 @@ on: - "main" jobs: + # -------------------------------- + # 1. BUILD & TEST + # -------------------------------- + build-and-test-rp: + strategy: + matrix: + arch: [amd64, arm64] + runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-latest-arm64' || '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: rp + tags: rp: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 \ + rp: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 \ + rp: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 \ + rp: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 --rm \ + --name rpclient \ + --network rp \ + -v $PWD/workdir-client:/workdir \ + rp: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 ===" + 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 # Use a matrix to build for both AMD64 and ARM64 strategy: matrix: @@ -55,6 +143,7 @@ jobs: target: rp platforms: linux/${{ matrix.arch }} docker-image-rosenpass: + needs: build-and-test-rp # Use a matrix to build for both AMD64 and ARM64 strategy: matrix: From f81e329a110744add772fad0b3e2c0d6906a7629 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 12:33:29 +0100 Subject: [PATCH 016/174] feat(docker): fix the integration test workflow --- .github/workflows/docker.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index bbba2fb6..61c61d3c 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -34,8 +34,8 @@ jobs: push: false # load the built image into the local Docker daemon on the runner load: true - target: rp - tags: rp:test + target: rosenpass + tags: rosenpass:test platforms: linux/${{ matrix.arch }} - name: Integration Test - Standalone Key Exchange run: | @@ -48,14 +48,14 @@ jobs: echo "=== GENERATE SERVER KEYS ===" docker run --rm \ -v $PWD/workdir-server:/workdir \ - rp:test gen-keys \ + 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 \ - rp:test gen-keys \ + rosenpass:test gen-keys \ --public-key=workdir/client-public \ --secret-key=workdir/client-secret @@ -68,7 +68,7 @@ jobs: --name rpserver \ --network rp \ -v $PWD/workdir-server:/workdir \ - rp:test exchange \ + rosenpass:test exchange \ private-key workdir/server-secret \ public-key workdir/server-public \ listen 0.0.0.0:9999 \ @@ -84,7 +84,7 @@ jobs: --name rpclient \ --network rp \ -v $PWD/workdir-client:/workdir \ - rp:test exchange \ + rosenpass:test exchange \ private-key workdir/client-secret \ public-key workdir/client-public \ peer public-key workdir/server-public \ From af9d83b472849daf7e4c1498f30f0d8173a13ab3 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 12:56:30 +0100 Subject: [PATCH 017/174] feat(docker): change the docker integration test workflow to wait until the shared key file is generated --- .github/workflows/docker.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 61c61d3c..bd075ce0 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -80,7 +80,7 @@ jobs: echo "SERVER_IP=$SERVER_IP" echo "=== START CLIENT CONTAINER ===" - docker run --rm \ + docker run -d --rm \ --name rpclient \ --network rp \ -v $PWD/workdir-client:/workdir \ @@ -92,6 +92,14 @@ jobs: 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 cmp workdir-server/server-sharedkey workdir-client/client-sharedkey echo "Standalone Key Exchange test OK." From b8ecdab8dc68a9276e81fe9327fa91363cce3059 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 13:03:56 +0100 Subject: [PATCH 018/174] feat(docker): docker build workflow integration test now compares the resulting key with sudo --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index bd075ce0..df76bfa2 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -100,7 +100,7 @@ jobs: fi sleep 1 done - cmp workdir-server/server-sharedkey workdir-client/client-sharedkey + sudo cmp workdir-server/server-sharedkey workdir-client/client-sharedkey echo "Standalone Key Exchange test OK." # -------------------------------- From 45a7c17cdd1359d3c3acfd3f57e2d775623b7b48 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 16:22:29 +0100 Subject: [PATCH 019/174] feat(docker): fix runs on designation to ubuntu-24.04-arm --- .github/workflows/docker.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index df76bfa2..d81fb8c0 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: arch: [amd64, arm64] - runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-latest-arm64' || 'ubuntu-latest' }} + runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -113,7 +113,7 @@ jobs: matrix: arch: [amd64, arm64] # Switch the runner based on the architecture - runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-latest-arm64' || 'ubuntu-latest' }} + runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} steps: - name: Checkout uses: actions/checkout@v4 @@ -157,7 +157,7 @@ jobs: matrix: arch: [amd64, arm64] # Switch the runner based on the architecture - runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-latest-arm64' || 'ubuntu-latest' }} + runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} steps: - name: Checkout uses: actions/checkout@v4 From 69538622b4272e4ba266594c491469129f860efe Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Tue, 25 Feb 2025 16:45:19 +0100 Subject: [PATCH 020/174] feat(docker): remove qemu from the second build and push job in the docker build workflow --- .github/workflows/docker.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index d81fb8c0..b2e11ab6 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -182,8 +182,6 @@ jobs: 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.actor }} --password-stdin - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push From 8e41cfc0b453209851abfcdc232421361acdb2ef Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Wed, 26 Feb 2025 00:05:37 +0100 Subject: [PATCH 021/174] feat(docker): remove stray quote, check if docker related files changes before running workflow --- .github/workflows/docker.yaml | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index b2e11ab6..6678d70b 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -11,10 +11,32 @@ on: - "main" jobs: + # ---------------------------------------- + # 1. Check if .docker/Dockerfile or .github/workflows/docker.yaml changed + # ---------------------------------------- + check-dockerfile-changes: + runs-on: ubuntu-latest + outputs: + docker_files_changed: ${{ steps.filter.outputs.docker_files_changed }} + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Paths filter + id: filter + uses: dorny/paths-filter@v2 + with: + filters: | + docker_files_changed: + - '.docker/Dockerfile' + - '.github/workflows/docker.yaml' # -------------------------------- - # 1. BUILD & TEST + # 2. BUILD & TEST # -------------------------------- build-and-test-rp: + # Run this job on all non-pull-request events, + # or if Docker-related files are changed in a pull request. + if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' }} strategy: matrix: arch: [amd64, arm64] @@ -104,9 +126,11 @@ jobs: echo "Standalone Key Exchange test OK." # -------------------------------- - # 2. PUSH (only if tests pass) + # 3. PUSH (only if tests pass) # -------------------------------- docker-image-rp: + # Skip if this is not a PR. Then we want to push this image + if: ${{ github.event_name != 'pull_request' }} needs: build-and-test-rp # Use a matrix to build for both AMD64 and ARM64 strategy: @@ -132,7 +156,7 @@ jobs: 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.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 @@ -151,6 +175,9 @@ jobs: target: rp platforms: linux/${{ matrix.arch }} docker-image-rosenpass: + # Run this job on all non-pull-request events, + # or if Docker-related files are changed in a pull request. + if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' || github.event_name != 'pull_request' }} needs: build-and-test-rp # Use a matrix to build for both AMD64 and ARM64 strategy: @@ -176,7 +203,7 @@ jobs: 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.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 From 43225c1fe8ecda18dcdfe6ce321d180ba796151b Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Wed, 26 Feb 2025 09:15:38 +0100 Subject: [PATCH 022/174] feat(docker): fix docker build workflow conditional checks --- .github/workflows/docker.yaml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 6678d70b..7e09363e 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -17,26 +17,28 @@ jobs: check-dockerfile-changes: runs-on: ubuntu-latest outputs: - docker_files_changed: ${{ steps.filter.outputs.docker_files_changed }} + docker_files_changed: ${{ steps.filter.outputs.dockerfile || steps.filter.outputs.dockerbuild_workflow }} steps: - name: Check out repository uses: actions/checkout@v4 - name: Paths filter id: filter - uses: dorny/paths-filter@v2 + uses: dorny/paths-filter@v3 with: filters: | - docker_files_changed: + dockerfile: - '.docker/Dockerfile' + dockerbuild_workflow: - '.github/workflows/docker.yaml' # -------------------------------- # 2. BUILD & TEST # -------------------------------- build-and-test-rp: + needs: check-dockerfile-changes # Run this job on all non-pull-request events, # or if Docker-related files are changed in a pull request. - if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' }} + if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' || github.event_name != 'pull_request' }} strategy: matrix: arch: [amd64, arm64] @@ -129,9 +131,11 @@ jobs: # 3. PUSH (only if tests pass) # -------------------------------- docker-image-rp: - # Skip if this is not a PR. Then we want to push this image + needs: + - build-and-test-rp + - check-dockerfile-changes + # Skip if this is not a PR. Then we want to push this image. if: ${{ github.event_name != 'pull_request' }} - needs: build-and-test-rp # Use a matrix to build for both AMD64 and ARM64 strategy: matrix: @@ -175,10 +179,12 @@ jobs: target: rp platforms: linux/${{ matrix.arch }} docker-image-rosenpass: + needs: + - build-and-test-rp + - check-dockerfile-changes # Run this job on all non-pull-request events, # or if Docker-related files are changed in a pull request. if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' || github.event_name != 'pull_request' }} - needs: build-and-test-rp # Use a matrix to build for both AMD64 and ARM64 strategy: matrix: From 09f1353dccd69a5daf46c38fb428565dfdbdaa24 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Wed, 26 Feb 2025 15:44:05 +0100 Subject: [PATCH 023/174] feat(docker): rename .docker to docker --- .github/workflows/docker.yaml | 10 +++++----- {.docker => docker}/Dockerfile | 0 {.docker => docker}/USAGE.md | 4 ++-- readme.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename {.docker => docker}/Dockerfile (100%) rename {.docker => docker}/USAGE.md (97%) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 7e09363e..df07e413 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -12,7 +12,7 @@ on: jobs: # ---------------------------------------- - # 1. Check if .docker/Dockerfile or .github/workflows/docker.yaml changed + # 1. Check if docker/Dockerfile or .github/workflows/docker.yaml changed # ---------------------------------------- check-dockerfile-changes: runs-on: ubuntu-latest @@ -28,7 +28,7 @@ jobs: with: filters: | dockerfile: - - '.docker/Dockerfile' + - 'docker/Dockerfile' dockerbuild_workflow: - '.github/workflows/docker.yaml' # -------------------------------- @@ -53,7 +53,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: .docker/Dockerfile + 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 @@ -172,7 +172,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: .docker/Dockerfile + file: docker/Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -221,7 +221,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: .docker/Dockerfile + file: docker/Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.docker/Dockerfile b/docker/Dockerfile similarity index 100% rename from .docker/Dockerfile rename to docker/Dockerfile diff --git a/.docker/USAGE.md b/docker/USAGE.md similarity index 97% rename from .docker/USAGE.md rename to docker/USAGE.md index 8999051d..5be4409c 100644 --- a/.docker/USAGE.md +++ b/docker/USAGE.md @@ -193,11 +193,11 @@ 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 . +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 . +docker build -f docker/Dockerfile -t ghcr.io/rosenpass/rosenpass --target rosenpass . ``` diff --git a/readme.md b/readme.md index bfaf7ff8..aa4c8814 100644 --- a/readme.md +++ b/readme.md @@ -85,7 +85,7 @@ 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). +For details on how to use these images, refer to the [Docker usage guide](docker/USAGE.md). # Mirrors From cbc1bb4be246095dd22ce5ba3a06003305e640a4 Mon Sep 17 00:00:00 2001 From: Amin Faez Date: Wed, 26 Feb 2025 16:41:55 +0100 Subject: [PATCH 024/174] feat(docker): change write permission on docker build workflow and fix its change filter --- .github/workflows/docker.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index df07e413..a646babd 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -10,6 +10,10 @@ on: branches: - "main" +permissions: + contents: read + packages: write + jobs: # ---------------------------------------- # 1. Check if docker/Dockerfile or .github/workflows/docker.yaml changed @@ -17,7 +21,7 @@ jobs: check-dockerfile-changes: runs-on: ubuntu-latest outputs: - docker_files_changed: ${{ steps.filter.outputs.dockerfile || steps.filter.outputs.dockerbuild_workflow }} + docker_files_changed: ${{ steps.filter.outputs.docker_files_changed }} steps: - name: Check out repository uses: actions/checkout@v4 @@ -27,9 +31,8 @@ jobs: uses: dorny/paths-filter@v3 with: filters: | - dockerfile: + docker_files_changed: - 'docker/Dockerfile' - dockerbuild_workflow: - '.github/workflows/docker.yaml' # -------------------------------- # 2. BUILD & TEST From 76d01ffaf97bfa196d80931ae391342abc73ca89 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Wed, 5 Mar 2025 10:29:55 +0100 Subject: [PATCH 025/174] 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 Signed-off-by: Paul Spooren --- .github/workflows/docker.yaml | 39 ++++++++--------------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index a646babd..2171cc9e 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -1,5 +1,7 @@ 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: @@ -7,6 +9,9 @@ on: tags: - "v*" pull_request: + paths: + - "docker/Dockerfile" + - ".github/workflows/docker.yaml" branches: - "main" @@ -15,33 +20,10 @@ permissions: packages: write jobs: - # ---------------------------------------- - # 1. Check if docker/Dockerfile or .github/workflows/docker.yaml changed - # ---------------------------------------- - check-dockerfile-changes: - runs-on: ubuntu-latest - outputs: - docker_files_changed: ${{ steps.filter.outputs.docker_files_changed }} - steps: - - name: Check out repository - uses: actions/checkout@v4 - - - name: Paths filter - id: filter - uses: dorny/paths-filter@v3 - with: - filters: | - docker_files_changed: - - 'docker/Dockerfile' - - '.github/workflows/docker.yaml' # -------------------------------- - # 2. BUILD & TEST + # 1. BUILD & TEST # -------------------------------- build-and-test-rp: - needs: check-dockerfile-changes - # Run this job on all non-pull-request events, - # or if Docker-related files are changed in a pull request. - if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' || github.event_name != 'pull_request' }} strategy: matrix: arch: [amd64, arm64] @@ -131,12 +113,11 @@ jobs: echo "Standalone Key Exchange test OK." # -------------------------------- - # 3. PUSH (only if tests pass) + # 2. PUSH (only if tests pass) # -------------------------------- docker-image-rp: needs: - build-and-test-rp - - check-dockerfile-changes # 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 @@ -184,10 +165,8 @@ jobs: docker-image-rosenpass: needs: - build-and-test-rp - - check-dockerfile-changes - # Run this job on all non-pull-request events, - # or if Docker-related files are changed in a pull request. - if: ${{ needs.check-dockerfile-changes.outputs.docker_files_changed == 'true' || github.event_name != 'pull_request' }} + # 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: From 62fe529d36efec61394d0351839135cfe9086a11 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Tue, 11 Mar 2025 14:49:53 +0100 Subject: [PATCH 026/174] ci(docker): Merge multi-platform job 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 --- .github/workflows/docker.yaml | 105 +++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 2171cc9e..5daf4517 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -129,15 +129,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Docker meta id: meta + uses: docker/metadata-action@v5 with: - images: ghcr.io/rosenpass/rp - tags: | - type=edge,branch=main - type=sha,branch=main - type=semver,pattern={{version}} + images: ghcr.io/${{ github.actor }}/rp labels: | maintainer=Karolin Varner , wucke13 org.opencontainers.image.authors=Karolin Varner , wucke13 @@ -148,20 +146,40 @@ jobs: 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.actor }} --password-stdin + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push + + - 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' }} - tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + tags: ghcr.io/${{ github.actor }}/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 @@ -176,15 +194,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Docker meta id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/rosenpass/rosenpass - tags: | - type=edge,branch=main - type=sha,branch=main - type=semver,pattern={{version}} + images: ghcr.io/${{ github.actor }}/rosenpass labels: | maintainer=Karolin Varner , wucke13 org.opencontainers.image.authors=Karolin Varner , wucke13 @@ -195,17 +210,79 @@ jobs: 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.actor }} --password-stdin + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push + + - 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' }} - tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + tags: ghcr.io/${{ github.actor }}/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.actor }} --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.actor }}/${{ 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.actor }}/${{ matrix.target }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ghcr.io/${{ github.actor }}/${{ matrix.target }}:${{ steps.meta.outputs.version }} From b36d30d89d85c6f5860207634f622dda236b7286 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 10:57:11 +0100 Subject: [PATCH 027/174] dev(cipher-traits): add KeyedHash(Instance) traits --- cipher-traits/src/keyed_hash.rs | 13 +++++++++++++ cipher-traits/src/lib.rs | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 cipher-traits/src/keyed_hash.rs diff --git a/cipher-traits/src/keyed_hash.rs b/cipher-traits/src/keyed_hash.rs new file mode 100644 index 00000000..dcc0e953 --- /dev/null +++ b/cipher-traits/src/keyed_hash.rs @@ -0,0 +1,13 @@ +pub trait KeyedHash { + type Error; + + fn keyed_hash(key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error>; +} + +pub trait KeyedHashInstance { + type KeyType; + type OutputType; + type Error; + + fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error>; +} \ No newline at end of file diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs index 99369d9b..f6059d93 100644 --- a/cipher-traits/src/lib.rs +++ b/cipher-traits/src/lib.rs @@ -1,2 +1,4 @@ mod kem; +mod keyed_hash; pub use kem::Kem; +pub use keyed_hash::*; From ac3f21c4bd271717f1f6d1bb3a3bf96ae8979442 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:19:01 +0100 Subject: [PATCH 028/174] dev: add sha3 dependency --- Cargo.lock | 20 ++++++++++++++++++++ Cargo.toml | 1 + ciphers/Cargo.toml | 1 + 3 files changed, 22 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 4364fa0e..1e1c0135 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1147,6 +1147,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1834,6 +1843,7 @@ dependencies = [ "rosenpass-secret-memory", "rosenpass-to", "rosenpass-util", + "sha3", "static_assertions", "zeroize", ] @@ -2134,6 +2144,16 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "shlex" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 56b76e5c..e14a00fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ oqs-sys = { version = "0.9.1", default-features = false, features = [ 'kyber', ] } blake2 = "0.10.6" +sha3 = "0.10.8" chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless", diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 9e5312d3..df6ca343 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -24,3 +24,4 @@ zeroize = { workspace = true } chacha20poly1305 = { workspace = true } blake2 = { workspace = true } libcrux = { workspace = true, optional = true } +sha3 = {workspace = true} From 5a2555a327d7cfd7c6e16908658fccbcc0b9888d Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 11:31:02 +0100 Subject: [PATCH 029/174] dev(ciphers): add implementation of shake256 --- Cargo.lock | 1 + ciphers/Cargo.toml | 1 + .../subtle/hash_functions/keyed_shake256.rs | 65 +++++++++++++++++++ ciphers/src/subtle/hash_functions/mod.rs | 1 + ciphers/src/subtle/mod.rs | 3 + 5 files changed, 71 insertions(+) create mode 100644 ciphers/src/subtle/hash_functions/keyed_shake256.rs create mode 100644 ciphers/src/subtle/hash_functions/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 1e1c0135..4e6ec911 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1838,6 +1838,7 @@ dependencies = [ "blake2", "chacha20poly1305", "libcrux", + "rosenpass-cipher-traits", "rosenpass-constant-time", "rosenpass-oqs", "rosenpass-secret-memory", diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index df6ca343..d794f373 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -19,6 +19,7 @@ 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 } diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/hash_functions/keyed_shake256.rs new file mode 100644 index 00000000..255d38bd --- /dev/null +++ b/ciphers/src/subtle/hash_functions/keyed_shake256.rs @@ -0,0 +1,65 @@ +use anyhow::ensure; +use sha3::digest::{ExtendableOutput, Update, XofReader}; +use sha3::Shake256; +use rosenpass_cipher_traits::KeyedHash; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SHAKE256Core; + +impl KeyedHash for SHAKE256Core { + type Error = anyhow::Error; + + /// TODO: Rework test + /// 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. + /// + /// TODO: Example/Test + /// #Examples + /// ```rust + /// # use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; + /// use rosenpass_cipher_traits::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] = &[44, 102, 248, 251, 141, 145, 55, 194, 165, 228, 156, 42, 220, + /// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87]; + /// # 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); + + // Following the NIST recommendations in Section A.2 of the FIPS 202 standard, + // (pages 24/25, i.e., 32/33 in the PDF) we append the length of the input to the end of + // the input. This prevents that if the same input is used with two different output lengths, + // the shorter output is a prefix of the longer output. See the Section A.2 of the FIPS 202 + // standard for more details. + // TODO: Explain why we do not include the length here + // shake256.update(&((HASH_LEN as u8).to_le_bytes())); + shake256.finalize_xof().read(out); + Ok(()) + } +} + +impl SHAKE256Core { + pub fn new() -> Self { + Self + } +} diff --git a/ciphers/src/subtle/hash_functions/mod.rs b/ciphers/src/subtle/hash_functions/mod.rs new file mode 100644 index 00000000..328de652 --- /dev/null +++ b/ciphers/src/subtle/hash_functions/mod.rs @@ -0,0 +1 @@ +pub mod keyed_shake256; \ No newline at end of file diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index 7e9431b7..93bd0947 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -11,3 +11,6 @@ pub mod chacha20poly1305_ietf; pub mod chacha20poly1305_ietf_libcrux; pub mod incorrect_hmac_blake2b; pub mod xchacha20poly1305_ietf; +pub mod hash_functions; + +pub use hash_functions::{keyed_shake256}; \ No newline at end of file From b96df1588c89b47dbdbd347bbbdca35a91931b9d Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:24:16 +0100 Subject: [PATCH 030/174] dev(ciphers): add InferredKeyedHash to instantiate KeyedHashFunctions generically --- .../subtle/hash_functions/infer_keyed_hash.rs | 82 +++++++++++++++++++ ciphers/src/subtle/hash_functions/mod.rs | 3 +- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 ciphers/src/subtle/hash_functions/infer_keyed_hash.rs diff --git a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs new file mode 100644 index 00000000..4422881d --- /dev/null +++ b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs @@ -0,0 +1,82 @@ +use std::marker::PhantomData; +use anyhow::Result; +use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; + +/// 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 +where + Static: KeyedHash, +{ + pub _phantom_keyed_hasher: PhantomData<*const Static>, +} + +impl InferKeyedHash +where + Static: KeyedHash, +{ + 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::keyed_hash(key, data, out) + } + + pub const fn key_len(self) -> usize { + Self::KEY_LEN + } + + pub const fn hash_len(self) -> usize { + Self::HASH_LEN + } +} + +impl> KeyedHashInstance for InferKeyedHash { + type KeyType = [u8; KEY_LEN]; + type OutputType = [u8; HASH_LEN]; + type Error = anyhow::Error; + + fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<()> { + self.keyed_hash_internal(key, data, out) + } +} + +/// Helper traits ///////////////////////////////////////////// + +impl Default for InferKeyedHash +where + Static: KeyedHash, +{ + fn default() -> Self { + Self::new() + } +} + +impl Clone for InferKeyedHash +where + Static: KeyedHash, +{ + fn clone(&self) -> Self { + Self::new() + } +} + +impl Copy for InferKeyedHash +where + Static: KeyedHash, +{ +} diff --git a/ciphers/src/subtle/hash_functions/mod.rs b/ciphers/src/subtle/hash_functions/mod.rs index 328de652..ac1404ed 100644 --- a/ciphers/src/subtle/hash_functions/mod.rs +++ b/ciphers/src/subtle/hash_functions/mod.rs @@ -1 +1,2 @@ -pub mod keyed_shake256; \ No newline at end of file +pub mod keyed_shake256; +mod infer_keyed_hash; \ No newline at end of file From 530f81b9d5f7653ce560a1a4bd092728f2e3d1f8 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:31:53 +0100 Subject: [PATCH 031/174] dev(ciphers): use InferredHash to provide KeyedHashInstance for SHAKE256 --- ciphers/src/subtle/hash_functions/keyed_shake256.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/hash_functions/keyed_shake256.rs index 255d38bd..2a072b39 100644 --- a/ciphers/src/subtle/hash_functions/keyed_shake256.rs +++ b/ciphers/src/subtle/hash_functions/keyed_shake256.rs @@ -2,6 +2,7 @@ use anyhow::ensure; use sha3::digest::{ExtendableOutput, Update, XofReader}; use sha3::Shake256; use rosenpass_cipher_traits::KeyedHash; +use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; #[derive(Clone, Debug, PartialEq, Eq)] pub struct SHAKE256Core; @@ -63,3 +64,9 @@ impl SHAKE256Core = +InferKeyedHash, KEY_LEN, HASH_LEN>; + +pub type SHAKE256_32 = SHAKE256<32, 32>; + From 6a9bbddde3e4f96f50b708a694967652cd2ee919 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:55:22 +0100 Subject: [PATCH 032/174] dev(ciphers): move blake2b.rs and incorrect_hmac_blake2b.rs to dedicated hash_functions directory --- .../src/subtle/{ => hash_functions}/blake2b.rs | 1 + .../incorrect_hmac_blake2b.rs | 18 ++++++++++++++++-- ciphers/src/subtle/hash_functions/mod.rs | 2 ++ ciphers/src/subtle/mod.rs | 6 ++---- 4 files changed, 21 insertions(+), 6 deletions(-) rename ciphers/src/subtle/{ => hash_functions}/blake2b.rs (99%) rename ciphers/src/subtle/{ => hash_functions}/incorrect_hmac_blake2b.rs (83%) diff --git a/ciphers/src/subtle/blake2b.rs b/ciphers/src/subtle/hash_functions/blake2b.rs similarity index 99% rename from ciphers/src/subtle/blake2b.rs rename to ciphers/src/subtle/hash_functions/blake2b.rs index 26dbf257..13e75325 100644 --- a/ciphers/src/subtle/blake2b.rs +++ b/ciphers/src/subtle/hash_functions/blake2b.rs @@ -33,6 +33,7 @@ pub const OUT_MAX: usize = OUT_LEN; /// 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). /// +/// TODO: Adapt example /// # Examples /// ///```rust diff --git a/ciphers/src/subtle/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs similarity index 83% rename from ciphers/src/subtle/incorrect_hmac_blake2b.rs rename to ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs index 0bc72cf0..343945cb 100644 --- a/ciphers/src/subtle/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs @@ -1,10 +1,11 @@ use anyhow::ensure; use zeroize::Zeroizing; - +use rosenpass_cipher_traits::KeyedHash; use rosenpass_constant_time::xor; use rosenpass_to::{ops::copy_slice, with_destination, To}; -use crate::subtle::blake2b; +use crate::subtle::hash_functions::blake2b; +use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; /// The key length, 32 bytes or 256 bits. pub const KEY_LEN: usize = 32; @@ -65,3 +66,16 @@ pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<( Ok(()) }) } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Blake2bCore; + +impl KeyedHash<32, 32> for Blake2bCore { + type Error = anyhow::Error; + + fn keyed_hash(key: &[u8; 32], data: &[u8], out: &mut [u8; 32]) -> Result<(), Self::Error> { + hash(key, data).to(out) + } +} + +pub type Blake2b = InferKeyedHash; diff --git a/ciphers/src/subtle/hash_functions/mod.rs b/ciphers/src/subtle/hash_functions/mod.rs index ac1404ed..af0ccb7b 100644 --- a/ciphers/src/subtle/hash_functions/mod.rs +++ b/ciphers/src/subtle/hash_functions/mod.rs @@ -1,2 +1,4 @@ +pub mod blake2b; +pub mod incorrect_hmac_blake2b; pub mod keyed_shake256; mod infer_keyed_hash; \ No newline at end of file diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index 93bd0947..e5f6947f 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -4,13 +4,11 @@ /// - [chacha20poly1305_ietf_libcrux]: The Chacha20Poly1305 AEAD as implemented in [libcrux](https://github.com/cryspen/libcrux) (only used when the feature `experiment_libcrux` is enabled). /// - [incorrect_hmac_blake2b]: An (incorrect) hmac based on [blake2b]. /// - [xchacha20poly1305_ietf] The Chacha20Poly1305 AEAD as implemented in [RustCrypto](https://crates.io/crates/chacha20poly1305) -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; -pub mod hash_functions; +mod hash_functions; -pub use hash_functions::{keyed_shake256}; \ No newline at end of file +pub use hash_functions::{blake2b, incorrect_hmac_blake2b, keyed_shake256}; \ No newline at end of file From 760ecdc457ea2e632454d26cdc994d12627aaf1a Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:19:56 +0100 Subject: [PATCH 033/174] dev(ciphers): add EitherHash enum and thus the functionality for choosing a hash function at runtime --- ciphers/src/subtle/either_hash.rs | 41 +++++++++++++++++++++++++++++++ ciphers/src/subtle/mod.rs | 1 + 2 files changed, 42 insertions(+) create mode 100644 ciphers/src/subtle/either_hash.rs diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs new file mode 100644 index 00000000..6539d53e --- /dev/null +++ b/ciphers/src/subtle/either_hash.rs @@ -0,0 +1,41 @@ +use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; +use anyhow::Result; +use crate::subtle::hash_functions::keyed_shake256::SHAKE256Core; +use crate::subtle::incorrect_hmac_blake2b::Blake2bCore; + +#[derive(Debug, Eq, PartialEq)] +pub enum EitherHash, + R: KeyedHash> +{ + Left(L), + Right(R), +} + +impl KeyedHashInstance for EitherHash +where + L: KeyedHash, + R: KeyedHash, +{ + type KeyType = [u8; KEY_LEN]; + type OutputType = [u8; HASH_LEN]; + type Error = Error; + + fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error> { + match self { + Self::Left(_) => L::keyed_hash(key, data, out), + Self::Right(_) => R::keyed_hash(key, data, out), + } + } +} + +pub type EitherShakeOrBlake = EitherHash<32, 32, anyhow::Error, SHAKE256Core<32, 32>, Blake2bCore>; + +impl Clone for EitherShakeOrBlake { + fn clone(&self) -> Self { + match self { + Self::Left(l) => Self::Left(l.clone()), + Self::Right(r) => Self::Right(r.clone()), + } + } +} diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index e5f6947f..64c31584 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -10,5 +10,6 @@ pub mod chacha20poly1305_ietf; pub mod chacha20poly1305_ietf_libcrux; pub mod xchacha20poly1305_ietf; mod hash_functions; +pub mod hash_choice; pub use hash_functions::{blake2b, incorrect_hmac_blake2b, keyed_shake256}; \ No newline at end of file From 1b0179e7510ba17f5e8f1fc47ca6a8f56e834cd3 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:32:35 +0100 Subject: [PATCH 034/174] dev(ciphers): provide implementations of KeyedHash and KeyedHashInstance for the incorrect hmac for blake2b. --- ciphers/src/subtle/either_hash.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs index 6539d53e..9e21e5bd 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/either_hash.rs @@ -28,14 +28,3 @@ where } } } - -pub type EitherShakeOrBlake = EitherHash<32, 32, anyhow::Error, SHAKE256Core<32, 32>, Blake2bCore>; - -impl Clone for EitherShakeOrBlake { - fn clone(&self) -> Self { - match self { - Self::Left(l) => Self::Left(l.clone()), - Self::Right(r) => Self::Right(r.clone()), - } - } -} From 54c8e91db40c227096f3c83894b03ab9fbb59d00 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:33:30 +0100 Subject: [PATCH 035/174] doc(ciphers): fix typo in comment --- ciphers/src/subtle/hash_functions/blake2b.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/src/subtle/hash_functions/blake2b.rs b/ciphers/src/subtle/hash_functions/blake2b.rs index 13e75325..4d4ff9d5 100644 --- a/ciphers/src/subtle/hash_functions/blake2b.rs +++ b/ciphers/src/subtle/hash_functions/blake2b.rs @@ -53,7 +53,7 @@ pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<( let mut h = Impl::new_from_slice(key)?; h.update(data); - // Jesus christ, blake2 crate, your usage of GenericArray might be nice and fancy + // 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. From 793cfd227fa7a58d81b63dfa23950e84fd95ce5f Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:34:42 +0100 Subject: [PATCH 036/174] dev(ciphers): provide EitherShakeOrBlake for 32 bytes KEY_LEN and 32 bytes of HASH_LEN based on SHAKE256 and the incorrect blake2b-hmac --- ciphers/src/subtle/either_hash.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs index 9e21e5bd..02245ff2 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/either_hash.rs @@ -28,3 +28,5 @@ where } } } + +pub type EitherShakeOrBlake = HashChoice<32, 32, anyhow::Error, SHAKE256Core<32, 32>, Blake2bCore>; From cf74584f511d83f8c15a9f96f6a32a479816de75 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 20:10:38 +0100 Subject: [PATCH 037/174] tests(ciphers): add rudimentary tests for the shake256 implementation --- .../subtle/hash_functions/keyed_shake256.rs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/hash_functions/keyed_shake256.rs index 2a072b39..0dc9f653 100644 --- a/ciphers/src/subtle/hash_functions/keyed_shake256.rs +++ b/ciphers/src/subtle/hash_functions/keyed_shake256.rs @@ -59,14 +59,47 @@ impl KeyedHash f } } + impl SHAKE256Core { pub fn new() -> Self { Self } } +/// TODO use nferred hash somehow here +/// ```rust +/// # use rosenpass_ciphers::subtle::keyed_shake256::{SHAKE256}; +/// use rosenpass_cipher_traits::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]; +/// // TODO: Note that we are using inferred hash here +/// assert!(SHAKE256::new().keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result"); +/// # let expected_hash: &[u8] = &[44, 102, 248, 251, 141, 145, 55, 194, 165, 228, 156, 42, 220, +/// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87]; +/// # assert_eq!(hash_data, expected_hash); +/// ``` pub type SHAKE256 = InferKeyedHash, KEY_LEN, HASH_LEN>; -pub type SHAKE256_32 = SHAKE256<32, 32>; +/// TODO: Documentation and more interesting test +/// ```rust +/// # use rosenpass_ciphers::subtle::keyed_shake256::{SHAKE256_32}; +/// use rosenpass_cipher_traits::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] = &[44, 102, 248, 251, 141, 145, 55, 194, 165, 228, 156, 42, 220, +/// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87]; +/// # assert_eq!(hash_data, expected_hash); +/// ``` +pub type SHAKE256_32 = SHAKE256<32, 32>; From 30e158f5941a903846da79adcaf3c00a12706a09 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 11 Feb 2025 21:14:24 +0100 Subject: [PATCH 038/174] dev(ciphers): change HashDomain and related structures to use EitherShakeOrBlake. Docu pending --- ciphers/src/hash_domain.rs | 65 +++++++++++-------- ciphers/src/subtle/either_hash.rs | 11 +++- .../subtle/hash_functions/keyed_shake256.rs | 2 +- ciphers/src/subtle/mod.rs | 2 +- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 2f674600..55443fd7 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -5,16 +5,20 @@ use rosenpass_to::To; use crate::keyed_hash as hash; pub use hash::KEY_LEN; +use rosenpass_cipher_traits::KeyedHashInstance; +use crate::subtle::either_hash::EitherShakeOrBlake; /// ///```rust /// # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace}; +/// use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +/// use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; /// 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(); +/// let mut hash_domain = HashDomain::zero(EitherShakeOrBlake::Left(SHAKE256Core)); /// hash_domain = hash_domain.mix(PROTOCOL_IDENTIFIER.as_bytes())?; /// // upgrade to reusable hash domain /// let hash_domain_namespace: HashDomainNamespace = hash_domain.dup(); @@ -39,36 +43,36 @@ pub use hash::KEY_LEN; /// 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], EitherShakeOrBlake); /// 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], EitherShakeOrBlake); /// 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); +pub struct SecretHashDomain(Secret, EitherShakeOrBlake); /// 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); +pub struct SecretHashDomainNamespace(Secret, EitherShakeOrBlake); impl HashDomain { /// Creates a nw [HashDomain] initialized with a all-zeros key. - pub fn zero() -> Self { - Self([0u8; KEY_LEN]) + pub fn zero(choice: EitherShakeOrBlake) -> 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 @@ -77,14 +81,16 @@ impl HashDomain { /// as the `data` and uses the result as the key for the new [HashDomain]. /// pub fn mix(self, v: &[u8]) -> Result { - 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(&self.0, v, &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(self, v: Secret) -> Result { - SecretHashDomain::invoke_primitive(&self.0, v.secret()) + SecretHashDomain::invoke_primitive(&self.0, v.secret(), self.1) } /// Gets the key of this [HashDomain]. @@ -98,9 +104,9 @@ impl HashDomainNamespace { /// 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 { - 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(&self.0, v, &mut new_key)?; + Ok(HashDomain(new_key, self.1.clone())) } /// Creates a new [SecretHashDomain] by mixing in a new key `v` @@ -109,7 +115,7 @@ impl HashDomainNamespace { /// /// It requires that `v` consists of exactly [KEY_LEN] many bytes. pub fn mix_secret(&self, v: Secret) -> Result { - SecretHashDomain::invoke_primitive(&self.0, v.secret()) + SecretHashDomain::invoke_primitive(&self.0, v.secret(), self.1.clone()) } } @@ -118,27 +124,30 @@ impl SecretHashDomain { /// [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. - pub fn invoke_primitive(k: &[u8], d: &[u8]) -> Result { - let mut r = SecretHashDomain(Secret::zero()); + /// TODO: docu + pub fn invoke_primitive(k: &[u8], d: &[u8], hash_choice: EitherShakeOrBlake) -> Result { + let mut new_secret_key = Secret::zero(); + hash_choice.keyed_hash(k.try_into()?, d, new_secret_key.secret_mut())?; + let mut r = SecretHashDomain(new_secret_key, hash_choice); hash::hash(k, d).to(r.0.secret_mut())?; Ok(r) } /// Creates a new [SecretHashDomain] that is initialized with an all zeros key. - pub fn zero() -> Self { - Self(Secret::zero()) + pub fn zero(hash_choice: EitherShakeOrBlake) -> 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) } /// 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) -> Self { - Self(k) + pub fn danger_from_secret(k: Secret, hash_choice: EitherShakeOrBlake) -> Self { + Self(k, hash_choice) } /// Creates a new [SecretHashDomain] by mixing in a new key `v`. Specifically, @@ -147,7 +156,7 @@ impl SecretHashDomain { /// /// It requires that `v` consists of exactly [KEY_LEN] many bytes. pub fn mix(self, v: &[u8]) -> Result { - 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` @@ -156,7 +165,7 @@ impl SecretHashDomain { /// /// It requires that `v` consists of exactly [KEY_LEN] many bytes. pub fn mix_secret(self, v: Secret) -> Result { - 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]. @@ -180,7 +189,7 @@ impl SecretHashDomainNamespace { /// /// It requires that `v` consists of exactly [KEY_LEN] many bytes. pub fn mix(&self, v: &[u8]) -> Result { - 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` @@ -189,7 +198,7 @@ impl SecretHashDomainNamespace { /// /// It requires that `v` consists of exactly [KEY_LEN] many bytes. pub fn mix_secret(&self, v: Secret) -> Result { - 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 @@ -199,4 +208,8 @@ impl SecretHashDomainNamespace { pub fn danger_into_secret(self) -> Secret { self.0 } + + pub fn shake_or_blake(&self) -> &EitherShakeOrBlake { + &self.1 + } } diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs index 02245ff2..6539d53e 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/either_hash.rs @@ -29,4 +29,13 @@ where } } -pub type EitherShakeOrBlake = HashChoice<32, 32, anyhow::Error, SHAKE256Core<32, 32>, Blake2bCore>; +pub type EitherShakeOrBlake = EitherHash<32, 32, anyhow::Error, SHAKE256Core<32, 32>, Blake2bCore>; + +impl Clone for EitherShakeOrBlake { + fn clone(&self) -> Self { + match self { + Self::Left(l) => Self::Left(l.clone()), + Self::Right(r) => Self::Right(r.clone()), + } + } +} diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/hash_functions/keyed_shake256.rs index 0dc9f653..7ff188de 100644 --- a/ciphers/src/subtle/hash_functions/keyed_shake256.rs +++ b/ciphers/src/subtle/hash_functions/keyed_shake256.rs @@ -66,7 +66,7 @@ impl SHAKE256Core Date: Fri, 14 Feb 2025 16:29:56 +0100 Subject: [PATCH 039/174] dev(rosenpass): add support for the shake256 hash function in the rosenpass crate --- rosenpass/benches/handshake.rs | 26 +- rosenpass/src/app_server.rs | 6 +- rosenpass/src/bin/gen-ipc-msg-types.rs | 61 +-- rosenpass/src/cli.rs | 1 + rosenpass/src/config.rs | 12 + rosenpass/src/hash_domains.rs | 39 +- rosenpass/src/protocol/build_crypto_server.rs | 30 +- rosenpass/src/protocol/mod.rs | 5 +- rosenpass/src/protocol/protocol.rs | 353 +++++++++++++----- .../tests/api-integration-tests-api-setup.rs | 15 +- rosenpass/tests/api-integration-tests.rs | 14 +- rosenpass/tests/app_server_example.rs | 14 +- rosenpass/tests/integration_test.rs | 2 +- rosenpass/tests/poll_example.rs | 41 +- 14 files changed, 435 insertions(+), 184 deletions(-) diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index bcb98992..28ca2351 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -1,5 +1,5 @@ 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; @@ -45,21 +45,30 @@ fn keygen() -> Result<(SSk, SPk)> { 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)?; + // TODO: Adapt this to both protocol versions + 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 +91,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); diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 75798b87..39e042ba 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -50,6 +50,7 @@ use crate::{ }; use rosenpass_util::attempt; use rosenpass_util::b64::B64Display; +use crate::config::ProtocolVersion; /// The maximum size of a base64 encoded symmetric key (estimate) pub const MAX_B64_KEY_SIZE: usize = 32 * 5 / 3; @@ -1042,11 +1043,12 @@ impl AppServer { outfile: Option, broker_peer: Option, hostname: Option, + protocol_version: ProtocolVersion ) -> anyhow::Result { 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()); diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index 028113ba..b85e0595 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -2,6 +2,9 @@ use anyhow::{Context, Result}; use heck::ToShoutySnakeCase; use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN}; +use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; +use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; /// Recursively calculate a concrete hash value for an API message type fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]> { @@ -12,8 +15,8 @@ fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN] } /// Print a hash literal for pasting into the Rosenpass source code -fn print_literal(path: &[&str]) -> Result<()> { - let val = calculate_hash_value(HashDomain::zero(), path)?; +fn print_literal(path: &[&str], shake_or_blake: EitherShakeOrBlake) -> Result<()> { + let val = calculate_hash_value(HashDomain::zero(shake_or_blake), path)?; let (last, prefix) = path.split_last().context("developer error!")?; let var_name = last.to_shouty_snake_case(); @@ -51,47 +54,53 @@ impl Tree { } } - fn gen_code_inner(&self, prefix: &[&str]) -> Result<()> { + fn gen_code_inner(&self, prefix: &[&str], shake_or_blake: EitherShakeOrBlake) -> 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: EitherShakeOrBlake) -> 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(), - vec![Tree::Branch( - "Rosenpass Protocol Server".to_owned(), - vec![ - Tree::Leaf("Ping Request".to_owned()), - Tree::Leaf("Ping Response".to_owned()), - Tree::Leaf("Supply Keypair Request".to_owned()), - Tree::Leaf("Supply Keypair Response".to_owned()), - Tree::Leaf("Add Listen Socket Request".to_owned()), - Tree::Leaf("Add Listen Socket Response".to_owned()), - Tree::Leaf("Add Psk Broker Request".to_owned()), - Tree::Leaf("Add Psk Broker Response".to_owned()), - ], - )], - ); + + fn print_IPC_API_info(shake_or_blake: EitherShakeOrBlake, name: String) -> Result<()> { + let tree = Tree::Branch( + format!("Rosenpass IPC API {}", name).to_owned(), + vec![Tree::Branch( + "Rosenpass Protocol Server".to_owned(), + vec![ + Tree::Leaf("Ping Request".to_owned()), + Tree::Leaf("Ping Response".to_owned()), + Tree::Leaf("Supply Keypair Request".to_owned()), + Tree::Leaf("Supply Keypair Response".to_owned()), + Tree::Leaf("Add Listen Socket Request".to_owned()), + Tree::Leaf("Add Listen Socket Response".to_owned()), + Tree::Leaf("Add Psk Broker Request".to_owned()), + Tree::Leaf("Add Psk Broker Response".to_owned()), + ], + )], + ); - println!("type RawMsgType = u128;"); - println!(); - tree.gen_code() + println!("type RawMsgType = u128;"); + println!(); + tree.gen_code(shake_or_blake) + } + + print_IPC_API_info(EitherShakeOrBlake::Left(SHAKE256Core), " (SHAKE256)".to_owned())?; + print_IPC_API_info(EitherShakeOrBlake::Right(Blake2bCore), " (Blake2b)".to_owned()) } diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 870045f1..68e8e41a 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -490,6 +490,7 @@ impl CliArgs { cfg_peer.key_out, broker_peer, cfg_peer.endpoint.clone(), + cfg_peer.protocol_version.into(), )?; } diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index fedddeb1..82397d23 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -109,6 +109,14 @@ pub enum Verbosity { Verbose, } +/// TODO: Documentation +#[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 { @@ -138,6 +146,10 @@ pub struct RosenpassPeer { /// Information for supplying exchanged keys directly to WireGuard #[serde(flatten)] pub wg: Option, + + #[serde(default)] + /// The protocol version to use for the exchange + pub protocol_version: ProtocolVersion, } /// Information for supplying exchanged keys directly to WireGuard diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index b37981b6..b9ed2617 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -13,6 +13,8 @@ //! use rosenpass::{hash_domain, hash_domain_ns}; //! use rosenpass::hash_domains::protocol; //! +//! use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +//! //! // Declaring a custom hash domain //! hash_domain_ns!(protocol, custom_domain, "my custom hash domain label"); //! @@ -26,15 +28,18 @@ //! hash_domain!(domain_separators, sep1, "1"); //! hash_domain!(domain_separators, sep2, "2"); //! +//! // We use the SHAKE256 hash function for this example +//! let hash_choice = EitherShakeOrBlake::Left(rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core); +//! //! // Generating values under hasher1 with both domain separators -//! let h1 = hasher1()?.mix(b"some data")?.dup(); -//! let h1v1 = h1.mix(&sep1()?)?.mix(b"More data")?.into_value(); -//! let h1v2 = h1.mix(&sep2()?)?.mix(b"More data")?.into_value(); +//! 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()?.mix(b"some data")?.dup(); -//! let h2v1 = h2.mix(&sep1()?)?.mix(b"More data")?.into_value(); -//! let h2v2 = h2.mix(&sep2()?)?.mix(b"More data")?.into_value(); +//! 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]; @@ -49,6 +54,9 @@ use anyhow::Result; use rosenpass_ciphers::hash_domain::HashDomain; +use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; +use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; /// Declare a hash function /// @@ -62,8 +70,8 @@ use rosenpass_ciphers::hash_domain::HashDomain; macro_rules! hash_domain_ns { ($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => { $(#[$($attrss)*])* - pub fn $name() -> ::anyhow::Result<::rosenpass_ciphers::hash_domain::HashDomain> { - let t = $base()?; + pub fn $name(hash_choice: EitherShakeOrBlake) -> ::anyhow::Result<::rosenpass_ciphers::hash_domain::HashDomain> { + let t = $base(hash_choice)?; $( let t = t.mix($lbl.as_bytes())?; )* Ok(t) } @@ -81,8 +89,8 @@ macro_rules! hash_domain_ns { macro_rules! hash_domain { ($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => { $(#[$($attrss)*])* - pub fn $name() -> ::anyhow::Result<[u8; ::rosenpass_ciphers::KEY_LEN]> { - let t = $base()?; + pub fn $name(hash_choice: EitherShakeOrBlake) -> ::anyhow::Result<[u8; ::rosenpass_ciphers::KEY_LEN]> { + let t = $base(hash_choice)?; $( let t = t.mix($lbl.as_bytes())?; )* Ok(t.into_value()) } @@ -95,14 +103,21 @@ macro_rules! hash_domain { /// used in various places in the rosenpass protocol. /// /// This is generally used to create further hash-domains for specific purposes. See +/// +/// TODO: Update documentation /// /// # 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() -> Result { - HashDomain::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes()) +pub fn protocol(hash_choice: EitherShakeOrBlake) -> Result { + // TODO: Update this string that is mixed in? + match hash_choice { + EitherShakeOrBlake::Left(SHAKE256Core) => HashDomain::zero(hash_choice).mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()), + EitherShakeOrBlake::Right(Blake2bCore) => HashDomain::zero(hash_choice).mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 Blake2b".as_bytes()), + } + } hash_domain_ns!( diff --git a/rosenpass/src/protocol/build_crypto_server.rs b/rosenpass/src/protocol/build_crypto_server.rs index fd4cce96..835cfc1e 100644 --- a/rosenpass/src/protocol/build_crypto_server.rs +++ b/rosenpass/src/protocol/build_crypto_server.rs @@ -4,7 +4,7 @@ use rosenpass_util::{ result::ensure_or, }; use thiserror::Error; - +use crate::config::ProtocolVersion; use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey}; #[derive(Debug, Clone)] @@ -146,17 +146,18 @@ pub struct MissingKeypair; /// ```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() }; -/// let peer2 = PeerParams { psk: None, pk: SPk::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); +/// builder.add_peer(peer2.psk.clone(), peer2.pk, ProtocolVersion::V02); /// /// let server = builder.build().expect("build failed"); /// assert_eq!(server.peers.len(), 2); @@ -186,8 +187,8 @@ impl Build 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.") } @@ -208,6 +209,8 @@ pub struct PeerParams { pub psk: Option, /// Public key identifying the peer. pub pk: SPk, + /// The used protocol version. + pub protocol_version: ProtocolVersion, } impl BuildCryptoServer { @@ -305,6 +308,7 @@ impl BuildCryptoServer { /// 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(); @@ -323,7 +327,7 @@ impl BuildCryptoServer { /// // 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()); + /// 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"); @@ -333,16 +337,16 @@ impl BuildCryptoServer { /// assert_eq!(peer.spkt, public_key); /// assert_eq!(peer_psk.secret(), pre_shared_key.secret()); /// ``` - pub fn with_added_peer(&mut self, psk: Option, pk: SPk) -> &mut Self { + pub fn with_added_peer(&mut self, psk: Option, 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 } /// 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, pk: SPk) -> PeerPtr { + pub fn add_peer(&mut self, psk: Option, 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 } @@ -356,6 +360,8 @@ impl BuildCryptoServer { /// /// ```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(); /// @@ -365,7 +371,7 @@ impl BuildCryptoServer { /// let keypair = Keypair::random(); /// let peer_pk = SPk::random(); /// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![]); - /// builder.add_peer(None, peer_pk); + /// builder.add_peer(None, peer_pk, ProtocolVersion::V02); /// /// // Extract configuration parameters from the decomissioned builder /// let (keypair_option, peers) = builder.take_parts(); diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index e8079308..9d50108e 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -33,6 +33,7 @@ //! # 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 ... @@ -49,8 +50,8 @@ //! 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)?; -//! b.add_peer(Some(psk), peer_a_pk)?; +//! 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()); diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 52e48441..7a3680cc 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -33,7 +33,9 @@ use rosenpass_util::functional::ApplyExt; use rosenpass_util::mem::DiscardResultExt; use rosenpass_util::{cat, mem::cpy_min, time::Timebase}; use zerocopy::{AsBytes, FromBytes, Ref}; - +use rosenpass_ciphers::subtle::either_hash::{EitherShakeOrBlake}; +use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; +use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; use crate::{hash_domains, msgs::*, RosenpassError}; // CONSTANTS & SETTINGS ////////////////////////// @@ -364,6 +366,30 @@ pub enum IndexKey { KnownInitConfResponse(KnownResponseHash), } +#[derive(Debug, Clone)] +pub enum ProtocolVersion { + V02, + V03 +} + +impl ProtocolVersion { + pub fn shake_or_blake(&self) -> EitherShakeOrBlake { + match self { + ProtocolVersion::V02 => EitherShakeOrBlake::Right(Blake2bCore), + ProtocolVersion::V03 => EitherShakeOrBlake::Left(SHAKE256Core), + } + } +} + +impl From for ProtocolVersion { + fn from(v: crate::config::ProtocolVersion) -> Self { + match v { + crate::config::ProtocolVersion::V02 => ProtocolVersion::V02, + crate::config::ProtocolVersion::V03 => ProtocolVersion::V03, + } + } +} + /// A peer that the server can execute a key exchange with. /// /// Peers generally live in [CryptoServer::peers]. [PeerNo] captures an array @@ -375,7 +401,7 @@ pub enum IndexKey { /// /// ``` /// use std::ops::DerefMut; -/// use rosenpass::protocol::{SSk, SPk, SymKey, Peer}; +/// use rosenpass::protocol::{SSk, SPk, SymKey, Peer, ProtocolVersion}; /// use rosenpass_ciphers::kem::StaticKem; /// use rosenpass_cipher_traits::Kem; /// @@ -390,13 +416,13 @@ pub enum IndexKey { /// let psk = SymKey::random(); /// /// // Creation with a PSK -/// let peer_psk = Peer::new(psk, spkt.clone()); +/// let peer_psk = Peer::new(psk, spkt.clone(), ProtocolVersion::V03); /// /// // Creation without a PSK -/// let peer_nopsk = Peer::new(SymKey::zero(), spkt); +/// let peer_nopsk = Peer::new(SymKey::zero(), spkt, ProtocolVersion::V03); /// /// // Create a second peer -/// let peer_psk_2 = Peer::new(SymKey::zero(), spkt2); +/// let peer_psk_2 = Peer::new(SymKey::zero(), spkt2, ProtocolVersion::V03); /// /// // Peer ID does not depend on PSK, but it does depend on the public key /// assert_eq!(peer_psk.pidt()?, peer_nopsk.pidt()?); @@ -452,6 +478,9 @@ pub struct Peer { /// This allows us to perform retransmission for the purpose of dealing with packet loss /// on the network without having to account for it in the cryptographic code itself. pub known_init_conf_response: Option, + + /// TODO: Documentation + pub protocol_version: ProtocolVersion, } impl Peer { @@ -460,14 +489,14 @@ impl Peer { /// This is dirty but allows us to perform easy incremental construction of [Self]. /// /// ``` - /// use rosenpass::protocol::{Peer, SymKey, SPk}; + /// use rosenpass::protocol::{Peer, SymKey, SPk, ProtocolVersion}; /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - /// let p = Peer::zero(); + /// let p = Peer::zero(ProtocolVersion::V03); /// assert_eq!(p.psk.secret(), SymKey::zero().secret()); /// assert_eq!(p.spkt, SPk::zero()); /// // etc. /// ``` - pub fn zero() -> Self { + pub fn zero(protocol_version: ProtocolVersion) -> Self { Self { psk: SymKey::zero(), spkt: SPk::zero(), @@ -476,6 +505,7 @@ impl Peer { initiation_requested: false, handshake: None, known_init_conf_response: None, + protocol_version: protocol_version, } } } @@ -625,7 +655,7 @@ fn known_response_format() { response: Envelope::new_zeroed(), }; let s = format!("{v:?}"); - assert!(s.contains("response")); // Smoke test only, its a formatter + assert!(s.contains("response")); // Smoke test only, it's a formatter } /// Known [EmptyData] response to an [InitConf] message @@ -804,11 +834,11 @@ pub trait Mortal { /// ``` /// use std::ops::DerefMut; /// use rosenpass_ciphers::kem::StaticKem; -/// use rosenpass::protocol::{SSk, SPk, testutils::ServerForTesting}; +/// use rosenpass::protocol::{SSk, SPk, testutils::ServerForTesting, ProtocolVersion}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// -/// let (peer, (_, spkt), mut srv) = ServerForTesting::new()?.tuple(); +/// let (peer, (_, spkt), mut srv) = ServerForTesting::new(ProtocolVersion::V03)?.tuple(); /// /// // Immutable access /// assert_eq!(peer.get(&srv).spkt, spkt); @@ -1344,7 +1374,7 @@ impl CryptoServer { /// /// ``` /// use std::ops::DerefMut; - /// use rosenpass::protocol::{SSk, SPk, CryptoServer}; + /// use rosenpass::protocol::{SSk, SPk, CryptoServer, ProtocolVersion}; /// use rosenpass_ciphers::kem::StaticKem; /// use rosenpass_cipher_traits::Kem; /// @@ -1388,9 +1418,9 @@ impl CryptoServer { /// Calculate the peer ID of this CryptoServer #[rustfmt::skip] - pub fn pidm(&self) -> Result { + pub fn pidm(&self, shake_or_blake: EitherShakeOrBlake) -> Result { Ok(Public::new( - hash_domains::peerid()? + hash_domains::peerid(shake_or_blake)? .mix(self.spkm.deref())? .into_value())) } @@ -1404,10 +1434,12 @@ impl CryptoServer { } /// Add a peer with an optional pre shared key (`psk`) and its public key (`pk`) + /// + /// TODO: Adapt documentation /// /// ``` /// use std::ops::DerefMut; - /// use rosenpass::protocol::{SSk, SPk, SymKey, CryptoServer}; + /// use rosenpass::protocol::{SSk, SPk, SymKey, CryptoServer, ProtocolVersion}; /// use rosenpass_ciphers::kem::StaticKem; /// use rosenpass_cipher_traits::Kem; /// @@ -1422,13 +1454,13 @@ impl CryptoServer { /// /// let psk = SymKey::random(); /// - /// let peer = srv.add_peer(Some(psk), spkt.clone())?; + /// let peer = srv.add_peer(Some(psk), spkt.clone(), ProtocolVersion::V03)?; /// /// assert_eq!(peer.get(&srv).spkt, spkt); /// /// Ok::<(), anyhow::Error>(()) /// ``` - pub fn add_peer(&mut self, psk: Option, pk: SPk) -> Result { + pub fn add_peer(&mut self, psk: Option, pk: SPk, protocol_version: ProtocolVersion) -> Result { let peer = Peer { psk: psk.unwrap_or_else(SymKey::zero), spkt: pk, @@ -1437,6 +1469,7 @@ impl CryptoServer { handshake: None, known_init_conf_response: None, initiation_requested: false, + protocol_version: protocol_version, }; let peerid = peer.pidt()?; let peerno = self.peers.len(); @@ -1549,7 +1582,7 @@ impl CryptoServer { /// Retrieve the active biscuit key, cycling biscuit keys if necessary. /// /// Two biscuit keys are maintained inside [Self::biscuit_keys]; they are - /// considered fresh ([Lifecycle::Young]) for one [BISCUIT_EPOCH] after creation + /// considered fresh ([Lifecycle::Young]) for one [BISCUIT_EPOCH] after creation, /// and they are considered stale ([Lifecycle::Retired]) for another [BISCUIT_EPOCH]. /// /// While young, they are used for encryption of biscuits and while retired they are @@ -1624,7 +1657,7 @@ impl Peer { /// # Examples /// /// See example in [Self]. - pub fn new(psk: SymKey, pk: SPk) -> Peer { + pub fn new(psk: SymKey, pk: SPk, protocol_version: ProtocolVersion) -> Peer { Peer { psk, spkt: pk, @@ -1633,11 +1666,12 @@ impl Peer { handshake: None, known_init_conf_response: None, initiation_requested: false, + protocol_version: protocol_version, } } /// Compute the peer ID of the peer, - /// as specified in the the [whitepaper](https://rosenpass.eu/whitepaper.pdf). + /// as specified in the [whitepaper](https://rosenpass.eu/whitepaper.pdf). /// /// # Examples /// @@ -1645,7 +1679,7 @@ impl Peer { #[rustfmt::skip] pub fn pidt(&self) -> Result { Ok(Public::new( - hash_domains::peerid()? + hash_domains::peerid(self.protocol_version.shake_or_blake())? .mix(self.spkt.deref())? .into_value())) } @@ -1658,20 +1692,22 @@ impl Session { /// /// ``` /// use rosenpass::protocol::{Session, HandshakeRole}; + /// use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; + /// use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// - /// let s = Session::zero(); + /// let s = Session::zero(EitherShakeOrBlake::Left(SHAKE256Core)); /// assert_eq!(s.created_at, 0.0); /// assert_eq!(s.handshake_role, HandshakeRole::Initiator); /// ``` - pub fn zero() -> Self { + pub fn zero(shake_or_blake: EitherShakeOrBlake) -> Self { Self { created_at: 0.0, sidm: SessionId::zero(), sidt: SessionId::zero(), handshake_role: HandshakeRole::Initiator, - ck: SecretHashDomain::zero().dup(), + ck: SecretHashDomain::zero(shake_or_blake).dup(), txkm: SymKey::zero(), txkt: SymKey::zero(), txnm: 0, @@ -1886,7 +1922,7 @@ impl Mortal for KnownInitConfResponsePtr { /// # Examples /// /// ``` -/// use rosenpass::protocol::{Timing, Mortal, MortalExt, Lifecycle, CryptoServer}; +/// use rosenpass::protocol::{Timing, Mortal, MortalExt, Lifecycle, CryptoServer, ProtocolVersion}; /// use rosenpass::protocol::testutils::{ServerForTesting, time_travel_forward}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); @@ -1896,7 +1932,7 @@ impl Mortal for KnownInitConfResponsePtr { /// const D : Timing = 24.0 * H; /// const Y : Timing = 356.0 * D; /// -/// let mut ts = ServerForTesting::new()?; +/// let mut ts = ServerForTesting::new(ProtocolVersion::V03)?; /// /// fn eq_up_to_minute(a: Timing, b: Timing) -> bool { /// (a - b) < M @@ -2136,7 +2172,7 @@ impl CryptoServer { let cookie_secret = cookie_secret.get(self).value.secret(); let mut cookie_value = [0u8; 16]; cookie_value.copy_from_slice( - &hash_domains::cookie_value()? + &hash_domains::cookie_value(EitherShakeOrBlake::Left(SHAKE256Core))? .mix(cookie_secret)? .mix(host_identification.encode())? .into_value()[..16], @@ -2152,7 +2188,7 @@ impl CryptoServer { let msg_in = Ref::<&[u8], Envelope>::new(rx_buf) .ok_or(RosenpassError::BufferSizeMismatch)?; expected.copy_from_slice( - &hash_domains::cookie()? + &hash_domains::cookie(EitherShakeOrBlake::Left(SHAKE256Core))? .mix(&cookie_value)? .mix(&msg_in.as_bytes()[span_of!(Envelope, msg_type..cookie)])? .into_value()[..16], @@ -2189,7 +2225,7 @@ impl CryptoServer { ); let cookie_value = active_cookie_value.unwrap(); - let cookie_key = hash_domains::cookie_key()? + let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))? .mix(self.spkm.deref())? .into_value(); @@ -2269,24 +2305,43 @@ impl CryptoServer { log::debug!("Rx {:?}, processing", msg_type); + let mut msg_out = truncating_cast_into::>(tx_buf)?; + let peer = match msg_type { Ok(MsgType::InitHello) => { let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; - ensure!(msg_in.check_seal(self)?, seal_broken); - let mut msg_out = truncating_cast_into::>(tx_buf)?; - let peer = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload)?; + // At this point, we do not know the hash functon used by the peer, thus we try both, + // with a preference for SHAKE256. + let peer_shake256 = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Left(SHAKE256Core)); + let (peer, peer_hash_choice) = match peer_shake256 { + Ok(peer ) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), + Err(_) => { + let peer_blake2b = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Right(Blake2bCore)); + match peer_blake2b { + Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)), + Err(_) => bail!("No valid hash function found for InitHello") + } + } + }; + // Now, we make sure that the hash function used by the peer is the same as the one + // that is specified in the local configuration. + self.verify_hash_choice_match(peer, peer_hash_choice.clone())?; + + ensure!(msg_in.check_seal(self, peer_hash_choice)?, seal_broken); + len = self.seal_and_commit_msg(peer, MsgType::RespHello, &mut msg_out)?; peer } Ok(MsgType::RespHello) => { let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; - ensure!(msg_in.check_seal(self)?, seal_broken); let mut msg_out = truncating_cast_into::>(tx_buf)?; let peer = self.handle_resp_hello(&msg_in.payload, &mut msg_out.payload)?; + ensure!(msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, seal_broken); + len = self.seal_and_commit_msg(peer, MsgType::InitConf, &mut msg_out)?; peer.hs() .store_msg_for_retransmission(self, &msg_out.as_bytes()[..len])?; @@ -2296,7 +2351,7 @@ impl CryptoServer { Ok(MsgType::InitConf) => { let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; - ensure!(msg_in.check_seal(self)?, seal_broken); + let mut msg_out = truncating_cast_into::>(tx_buf)?; @@ -2305,6 +2360,7 @@ impl CryptoServer { // Cached response; copy out of cache Some(cached) => { let peer = cached.peer(); + ensure!(msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, seal_broken); let cached = cached .get(self) .map(|v| v.response.borrow()) @@ -2316,8 +2372,24 @@ impl CryptoServer { // No cached response, actually call cryptographic handler None => { - let peer = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload)?; - + // At this point, we do not know the hash functon used by the peer, thus we try both, + // with a preference for SHAKE256. + let peer_shake256 = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Left(SHAKE256Core)); + let (peer, peer_hash_choice) = match peer_shake256 { + Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), + Err(_) => { + let peer_blake2b = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Right(Blake2bCore)); + match peer_blake2b { + Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)), + Err(_) => bail!("No valid hash function found for InitHello") + } + } + }; + // Now, we make sure that the hash function used by the peer is the same as the one + // that is specified in the local configuration. + self.verify_hash_choice_match(peer, peer_hash_choice.clone())?; + ensure!(msg_in.check_seal(self, peer_hash_choice)?, seal_broken); + KnownInitConfResponsePtr::insert_for_request_msg( self, peer, @@ -2336,9 +2408,8 @@ impl CryptoServer { Ok(MsgType::EmptyData) => { let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; - ensure!(msg_in.check_seal(self)?, seal_broken); - self.handle_resp_conf(&msg_in.payload)? + self.handle_resp_conf(&msg_in, seal_broken.to_string())? } Ok(MsgType::CookieReply) => { let msg_in: Ref<&[u8], CookieReply> = @@ -2357,6 +2428,20 @@ impl CryptoServer { resp: if len == 0 { None } else { Some(len) }, }) } + + /// TODO documentation + fn verify_hash_choice_match(&self, peer: PeerPtr, peer_hash_choice: EitherShakeOrBlake) -> Result<()> { + match peer.get(self).protocol_version.shake_or_blake() { + EitherShakeOrBlake::Left(SHAKE256Core) => match peer_hash_choice { + EitherShakeOrBlake::Left(SHAKE256Core) => Ok(()), + EitherShakeOrBlake::Right(Blake2bCore) => bail!("Hash function mismatch"), + }, + EitherShakeOrBlake::Right(Blake2bCore) => match peer_hash_choice { + EitherShakeOrBlake::Left(SHAKE256Core) => bail!("Hash function mismatch"), + EitherShakeOrBlake::Right(Blake2bCore) => Ok(()), + } + } + } /// This is used to finalize a message in a transmission buffer /// while ensuring that the [Envelope::mac] and [Envelope::cookie] @@ -3096,7 +3181,7 @@ where { /// Internal business logic: Calculate the message authentication code (`mac`) and also append cookie value pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { - let mac = hash_domains::mac()? + let mac = hash_domains::mac(peer.get(srv).protocol_version.shake_or_blake())? .mix(peer.get(srv).spkt.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); @@ -3109,7 +3194,7 @@ where /// This is called inside [Self::seal] and does not need to be called again separately. pub fn seal_cookie(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { if let Some(cookie_key) = &peer.cv().get(srv) { - let cookie = hash_domains::cookie()? + let cookie = hash_domains::cookie(peer.get(srv).protocol_version.shake_or_blake())? .mix(cookie_key.value.secret())? .mix(&self.as_bytes()[span_of!(Self, msg_type..cookie)])?; self.cookie @@ -3124,8 +3209,8 @@ where M: AsBytes + FromBytes, { /// Internal business logic: Check the message authentication code produced by [Self::seal] - pub fn check_seal(&self, srv: &CryptoServer) -> Result { - let expected = hash_domains::mac()? + pub fn check_seal(&self, srv: &CryptoServer, shake_or_blake: EitherShakeOrBlake) -> Result { + let expected = hash_domains::mac(shake_or_blake)? .mix(srv.spkm.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; Ok(constant_time::memcmp( @@ -3137,11 +3222,11 @@ where impl InitiatorHandshake { /// Zero initialization of an InitiatorHandshake, with up to date timestamp - pub fn zero_with_timestamp(srv: &CryptoServer) -> Self { + pub fn zero_with_timestamp(srv: &CryptoServer, shake_or_blake: EitherShakeOrBlake) -> Self { InitiatorHandshake { created_at: srv.timebase.now(), next: HandshakeStateMachine::RespHello, - core: HandshakeState::zero(), + core: HandshakeState::zero(shake_or_blake), eski: ESk::zero(), epki: EPk::zero(), tx_at: 0.0, @@ -3156,37 +3241,37 @@ impl InitiatorHandshake { impl HandshakeState { /// Zero initialization of an HandshakeState - pub fn zero() -> Self { + pub fn zero(shake_or_blake: EitherShakeOrBlake) -> Self { Self { sidi: SessionId::zero(), sidr: SessionId::zero(), - ck: SecretHashDomain::zero().dup(), + ck: SecretHashDomain::zero(shake_or_blake).dup(), } } /// Securely erase the chaining key pub fn erase(&mut self) { - self.ck = SecretHashDomain::zero().dup(); + self.ck = SecretHashDomain::zero(self.ck.shake_or_blake().clone()).dup(); } /// Initialize the handshake state with the responder public key and the protocol domain /// separator pub fn init(&mut self, spkr: &[u8]) -> Result<&mut Self> { - self.ck = hash_domains::ckinit()?.turn_secret().mix(spkr)?.dup(); + self.ck = hash_domains::ckinit(self.ck.shake_or_blake().clone())?.turn_secret().mix(spkr)?.dup(); Ok(self) } /// Mix some data into the chaining key. This is used for mixing cryptographic keys and public /// data alike into the chaining key pub fn mix(&mut self, a: &[u8]) -> Result<&mut Self> { - self.ck = self.ck.mix(&hash_domains::mix()?)?.mix(a)?.dup(); + self.ck = self.ck.mix(&hash_domains::mix(self.ck.shake_or_blake().clone())?)?.mix(a)?.dup(); Ok(self) } /// Encrypt some data with a value derived from the current chaining key and mix that data /// into the protocol state. pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> { - let k = self.ck.mix(&hash_domains::hs_enc()?)?.into_secret(); + let k = self.ck.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?.into_secret(); aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?; self.mix(ct) } @@ -3196,7 +3281,7 @@ impl HandshakeState { /// Makes sure that the same values are mixed into the chaining that where mixed in on the /// sender side. pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> { - let k = self.ck.mix(&hash_domains::hs_enc()?)?.into_secret(); + let k = self.ck.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?.into_secret(); aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?; self.mix(ct) } @@ -3259,7 +3344,7 @@ impl HandshakeState { .copy_from_slice(self.ck.clone().danger_into_secret().secret()); // calculate ad contents - let ad = hash_domains::biscuit_ad()? + let ad = hash_domains::biscuit_ad(peer.get(srv).protocol_version.shake_or_blake())? .mix(srv.spkm.deref())? .mix(self.sidi.as_slice())? .mix(self.sidr.as_slice())? @@ -3288,12 +3373,13 @@ impl HandshakeState { biscuit_ct: &[u8], sidi: SessionId, sidr: SessionId, + shake_or_blake: EitherShakeOrBlake ) -> Result<(PeerPtr, BiscuitId, HandshakeState)> { // The first bit of the biscuit indicates which biscuit key was used let bk = BiscuitKeyPtr(((biscuit_ct[0] & 0b1000_0000) >> 7) as usize); // Calculate additional data fields - let ad = hash_domains::biscuit_ad()? + let ad = hash_domains::biscuit_ad(shake_or_blake)? .mix(srv.spkm.deref())? .mix(sidi.as_slice())? .mix(sidr.as_slice())? @@ -3312,18 +3398,17 @@ impl HandshakeState { // Reconstruct the biscuit fields let no = BiscuitId::from_slice(&biscuit.biscuit_no); - let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(&biscuit.ck)).dup(); let pid = PeerId::from_slice(&biscuit.pidi); - - // Reconstruct the handshake state - let mut hs = Self { sidi, sidr, ck }; - hs.mix(biscuit_ct)?; - // Look up the associated peer 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."))?; + let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(&biscuit.ck), peer.get(srv).protocol_version.shake_or_blake()).dup(); + // Reconstruct the handshake state + let mut hs = Self { sidi, sidr, ck }; + hs.mix(biscuit_ct)?; + Ok((peer, no, hs)) } @@ -3332,10 +3417,10 @@ impl HandshakeState { /// This called by either party. /// /// `role` indicates whether the local peer was an initiator or responder in the handshake. - pub fn enter_live(self, srv: &CryptoServer, role: HandshakeRole) -> Result { + pub fn enter_live(self, srv: &CryptoServer, role: HandshakeRole, either_shake_or_blake: EitherShakeOrBlake) -> Result { let HandshakeState { ck, sidi, sidr } = self; - let tki = ck.mix(&hash_domains::ini_enc()?)?.into_secret(); - let tkr = ck.mix(&hash_domains::res_enc()?)?.into_secret(); + let tki = ck.mix(&hash_domains::ini_enc(either_shake_or_blake.clone())?)?.into_secret(); + let tkr = ck.mix(&hash_domains::res_enc(either_shake_or_blake)?)?.into_secret(); let created_at = srv.timebase.now(); let (ntx, nrx) = (0, 0); let (mysid, peersid, ktx, krx) = match role { @@ -3373,7 +3458,7 @@ impl CryptoServer { .get(self) .as_ref() .with_context(|| format!("No current session for peer {:?}", peer))?; - Ok(session.ck.mix(&hash_domains::osk()?)?.into_secret()) + Ok(session.ck.mix(&hash_domains::osk(peer.get(self).protocol_version.shake_or_blake())?)?.into_secret()) } } @@ -3381,7 +3466,7 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Kicks of the handshake /// on the initiator side, producing the InitHello message. pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { - let mut hs = InitiatorHandshake::zero_with_timestamp(self); + let mut hs = InitiatorHandshake::zero_with_timestamp(self, peer.get(self).protocol_version.shake_or_blake()); // IHI1 hs.core.init(peer.get(self).spkt.deref())?; @@ -3406,7 +3491,7 @@ impl CryptoServer { // IHI6 hs.core - .encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm()?.as_ref())?; + .encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm(peer.get(self).protocol_version.shake_or_blake())?.as_ref())?; // IHI7 hs.core @@ -3424,8 +3509,9 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a /// [RespHello] message on the responder side. - pub fn handle_init_hello(&mut self, ih: &InitHello, rh: &mut RespHello) -> Result { - let mut core = HandshakeState::zero(); + /// TODO: Document Hash Functon usage + pub fn handle_init_hello(&mut self, ih: &InitHello, rh: &mut RespHello, shake_or_blake: EitherShakeOrBlake) -> Result { + let mut core = HandshakeState::zero(shake_or_blake); core.sidi = SessionId::from_slice(&ih.sidi); @@ -3570,7 +3656,7 @@ impl CryptoServer { // ICI7 peer.session() - .insert(self, core.enter_live(self, HandshakeRole::Initiator)?)?; + .insert(self, core.enter_live(self, HandshakeRole::Initiator, peer.get(self).protocol_version.shake_or_blake())?)?; hs_mut!().core.erase(); hs_mut!().next = HandshakeStateMachine::RespConf; @@ -3578,11 +3664,12 @@ impl CryptoServer { } /// Core cryptographic protocol implementation: Parses an [InitConf] message and produces an - /// [EmptyData] (responder confimation) message on the responder side. + /// [EmptyData] (responder confirmation) message on the responder side. /// /// This concludes the handshake on the cryptographic level; the [EmptyData] message is just /// an acknowledgement message telling the initiator to stop performing retransmissions. - pub fn handle_init_conf(&mut self, ic: &InitConf, rc: &mut EmptyData) -> Result { + /// TODO: documentation + pub fn handle_init_conf(&mut self, ic: &InitConf, rc: &mut EmptyData, shake_or_blake: EitherShakeOrBlake) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit( @@ -3590,6 +3677,7 @@ impl CryptoServer { &ic.biscuit, SessionId::from_slice(&ic.sidi), SessionId::from_slice(&ic.sidr), + shake_or_blake, )?; // ICR2 @@ -3615,7 +3703,7 @@ impl CryptoServer { // ICR7 peer.session() - .insert(self, core.enter_live(self, HandshakeRole::Responder)?)?; + .insert(self, core.enter_live(self, HandshakeRole::Responder, peer.get(self).protocol_version.shake_or_blake())?)?; // TODO: This should be part of the protocol specification. // Abort any ongoing handshake from initiator role peer.hs().take(self); @@ -3663,11 +3751,13 @@ impl CryptoServer { /// message then terminates the handshake. /// /// The EmptyData message is just there to tell the initiator to abort retransmissions. - pub fn handle_resp_conf(&mut self, rc: &EmptyData) -> Result { + pub fn handle_resp_conf(&mut self, msg_in: &Ref<&[u8], Envelope>, seal_broken: String) -> Result { + let rc: &EmptyData = &msg_in.payload; let sid = SessionId::from_slice(&rc.sid); let hs = self .lookup_handshake(sid) .with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?; + ensure!(msg_in.check_seal(self, hs.peer().get(self).protocol_version.shake_or_blake())?, seal_broken); let ses = hs.peer().session(); let exp = hs.get(self).as_ref().map(|h| h.next); @@ -3685,7 +3775,7 @@ impl CryptoServer { 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 + // the unwrapping can not fail, because the slice returned by ctr() is // guaranteed to have the correct size let n = u64::from_le_bytes(rc.ctr); ensure!(n >= s.txnt, "Stale nonce"); @@ -3750,7 +3840,7 @@ impl CryptoServer { }?; let spkt = peer.get(self).spkt.deref(); - let cookie_key = hash_domains::cookie_key()?.mix(spkt)?.into_value(); + let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))?.mix(spkt)?.into_value(); let cookie_value = peer.cv().update_mut(self).unwrap(); xaead::decrypt(cookie_value, &cookie_key, &mac, &cr.inner.cookie_encrypted)?; @@ -3795,15 +3885,16 @@ pub mod testutils { pub srv: CryptoServer, } + /// TODO: Document that the protocol verson s only used for creating the peer for testing impl ServerForTesting { - pub fn new() -> anyhow::Result { + pub fn new(protocol_version: ProtocolVersion) -> anyhow::Result { let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); StaticKem::keygen(sskm.secret_mut(), spkm.deref_mut())?; let mut srv = CryptoServer::new(sskm, spkm); let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); StaticKem::keygen(sskt.secret_mut(), spkt.deref_mut())?; - let peer = srv.add_peer(None, spkt.clone())?; + let peer = srv.add_peer(None, spkt.clone(), protocol_version)?; let peer_keys = (sskt, spkt); Ok(ServerForTesting { @@ -3868,6 +3959,17 @@ mod test { #[test] #[serial] + fn handles_incorrect_size_messages_v02() { + handles_incorrect_size_messages(ProtocolVersion::V02) + } + + #[test] + #[serial] + fn handles_incorrect_size_messages_v03() { + handles_incorrect_size_messages(ProtocolVersion::V03) + } + + /// Ensure that the protocol implementation can deal with truncated /// messages and with overlong messages. /// @@ -3882,7 +3984,7 @@ mod test { /// /// 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() { + fn handles_incorrect_size_messages(protocol_version: ProtocolVersion) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { @@ -3891,7 +3993,7 @@ mod test { const PEER0: PeerPtr = PeerPtr(0); - let (mut me, mut they) = make_server_pair().unwrap(); + let (mut me, mut they) = make_server_pair(protocol_version).unwrap(); let (mut msgbuf, mut resbuf) = (MsgBufPlus::zero(), MsgBufPlus::zero()); // Process the entire handshake @@ -3941,7 +4043,7 @@ mod test { Ok((sk, pk)) } - fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> { + fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> { // TODO: Copied from the benchmark; deduplicate let psk = SymKey::random(); let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?); @@ -3949,19 +4051,29 @@ mod test { 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)) } #[test] #[serial] - fn test_regular_exchange() { + fn test_regular_exchange_v02() { + test_regular_exchange(ProtocolVersion::V02) + } + + #[test] + #[serial] + fn test_regular_exchange_v03() { + test_regular_exchange(ProtocolVersion::V03) + } + + fn test_regular_exchange(protocol_version: ProtocolVersion) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair().unwrap(); + let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); let mut a_to_b_buf = MsgBufPlus::zero(); let mut b_to_a_buf = MsgBufPlus::zero(); @@ -4017,12 +4129,22 @@ mod test { #[test] #[serial] - fn test_regular_init_conf_retransmit() { + fn test_regular_init_conf_retransmit_v02() { + test_regular_init_conf_retransmit(ProtocolVersion::V02) + } + + #[test] + #[serial] + fn test_regular_init_conf_retransmit_v03() { + test_regular_init_conf_retransmit(ProtocolVersion::V03) + } + + fn test_regular_init_conf_retransmit(protocol_version: ProtocolVersion) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair().unwrap(); + let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); let mut a_to_b_buf = MsgBufPlus::zero(); let mut b_to_a_buf = MsgBufPlus::zero(); @@ -4092,12 +4214,22 @@ mod test { #[test] #[serial] - fn cookie_reply_mechanism_responder_under_load() { + fn cookie_reply_mechanism_responder_under_load_v02() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) + } + + #[test] + #[serial] + fn cookie_reply_mechanism_responder_under_load_v03() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) + } + + fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair().unwrap(); + let (mut a, mut b) = make_server_pair(protocol_version.clone()).unwrap(); let mut a_to_b_buf = MsgBufPlus::zero(); let mut b_to_a_buf = MsgBufPlus::zero(); @@ -4136,7 +4268,7 @@ mod test { assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young); - let expected_cookie_value = hash_domains::cookie_value() + let expected_cookie_value = hash_domains::cookie_value(protocol_version.shake_or_blake()) .unwrap() .mix( b.active_or_retired_cookie_secrets()[0] @@ -4189,12 +4321,22 @@ mod test { #[test] #[serial] - fn cookie_reply_mechanism_initiator_bails_on_message_under_load() { + fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v02() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) + } + + #[test] + #[serial] + fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v03() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) + } + + fn cookie_reply_mechanism_initiator_bails_on_message_under_load(protocol_version: ProtocolVersion) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair().unwrap(); + let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); let mut a_to_b_buf = MsgBufPlus::zero(); let mut b_to_a_buf = MsgBufPlus::zero(); @@ -4249,10 +4391,19 @@ mod test { } #[test] - fn init_conf_retransmission() -> anyhow::Result<()> { + fn init_conf_retransmission_v02() -> Result<()> { + init_conf_retransmission(ProtocolVersion::V02) + } + + #[test] + fn init_conf_retransmission_v03() -> Result<()> { + init_conf_retransmission(ProtocolVersion::V03) + } + + fn init_conf_retransmission(protocol_version: ProtocolVersion) -> anyhow::Result<()> { rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - fn keypair() -> anyhow::Result<(SSk, SPk)> { + fn keypair() -> Result<(SSk, SPk)> { let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?; Ok((sk, pk)) @@ -4261,7 +4412,7 @@ mod test { fn proc_initiation( srv: &mut CryptoServer, peer: PeerPtr, - ) -> anyhow::Result> { + ) -> Result> { let mut buf = MsgBuf::zero(); srv.initiate_handshake(peer, buf.as_mut_slice())? .discard_result(); @@ -4331,12 +4482,8 @@ mod test { assert!(res.is_err()); } - fn check_retransmission( - srv: &mut CryptoServer, - ic: &Envelope, - ic_broken: &Envelope, - rc: &Envelope, - ) -> anyhow::Result<()> { + // we this as a closure in orer to use the protocol_version variable in it. + let check_retransmission = |srv: &mut CryptoServer, ic: &Envelope, ic_broken: &Envelope, rc: &Envelope| -> Result<()> { // Processing the same RespHello package again leads to retransmission (i.e. exactly the // same output) let rc_dup = proc_init_conf(srv, ic)?; @@ -4345,14 +4492,14 @@ mod test { // Though if we directly call handle_resp_hello() we get an error since // retransmission is not being handled by the cryptographic code let mut discard_resp_conf = EmptyData::new_zeroed(); - let res = srv.handle_init_conf(&ic.payload, &mut discard_resp_conf); + let res = srv.handle_init_conf(&ic.payload, &mut discard_resp_conf, protocol_version.clone().shake_or_blake()); assert!(res.is_err()); // Obviously, a broken InitConf message should still be rejected check_faulty_proc_init_conf(srv, ic_broken); Ok(()) - } + }; let (ska, pka) = keypair()?; let (skb, pkb) = keypair()?; @@ -4362,8 +4509,8 @@ mod test { let mut b = CryptoServer::new(skb, pkb.clone()); // introduce peers to each other - let b_peer = a.add_peer(None, pkb)?; - let a_peer = b.add_peer(None, pka)?; + let b_peer = a.add_peer(None, pkb, protocol_version.clone())?; + let a_peer = b.add_peer(None, pka, protocol_version.clone())?; // Execute protocol up till the responder confirmation (EmptyData) let ih1 = proc_initiation(&mut a, b_peer)?; diff --git a/rosenpass/tests/api-integration-tests-api-setup.rs b/rosenpass/tests/api-integration-tests-api-setup.rs index bb5ff8c4..e70e77a7 100644 --- a/rosenpass/tests/api-integration-tests-api-setup.rs +++ b/rosenpass/tests/api-integration-tests-api-setup.rs @@ -26,7 +26,7 @@ use rosenpass_util::{ use std::os::fd::{AsFd, AsRawFd}; use tempfile::TempDir; use zerocopy::AsBytes; - +use rosenpass::config::ProtocolVersion; use rosenpass::protocol::SymKey; struct KillChild(std::process::Child); @@ -48,7 +48,16 @@ impl Drop for KillChild { } #[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")?; @@ -96,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(), }], }; @@ -116,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(), }], }; diff --git a/rosenpass/tests/api-integration-tests.rs b/rosenpass/tests/api-integration-tests.rs index e7e02594..99761e24 100644 --- a/rosenpass/tests/api-integration-tests.rs +++ b/rosenpass/tests/api-integration-tests.rs @@ -17,6 +17,7 @@ use tempfile::TempDir; use zerocopy::AsBytes; use rosenpass::protocol::SymKey; +use rosenpass::config::ProtocolVersion; struct KillChild(std::process::Child); @@ -37,7 +38,16 @@ impl Drop for KillChild { } #[test] -fn api_integration_test() -> anyhow::Result<()> { +fn api_integration_test_v02() -> anyhow::Result<()> { + api_integration_test(ProtocolVersion::V02) +} + +fn api_integration_test_v03() -> anyhow::Result<()> { + api_integration_test(ProtocolVersion::V03) +} + + +fn api_integration_test(protocol_version: ProtocolVersion) -> anyhow::Result<()> { rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); let dir = TempDir::with_prefix("rosenpass-api-integration-test")?; @@ -73,6 +83,7 @@ fn api_integration_test() -> anyhow::Result<()> { endpoint: None, pre_shared_key: None, wg: None, + protocol_version: protocol_version.clone(), }], }; @@ -93,6 +104,7 @@ fn api_integration_test() -> anyhow::Result<()> { endpoint: Some(peer_a_endpoint.to_owned()), pre_shared_key: None, wg: None, + protocol_version: protocol_version.clone(), }], }; diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index 23ac4625..294898ff 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -13,13 +13,23 @@ use rosenpass::{ app_server::{ipv4_any_binding, ipv6_any_binding, AppServer, AppServerTest, MAX_B64_KEY_SIZE}, protocol::{SPk, SSk, SymKey}, }; +use rosenpass::config::ProtocolVersion; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::Secret; use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt}; #[test] -fn key_exchange_with_app_server() -> anyhow::Result<()> { +fn key_exchange_with_app_server_v02() -> anyhow::Result<()> { + key_exchange_with_app_server(ProtocolVersion::V02) +} + +#[test] +fn key_exchange_with_app_server_v03() -> anyhow::Result<()> { + key_exchange_with_app_server(ProtocolVersion::V03) +} + +fn key_exchange_with_app_server(protocol_version: ProtocolVersion) -> anyhow::Result<()> { let tmpdir = tempfile::tempdir()?; let outfile_a = tmpdir.path().join("osk_a"); let outfile_b = tmpdir.path().join("osk_b"); @@ -57,7 +67,7 @@ fn key_exchange_with_app_server() -> anyhow::Result<()> { let port = otr_port; let hostname = is_client.then(|| format!("[::1]:{port}")); srv.app_srv - .add_peer(psk, pk, outfile, broker_peer, hostname)?; + .add_peer(psk, pk, outfile, broker_peer, hostname, protocol_version.clone())?; srv.app_srv.event_loop() }) diff --git a/rosenpass/tests/integration_test.rs b/rosenpass/tests/integration_test.rs index 67ddc78e..b45dbd6a 100644 --- a/rosenpass/tests/integration_test.rs +++ b/rosenpass/tests/integration_test.rs @@ -251,7 +251,7 @@ fn check_exchange_under_normal() { fs::remove_dir_all(&tmpdir).unwrap(); } -// check that we can trigger a DoS condition and we can exchange keys under DoS +// check that we can trigger a DoS condition, and we can exchange keys under DoS // This test creates a responder (server) with the feature flag "integration_test_always_under_load" to always be under load condition for the test. #[test] #[serial] diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index 56faf792..a7d7eb29 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -9,20 +9,26 @@ use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_util::result::OkExt; -use rosenpass::protocol::{ - testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, - SPk, SSk, SymKey, Timing, UNENDING, -}; +use rosenpass::protocol::{testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, ProtocolVersion, SPk, SSk, SymKey, Timing, UNENDING}; // TODO: Most of the utility functions in here should probably be moved to // rosenpass::protocol::testutils; #[test] -fn test_successful_exchange_with_poll() -> anyhow::Result<()> { +fn test_successful_exchange_with_poll_v02() -> anyhow::Result<()> { + test_successful_exchange_with_poll(ProtocolVersion::V02) +} + +#[test] +fn test_successful_exchange_with_poll_v03() -> anyhow::Result<()> { + test_successful_exchange_with_poll(ProtocolVersion::V03) +} + +fn test_successful_exchange_with_poll(protocol_version: ProtocolVersion) -> anyhow::Result<()> { // Set security policy for storing secrets; choose the one that is faster for testing rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); - let mut sim = RosenpassSimulator::new()?; + let mut sim = RosenpassSimulator::new(protocol_version)?; sim.poll_loop(150)?; // Poll 75 times let transcript = sim.transcript; @@ -79,12 +85,21 @@ fn test_successful_exchange_with_poll() -> anyhow::Result<()> { } #[test] -fn test_successful_exchange_under_packet_loss() -> anyhow::Result<()> { +fn test_successful_exchange_under_packet_loss_v02() -> anyhow::Result<()> { + test_successful_exchange_under_packet_loss(ProtocolVersion::V02) +} + +#[test] +fn test_successful_exchange_under_packet_loss_v03() -> anyhow::Result<()> { + test_successful_exchange_under_packet_loss(ProtocolVersion::V03) +} + +fn test_successful_exchange_under_packet_loss(protocol_version: ProtocolVersion) -> anyhow::Result<()> { // Set security policy for storing secrets; choose the one that is faster for testing rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); // Create the simulator - let mut sim = RosenpassSimulator::new()?; + let mut sim = RosenpassSimulator::new(protocol_version)?; // Make sure the servers are set to under load condition sim.srv_a.under_load = true; @@ -272,7 +287,7 @@ struct SimulatorServer { impl RosenpassSimulator { /// Set up the simulator - fn new() -> anyhow::Result { + fn new(protocol_version: ProtocolVersion) -> anyhow::Result { // Set up the first server 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())?; @@ -285,8 +300,8 @@ impl RosenpassSimulator { // Generate a PSK and introduce the Peers to each other. let psk = SymKey::random(); - let peer_a = srv_a.add_peer(Some(psk.clone()), peer_b_pk)?; - let peer_b = srv_b.add_peer(Some(psk), peer_a_pk)?; + let peer_a = srv_a.add_peer(Some(psk.clone()), peer_b_pk, protocol_version.clone())?; + let peer_b = srv_b.add_peer(Some(psk), peer_a_pk, protocol_version.clone())?; // Set up the individual server data structures let srv_a = SimulatorServer::new(srv_a, peer_b); @@ -314,8 +329,8 @@ impl RosenpassSimulator { Ok(()) } - /// Every call to poll produces one [TranscriptEvent] and - /// and implicitly adds it to [Self:::transcript] + /// Every call to poll produces one [TranscriptEvent] + /// and implicitly adds it to [Self::transcript] fn poll(&mut self) -> anyhow::Result<&TranscriptEvent> { let ev = TranscriptEvent::begin_poll() .try_fold_with(|| self.poll_focus.poll(self))? From 6d25c13fd172e86a6dbffc4b6756500a86158fec Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 14 Mar 2025 10:47:07 +0100 Subject: [PATCH 040/174] dev(ciphers): make the libcrux implementation of chachapoly return an error instead of panicking when decryption fails. This makes tests decryptions possible. --- .../src/subtle/chacha20poly1305_ietf_libcrux.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs b/ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs index ccf9735e..08082dd5 100644 --- a/ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs +++ b/ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs @@ -1,3 +1,4 @@ +use std::fmt::format; use rosenpass_to::ops::copy_slice; use rosenpass_to::To; @@ -101,12 +102,18 @@ pub fn decrypt( 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(); + let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into()?)); + let crux_iv = C::Iv(nonce.try_into()?); + let crux_tag = match C::Tag::from_slice(mac) { + Ok(tag) => tag, + Err(err) => return Err(anyhow::anyhow!(format!("{:?}", err))), + }; copy_slice(ciphertext).to(plaintext); - libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap(); + let dec_res = libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag); + if dec_res.is_err() { + return Err(anyhow::anyhow!("Decryption failed {:?}", dec_res.err())); + } match crux_key { C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(), From d7398d9bcf9e30a6c40935d937cb034e2ce1b8be Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:27:49 +0100 Subject: [PATCH 041/174] doc(rosenpass): fix typo --- rosenpass/src/app_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 39e042ba..1852ac2c 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -1264,7 +1264,7 @@ impl AppServer { } /// Used as a helper by [Self::event_loop_without_error_handling] when - /// a new output key has been echanged + /// a new output key has been exchanged pub fn output_key( &mut self, peer: AppPeerPtr, From c9aad280b20d22c502c5526e0e6ae6cd2737d6f4 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:29:11 +0100 Subject: [PATCH 042/174] test(rosenpass): adapt gen-ipc-msg-types to fully go through. Explicit test for SHAKE256 still missing --- rosenpass/src/bin/gen-ipc-msg-types.rs | 47 ++++++++++++-------------- rosenpass/tests/gen-ipc-msg-types.rs | 3 +- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index b85e0595..4b3ea476 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -33,7 +33,7 @@ fn print_literal(path: &[&str], shake_or_blake: EitherShakeOrBlake) -> Result<() .map(|chunk| chunk.iter().collect::()) .collect::>(); println!("const {var_name} : RawMsgType = RawMsgType::from_le_bytes(hex!(\"{} {} {} {} {} {} {} {}\"));", - c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); Ok(()) } @@ -77,30 +77,25 @@ impl Tree { /// Helper for generating hash-based message IDs for the IPC API fn main() -> Result<()> { - - fn print_IPC_API_info(shake_or_blake: EitherShakeOrBlake, name: String) -> Result<()> { - let tree = Tree::Branch( - format!("Rosenpass IPC API {}", name).to_owned(), - vec![Tree::Branch( - "Rosenpass Protocol Server".to_owned(), - vec![ - Tree::Leaf("Ping Request".to_owned()), - Tree::Leaf("Ping Response".to_owned()), - Tree::Leaf("Supply Keypair Request".to_owned()), - Tree::Leaf("Supply Keypair Response".to_owned()), - Tree::Leaf("Add Listen Socket Request".to_owned()), - Tree::Leaf("Add Listen Socket Response".to_owned()), - Tree::Leaf("Add Psk Broker Request".to_owned()), - Tree::Leaf("Add Psk Broker Response".to_owned()), - ], - )], - ); + let tree = Tree::Branch( + "Rosenpass IPC API".to_owned(), + vec![Tree::Branch( + "Rosenpass Protocol Server".to_owned(), + vec![ + Tree::Leaf("Ping Request".to_owned()), + Tree::Leaf("Ping Response".to_owned()), + Tree::Leaf("Supply Keypair Request".to_owned()), + Tree::Leaf("Supply Keypair Response".to_owned()), + Tree::Leaf("Add Listen Socket Request".to_owned()), + Tree::Leaf("Add Listen Socket Response".to_owned()), + Tree::Leaf("Add Psk Broker Request".to_owned()), + Tree::Leaf("Add Psk Broker Response".to_owned()), + ], + )], + ); - println!("type RawMsgType = u128;"); - println!(); - tree.gen_code(shake_or_blake) - } - - print_IPC_API_info(EitherShakeOrBlake::Left(SHAKE256Core), " (SHAKE256)".to_owned())?; - print_IPC_API_info(EitherShakeOrBlake::Right(Blake2bCore), " (Blake2b)".to_owned()) + println!("type RawMsgType = u128;"); + println!(); + tree.gen_code(EitherShakeOrBlake::Left(SHAKE256Core))?; + tree.gen_code(EitherShakeOrBlake::Right(Blake2bCore)) } diff --git a/rosenpass/tests/gen-ipc-msg-types.rs b/rosenpass/tests/gen-ipc-msg-types.rs index 3d4629e2..1b46144a 100644 --- a/rosenpass/tests/gen-ipc-msg-types.rs +++ b/rosenpass/tests/gen-ipc-msg-types.rs @@ -1,4 +1,4 @@ -use std::{borrow::Borrow, process::Command}; +use std::{process::Command}; #[test] fn test_gen_ipc_msg_types() -> anyhow::Result<()> { @@ -11,5 +11,6 @@ fn test_gen_ipc_msg_types() -> anyhow::Result<()> { assert!(stdout.contains("type RawMsgType = u128;")); assert!(stdout.contains("const SUPPLY_KEYPAIR_RESPONSE : RawMsgType = RawMsgType::from_le_bytes(hex!(\"f2dc 49bd e261 5f10 40b7 3c16 ec61 edb9\"));")); + // TODO: Also test SHAKE256 here Ok(()) } From 2ddd1488b3e7f8e55be0ba6bfa679bd1de48bac3 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 14 Mar 2025 17:14:48 +0100 Subject: [PATCH 043/174] doc(rosenpass): fix typo --- rosenpass/src/app_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 1852ac2c..e948a1aa 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -333,7 +333,7 @@ pub struct AppServer { /// /// Because the API supports initializing the server with a keypair /// and CryptoServer needs to be initialized with a keypair, the struct - /// struct is wrapped in a ConstructionSite + /// is wrapped in a ConstructionSite pub crypto_site: ConstructionSite, /// The UDP sockets used to send and receive protocol messages pub sockets: Vec, From 44e46895aa49ea245b176ddd945189fb8badbf60 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Fri, 21 Feb 2025 14:56:35 +0100 Subject: [PATCH 044/174] fmt --- cipher-traits/src/keyed_hash.rs | 17 +- ciphers/src/hash_domain.rs | 8 +- ciphers/src/subtle/either_hash.rs | 23 +- .../hash_functions/incorrect_hmac_blake2b.rs | 2 +- .../subtle/hash_functions/infer_keyed_hash.rs | 24 +- .../subtle/hash_functions/keyed_shake256.rs | 18 +- ciphers/src/subtle/hash_functions/mod.rs | 2 +- ciphers/src/subtle/mod.rs | 6 +- initial | 1 + rosenpass/benches/handshake.rs | 4 +- rosenpass/src/app_server.rs | 4 +- rosenpass/src/bin/gen-ipc-msg-types.rs | 2 +- rosenpass/src/hash_domains.rs | 9 +- rosenpass/src/protocol/build_crypto_server.rs | 34 ++- rosenpass/src/protocol/protocol.rs | 254 +++++++++++++----- .../tests/api-integration-tests-api-setup.rs | 4 +- rosenpass/tests/api-integration-tests.rs | 3 +- rosenpass/tests/app_server_example.rs | 12 +- rosenpass/tests/poll_example.rs | 9 +- 19 files changed, 310 insertions(+), 126 deletions(-) create mode 160000 initial diff --git a/cipher-traits/src/keyed_hash.rs b/cipher-traits/src/keyed_hash.rs index dcc0e953..d9785568 100644 --- a/cipher-traits/src/keyed_hash.rs +++ b/cipher-traits/src/keyed_hash.rs @@ -1,7 +1,11 @@ pub trait KeyedHash { type Error; - - fn keyed_hash(key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error>; + + fn keyed_hash( + key: &[u8; KEY_LEN], + data: &[u8], + out: &mut [u8; HASH_LEN], + ) -> Result<(), Self::Error>; } pub trait KeyedHashInstance { @@ -9,5 +13,10 @@ pub trait KeyedHashInstance { type OutputType; type Error; - fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error>; -} \ No newline at end of file + fn keyed_hash( + &self, + key: &[u8; KEY_LEN], + data: &[u8], + out: &mut [u8; HASH_LEN], + ) -> Result<(), Self::Error>; +} diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 55443fd7..7c5c04f4 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -4,9 +4,9 @@ use rosenpass_to::To; use crate::keyed_hash as hash; +use crate::subtle::either_hash::EitherShakeOrBlake; pub use hash::KEY_LEN; use rosenpass_cipher_traits::KeyedHashInstance; -use crate::subtle::either_hash::EitherShakeOrBlake; /// ///```rust @@ -125,7 +125,11 @@ impl SecretHashDomain { /// 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: EitherShakeOrBlake) -> Result { + pub fn invoke_primitive( + k: &[u8], + d: &[u8], + hash_choice: EitherShakeOrBlake, + ) -> Result { let mut new_secret_key = Secret::zero(); hash_choice.keyed_hash(k.try_into()?, d, new_secret_key.secret_mut())?; let mut r = SecretHashDomain(new_secret_key, hash_choice); diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs index 6539d53e..09023774 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/either_hash.rs @@ -1,18 +1,22 @@ -use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; -use anyhow::Result; use crate::subtle::hash_functions::keyed_shake256::SHAKE256Core; use crate::subtle::incorrect_hmac_blake2b::Blake2bCore; +use anyhow::Result; +use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; #[derive(Debug, Eq, PartialEq)] -pub enum EitherHash, - R: KeyedHash> -{ + R: KeyedHash, +> { Left(L), Right(R), } -impl KeyedHashInstance for EitherHash +impl KeyedHashInstance + for EitherHash where L: KeyedHash, R: KeyedHash, @@ -21,7 +25,12 @@ where type OutputType = [u8; HASH_LEN]; type Error = Error; - fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error> { + fn keyed_hash( + &self, + key: &[u8; KEY_LEN], + data: &[u8], + out: &mut [u8; HASH_LEN], + ) -> Result<(), Self::Error> { match self { Self::Left(_) => L::keyed_hash(key, data, out), Self::Right(_) => R::keyed_hash(key, data, out), diff --git a/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs index 343945cb..50db959d 100644 --- a/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs @@ -1,8 +1,8 @@ use anyhow::ensure; -use zeroize::Zeroizing; use rosenpass_cipher_traits::KeyedHash; use rosenpass_constant_time::xor; use rosenpass_to::{ops::copy_slice, with_destination, To}; +use zeroize::Zeroizing; use crate::subtle::hash_functions::blake2b; use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; diff --git a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs index 4422881d..b38a5ae7 100644 --- a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs +++ b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs @@ -1,6 +1,6 @@ -use std::marker::PhantomData; use anyhow::Result; use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; +use std::marker::PhantomData; /// This is a helper to allow for type parameter inference when calling functions /// that need a [KeyedHash]. @@ -19,11 +19,13 @@ impl InferKeyedHash, { - pub const KEY_LEN : usize = KEY_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 } + Self { + _phantom_keyed_hasher: PhantomData, + } } /// This just forwards to [KeyedHash::keyed_hash] of the type parameter `Static` @@ -45,7 +47,12 @@ where } } -impl> KeyedHashInstance for InferKeyedHash { +impl< + const KEY_LEN: usize, + const HASH_LEN: usize, + Static: KeyedHash, + > KeyedHashInstance for InferKeyedHash +{ type KeyType = [u8; KEY_LEN]; type OutputType = [u8; HASH_LEN]; type Error = anyhow::Error; @@ -57,7 +64,8 @@ impl Default for InferKeyedHash +impl Default + for InferKeyedHash where Static: KeyedHash, { @@ -66,7 +74,8 @@ where } } -impl Clone for InferKeyedHash +impl Clone + for InferKeyedHash where Static: KeyedHash, { @@ -75,7 +84,8 @@ where } } -impl Copy for InferKeyedHash +impl Copy + for InferKeyedHash where Static: KeyedHash, { diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/hash_functions/keyed_shake256.rs index 7ff188de..60016c7d 100644 --- a/ciphers/src/subtle/hash_functions/keyed_shake256.rs +++ b/ciphers/src/subtle/hash_functions/keyed_shake256.rs @@ -1,13 +1,15 @@ +use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; use anyhow::ensure; +use rosenpass_cipher_traits::KeyedHash; use sha3::digest::{ExtendableOutput, Update, XofReader}; use sha3::Shake256; -use rosenpass_cipher_traits::KeyedHash; -use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; #[derive(Clone, Debug, PartialEq, Eq)] pub struct SHAKE256Core; -impl KeyedHash for SHAKE256Core { +impl KeyedHash + for SHAKE256Core +{ type Error = anyhow::Error; /// TODO: Rework test @@ -36,7 +38,11 @@ impl KeyedHash f /// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87]; /// # assert_eq!(hash_data, expected_hash); /// ``` - fn keyed_hash(key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<(), Self::Error> { + 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); @@ -59,7 +65,6 @@ impl KeyedHash f } } - impl SHAKE256Core { pub fn new() -> Self { Self @@ -83,7 +88,7 @@ impl SHAKE256Core = -InferKeyedHash, KEY_LEN, HASH_LEN>; + InferKeyedHash, KEY_LEN, HASH_LEN>; /// TODO: Documentation and more interesting test /// ```rust @@ -102,4 +107,3 @@ InferKeyedHash, KEY_LEN, HASH_LEN>; /// # assert_eq!(hash_data, expected_hash); /// ``` pub type SHAKE256_32 = SHAKE256<32, 32>; - diff --git a/ciphers/src/subtle/hash_functions/mod.rs b/ciphers/src/subtle/hash_functions/mod.rs index af0ccb7b..6ca1c10b 100644 --- a/ciphers/src/subtle/hash_functions/mod.rs +++ b/ciphers/src/subtle/hash_functions/mod.rs @@ -1,4 +1,4 @@ pub mod blake2b; pub mod incorrect_hmac_blake2b; +mod infer_keyed_hash; pub mod keyed_shake256; -mod infer_keyed_hash; \ No newline at end of file diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index dbd4a34b..bc4de117 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -8,8 +8,8 @@ pub mod chacha20poly1305_ietf; #[cfg(feature = "experiment_libcrux")] pub mod chacha20poly1305_ietf_libcrux; -pub mod xchacha20poly1305_ietf; -mod hash_functions; pub mod either_hash; +mod hash_functions; +pub mod xchacha20poly1305_ietf; -pub use hash_functions::{blake2b, incorrect_hmac_blake2b, keyed_shake256}; \ No newline at end of file +pub use hash_functions::{blake2b, incorrect_hmac_blake2b, keyed_shake256}; diff --git a/initial b/initial new file mode 160000 index 00000000..f1122f1f --- /dev/null +++ b/initial @@ -0,0 +1 @@ +Subproject commit f1122f1f62fb210a02891ea2101e4f699adf772e diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index 28ca2351..5756960b 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -1,5 +1,7 @@ use anyhow::Result; -use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey}; +use rosenpass::protocol::{ + CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, +}; use std::ops::DerefMut; use rosenpass_cipher_traits::Kem; diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index e948a1aa..58fc8ed1 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -42,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::{ @@ -50,7 +51,6 @@ use crate::{ }; use rosenpass_util::attempt; use rosenpass_util::b64::B64Display; -use crate::config::ProtocolVersion; /// The maximum size of a base64 encoded symmetric key (estimate) pub const MAX_B64_KEY_SIZE: usize = 32 * 5 / 3; @@ -1043,7 +1043,7 @@ impl AppServer { outfile: Option, broker_peer: Option, hostname: Option, - protocol_version: ProtocolVersion + protocol_version: ProtocolVersion, ) -> anyhow::Result { let PeerPtr(pn) = match &mut self.crypto_site { ConstructionSite::Void => bail!("Crypto server construction site is void"), diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index 4b3ea476..5cecdeb4 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -1,10 +1,10 @@ use anyhow::{Context, Result}; use heck::ToShoutySnakeCase; -use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN}; use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; +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]> { diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index b9ed2617..bd959680 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -103,7 +103,7 @@ macro_rules! hash_domain { /// used in various places in the rosenpass protocol. /// /// This is generally used to create further hash-domains for specific purposes. See -/// +/// /// TODO: Update documentation /// /// # Examples @@ -114,10 +114,11 @@ macro_rules! hash_domain { pub fn protocol(hash_choice: EitherShakeOrBlake) -> Result { // TODO: Update this string that is mixed in? match hash_choice { - EitherShakeOrBlake::Left(SHAKE256Core) => HashDomain::zero(hash_choice).mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()), - EitherShakeOrBlake::Right(Blake2bCore) => HashDomain::zero(hash_choice).mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 Blake2b".as_bytes()), + EitherShakeOrBlake::Left(SHAKE256Core) => HashDomain::zero(hash_choice) + .mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()), + EitherShakeOrBlake::Right(Blake2bCore) => HashDomain::zero(hash_choice) + .mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 Blake2b".as_bytes()), } - } hash_domain_ns!( diff --git a/rosenpass/src/protocol/build_crypto_server.rs b/rosenpass/src/protocol/build_crypto_server.rs index 835cfc1e..f372e4ea 100644 --- a/rosenpass/src/protocol/build_crypto_server.rs +++ b/rosenpass/src/protocol/build_crypto_server.rs @@ -1,11 +1,11 @@ +use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey}; +use crate::config::ProtocolVersion; use rosenpass_util::{ build::Build, mem::{DiscardResultExt, SwapWithDefaultExt}, result::ensure_or, }; use thiserror::Error; -use crate::config::ProtocolVersion; -use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey}; #[derive(Debug, Clone)] /// A pair of matching public/secret keys used to launch the crypto server. @@ -187,7 +187,15 @@ impl Build for BuildCryptoServer { let mut srv = CryptoServer::new(sk, pk); - for (idx, PeerParams { psk, pk , protocol_version}) in self.peers.into_iter().enumerate() { + 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.") } @@ -337,14 +345,28 @@ impl BuildCryptoServer { /// assert_eq!(peer.spkt, public_key); /// assert_eq!(peer_psk.secret(), pre_shared_key.secret()); /// ``` - pub fn with_added_peer(&mut self, psk: Option, pk: SPk, protocol_version: ProtocolVersion) -> &mut Self { + pub fn with_added_peer( + &mut self, + psk: Option, + pk: SPk, + protocol_version: ProtocolVersion, + ) -> &mut Self { // TODO: Check here already whether peer was already added - self.peers.push(PeerParams { psk, pk, protocol_version }); + self.peers.push(PeerParams { + psk, + pk, + protocol_version, + }); self } /// 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, pk: SPk, protocol_version: ProtocolVersion) -> PeerPtr { + pub fn add_peer( + &mut self, + psk: Option, + pk: SPk, + protocol_version: ProtocolVersion, + ) -> PeerPtr { let id = PeerPtr(self.peers.len()); self.with_added_peer(psk, pk, protocol_version); id diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 7a3680cc..380ea56b 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -19,11 +19,15 @@ use std::{ use anyhow::{bail, ensure, Context, Result}; use rand::Fill as Randomize; +use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::keyed_hash; +use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; +use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_constant_time as constant_time; use rosenpass_secret_memory::{Public, PublicBox, Secret}; @@ -33,10 +37,6 @@ use rosenpass_util::functional::ApplyExt; use rosenpass_util::mem::DiscardResultExt; use rosenpass_util::{cat, mem::cpy_min, time::Timebase}; use zerocopy::{AsBytes, FromBytes, Ref}; -use rosenpass_ciphers::subtle::either_hash::{EitherShakeOrBlake}; -use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; -use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; -use crate::{hash_domains, msgs::*, RosenpassError}; // CONSTANTS & SETTINGS ////////////////////////// @@ -369,7 +369,7 @@ pub enum IndexKey { #[derive(Debug, Clone)] pub enum ProtocolVersion { V02, - V03 + V03, } impl ProtocolVersion { @@ -1434,7 +1434,7 @@ impl CryptoServer { } /// Add a peer with an optional pre shared key (`psk`) and its public key (`pk`) - /// + /// /// TODO: Adapt documentation /// /// ``` @@ -1460,7 +1460,12 @@ impl CryptoServer { /// /// Ok::<(), anyhow::Error>(()) /// ``` - pub fn add_peer(&mut self, psk: Option, pk: SPk, protocol_version: ProtocolVersion) -> Result { + pub fn add_peer( + &mut self, + psk: Option, + pk: SPk, + protocol_version: ProtocolVersion, + ) -> Result { let peer = Peer { psk: psk.unwrap_or_else(SymKey::zero), spkt: pk, @@ -2306,7 +2311,7 @@ impl CryptoServer { log::debug!("Rx {:?}, processing", msg_type); let mut msg_out = truncating_cast_into::>(tx_buf)?; - + let peer = match msg_type { Ok(MsgType::InitHello) => { let msg_in: Ref<&[u8], Envelope> = @@ -2314,23 +2319,31 @@ impl CryptoServer { // At this point, we do not know the hash functon used by the peer, thus we try both, // with a preference for SHAKE256. - let peer_shake256 = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Left(SHAKE256Core)); + let peer_shake256 = self.handle_init_hello( + &msg_in.payload, + &mut msg_out.payload, + EitherShakeOrBlake::Left(SHAKE256Core), + ); let (peer, peer_hash_choice) = match peer_shake256 { - Ok(peer ) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), + Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), Err(_) => { - let peer_blake2b = self.handle_init_hello(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Right(Blake2bCore)); + let peer_blake2b = self.handle_init_hello( + &msg_in.payload, + &mut msg_out.payload, + EitherShakeOrBlake::Right(Blake2bCore), + ); match peer_blake2b { Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)), - Err(_) => bail!("No valid hash function found for InitHello") + Err(_) => bail!("No valid hash function found for InitHello"), } } }; // Now, we make sure that the hash function used by the peer is the same as the one // that is specified in the local configuration. self.verify_hash_choice_match(peer, peer_hash_choice.clone())?; - + ensure!(msg_in.check_seal(self, peer_hash_choice)?, seal_broken); - + len = self.seal_and_commit_msg(peer, MsgType::RespHello, &mut msg_out)?; peer } @@ -2340,8 +2353,11 @@ impl CryptoServer { let mut msg_out = truncating_cast_into::>(tx_buf)?; let peer = self.handle_resp_hello(&msg_in.payload, &mut msg_out.payload)?; - ensure!(msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, seal_broken); - + ensure!( + msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, + seal_broken + ); + len = self.seal_and_commit_msg(peer, MsgType::InitConf, &mut msg_out)?; peer.hs() .store_msg_for_retransmission(self, &msg_out.as_bytes()[..len])?; @@ -2351,7 +2367,6 @@ impl CryptoServer { Ok(MsgType::InitConf) => { let msg_in: Ref<&[u8], Envelope> = Ref::new(rx_buf).ok_or(RosenpassError::BufferSizeMismatch)?; - let mut msg_out = truncating_cast_into::>(tx_buf)?; @@ -2360,7 +2375,13 @@ impl CryptoServer { // Cached response; copy out of cache Some(cached) => { let peer = cached.peer(); - ensure!(msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, seal_broken); + ensure!( + msg_in.check_seal( + self, + peer.get(self).protocol_version.shake_or_blake() + )?, + seal_broken + ); let cached = cached .get(self) .map(|v| v.response.borrow()) @@ -2374,14 +2395,22 @@ impl CryptoServer { None => { // At this point, we do not know the hash functon used by the peer, thus we try both, // with a preference for SHAKE256. - let peer_shake256 = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Left(SHAKE256Core)); + let peer_shake256 = self.handle_init_conf( + &msg_in.payload, + &mut msg_out.payload, + EitherShakeOrBlake::Left(SHAKE256Core), + ); let (peer, peer_hash_choice) = match peer_shake256 { Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), Err(_) => { - let peer_blake2b = self.handle_init_conf(&msg_in.payload, &mut msg_out.payload, EitherShakeOrBlake::Right(Blake2bCore)); + let peer_blake2b = self.handle_init_conf( + &msg_in.payload, + &mut msg_out.payload, + EitherShakeOrBlake::Right(Blake2bCore), + ); match peer_blake2b { Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)), - Err(_) => bail!("No valid hash function found for InitHello") + Err(_) => bail!("No valid hash function found for InitHello"), } } }; @@ -2389,7 +2418,7 @@ impl CryptoServer { // that is specified in the local configuration. self.verify_hash_choice_match(peer, peer_hash_choice.clone())?; ensure!(msg_in.check_seal(self, peer_hash_choice)?, seal_broken); - + KnownInitConfResponsePtr::insert_for_request_msg( self, peer, @@ -2428,9 +2457,13 @@ impl CryptoServer { resp: if len == 0 { None } else { Some(len) }, }) } - + /// TODO documentation - fn verify_hash_choice_match(&self, peer: PeerPtr, peer_hash_choice: EitherShakeOrBlake) -> Result<()> { + fn verify_hash_choice_match( + &self, + peer: PeerPtr, + peer_hash_choice: EitherShakeOrBlake, + ) -> Result<()> { match peer.get(self).protocol_version.shake_or_blake() { EitherShakeOrBlake::Left(SHAKE256Core) => match peer_hash_choice { EitherShakeOrBlake::Left(SHAKE256Core) => Ok(()), @@ -2439,7 +2472,7 @@ impl CryptoServer { EitherShakeOrBlake::Right(Blake2bCore) => match peer_hash_choice { EitherShakeOrBlake::Left(SHAKE256Core) => bail!("Hash function mismatch"), EitherShakeOrBlake::Right(Blake2bCore) => Ok(()), - } + }, } } @@ -3209,7 +3242,11 @@ where M: AsBytes + FromBytes, { /// Internal business logic: Check the message authentication code produced by [Self::seal] - pub fn check_seal(&self, srv: &CryptoServer, shake_or_blake: EitherShakeOrBlake) -> Result { + pub fn check_seal( + &self, + srv: &CryptoServer, + shake_or_blake: EitherShakeOrBlake, + ) -> Result { let expected = hash_domains::mac(shake_or_blake)? .mix(srv.spkm.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; @@ -3257,21 +3294,31 @@ impl HandshakeState { /// Initialize the handshake state with the responder public key and the protocol domain /// separator pub fn init(&mut self, spkr: &[u8]) -> Result<&mut Self> { - self.ck = hash_domains::ckinit(self.ck.shake_or_blake().clone())?.turn_secret().mix(spkr)?.dup(); + self.ck = hash_domains::ckinit(self.ck.shake_or_blake().clone())? + .turn_secret() + .mix(spkr)? + .dup(); Ok(self) } /// Mix some data into the chaining key. This is used for mixing cryptographic keys and public /// data alike into the chaining key pub fn mix(&mut self, a: &[u8]) -> Result<&mut Self> { - self.ck = self.ck.mix(&hash_domains::mix(self.ck.shake_or_blake().clone())?)?.mix(a)?.dup(); + self.ck = self + .ck + .mix(&hash_domains::mix(self.ck.shake_or_blake().clone())?)? + .mix(a)? + .dup(); Ok(self) } /// Encrypt some data with a value derived from the current chaining key and mix that data /// into the protocol state. pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> { - let k = self.ck.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?.into_secret(); + let k = self + .ck + .mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)? + .into_secret(); aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?; self.mix(ct) } @@ -3281,7 +3328,10 @@ impl HandshakeState { /// Makes sure that the same values are mixed into the chaining that where mixed in on the /// sender side. pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> { - let k = self.ck.mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)?.into_secret(); + let k = self + .ck + .mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)? + .into_secret(); aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?; self.mix(ct) } @@ -3373,7 +3423,7 @@ impl HandshakeState { biscuit_ct: &[u8], sidi: SessionId, sidr: SessionId, - shake_or_blake: EitherShakeOrBlake + shake_or_blake: EitherShakeOrBlake, ) -> Result<(PeerPtr, BiscuitId, HandshakeState)> { // The first bit of the biscuit indicates which biscuit key was used let bk = BiscuitKeyPtr(((biscuit_ct[0] & 0b1000_0000) >> 7) as usize); @@ -3404,7 +3454,11 @@ impl HandshakeState { .find_peer(pid) // TODO: FindPeer should return a Result<()> .with_context(|| format!("Could not decode biscuit for peer {pid:?}: No such peer."))?; - let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(&biscuit.ck), peer.get(srv).protocol_version.shake_or_blake()).dup(); + let ck = SecretHashDomain::danger_from_secret( + Secret::from_slice(&biscuit.ck), + peer.get(srv).protocol_version.shake_or_blake(), + ) + .dup(); // Reconstruct the handshake state let mut hs = Self { sidi, sidr, ck }; hs.mix(biscuit_ct)?; @@ -3417,10 +3471,19 @@ impl HandshakeState { /// This called by either party. /// /// `role` indicates whether the local peer was an initiator or responder in the handshake. - pub fn enter_live(self, srv: &CryptoServer, role: HandshakeRole, either_shake_or_blake: EitherShakeOrBlake) -> Result { + pub fn enter_live( + self, + srv: &CryptoServer, + role: HandshakeRole, + either_shake_or_blake: EitherShakeOrBlake, + ) -> Result { let HandshakeState { ck, sidi, sidr } = self; - let tki = ck.mix(&hash_domains::ini_enc(either_shake_or_blake.clone())?)?.into_secret(); - let tkr = ck.mix(&hash_domains::res_enc(either_shake_or_blake)?)?.into_secret(); + let tki = ck + .mix(&hash_domains::ini_enc(either_shake_or_blake.clone())?)? + .into_secret(); + let tkr = ck + .mix(&hash_domains::res_enc(either_shake_or_blake)?)? + .into_secret(); let created_at = srv.timebase.now(); let (ntx, nrx) = (0, 0); let (mysid, peersid, ktx, krx) = match role { @@ -3458,7 +3521,12 @@ impl CryptoServer { .get(self) .as_ref() .with_context(|| format!("No current session for peer {:?}", peer))?; - Ok(session.ck.mix(&hash_domains::osk(peer.get(self).protocol_version.shake_or_blake())?)?.into_secret()) + Ok(session + .ck + .mix(&hash_domains::osk( + peer.get(self).protocol_version.shake_or_blake(), + )?)? + .into_secret()) } } @@ -3466,7 +3534,10 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Kicks of the handshake /// on the initiator side, producing the InitHello message. pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { - let mut hs = InitiatorHandshake::zero_with_timestamp(self, peer.get(self).protocol_version.shake_or_blake()); + let mut hs = InitiatorHandshake::zero_with_timestamp( + self, + peer.get(self).protocol_version.shake_or_blake(), + ); // IHI1 hs.core.init(peer.get(self).spkt.deref())?; @@ -3490,8 +3561,11 @@ impl CryptoServer { )?; // IHI6 - hs.core - .encrypt_and_mix(ih.pidic.as_mut_slice(), self.pidm(peer.get(self).protocol_version.shake_or_blake())?.as_ref())?; + hs.core.encrypt_and_mix( + ih.pidic.as_mut_slice(), + self.pidm(peer.get(self).protocol_version.shake_or_blake())? + .as_ref(), + )?; // IHI7 hs.core @@ -3510,7 +3584,12 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a /// [RespHello] message on the responder side. /// TODO: Document Hash Functon usage - pub fn handle_init_hello(&mut self, ih: &InitHello, rh: &mut RespHello, shake_or_blake: EitherShakeOrBlake) -> Result { + pub fn handle_init_hello( + &mut self, + ih: &InitHello, + rh: &mut RespHello, + shake_or_blake: EitherShakeOrBlake, + ) -> Result { let mut core = HandshakeState::zero(shake_or_blake); core.sidi = SessionId::from_slice(&ih.sidi); @@ -3655,8 +3734,14 @@ impl CryptoServer { // we still need it for InitConf message retransmission to function. // ICI7 - peer.session() - .insert(self, core.enter_live(self, HandshakeRole::Initiator, peer.get(self).protocol_version.shake_or_blake())?)?; + peer.session().insert( + self, + core.enter_live( + self, + HandshakeRole::Initiator, + peer.get(self).protocol_version.shake_or_blake(), + )?, + )?; hs_mut!().core.erase(); hs_mut!().next = HandshakeStateMachine::RespConf; @@ -3669,7 +3754,12 @@ impl CryptoServer { /// This concludes the handshake on the cryptographic level; the [EmptyData] message is just /// an acknowledgement message telling the initiator to stop performing retransmissions. /// TODO: documentation - pub fn handle_init_conf(&mut self, ic: &InitConf, rc: &mut EmptyData, shake_or_blake: EitherShakeOrBlake) -> Result { + pub fn handle_init_conf( + &mut self, + ic: &InitConf, + rc: &mut EmptyData, + shake_or_blake: EitherShakeOrBlake, + ) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit( @@ -3702,8 +3792,14 @@ impl CryptoServer { peer.get_mut(self).biscuit_used = biscuit_no; // ICR7 - peer.session() - .insert(self, core.enter_live(self, HandshakeRole::Responder, peer.get(self).protocol_version.shake_or_blake())?)?; + peer.session().insert( + self, + core.enter_live( + self, + HandshakeRole::Responder, + peer.get(self).protocol_version.shake_or_blake(), + )?, + )?; // TODO: This should be part of the protocol specification. // Abort any ongoing handshake from initiator role peer.hs().take(self); @@ -3751,13 +3847,20 @@ impl CryptoServer { /// message then terminates the handshake. /// /// The EmptyData message is just there to tell the initiator to abort retransmissions. - pub fn handle_resp_conf(&mut self, msg_in: &Ref<&[u8], Envelope>, seal_broken: String) -> Result { + pub fn handle_resp_conf( + &mut self, + msg_in: &Ref<&[u8], Envelope>, + seal_broken: String, + ) -> Result { let rc: &EmptyData = &msg_in.payload; let sid = SessionId::from_slice(&rc.sid); let hs = self .lookup_handshake(sid) .with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?; - ensure!(msg_in.check_seal(self, hs.peer().get(self).protocol_version.shake_or_blake())?, seal_broken); + ensure!( + msg_in.check_seal(self, hs.peer().get(self).protocol_version.shake_or_blake())?, + seal_broken + ); let ses = hs.peer().session(); let exp = hs.get(self).as_ref().map(|h| h.next); @@ -3840,7 +3943,9 @@ impl CryptoServer { }?; let spkt = peer.get(self).spkt.deref(); - let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))?.mix(spkt)?.into_value(); + let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))? + .mix(spkt)? + .into_value(); let cookie_value = peer.cv().update_mut(self).unwrap(); xaead::decrypt(cookie_value, &cookie_key, &mac, &cr.inner.cookie_encrypted)?; @@ -3969,7 +4074,6 @@ mod test { handles_incorrect_size_messages(ProtocolVersion::V03) } - /// Ensure that the protocol implementation can deal with truncated /// messages and with overlong messages. /// @@ -4268,20 +4372,21 @@ mod test { assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young); - let expected_cookie_value = hash_domains::cookie_value(protocol_version.shake_or_blake()) - .unwrap() - .mix( - b.active_or_retired_cookie_secrets()[0] - .unwrap() - .get(&b) - .value - .secret(), - ) - .unwrap() - .mix(ip_addr_port_a.encode()) - .unwrap() - .into_value()[..16] - .to_vec(); + let expected_cookie_value = + hash_domains::cookie_value(protocol_version.shake_or_blake()) + .unwrap() + .mix( + b.active_or_retired_cookie_secrets()[0] + .unwrap() + .get(&b) + .value + .secret(), + ) + .unwrap() + .mix(ip_addr_port_a.encode()) + .unwrap() + .into_value()[..16] + .to_vec(); assert_eq!( PeerPtr(0).cv().get(&a).map(|x| &x.value.secret()[..]), @@ -4331,7 +4436,9 @@ mod test { cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) } - fn cookie_reply_mechanism_initiator_bails_on_message_under_load(protocol_version: ProtocolVersion) { + fn cookie_reply_mechanism_initiator_bails_on_message_under_load( + protocol_version: ProtocolVersion, + ) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { @@ -4409,10 +4516,7 @@ mod test { Ok((sk, pk)) } - fn proc_initiation( - srv: &mut CryptoServer, - peer: PeerPtr, - ) -> Result> { + fn proc_initiation(srv: &mut CryptoServer, peer: PeerPtr) -> Result> { let mut buf = MsgBuf::zero(); srv.initiate_handshake(peer, buf.as_mut_slice())? .discard_result(); @@ -4482,8 +4586,12 @@ mod test { assert!(res.is_err()); } - // we this as a closure in orer to use the protocol_version variable in it. - let check_retransmission = |srv: &mut CryptoServer, ic: &Envelope, ic_broken: &Envelope, rc: &Envelope| -> Result<()> { + // we this as a closure in orer to use the protocol_version variable in it. + let check_retransmission = |srv: &mut CryptoServer, + ic: &Envelope, + ic_broken: &Envelope, + rc: &Envelope| + -> Result<()> { // Processing the same RespHello package again leads to retransmission (i.e. exactly the // same output) let rc_dup = proc_init_conf(srv, ic)?; @@ -4492,7 +4600,11 @@ mod test { // Though if we directly call handle_resp_hello() we get an error since // retransmission is not being handled by the cryptographic code let mut discard_resp_conf = EmptyData::new_zeroed(); - let res = srv.handle_init_conf(&ic.payload, &mut discard_resp_conf, protocol_version.clone().shake_or_blake()); + let res = srv.handle_init_conf( + &ic.payload, + &mut discard_resp_conf, + protocol_version.clone().shake_or_blake(), + ); assert!(res.is_err()); // Obviously, a broken InitConf message should still be rejected diff --git a/rosenpass/tests/api-integration-tests-api-setup.rs b/rosenpass/tests/api-integration-tests-api-setup.rs index e70e77a7..c4f2d22a 100644 --- a/rosenpass/tests/api-integration-tests-api-setup.rs +++ b/rosenpass/tests/api-integration-tests-api-setup.rs @@ -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, @@ -26,8 +28,6 @@ use rosenpass_util::{ use std::os::fd::{AsFd, AsRawFd}; use tempfile::TempDir; use zerocopy::AsBytes; -use rosenpass::config::ProtocolVersion; -use rosenpass::protocol::SymKey; struct KillChild(std::process::Child); diff --git a/rosenpass/tests/api-integration-tests.rs b/rosenpass/tests/api-integration-tests.rs index 99761e24..de02832b 100644 --- a/rosenpass/tests/api-integration-tests.rs +++ b/rosenpass/tests/api-integration-tests.rs @@ -16,8 +16,8 @@ use rosenpass_util::{mem::DiscardResultExt, zerocopy::ZerocopySliceExt}; use tempfile::TempDir; use zerocopy::AsBytes; -use rosenpass::protocol::SymKey; use rosenpass::config::ProtocolVersion; +use rosenpass::protocol::SymKey; struct KillChild(std::process::Child); @@ -46,7 +46,6 @@ fn api_integration_test_v03() -> anyhow::Result<()> { api_integration_test(ProtocolVersion::V03) } - fn api_integration_test(protocol_version: ProtocolVersion) -> anyhow::Result<()> { rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index 294898ff..31fc78eb 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -9,11 +9,11 @@ use std::{ }; use anyhow::ensure; +use rosenpass::config::ProtocolVersion; use rosenpass::{ app_server::{ipv4_any_binding, ipv6_any_binding, AppServer, AppServerTest, MAX_B64_KEY_SIZE}, protocol::{SPk, SSk, SymKey}, }; -use rosenpass::config::ProtocolVersion; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::Secret; @@ -66,8 +66,14 @@ fn key_exchange_with_app_server(protocol_version: ProtocolVersion) -> anyhow::Re let outfile = Some(osk); let port = otr_port; let hostname = is_client.then(|| format!("[::1]:{port}")); - srv.app_srv - .add_peer(psk, pk, outfile, broker_peer, hostname, protocol_version.clone())?; + srv.app_srv.add_peer( + psk, + pk, + outfile, + broker_peer, + hostname, + protocol_version.clone(), + )?; srv.app_srv.event_loop() }) diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index a7d7eb29..f594c4c4 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -9,7 +9,10 @@ use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_util::result::OkExt; -use rosenpass::protocol::{testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, ProtocolVersion, SPk, SSk, SymKey, Timing, UNENDING}; +use rosenpass::protocol::{ + testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, + ProtocolVersion, SPk, SSk, SymKey, Timing, UNENDING, +}; // TODO: Most of the utility functions in here should probably be moved to // rosenpass::protocol::testutils; @@ -94,7 +97,9 @@ fn test_successful_exchange_under_packet_loss_v03() -> anyhow::Result<()> { test_successful_exchange_under_packet_loss(ProtocolVersion::V03) } -fn test_successful_exchange_under_packet_loss(protocol_version: ProtocolVersion) -> anyhow::Result<()> { +fn test_successful_exchange_under_packet_loss( + protocol_version: ProtocolVersion, +) -> anyhow::Result<()> { // Set security policy for storing secrets; choose the one that is faster for testing rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); From b94ddd980d7bf285a283ae55e5245b7e8f87535e Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Fri, 21 Feb 2025 14:59:55 +0100 Subject: [PATCH 045/174] remove superfluous associated types --- cipher-traits/src/keyed_hash.rs | 2 -- ciphers/src/subtle/either_hash.rs | 2 -- ciphers/src/subtle/hash_functions/infer_keyed_hash.rs | 2 -- 3 files changed, 6 deletions(-) diff --git a/cipher-traits/src/keyed_hash.rs b/cipher-traits/src/keyed_hash.rs index d9785568..ba4d52e0 100644 --- a/cipher-traits/src/keyed_hash.rs +++ b/cipher-traits/src/keyed_hash.rs @@ -9,8 +9,6 @@ pub trait KeyedHash { } pub trait KeyedHashInstance { - type KeyType; - type OutputType; type Error; fn keyed_hash( diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs index 09023774..e2d5549a 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/either_hash.rs @@ -21,8 +21,6 @@ where L: KeyedHash, R: KeyedHash, { - type KeyType = [u8; KEY_LEN]; - type OutputType = [u8; HASH_LEN]; type Error = Error; fn keyed_hash( diff --git a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs index b38a5ae7..07183da2 100644 --- a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs +++ b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs @@ -53,8 +53,6 @@ impl< Static: KeyedHash, > KeyedHashInstance for InferKeyedHash { - type KeyType = [u8; KEY_LEN]; - type OutputType = [u8; HASH_LEN]; type Error = anyhow::Error; fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<()> { From 32ae8f7051d091fe3a746b89646e97d7e6b029ed Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 24 Feb 2025 12:42:49 +0100 Subject: [PATCH 046/174] Rename hash selection enum to KeyedHash, restructure traits --- Cargo.lock | 2 +- cipher-traits/src/algorithms.rs | 20 +++++ cipher-traits/src/lib.rs | 8 +- cipher-traits/src/primitives.rs | 1 + .../src/{ => primitives}/keyed_hash.rs | 0 ciphers/src/hash_domain.rs | 82 +++++++++---------- ciphers/src/subtle/either_hash.rs | 55 +++++-------- .../hash_functions/incorrect_hmac_blake2b.rs | 2 +- .../subtle/hash_functions/infer_keyed_hash.rs | 6 +- .../subtle/hash_functions/keyed_shake256.rs | 8 +- rosenpass/src/hash_domains.rs | 14 ++-- rosenpass/src/protocol/protocol.rs | 82 ++++++++----------- 12 files changed, 144 insertions(+), 136 deletions(-) create mode 100644 cipher-traits/src/algorithms.rs create mode 100644 cipher-traits/src/primitives.rs rename cipher-traits/src/{ => primitives}/keyed_hash.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 4e6ec911..49342756 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" diff --git a/cipher-traits/src/algorithms.rs b/cipher-traits/src/algorithms.rs new file mode 100644 index 00000000..14ccdada --- /dev/null +++ b/cipher-traits/src/algorithms.rs @@ -0,0 +1,20 @@ +pub mod keyed_hash_blake2b { + use crate::primitives::keyed_hash::*; + + pub const KEY_LEN: usize = 32; + pub const OUT_LEN: usize = 32; + + pub trait KeyedHashBlake2b: KeyedHash {} +} + +pub mod keyed_hash_shake256 { + use crate::primitives::keyed_hash::*; + + pub const KEY_LEN: usize = 32; + pub const OUT_LEN: usize = 32; + + pub trait KeyedHashShake256: KeyedHash {} +} + +pub use keyed_hash_blake2b::KeyedHashBlake2b; +pub use keyed_hash_shake256::KeyedHashShake256; diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs index f6059d93..a755325d 100644 --- a/cipher-traits/src/lib.rs +++ b/cipher-traits/src/lib.rs @@ -1,4 +1,8 @@ +mod algorithms; +mod primitives; + +pub use algorithms::*; +pub use primitives::*; + mod kem; -mod keyed_hash; pub use kem::Kem; -pub use keyed_hash::*; diff --git a/cipher-traits/src/primitives.rs b/cipher-traits/src/primitives.rs new file mode 100644 index 00000000..4ea27d78 --- /dev/null +++ b/cipher-traits/src/primitives.rs @@ -0,0 +1 @@ +pub mod keyed_hash; diff --git a/cipher-traits/src/keyed_hash.rs b/cipher-traits/src/primitives/keyed_hash.rs similarity index 100% rename from cipher-traits/src/keyed_hash.rs rename to cipher-traits/src/primitives/keyed_hash.rs diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 7c5c04f4..a0f2412f 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -1,66 +1,66 @@ +//! +//!```rust +//! # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace}; +//! use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +//! use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; +//! 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(EitherShakeOrBlake::Left(SHAKE256Core)); +//! 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 crate::keyed_hash as hash; -use crate::subtle::either_hash::EitherShakeOrBlake; +use crate::subtle::either_hash::KeyedHash; pub use hash::KEY_LEN; -use rosenpass_cipher_traits::KeyedHashInstance; - -/// -///```rust -/// # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace}; -/// use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; -/// use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; -/// 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(EitherShakeOrBlake::Left(SHAKE256Core)); -/// 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 rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; // 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], EitherShakeOrBlake); +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], EitherShakeOrBlake); +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, EitherShakeOrBlake); +pub struct SecretHashDomain(Secret, 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, EitherShakeOrBlake); +pub struct SecretHashDomainNamespace(Secret, KeyedHash); impl HashDomain { /// Creates a nw [HashDomain] initialized with a all-zeros key. - pub fn zero(choice: EitherShakeOrBlake) -> Self { + pub fn zero(choice: KeyedHash) -> Self { Self([0u8; KEY_LEN], choice) } @@ -128,7 +128,7 @@ impl SecretHashDomain { pub fn invoke_primitive( k: &[u8], d: &[u8], - hash_choice: EitherShakeOrBlake, + hash_choice: KeyedHash, ) -> Result { let mut new_secret_key = Secret::zero(); hash_choice.keyed_hash(k.try_into()?, d, new_secret_key.secret_mut())?; @@ -138,7 +138,7 @@ impl SecretHashDomain { } /// Creates a new [SecretHashDomain] that is initialized with an all zeros key. - pub fn zero(hash_choice: EitherShakeOrBlake) -> Self { + pub fn zero(hash_choice: KeyedHash) -> Self { Self(Secret::zero(), hash_choice) } @@ -150,7 +150,7 @@ impl SecretHashDomain { /// 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, hash_choice: EitherShakeOrBlake) -> Self { + pub fn danger_from_secret(k: Secret, hash_choice: KeyedHash) -> Self { Self(k, hash_choice) } @@ -213,7 +213,7 @@ impl SecretHashDomainNamespace { self.0 } - pub fn shake_or_blake(&self) -> &EitherShakeOrBlake { + pub fn shake_or_blake(&self) -> &KeyedHash { &self.1 } } diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/either_hash.rs index e2d5549a..86e3e825 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/either_hash.rs @@ -1,27 +1,27 @@ -use crate::subtle::hash_functions::keyed_shake256::SHAKE256Core; -use crate::subtle::incorrect_hmac_blake2b::Blake2bCore; use anyhow::Result; -use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; +use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; -#[derive(Debug, Eq, PartialEq)] -pub enum EitherHash< - const KEY_LEN: usize, - const HASH_LEN: usize, - Error, - L: KeyedHash, - R: KeyedHash, -> { - Left(L), - Right(R), +pub const KEY_LEN: usize = 32; +pub const HASH_LEN: usize = 32; + +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum KeyedHash { + KeyedShake256(super::hash_functions::keyed_shake256::SHAKE256), + IncorrectHmacBlake2b(super::hash_functions::incorrect_hmac_blake2b::Blake2b), } -impl KeyedHashInstance - for EitherHash -where - L: KeyedHash, - R: KeyedHash, -{ - type Error = Error; +impl KeyedHash { + pub fn keyed_shake256() -> Self { + Self::KeyedShake256(Default::default()) + } + + pub fn incorrect_hmac_blake2b() -> Self { + Self::IncorrectHmacBlake2b(Default::default()) + } +} + +impl KeyedHashInstance for KeyedHash { + type Error = anyhow::Error; fn keyed_hash( &self, @@ -30,19 +30,8 @@ where out: &mut [u8; HASH_LEN], ) -> Result<(), Self::Error> { match self { - Self::Left(_) => L::keyed_hash(key, data, out), - Self::Right(_) => R::keyed_hash(key, data, out), - } - } -} - -pub type EitherShakeOrBlake = EitherHash<32, 32, anyhow::Error, SHAKE256Core<32, 32>, Blake2bCore>; - -impl Clone for EitherShakeOrBlake { - fn clone(&self) -> Self { - match self { - Self::Left(l) => Self::Left(l.clone()), - Self::Right(r) => Self::Right(r.clone()), + Self::KeyedShake256(h) => h.keyed_hash(key, data, out), + Self::IncorrectHmacBlake2b(h) => h.keyed_hash(key, data, out), } } } diff --git a/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs index 50db959d..c2bf64a6 100644 --- a/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs @@ -1,5 +1,5 @@ use anyhow::ensure; -use rosenpass_cipher_traits::KeyedHash; +use rosenpass_cipher_traits::keyed_hash::KeyedHash; use rosenpass_constant_time::xor; use rosenpass_to::{ops::copy_slice, with_destination, To}; use zeroize::Zeroizing; diff --git a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs index 07183da2..a84710bb 100644 --- a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs +++ b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use rosenpass_cipher_traits::{KeyedHash, KeyedHashInstance}; +use rosenpass_cipher_traits::keyed_hash::{KeyedHash, KeyedHashInstance}; use std::marker::PhantomData; /// This is a helper to allow for type parameter inference when calling functions @@ -60,7 +60,7 @@ impl< } } -/// Helper traits ///////////////////////////////////////////// +// Helper traits ///////////////////////////////////////////// impl Default for InferKeyedHash @@ -78,7 +78,7 @@ where Static: KeyedHash, { fn clone(&self) -> Self { - Self::new() + *self } } diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/hash_functions/keyed_shake256.rs index 60016c7d..017a54c1 100644 --- a/ciphers/src/subtle/hash_functions/keyed_shake256.rs +++ b/ciphers/src/subtle/hash_functions/keyed_shake256.rs @@ -1,6 +1,6 @@ use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; use anyhow::ensure; -use rosenpass_cipher_traits::KeyedHash; +use rosenpass_cipher_traits::keyed_hash::KeyedHash; use sha3::digest::{ExtendableOutput, Update, XofReader}; use sha3::Shake256; @@ -71,6 +71,12 @@ impl SHAKE256Core Default for SHAKE256Core { + fn default() -> Self { + Self::new() + } +} + /// TODO use inferred hash somehow here /// ```rust /// # use rosenpass_ciphers::subtle::keyed_shake256::{SHAKE256}; diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index bd959680..70c4a008 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -54,9 +54,7 @@ use anyhow::Result; use rosenpass_ciphers::hash_domain::HashDomain; -use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; -use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; -use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; +use rosenpass_ciphers::subtle::either_hash::KeyedHash; /// Declare a hash function /// @@ -70,7 +68,7 @@ use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; macro_rules! hash_domain_ns { ($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => { $(#[$($attrss)*])* - pub fn $name(hash_choice: EitherShakeOrBlake) -> ::anyhow::Result<::rosenpass_ciphers::hash_domain::HashDomain> { + 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) @@ -89,7 +87,7 @@ macro_rules! hash_domain_ns { macro_rules! hash_domain { ($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => { $(#[$($attrss)*])* - pub fn $name(hash_choice: EitherShakeOrBlake) -> ::anyhow::Result<[u8; ::rosenpass_ciphers::KEY_LEN]> { + 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()) @@ -111,12 +109,12 @@ macro_rules! hash_domain { /// 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: EitherShakeOrBlake) -> Result { +pub fn protocol(hash_choice: KeyedHash) -> Result { // TODO: Update this string that is mixed in? match hash_choice { - EitherShakeOrBlake::Left(SHAKE256Core) => HashDomain::zero(hash_choice) + KeyedHash::KeyedShake256(_) => HashDomain::zero(hash_choice) .mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()), - EitherShakeOrBlake::Right(Blake2bCore) => HashDomain::zero(hash_choice) + KeyedHash::IncorrectHmacBlake2b(_) => HashDomain::zero(hash_choice) .mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 Blake2b".as_bytes()), } } diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 380ea56b..61ffb80e 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -25,9 +25,7 @@ use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::keyed_hash; -use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; -use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; -use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; +use rosenpass_ciphers::subtle::either_hash::KeyedHash; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_constant_time as constant_time; use rosenpass_secret_memory::{Public, PublicBox, Secret}; @@ -373,10 +371,10 @@ pub enum ProtocolVersion { } impl ProtocolVersion { - pub fn shake_or_blake(&self) -> EitherShakeOrBlake { + pub fn shake_or_blake(&self) -> KeyedHash { match self { - ProtocolVersion::V02 => EitherShakeOrBlake::Right(Blake2bCore), - ProtocolVersion::V03 => EitherShakeOrBlake::Left(SHAKE256Core), + ProtocolVersion::V02 => KeyedHash::incorrect_hmac_blake2b(), + ProtocolVersion::V03 => KeyedHash::keyed_shake256(), } } } @@ -505,7 +503,7 @@ impl Peer { initiation_requested: false, handshake: None, known_init_conf_response: None, - protocol_version: protocol_version, + protocol_version, } } } @@ -1418,7 +1416,7 @@ impl CryptoServer { /// Calculate the peer ID of this CryptoServer #[rustfmt::skip] - pub fn pidm(&self, shake_or_blake: EitherShakeOrBlake) -> Result { + pub fn pidm(&self, shake_or_blake: KeyedHash) -> Result { Ok(Public::new( hash_domains::peerid(shake_or_blake)? .mix(self.spkm.deref())? @@ -1474,7 +1472,7 @@ impl CryptoServer { handshake: None, known_init_conf_response: None, initiation_requested: false, - protocol_version: protocol_version, + protocol_version, }; let peerid = peer.pidt()?; let peerno = self.peers.len(); @@ -1671,7 +1669,7 @@ impl Peer { handshake: None, known_init_conf_response: None, initiation_requested: false, - protocol_version: protocol_version, + protocol_version, } } @@ -1702,11 +1700,11 @@ impl Session { /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// - /// let s = Session::zero(EitherShakeOrBlake::Left(SHAKE256Core)); + /// let s = Session::zero(EitherShakeOrBlake::keyed_shake256()); /// assert_eq!(s.created_at, 0.0); /// assert_eq!(s.handshake_role, HandshakeRole::Initiator); /// ``` - pub fn zero(shake_or_blake: EitherShakeOrBlake) -> Self { + pub fn zero(shake_or_blake: KeyedHash) -> Self { Self { created_at: 0.0, sidm: SessionId::zero(), @@ -2177,7 +2175,7 @@ impl CryptoServer { let cookie_secret = cookie_secret.get(self).value.secret(); let mut cookie_value = [0u8; 16]; cookie_value.copy_from_slice( - &hash_domains::cookie_value(EitherShakeOrBlake::Left(SHAKE256Core))? + &hash_domains::cookie_value(KeyedHash::keyed_shake256())? .mix(cookie_secret)? .mix(host_identification.encode())? .into_value()[..16], @@ -2193,7 +2191,7 @@ impl CryptoServer { let msg_in = Ref::<&[u8], Envelope>::new(rx_buf) .ok_or(RosenpassError::BufferSizeMismatch)?; expected.copy_from_slice( - &hash_domains::cookie(EitherShakeOrBlake::Left(SHAKE256Core))? + &hash_domains::cookie(KeyedHash::keyed_shake256())? .mix(&cookie_value)? .mix(&msg_in.as_bytes()[span_of!(Envelope, msg_type..cookie)])? .into_value()[..16], @@ -2230,7 +2228,7 @@ impl CryptoServer { ); let cookie_value = active_cookie_value.unwrap(); - let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))? + let cookie_key = hash_domains::cookie_key(KeyedHash::keyed_shake256())? .mix(self.spkm.deref())? .into_value(); @@ -2322,18 +2320,18 @@ impl CryptoServer { let peer_shake256 = self.handle_init_hello( &msg_in.payload, &mut msg_out.payload, - EitherShakeOrBlake::Left(SHAKE256Core), + KeyedHash::keyed_shake256(), ); let (peer, peer_hash_choice) = match peer_shake256 { - Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), + Ok(peer) => (peer, KeyedHash::keyed_shake256()), Err(_) => { let peer_blake2b = self.handle_init_hello( &msg_in.payload, &mut msg_out.payload, - EitherShakeOrBlake::Right(Blake2bCore), + KeyedHash::incorrect_hmac_blake2b(), ); match peer_blake2b { - Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)), + Ok(peer) => (peer, KeyedHash::incorrect_hmac_blake2b()), Err(_) => bail!("No valid hash function found for InitHello"), } } @@ -2398,18 +2396,18 @@ impl CryptoServer { let peer_shake256 = self.handle_init_conf( &msg_in.payload, &mut msg_out.payload, - EitherShakeOrBlake::Left(SHAKE256Core), + KeyedHash::keyed_shake256(), ); let (peer, peer_hash_choice) = match peer_shake256 { - Ok(peer) => (peer, EitherShakeOrBlake::Left(SHAKE256Core)), + Ok(peer) => (peer, KeyedHash::keyed_shake256()), Err(_) => { let peer_blake2b = self.handle_init_conf( &msg_in.payload, &mut msg_out.payload, - EitherShakeOrBlake::Right(Blake2bCore), + KeyedHash::incorrect_hmac_blake2b(), ); match peer_blake2b { - Ok(peer) => (peer, EitherShakeOrBlake::Right(Blake2bCore)), + Ok(peer) => (peer, KeyedHash::incorrect_hmac_blake2b()), Err(_) => bail!("No valid hash function found for InitHello"), } } @@ -2459,19 +2457,15 @@ impl CryptoServer { } /// TODO documentation - fn verify_hash_choice_match( - &self, - peer: PeerPtr, - peer_hash_choice: EitherShakeOrBlake, - ) -> Result<()> { + fn verify_hash_choice_match(&self, peer: PeerPtr, peer_hash_choice: KeyedHash) -> Result<()> { match peer.get(self).protocol_version.shake_or_blake() { - EitherShakeOrBlake::Left(SHAKE256Core) => match peer_hash_choice { - EitherShakeOrBlake::Left(SHAKE256Core) => Ok(()), - EitherShakeOrBlake::Right(Blake2bCore) => bail!("Hash function mismatch"), + KeyedHash::KeyedShake256(_) => match peer_hash_choice { + KeyedHash::KeyedShake256(_) => Ok(()), + KeyedHash::IncorrectHmacBlake2b(_) => bail!("Hash function mismatch"), }, - EitherShakeOrBlake::Right(Blake2bCore) => match peer_hash_choice { - EitherShakeOrBlake::Left(SHAKE256Core) => bail!("Hash function mismatch"), - EitherShakeOrBlake::Right(Blake2bCore) => Ok(()), + KeyedHash::IncorrectHmacBlake2b(_) => match peer_hash_choice { + KeyedHash::KeyedShake256(_) => bail!("Hash function mismatch"), + KeyedHash::IncorrectHmacBlake2b(_) => Ok(()), }, } } @@ -3242,11 +3236,7 @@ where M: AsBytes + FromBytes, { /// Internal business logic: Check the message authentication code produced by [Self::seal] - pub fn check_seal( - &self, - srv: &CryptoServer, - shake_or_blake: EitherShakeOrBlake, - ) -> Result { + pub fn check_seal(&self, srv: &CryptoServer, shake_or_blake: KeyedHash) -> Result { let expected = hash_domains::mac(shake_or_blake)? .mix(srv.spkm.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; @@ -3259,7 +3249,7 @@ where impl InitiatorHandshake { /// Zero initialization of an InitiatorHandshake, with up to date timestamp - pub fn zero_with_timestamp(srv: &CryptoServer, shake_or_blake: EitherShakeOrBlake) -> Self { + pub fn zero_with_timestamp(srv: &CryptoServer, shake_or_blake: KeyedHash) -> Self { InitiatorHandshake { created_at: srv.timebase.now(), next: HandshakeStateMachine::RespHello, @@ -3278,7 +3268,7 @@ impl InitiatorHandshake { impl HandshakeState { /// Zero initialization of an HandshakeState - pub fn zero(shake_or_blake: EitherShakeOrBlake) -> Self { + pub fn zero(shake_or_blake: KeyedHash) -> Self { Self { sidi: SessionId::zero(), sidr: SessionId::zero(), @@ -3423,7 +3413,7 @@ impl HandshakeState { biscuit_ct: &[u8], sidi: SessionId, sidr: SessionId, - shake_or_blake: EitherShakeOrBlake, + shake_or_blake: KeyedHash, ) -> Result<(PeerPtr, BiscuitId, HandshakeState)> { // The first bit of the biscuit indicates which biscuit key was used let bk = BiscuitKeyPtr(((biscuit_ct[0] & 0b1000_0000) >> 7) as usize); @@ -3475,7 +3465,7 @@ impl HandshakeState { self, srv: &CryptoServer, role: HandshakeRole, - either_shake_or_blake: EitherShakeOrBlake, + either_shake_or_blake: KeyedHash, ) -> Result { let HandshakeState { ck, sidi, sidr } = self; let tki = ck @@ -3588,7 +3578,7 @@ impl CryptoServer { &mut self, ih: &InitHello, rh: &mut RespHello, - shake_or_blake: EitherShakeOrBlake, + shake_or_blake: KeyedHash, ) -> Result { let mut core = HandshakeState::zero(shake_or_blake); @@ -3758,7 +3748,7 @@ impl CryptoServer { &mut self, ic: &InitConf, rc: &mut EmptyData, - shake_or_blake: EitherShakeOrBlake, + shake_or_blake: KeyedHash, ) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 @@ -3943,7 +3933,7 @@ impl CryptoServer { }?; let spkt = peer.get(self).spkt.deref(); - let cookie_key = hash_domains::cookie_key(EitherShakeOrBlake::Left(SHAKE256Core))? + let cookie_key = hash_domains::cookie_key(KeyedHash::keyed_shake256())? .mix(spkt)? .into_value(); let cookie_value = peer.cv().update_mut(self).unwrap(); From 46ebb6f46ce96e471b43081545a4a7ea79c4ccff Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 24 Feb 2025 12:55:15 +0100 Subject: [PATCH 047/174] Remove algorithm traits for now --- cipher-traits/src/algorithms.rs | 20 -------------------- cipher-traits/src/lib.rs | 3 --- 2 files changed, 23 deletions(-) delete mode 100644 cipher-traits/src/algorithms.rs diff --git a/cipher-traits/src/algorithms.rs b/cipher-traits/src/algorithms.rs deleted file mode 100644 index 14ccdada..00000000 --- a/cipher-traits/src/algorithms.rs +++ /dev/null @@ -1,20 +0,0 @@ -pub mod keyed_hash_blake2b { - use crate::primitives::keyed_hash::*; - - pub const KEY_LEN: usize = 32; - pub const OUT_LEN: usize = 32; - - pub trait KeyedHashBlake2b: KeyedHash {} -} - -pub mod keyed_hash_shake256 { - use crate::primitives::keyed_hash::*; - - pub const KEY_LEN: usize = 32; - pub const OUT_LEN: usize = 32; - - pub trait KeyedHashShake256: KeyedHash {} -} - -pub use keyed_hash_blake2b::KeyedHashBlake2b; -pub use keyed_hash_shake256::KeyedHashShake256; diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs index a755325d..f4e3eaa3 100644 --- a/cipher-traits/src/lib.rs +++ b/cipher-traits/src/lib.rs @@ -1,7 +1,4 @@ -mod algorithms; mod primitives; - -pub use algorithms::*; pub use primitives::*; mod kem; From a1f41953b7b2be75011151b62ba51d622197c2ee Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 25 Feb 2025 11:50:43 +0100 Subject: [PATCH 048/174] Reorganize the ciphers crate --- cipher-traits/src/primitives/keyed_hash.rs | 91 +++++++++++++++++++ ciphers/src/hash_domain.rs | 38 +++++--- ciphers/src/lib.rs | 17 ++-- .../incorrect_hmac_blake2b.rs | 49 ++++------ ciphers/src/subtle/custom/mod.rs | 3 + .../subtle/hash_functions/infer_keyed_hash.rs | 90 ------------------ ciphers/src/subtle/hash_functions/mod.rs | 4 - .../subtle/{either_hash.rs => keyed_hash.rs} | 4 +- .../chacha20poly1305_ietf.rs} | 0 ciphers/src/subtle/libcrux/mod.rs | 3 + ciphers/src/subtle/mod.rs | 23 ++--- .../blake2b.rs | 0 .../chacha20poly1305_ietf.rs | 0 .../keyed_shake256.rs | 3 +- ciphers/src/subtle/rust_crypto/mod.rs | 7 ++ .../xchacha20poly1305_ietf.rs | 0 rosenpass/src/bin/gen-ipc-msg-types.rs | 9 +- rosenpass/src/hash_domains.rs | 6 +- rosenpass/src/protocol/protocol.rs | 83 ++++++++--------- rosenpass/tests/app_server_example.rs | 5 +- 20 files changed, 217 insertions(+), 218 deletions(-) rename ciphers/src/subtle/{hash_functions => custom}/incorrect_hmac_blake2b.rs (61%) create mode 100644 ciphers/src/subtle/custom/mod.rs delete mode 100644 ciphers/src/subtle/hash_functions/infer_keyed_hash.rs delete mode 100644 ciphers/src/subtle/hash_functions/mod.rs rename ciphers/src/subtle/{either_hash.rs => keyed_hash.rs} (83%) rename ciphers/src/subtle/{chacha20poly1305_ietf_libcrux.rs => libcrux/chacha20poly1305_ietf.rs} (100%) create mode 100644 ciphers/src/subtle/libcrux/mod.rs rename ciphers/src/subtle/{hash_functions => rust_crypto}/blake2b.rs (100%) rename ciphers/src/subtle/{ => rust_crypto}/chacha20poly1305_ietf.rs (100%) rename ciphers/src/subtle/{hash_functions => rust_crypto}/keyed_shake256.rs (97%) create mode 100644 ciphers/src/subtle/rust_crypto/mod.rs rename ciphers/src/subtle/{ => rust_crypto}/xchacha20poly1305_ietf.rs (100%) diff --git a/cipher-traits/src/primitives/keyed_hash.rs b/cipher-traits/src/primitives/keyed_hash.rs index ba4d52e0..175bd9e5 100644 --- a/cipher-traits/src/primitives/keyed_hash.rs +++ b/cipher-traits/src/primitives/keyed_hash.rs @@ -18,3 +18,94 @@ pub trait KeyedHashInstance { out: &mut [u8; HASH_LEN], ) -> Result<(), Self::Error>; } + +use std::marker::PhantomData; + +/// 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 +where + Static: KeyedHash, +{ + pub _phantom_keyed_hasher: PhantomData<*const Static>, +} + +impl InferKeyedHash +where + Static: KeyedHash, +{ + 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) + } + + pub const fn key_len(self) -> usize { + Self::KEY_LEN + } + + pub const fn hash_len(self) -> usize { + Self::HASH_LEN + } +} + +impl> + KeyedHashInstance for InferKeyedHash +{ + 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 Default + for InferKeyedHash +where + Static: KeyedHash, +{ + fn default() -> Self { + Self::new() + } +} + +impl Clone + for InferKeyedHash +where + Static: KeyedHash, +{ + fn clone(&self) -> Self { + *self + } +} + +impl Copy + for InferKeyedHash +where + Static: KeyedHash, +{ +} diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index a0f2412f..e80215b6 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -1,14 +1,13 @@ //! //!```rust //! # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace}; -//! use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; -//! use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; +//! use rosenpass_ciphers::keyed_hash::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(EitherShakeOrBlake::Left(SHAKE256Core)); +//! 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(); @@ -30,12 +29,9 @@ use anyhow::Result; use rosenpass_secret_memory::Secret; -use rosenpass_to::To; -use crate::keyed_hash as hash; +pub use crate::keyed_hash::{KeyedHash, KEY_LEN}; -use crate::subtle::either_hash::KeyedHash; -pub use hash::KEY_LEN; use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; // TODO Use a proper Dec interface @@ -120,6 +116,8 @@ impl HashDomainNamespace { } impl SecretHashDomain { + // XXX: Why is the old hash still used unconditionally? + // /// 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]. @@ -133,7 +131,7 @@ impl SecretHashDomain { let mut new_secret_key = Secret::zero(); hash_choice.keyed_hash(k.try_into()?, d, new_secret_key.secret_mut())?; let mut r = SecretHashDomain(new_secret_key, hash_choice); - hash::hash(k, d).to(r.0.secret_mut())?; + KeyedHash::incorrect_hmac_blake2b().keyed_hash(k.try_into()?, d, r.0.secret_mut())?; Ok(r) } @@ -177,13 +175,23 @@ impl SecretHashDomain { self.0 } - /// Evaluate [hash::hash] with this [SecretHashDomain]'s data as the `key` and - /// `dst` as the `data` and stores the result as the new data for this [SecretHashDomain]. - /// - /// It requires that both `v` and `d` consist of exactly [KEY_LEN] many bytes. - pub fn into_secret_slice(mut self, v: &[u8], dst: &[u8]) -> Result<()> { - hash::hash(v, dst).to(self.0.secret_mut()) - } + /* XXX: This code was calling the specific hmac-blake2b code as well as the new KeyedHash enum + * (f.k.a. EitherHash). I was confused by the way the code used the local variables, because it + * didn't match the code. I made the code match the documentation, but I'm not sure that is + * correct. Either way, it doesn't look like this is used anywhere. Maybe just remove it? + * + * /// Evaluate [hash::hash] with this [SecretHashDomain]'s data as the `key` and + * /// `dst` as the `data` and stores the result as the new data for this [SecretHashDomain]. + * pub fn into_secret_slice(mut self, v: &[u8; KEY_LEN], dst: &[u8; KEY_LEN]) -> Result<()> { + * let SecretHashDomain(secret, hash_choice) = &self; + * + * let mut new_secret = Secret::zero(); + * hash_choice.keyed_hash(secret.secret(), dst, new_secret.secret_mut())?; + * self.0 = new_secret; + * + * Ok(()) + * } + */ } impl SecretHashDomainNamespace { diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index 6b6c67fb..77bf498d 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -13,19 +13,18 @@ const_assert!(KEY_LEN == hash_domain::KEY_LEN); /// 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 mod keyed_hash { - pub use crate::subtle::incorrect_hmac_blake2b::{ - hash, KEY_LEN, KEY_MAX, KEY_MIN, OUT_MAX, OUT_MIN, - }; -} +pub use crate::subtle::keyed_hash; /// Authenticated encryption with associated data /// Chacha20poly1305 is used. 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::{ + pub use crate::subtle::libcrux::chacha20poly1305_ietf::{ + decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, + }; + + #[cfg(not(feature = "experiment_libcrux"))] + pub use crate::subtle::rust_crypto::chacha20poly1305_ietf::{ decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, }; } @@ -33,7 +32,7 @@ pub mod aead { /// Authenticated encryption with associated data with a constant nonce /// XChacha20poly1305 is used. pub mod xaead { - pub use crate::subtle::xchacha20poly1305_ietf::{ + pub use crate::subtle::rust_crypto::xchacha20poly1305_ietf::{ decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, }; } diff --git a/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs similarity index 61% rename from ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs rename to ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs index c2bf64a6..ca1b3b96 100644 --- a/ciphers/src/subtle/hash_functions/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs @@ -1,22 +1,16 @@ use anyhow::ensure; -use rosenpass_cipher_traits::keyed_hash::KeyedHash; +use rosenpass_cipher_traits::keyed_hash::{InferKeyedHash, KeyedHash}; use rosenpass_constant_time::xor; -use rosenpass_to::{ops::copy_slice, with_destination, To}; +use rosenpass_to::{ops::copy_slice, To}; use zeroize::Zeroizing; -use crate::subtle::hash_functions::blake2b; -use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; +use crate::subtle::rust_crypto::blake2b; /// The key length, 32 bytes or 256 bits. pub const KEY_LEN: usize = 32; -/// The minimal key length, identical to [KEY_LEN] -pub const KEY_MIN: usize = KEY_LEN; -/// The maximal key length, identical to [KEY_LEN] -pub const KEY_MAX: usize = KEY_LEN; -/// The minimal output length, see [blake2b::OUT_MIN] -pub const OUT_MIN: usize = blake2b::OUT_MIN; -/// The maximal output length, see [blake2b::OUT_MAX] -pub const OUT_MAX: usize = blake2b::OUT_MAX; + +/// The hash length, 32 bytes or 256 bits. +pub const HASH_LEN: usize = 32; /// This is a woefully incorrect implementation of hmac_blake2b. /// See @@ -41,12 +35,20 @@ pub const OUT_MAX: usize = blake2b::OUT_MAX; /// # assert_eq!(hash_data, expected_hash); ///``` /// -#[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]; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct IncorrectHmacBlake2bCore; + +impl KeyedHash for IncorrectHmacBlake2bCore { + type Error = anyhow::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]; - with_destination(|out: &mut [u8]| { // Not bothering with padding; the implementation // uses appropriately sized keys. ensure!(key.len() == KEY_LEN); @@ -64,18 +66,7 @@ pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<( blake2b::hash(tmp_key.as_ref(), outer_data.as_ref()).to(out)?; Ok(()) - }) -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Blake2bCore; - -impl KeyedHash<32, 32> for Blake2bCore { - type Error = anyhow::Error; - - fn keyed_hash(key: &[u8; 32], data: &[u8], out: &mut [u8; 32]) -> Result<(), Self::Error> { - hash(key, data).to(out) } } -pub type Blake2b = InferKeyedHash; +pub type IncorrectHmacBlake2b = InferKeyedHash; diff --git a/ciphers/src/subtle/custom/mod.rs b/ciphers/src/subtle/custom/mod.rs new file mode 100644 index 00000000..642d8eaf --- /dev/null +++ b/ciphers/src/subtle/custom/mod.rs @@ -0,0 +1,3 @@ +//! Own implementations of custom algorithms + +pub mod incorrect_hmac_blake2b; diff --git a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs b/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs deleted file mode 100644 index a84710bb..00000000 --- a/ciphers/src/subtle/hash_functions/infer_keyed_hash.rs +++ /dev/null @@ -1,90 +0,0 @@ -use anyhow::Result; -use rosenpass_cipher_traits::keyed_hash::{KeyedHash, KeyedHashInstance}; -use std::marker::PhantomData; - -/// 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 -where - Static: KeyedHash, -{ - pub _phantom_keyed_hasher: PhantomData<*const Static>, -} - -impl InferKeyedHash -where - Static: KeyedHash, -{ - 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::keyed_hash(key, data, out) - } - - pub const fn key_len(self) -> usize { - Self::KEY_LEN - } - - pub const fn hash_len(self) -> usize { - Self::HASH_LEN - } -} - -impl< - const KEY_LEN: usize, - const HASH_LEN: usize, - Static: KeyedHash, - > KeyedHashInstance for InferKeyedHash -{ - type Error = anyhow::Error; - - fn keyed_hash(&self, key: &[u8; KEY_LEN], data: &[u8], out: &mut [u8; HASH_LEN]) -> Result<()> { - self.keyed_hash_internal(key, data, out) - } -} - -// Helper traits ///////////////////////////////////////////// - -impl Default - for InferKeyedHash -where - Static: KeyedHash, -{ - fn default() -> Self { - Self::new() - } -} - -impl Clone - for InferKeyedHash -where - Static: KeyedHash, -{ - fn clone(&self) -> Self { - *self - } -} - -impl Copy - for InferKeyedHash -where - Static: KeyedHash, -{ -} diff --git a/ciphers/src/subtle/hash_functions/mod.rs b/ciphers/src/subtle/hash_functions/mod.rs deleted file mode 100644 index 6ca1c10b..00000000 --- a/ciphers/src/subtle/hash_functions/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod blake2b; -pub mod incorrect_hmac_blake2b; -mod infer_keyed_hash; -pub mod keyed_shake256; diff --git a/ciphers/src/subtle/either_hash.rs b/ciphers/src/subtle/keyed_hash.rs similarity index 83% rename from ciphers/src/subtle/either_hash.rs rename to ciphers/src/subtle/keyed_hash.rs index 86e3e825..9177c6e4 100644 --- a/ciphers/src/subtle/either_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -6,8 +6,8 @@ pub const HASH_LEN: usize = 32; #[derive(Debug, Eq, PartialEq, Clone)] pub enum KeyedHash { - KeyedShake256(super::hash_functions::keyed_shake256::SHAKE256), - IncorrectHmacBlake2b(super::hash_functions::incorrect_hmac_blake2b::Blake2b), + KeyedShake256(super::rust_crypto::keyed_shake256::SHAKE256), + IncorrectHmacBlake2b(super::custom::incorrect_hmac_blake2b::IncorrectHmacBlake2b), } impl KeyedHash { diff --git a/ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs b/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs similarity index 100% rename from ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs rename to ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs new file mode 100644 index 00000000..b1c89034 --- /dev/null +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -0,0 +1,3 @@ +//! Implementations backed by libcrux, a verified crypto library + +pub mod chacha20poly1305_ietf; diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index bc4de117..18a6b54c 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -1,15 +1,10 @@ -/// This module provides the following cryptographic schemes: -/// - [blake2b]: The blake2b hash function -/// - [chacha20poly1305_ietf]: The Chacha20Poly1305 AEAD as implemented in [RustCrypto](https://crates.io/crates/chacha20poly1305) (only used when the feature `experiment_libcrux` is disabled). -/// - [chacha20poly1305_ietf_libcrux]: The Chacha20Poly1305 AEAD as implemented in [libcrux](https://github.com/cryspen/libcrux) (only used when the feature `experiment_libcrux` is enabled). -/// - [incorrect_hmac_blake2b]: An (incorrect) hmac based on [blake2b]. -/// - [xchacha20poly1305_ietf] The Chacha20Poly1305 AEAD as implemented in [RustCrypto](https://crates.io/crates/chacha20poly1305) -#[cfg(not(feature = "experiment_libcrux"))] -pub mod chacha20poly1305_ietf; -#[cfg(feature = "experiment_libcrux")] -pub mod chacha20poly1305_ietf_libcrux; -pub mod either_hash; -mod hash_functions; -pub mod xchacha20poly1305_ietf; +pub mod keyed_hash; -pub use hash_functions::{blake2b, incorrect_hmac_blake2b, keyed_shake256}; +pub use custom::incorrect_hmac_blake2b; +pub use rust_crypto::{blake2b, keyed_shake256}; + +pub mod custom; +pub mod rust_crypto; + +#[cfg(feature = "experiment_libcrux")] +pub mod libcrux; diff --git a/ciphers/src/subtle/hash_functions/blake2b.rs b/ciphers/src/subtle/rust_crypto/blake2b.rs similarity index 100% rename from ciphers/src/subtle/hash_functions/blake2b.rs rename to ciphers/src/subtle/rust_crypto/blake2b.rs diff --git a/ciphers/src/subtle/chacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs similarity index 100% rename from ciphers/src/subtle/chacha20poly1305_ietf.rs rename to ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs diff --git a/ciphers/src/subtle/hash_functions/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs similarity index 97% rename from ciphers/src/subtle/hash_functions/keyed_shake256.rs rename to ciphers/src/subtle/rust_crypto/keyed_shake256.rs index 017a54c1..297a78e3 100644 --- a/ciphers/src/subtle/hash_functions/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -1,6 +1,5 @@ -use crate::subtle::hash_functions::infer_keyed_hash::InferKeyedHash; use anyhow::ensure; -use rosenpass_cipher_traits::keyed_hash::KeyedHash; +use rosenpass_cipher_traits::keyed_hash::{InferKeyedHash, KeyedHash}; use sha3::digest::{ExtendableOutput, Update, XofReader}; use sha3::Shake256; diff --git a/ciphers/src/subtle/rust_crypto/mod.rs b/ciphers/src/subtle/rust_crypto/mod.rs new file mode 100644 index 00000000..062ef105 --- /dev/null +++ b/ciphers/src/subtle/rust_crypto/mod.rs @@ -0,0 +1,7 @@ +//! Implementations backed by RustCrypto + +pub mod blake2b; +pub mod keyed_shake256; + +pub mod chacha20poly1305_ietf; +pub mod xchacha20poly1305_ietf; diff --git a/ciphers/src/subtle/xchacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs similarity index 100% rename from ciphers/src/subtle/xchacha20poly1305_ietf.rs rename to ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index 5cecdeb4..3dfee4bd 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -1,8 +1,7 @@ use anyhow::{Context, Result}; use heck::ToShoutySnakeCase; -use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; -use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::Blake2bCore; +use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN}; @@ -15,7 +14,7 @@ fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN] } /// Print a hash literal for pasting into the Rosenpass source code -fn print_literal(path: &[&str], shake_or_blake: EitherShakeOrBlake) -> Result<()> { +fn print_literal(path: &[&str], shake_or_blake: KeyedHash) -> Result<()> { let val = calculate_hash_value(HashDomain::zero(shake_or_blake), path)?; let (last, prefix) = path.split_last().context("developer error!")?; let var_name = last.to_shouty_snake_case(); @@ -54,7 +53,7 @@ impl Tree { } } - fn gen_code_inner(&self, prefix: &[&str], shake_or_blake: EitherShakeOrBlake) -> Result<()> { + fn gen_code_inner(&self, prefix: &[&str], shake_or_blake: KeyedHash) -> Result<()> { let mut path = prefix.to_owned(); path.push(self.name()); @@ -70,7 +69,7 @@ impl Tree { Ok(()) } - fn gen_code(&self, shake_or_blake: EitherShakeOrBlake) -> Result<()> { + fn gen_code(&self, shake_or_blake: KeyedHash) -> Result<()> { self.gen_code_inner(&[], shake_or_blake) } } diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index 70c4a008..5d23e637 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -13,7 +13,7 @@ //! use rosenpass::{hash_domain, hash_domain_ns}; //! use rosenpass::hash_domains::protocol; //! -//! use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; +//! use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; //! //! // Declaring a custom hash domain //! hash_domain_ns!(protocol, custom_domain, "my custom hash domain label"); @@ -29,7 +29,7 @@ //! hash_domain!(domain_separators, sep2, "2"); //! //! // We use the SHAKE256 hash function for this example -//! let hash_choice = EitherShakeOrBlake::Left(rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core); +//! 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(); @@ -54,7 +54,7 @@ use anyhow::Result; use rosenpass_ciphers::hash_domain::HashDomain; -use rosenpass_ciphers::subtle::either_hash::KeyedHash; +use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; /// Declare a hash function /// diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 61ffb80e..8f97ac2a 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -21,11 +21,11 @@ use rand::Fill as Randomize; use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; +use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; -use rosenpass_ciphers::keyed_hash; -use rosenpass_ciphers::subtle::either_hash::KeyedHash; +use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; use rosenpass_constant_time as constant_time; use rosenpass_secret_memory::{Public, PublicBox, Secret}; @@ -371,7 +371,7 @@ pub enum ProtocolVersion { } impl ProtocolVersion { - pub fn shake_or_blake(&self) -> KeyedHash { + pub fn keyed_hash(&self) -> KeyedHash { match self { ProtocolVersion::V02 => KeyedHash::incorrect_hmac_blake2b(), ProtocolVersion::V03 => KeyedHash::keyed_shake256(), @@ -720,8 +720,13 @@ impl KnownResponseHasher { /// Panics in case of a problem with this underlying hash function pub fn hash(&self, msg: &Envelope) -> KnownResponseHash { let data = &msg.as_bytes()[span_of!(Envelope, msg_type..cookie)]; - let hash = keyed_hash::hash(self.key.secret(), data) - .to_this(Public::<32>::zero) + // TODO: the hash choice hasn't been propagated here so far + let hash_choice = + rosenpass_ciphers::subtle::keyed_hash::KeyedHash::incorrect_hmac_blake2b(); + + let mut hash = [0; 32]; + hash_choice + .keyed_hash(self.key.secret(), data, &mut hash) .unwrap(); Public::from_slice(&hash[0..16]) // truncate to 16 bytes } @@ -1682,7 +1687,7 @@ impl Peer { #[rustfmt::skip] pub fn pidt(&self) -> Result { Ok(Public::new( - hash_domains::peerid(self.protocol_version.shake_or_blake())? + hash_domains::peerid(self.protocol_version.keyed_hash())? .mix(self.spkt.deref())? .into_value())) } @@ -1695,12 +1700,11 @@ impl Session { /// /// ``` /// use rosenpass::protocol::{Session, HandshakeRole}; - /// use rosenpass_ciphers::subtle::either_hash::EitherShakeOrBlake; - /// use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; + /// use rosenpass_ciphers::keyed_hash::KeyedHash; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// - /// let s = Session::zero(EitherShakeOrBlake::keyed_shake256()); + /// let s = Session::zero(KeyedHash::keyed_shake256()); /// assert_eq!(s.created_at, 0.0); /// assert_eq!(s.handshake_role, HandshakeRole::Initiator); /// ``` @@ -2352,7 +2356,7 @@ impl CryptoServer { let mut msg_out = truncating_cast_into::>(tx_buf)?; let peer = self.handle_resp_hello(&msg_in.payload, &mut msg_out.payload)?; ensure!( - msg_in.check_seal(self, peer.get(self).protocol_version.shake_or_blake())?, + msg_in.check_seal(self, peer.get(self).protocol_version.keyed_hash())?, seal_broken ); @@ -2374,10 +2378,8 @@ impl CryptoServer { Some(cached) => { let peer = cached.peer(); ensure!( - msg_in.check_seal( - self, - peer.get(self).protocol_version.shake_or_blake() - )?, + msg_in + .check_seal(self, peer.get(self).protocol_version.keyed_hash())?, seal_broken ); let cached = cached @@ -2458,7 +2460,7 @@ impl CryptoServer { /// TODO documentation fn verify_hash_choice_match(&self, peer: PeerPtr, peer_hash_choice: KeyedHash) -> Result<()> { - match peer.get(self).protocol_version.shake_or_blake() { + match peer.get(self).protocol_version.keyed_hash() { KeyedHash::KeyedShake256(_) => match peer_hash_choice { KeyedHash::KeyedShake256(_) => Ok(()), KeyedHash::IncorrectHmacBlake2b(_) => bail!("Hash function mismatch"), @@ -3208,7 +3210,7 @@ where { /// Internal business logic: Calculate the message authentication code (`mac`) and also append cookie value pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { - let mac = hash_domains::mac(peer.get(srv).protocol_version.shake_or_blake())? + let mac = hash_domains::mac(peer.get(srv).protocol_version.keyed_hash())? .mix(peer.get(srv).spkt.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); @@ -3221,7 +3223,7 @@ where /// This is called inside [Self::seal] and does not need to be called again separately. pub fn seal_cookie(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { if let Some(cookie_key) = &peer.cv().get(srv) { - let cookie = hash_domains::cookie(peer.get(srv).protocol_version.shake_or_blake())? + let cookie = hash_domains::cookie(peer.get(srv).protocol_version.keyed_hash())? .mix(cookie_key.value.secret())? .mix(&self.as_bytes()[span_of!(Self, msg_type..cookie)])?; self.cookie @@ -3384,7 +3386,7 @@ impl HandshakeState { .copy_from_slice(self.ck.clone().danger_into_secret().secret()); // calculate ad contents - let ad = hash_domains::biscuit_ad(peer.get(srv).protocol_version.shake_or_blake())? + let ad = hash_domains::biscuit_ad(peer.get(srv).protocol_version.keyed_hash())? .mix(srv.spkm.deref())? .mix(self.sidi.as_slice())? .mix(self.sidr.as_slice())? @@ -3446,7 +3448,7 @@ impl HandshakeState { let ck = SecretHashDomain::danger_from_secret( Secret::from_slice(&biscuit.ck), - peer.get(srv).protocol_version.shake_or_blake(), + peer.get(srv).protocol_version.keyed_hash(), ) .dup(); // Reconstruct the handshake state @@ -3514,7 +3516,7 @@ impl CryptoServer { Ok(session .ck .mix(&hash_domains::osk( - peer.get(self).protocol_version.shake_or_blake(), + peer.get(self).protocol_version.keyed_hash(), )?)? .into_secret()) } @@ -3526,7 +3528,7 @@ impl CryptoServer { pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { let mut hs = InitiatorHandshake::zero_with_timestamp( self, - peer.get(self).protocol_version.shake_or_blake(), + peer.get(self).protocol_version.keyed_hash(), ); // IHI1 @@ -3553,7 +3555,7 @@ impl CryptoServer { // IHI6 hs.core.encrypt_and_mix( ih.pidic.as_mut_slice(), - self.pidm(peer.get(self).protocol_version.shake_or_blake())? + self.pidm(peer.get(self).protocol_version.keyed_hash())? .as_ref(), )?; @@ -3729,7 +3731,7 @@ impl CryptoServer { core.enter_live( self, HandshakeRole::Initiator, - peer.get(self).protocol_version.shake_or_blake(), + peer.get(self).protocol_version.keyed_hash(), )?, )?; hs_mut!().core.erase(); @@ -3787,7 +3789,7 @@ impl CryptoServer { core.enter_live( self, HandshakeRole::Responder, - peer.get(self).protocol_version.shake_or_blake(), + peer.get(self).protocol_version.keyed_hash(), )?, )?; // TODO: This should be part of the protocol specification. @@ -3848,7 +3850,7 @@ impl CryptoServer { .lookup_handshake(sid) .with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?; ensure!( - msg_in.check_seal(self, hs.peer().get(self).protocol_version.shake_or_blake())?, + msg_in.check_seal(self, hs.peer().get(self).protocol_version.keyed_hash())?, seal_broken ); let ses = hs.peer().session(); @@ -4362,21 +4364,20 @@ mod test { assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young); - let expected_cookie_value = - hash_domains::cookie_value(protocol_version.shake_or_blake()) - .unwrap() - .mix( - b.active_or_retired_cookie_secrets()[0] - .unwrap() - .get(&b) - .value - .secret(), - ) - .unwrap() - .mix(ip_addr_port_a.encode()) - .unwrap() - .into_value()[..16] - .to_vec(); + let expected_cookie_value = hash_domains::cookie_value(protocol_version.keyed_hash()) + .unwrap() + .mix( + b.active_or_retired_cookie_secrets()[0] + .unwrap() + .get(&b) + .value + .secret(), + ) + .unwrap() + .mix(ip_addr_port_a.encode()) + .unwrap() + .into_value()[..16] + .to_vec(); assert_eq!( PeerPtr(0).cv().get(&a).map(|x| &x.value.secret()[..]), @@ -4593,7 +4594,7 @@ mod test { let res = srv.handle_init_conf( &ic.payload, &mut discard_resp_conf, - protocol_version.clone().shake_or_blake(), + protocol_version.clone().keyed_hash(), ); assert!(res.is_err()); diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index 31fc78eb..fe8ff1f4 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -1,22 +1,19 @@ use std::{ net::SocketAddr, ops::DerefMut, - path::PathBuf, str::FromStr, sync::mpsc, thread::{self, sleep}, time::Duration, }; -use anyhow::ensure; use rosenpass::config::ProtocolVersion; use rosenpass::{ - app_server::{ipv4_any_binding, ipv6_any_binding, AppServer, AppServerTest, MAX_B64_KEY_SIZE}, + app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}, protocol::{SPk, SSk, SymKey}, }; use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::kem::StaticKem; -use rosenpass_secret_memory::Secret; use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt}; #[test] From d61b1377617bcde05a7a1fed3ab8ba7401085ee2 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Wed, 26 Feb 2025 13:01:29 +0100 Subject: [PATCH 049/174] update KEM trait --- Cargo.lock | 1 + cipher-traits/Cargo.toml | 3 +- cipher-traits/src/lib.rs | 3 - cipher-traits/src/primitives.rs | 1 + cipher-traits/src/primitives/kem.rs | 171 ++++++++++++++++++++++++++ fuzz/fuzz_targets/handle_msg.rs | 2 +- fuzz/fuzz_targets/kyber_encaps.rs | 2 +- fuzz/fuzz_targets/mceliece_encaps.rs | 2 +- oqs/src/kem_macro.rs | 31 ++--- rosenpass/benches/handshake.rs | 2 +- rosenpass/src/cli.rs | 2 +- rosenpass/src/msgs.rs | 2 +- rosenpass/src/protocol/protocol.rs | 97 +++++++++------ rosenpass/tests/app_server_example.rs | 2 +- rosenpass/tests/poll_example.rs | 2 +- rp/src/key.rs | 2 +- 16 files changed, 256 insertions(+), 69 deletions(-) create mode 100644 cipher-traits/src/primitives/kem.rs diff --git a/Cargo.lock b/Cargo.lock index 49342756..a4ab6684 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1828,6 +1828,7 @@ dependencies = [ "anyhow", "rosenpass-oqs", "rosenpass-secret-memory", + "thiserror 2.0.11", ] [[package]] diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml index 2e9ea32c..9d859588 100644 --- a/cipher-traits/Cargo.toml +++ b/cipher-traits/Cargo.toml @@ -10,8 +10,9 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] +thiserror = "2" [dev-dependencies] rosenpass-oqs = { workspace = true } rosenpass-secret-memory = { workspace = true } -anyhow = {workspace = true} +anyhow = { workspace = true } diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs index f4e3eaa3..dbe4e5d5 100644 --- a/cipher-traits/src/lib.rs +++ b/cipher-traits/src/lib.rs @@ -1,5 +1,2 @@ mod primitives; pub use primitives::*; - -mod kem; -pub use kem::Kem; diff --git a/cipher-traits/src/primitives.rs b/cipher-traits/src/primitives.rs index 4ea27d78..24a8ead2 100644 --- a/cipher-traits/src/primitives.rs +++ b/cipher-traits/src/primitives.rs @@ -1 +1,2 @@ +pub mod kem; pub mod keyed_hash; diff --git a/cipher-traits/src/primitives/kem.rs b/cipher-traits/src/primitives/kem.rs new file mode 100644 index 00000000..f30aae43 --- /dev/null +++ b/cipher-traits/src/primitives/kem.rs @@ -0,0 +1,171 @@ +//! 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::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>(()) +//!``` +//! + +use thiserror::Error; + +/// Key Encapsulation Mechanism +/// +/// The KEM interface defines three operations: Key generation, key encapsulation and key +/// decapsulation. +pub trait Kem { + const SK_LEN: usize = SK_LEN; + const PK_LEN: usize = PK_LEN; + const CT_LEN: usize = CT_LEN; + const SHK_LEN: usize = SHK_LEN; + + /// Generate a keypair consisting of secret key (`sk`) and public key (`pk`) + /// + /// `keygen() -> sk, pk` + fn keygen(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( + 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(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, +} diff --git a/fuzz/fuzz_targets/handle_msg.rs b/fuzz/fuzz_targets/handle_msg.rs index 6423fe7e..6185ccac 100644 --- a/fuzz/fuzz_targets/handle_msg.rs +++ b/fuzz/fuzz_targets/handle_msg.rs @@ -4,7 +4,7 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; use rosenpass::protocol::CryptoServer; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::policy::*; use rosenpass_secret_memory::{PublicBox, Secret}; diff --git a/fuzz/fuzz_targets/kyber_encaps.rs b/fuzz/fuzz_targets/kyber_encaps.rs index 6a342f35..f1480ab5 100644 --- a/fuzz/fuzz_targets/kyber_encaps.rs +++ b/fuzz/fuzz_targets/kyber_encaps.rs @@ -4,7 +4,7 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::EphemeralKem; #[derive(arbitrary::Arbitrary, Debug)] diff --git a/fuzz/fuzz_targets/mceliece_encaps.rs b/fuzz/fuzz_targets/mceliece_encaps.rs index ad66a6e4..27a5e410 100644 --- a/fuzz/fuzz_targets/mceliece_encaps.rs +++ b/fuzz/fuzz_targets/mceliece_encaps.rs @@ -3,7 +3,7 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; fuzz_target!(|input: [u8; StaticKem::PK_LEN]| { diff --git a/oqs/src/kem_macro.rs b/oqs/src/kem_macro.rs index fc05b4f7..eeb7b5fe 100644 --- a/oqs/src/kem_macro.rs +++ b/oqs/src/kem_macro.rs @@ -5,8 +5,7 @@ macro_rules! oqs_kem { ($name:ident) => { ::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::kem; #[doc = "Bindings for ::oqs_sys::kem::" [<"OQS_KEM" _ $name:snake>] "_*"] #[doc = ""] @@ -39,6 +38,11 @@ macro_rules! oqs_kem { #[doc = "```"] pub enum [< $name:camel >] {} + pub const SK_LEN: usize = ::oqs_sys::kem::[] as usize; + pub const PK_LEN: usize = ::oqs_sys::kem::[] as usize; + pub const CT_LEN: usize = ::oqs_sys::kem::[] as usize; + pub const SHK_LEN: usize = ::oqs_sys::kem::[] as usize; + /// # Panic & Safety /// /// This Trait impl calls unsafe [oqs_sys] functions, that write to byte @@ -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::[] as usize; - const PK_LEN: usize = ::oqs_sys::kem::[] as usize; - const CT_LEN: usize = ::oqs_sys::kem::[] as usize; - const SHK_LEN: usize = ::oqs_sys::kem::[] 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::Kem for [< $name:camel >] { + fn keygen(sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), kem::Error> { 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(shk: &mut [u8; SHK_LEN], ct: &mut [u8; CT_LEN], pk: &[u8; PK_LEN]) -> Result<(), kem::Error> { 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(shk: &mut [u8; SHK_LEN], sk: &[u8; SK_LEN], ct: &[u8; CT_LEN]) -> Result<(), kem::Error> { unsafe { oqs_call!( ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >], diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index 5756960b..e329ca4d 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -4,7 +4,7 @@ use rosenpass::protocol::{ }; use std::ops::DerefMut; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; use criterion::{black_box, criterion_group, criterion_main, Criterion}; diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 68e8e41a..b4ec5783 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -5,7 +5,7 @@ use anyhow::{bail, ensure, Context}; use clap::{Parser, Subcommand}; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::file::StoreSecret; use rosenpass_util::file::{LoadValue, LoadValueB64, StoreValue}; diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index fd281c60..8ef7da5f 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -13,7 +13,7 @@ use std::u8; use zerocopy::{AsBytes, FromBytes, FromZeroes}; use super::RosenpassError; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::{aead, xaead, KEY_LEN}; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 8f97ac2a..60a3fcd6 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -4,7 +4,6 @@ //! files. use std::borrow::Borrow; -use std::convert::Infallible; use std::fmt::Debug; use std::mem::size_of; use std::ops::Deref; @@ -21,8 +20,8 @@ use rand::Fill as Randomize; use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; -use rosenpass_cipher_traits::Kem; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; @@ -3334,31 +3333,77 @@ impl HandshakeState { /// /// This is used to include asymmetric cryptography in the rosenpass protocol // I loathe "error: constant expression depends on a generic parameter" - pub fn encaps_and_mix, const SHK_LEN: usize>( + pub fn encaps_and_mix< + const KEM_SK_LEN: usize, + const KEM_PK_LEN: usize, + const KEM_CT_LEN: usize, + const KEM_SHK_LEN: usize, + T: Kem, + >( &mut self, - ct: &mut [u8], - pk: &[u8], + ct: &mut [u8; KEM_CT_LEN], + pk: &[u8; KEM_PK_LEN], ) -> Result<&mut Self> { - let mut shk = Secret::::zero(); + let mut shk = Secret::::zero(); T::encaps(shk.secret_mut(), ct, pk)?; self.mix(pk)?.mix(shk.secret())?.mix(ct) } + pub fn encaps_and_mix_static( + &mut self, + ct: &mut [u8; StaticKem::CT_LEN], + pk: &[u8; StaticKem::PK_LEN], + ) -> Result<&mut Self> { + self.encaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(ct, pk) + } + + pub fn encaps_and_mix_ephemeral( + &mut self, + ct: &mut [u8; EphemeralKem::CT_LEN], + pk: &[u8; EphemeralKem::PK_LEN], + ) -> Result<&mut Self> { + self.encaps_and_mix::<{EphemeralKem::SK_LEN},{ EphemeralKem::PK_LEN}, {EphemeralKem::CT_LEN}, {EphemeralKem::SHK_LEN}, EphemeralKem>(ct, pk) + } + /// Decapsulation (decryption) counterpart to [Self::encaps_and_mix]. /// /// Makes sure that the same values are mixed into the chaining that where mixed in on the /// sender side. - pub fn decaps_and_mix, const SHK_LEN: usize>( + pub fn decaps_and_mix< + const KEM_SK_LEN: usize, + const KEM_PK_LEN: usize, + const KEM_CT_LEN: usize, + const KEM_SHK_LEN: usize, + T: Kem, + >( &mut self, - sk: &[u8], - pk: &[u8], - ct: &[u8], + sk: &[u8; KEM_SK_LEN], + pk: &[u8; KEM_PK_LEN], + ct: &[u8; KEM_CT_LEN], ) -> Result<&mut Self> { - let mut shk = Secret::::zero(); + let mut shk = Secret::::zero(); T::decaps(shk.secret_mut(), sk, ct)?; self.mix(pk)?.mix(shk.secret())?.mix(ct) } + pub fn decaps_and_mix_static( + &mut self, + sk: &[u8; StaticKem::SK_LEN], + pk: &[u8; StaticKem::PK_LEN], + ct: &[u8; StaticKem::CT_LEN], + ) -> Result<&mut Self> { + self.decaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(sk, pk, ct) + } + + pub fn decaps_and_mix_ephemeral( + &mut self, + sk: &[u8; EphemeralKem::SK_LEN], + pk: &[u8; EphemeralKem::PK_LEN], + ct: &[u8; EphemeralKem::CT_LEN], + ) -> Result<&mut Self> { + self.decaps_and_mix::<{EphemeralKem::SK_LEN},{ EphemeralKem::PK_LEN}, {EphemeralKem::CT_LEN}, {EphemeralKem::SHK_LEN}, EphemeralKem>(sk, pk, ct) + } + /// Store the chaining key inside a cookie value called a "biscuit". /// /// This biscuit can be transmitted to the other party and must be returned @@ -3547,10 +3592,7 @@ impl CryptoServer { // IHI5 hs.core - .encaps_and_mix::( - ih.sctr.as_mut_slice(), - peer.get(self).spkt.deref(), - )?; + .encaps_and_mix_static(&mut ih.sctr, peer.get(self).spkt.deref())?; // IHI6 hs.core.encrypt_and_mix( @@ -3593,11 +3635,7 @@ impl CryptoServer { core.mix(&ih.sidi)?.mix(&ih.epki)?; // IHR5 - core.decaps_and_mix::( - self.sskm.secret(), - self.spkm.deref(), - &ih.sctr, - )?; + core.decaps_and_mix_static(self.sskm.secret(), self.spkm.deref(), &ih.sctr)?; // IHR6 let peer = { @@ -3623,13 +3661,10 @@ impl CryptoServer { core.mix(&rh.sidr)?.mix(&rh.sidi)?; // RHR4 - core.encaps_and_mix::(&mut rh.ecti, &ih.epki)?; + core.encaps_and_mix_ephemeral(&mut rh.ecti, &ih.epki)?; // RHR5 - core.encaps_and_mix::( - &mut rh.scti, - peer.get(self).spkt.deref(), - )?; + core.encaps_and_mix_static(&mut rh.scti, peer.get(self).spkt.deref())?; // RHR6 core.store_biscuit(self, peer, &mut rh.biscuit)?; @@ -3689,18 +3724,10 @@ impl CryptoServer { core.mix(&rh.sidr)?.mix(&rh.sidi)?; // RHI4 - core.decaps_and_mix::( - hs!().eski.secret(), - hs!().epki.deref(), - &rh.ecti, - )?; + core.decaps_and_mix_ephemeral(hs!().eski.secret(), hs!().epki.deref(), &rh.ecti)?; // RHI5 - core.decaps_and_mix::( - self.sskm.secret(), - self.spkm.deref(), - &rh.scti, - )?; + core.decaps_and_mix_static(self.sskm.secret(), self.spkm.deref(), &rh.scti)?; // RHI6 core.mix(&rh.biscuit)?; diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index fe8ff1f4..4ad6daaf 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -12,7 +12,7 @@ use rosenpass::{ app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}, protocol::{SPk, SSk, SymKey}, }; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt}; diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index f594c4c4..305c80c0 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -5,7 +5,7 @@ use std::{ ops::DerefMut, }; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_util::result::OkExt; diff --git a/rp/src/key.rs b/rp/src/key.rs index f061d058..0ec31219 100644 --- a/rp/src/key.rs +++ b/rp/src/key.rs @@ -10,7 +10,7 @@ use rosenpass_util::file::{LoadValueB64, StoreValue, StoreValueB64}; use zeroize::Zeroize; use rosenpass::protocol::{SPk, SSk}; -use rosenpass_cipher_traits::Kem; +use rosenpass_cipher_traits::kem::Kem; use rosenpass_ciphers::kem::StaticKem; use rosenpass_secret_memory::{file::StoreSecret as _, Public, Secret}; From 949a3e4d2384d0c98d4e9eb2204fb3bee2c7cd6d Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Wed, 26 Feb 2025 15:06:01 +0100 Subject: [PATCH 050/174] Add &self receiver to KEM trait methods --- cipher-traits/src/primitives/kem.rs | 19 ++++++++++++++++--- fuzz/fuzz_targets/kyber_encaps.rs | 4 +++- fuzz/fuzz_targets/mceliece_encaps.rs | 2 +- oqs/src/kem_macro.rs | 13 +++++++++---- rosenpass/benches/handshake.rs | 2 +- rosenpass/src/cli.rs | 2 +- rosenpass/src/protocol/protocol.rs | 18 +++++++++--------- rosenpass/tests/app_server_example.rs | 2 +- rosenpass/tests/poll_example.rs | 4 ++-- rp/src/key.rs | 2 +- 10 files changed, 44 insertions(+), 24 deletions(-) diff --git a/cipher-traits/src/primitives/kem.rs b/cipher-traits/src/primitives/kem.rs index f30aae43..85b51b05 100644 --- a/cipher-traits/src/primitives/kem.rs +++ b/cipher-traits/src/primitives/kem.rs @@ -133,23 +133,31 @@ use thiserror::Error; /// Key Encapsulation Mechanism /// /// The KEM interface defines three operations: Key generation, key encapsulation and key -/// decapsulation. +/// decapsulation. The parameters are made available as associated constants for convenience. pub trait Kem { + /// 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(sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), Error>; + 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], @@ -159,7 +167,12 @@ pub trait Kem shk` - fn decaps(shk: &mut [u8; SHK_LEN], sk: &[u8; SK_LEN], ct: &[u8; CT_LEN]) -> Result<(), Error>; + fn decaps( + &self, + shk: &mut [u8; SHK_LEN], + sk: &[u8; SK_LEN], + ct: &[u8; CT_LEN], + ) -> Result<(), Error>; } #[derive(Debug, Error)] diff --git a/fuzz/fuzz_targets/kyber_encaps.rs b/fuzz/fuzz_targets/kyber_encaps.rs index f1480ab5..38a6887a 100644 --- a/fuzz/fuzz_targets/kyber_encaps.rs +++ b/fuzz/fuzz_targets/kyber_encaps.rs @@ -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(); }); diff --git a/fuzz/fuzz_targets/mceliece_encaps.rs b/fuzz/fuzz_targets/mceliece_encaps.rs index 27a5e410..1eebf119 100644 --- a/fuzz/fuzz_targets/mceliece_encaps.rs +++ b/fuzz/fuzz_targets/mceliece_encaps.rs @@ -11,5 +11,5 @@ fuzz_target!(|input: [u8; StaticKem::PK_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); }); diff --git a/oqs/src/kem_macro.rs b/oqs/src/kem_macro.rs index eeb7b5fe..b3fd3cae 100644 --- a/oqs/src/kem_macro.rs +++ b/oqs/src/kem_macro.rs @@ -36,7 +36,7 @@ macro_rules! oqs_kem { #[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 = "```"] - pub enum [< $name:camel >] {} + pub struct [< $name:camel >]; pub const SK_LEN: usize = ::oqs_sys::kem::[] as usize; pub const PK_LEN: usize = ::oqs_sys::kem::[] as usize; @@ -56,7 +56,7 @@ macro_rules! oqs_kem { /// bigger. However, from a correctness point of view it does not make sense to /// allow bigger buffers. impl kem::Kem for [< $name:camel >] { - fn keygen(sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), kem::Error> { + fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), kem::Error> { unsafe { oqs_call!( ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ keypair >], @@ -68,7 +68,7 @@ macro_rules! oqs_kem { Ok(()) } - fn encaps(shk: &mut [u8; SHK_LEN], ct: &mut [u8; CT_LEN], pk: &[u8; PK_LEN]) -> Result<(), kem::Error> { + fn encaps(&self, shk: &mut [u8; SHK_LEN], ct: &mut [u8; CT_LEN], pk: &[u8; PK_LEN]) -> Result<(), kem::Error> { unsafe { oqs_call!( ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ encaps >], @@ -81,7 +81,7 @@ macro_rules! oqs_kem { Ok(()) } - fn decaps(shk: &mut [u8; SHK_LEN], sk: &[u8; SK_LEN], ct: &[u8; CT_LEN]) -> Result<(), kem::Error> { + fn decaps(&self, shk: &mut [u8; SHK_LEN], sk: &[u8; SK_LEN], ct: &[u8; CT_LEN]) -> Result<(), kem::Error> { unsafe { oqs_call!( ::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >], @@ -94,7 +94,12 @@ macro_rules! oqs_kem { Ok(()) } } + } + impl Default for [< $name:camel >] { + fn default() -> Self { + Self + } } pub use [< $name:snake >] :: [< $name:camel >]; diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index e329ca4d..8ba17a02 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -43,7 +43,7 @@ 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)) } diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index b4ec5783..eeb28fae 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -609,7 +609,7 @@ impl CliArgs { 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) } diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 60a3fcd6..6c0ba8cf 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3338,14 +3338,14 @@ impl HandshakeState { const KEM_PK_LEN: usize, const KEM_CT_LEN: usize, const KEM_SHK_LEN: usize, - T: Kem, + T: Default + Kem, >( &mut self, ct: &mut [u8; KEM_CT_LEN], pk: &[u8; KEM_PK_LEN], ) -> Result<&mut Self> { let mut shk = Secret::::zero(); - T::encaps(shk.secret_mut(), ct, pk)?; + T::default().encaps(shk.secret_mut(), ct, pk)?; self.mix(pk)?.mix(shk.secret())?.mix(ct) } @@ -3374,7 +3374,7 @@ impl HandshakeState { const KEM_PK_LEN: usize, const KEM_CT_LEN: usize, const KEM_SHK_LEN: usize, - T: Kem, + T: Default + Kem, >( &mut self, sk: &[u8; KEM_SK_LEN], @@ -3382,7 +3382,7 @@ impl HandshakeState { ct: &[u8; KEM_CT_LEN], ) -> Result<&mut Self> { let mut shk = Secret::::zero(); - T::decaps(shk.secret_mut(), sk, ct)?; + T::default().decaps(shk.secret_mut(), sk, ct)?; self.mix(pk)?.mix(shk.secret())?.mix(ct) } @@ -3584,7 +3584,7 @@ impl CryptoServer { ih.sidi.copy_from_slice(&hs.core.sidi.value); // IHI3 - EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?; + EphemeralKem.keygen(hs.eski.secret_mut(), &mut *hs.epki)?; ih.epki.copy_from_slice(&hs.epki.value); // IHI4 @@ -4013,11 +4013,11 @@ pub mod testutils { impl ServerForTesting { pub fn new(protocol_version: ProtocolVersion) -> anyhow::Result { let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); - StaticKem::keygen(sskm.secret_mut(), spkm.deref_mut())?; + StaticKem.keygen(sskm.secret_mut(), spkm.deref_mut())?; let mut srv = CryptoServer::new(sskm, spkm); let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); - StaticKem::keygen(sskt.secret_mut(), spkt.deref_mut())?; + StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; let peer = srv.add_peer(None, spkt.clone(), protocol_version)?; let peer_keys = (sskt, spkt); @@ -4162,7 +4162,7 @@ mod test { fn keygen() -> Result<(SSk, SPk)> { // TODO: Copied from the benchmark; deduplicate let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); - StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?; + StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?; Ok((sk, pk)) } @@ -4530,7 +4530,7 @@ mod test { fn keypair() -> 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)) } diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index 4ad6daaf..4897704f 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -114,7 +114,7 @@ struct TestServer { impl TestServer { fn new(termination_queue: mpsc::Receiver<()>) -> anyhow::Result { 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())?; let keypair = Some((sk, pk)); let addrs = vec![ diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index 305c80c0..7fc3add6 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -295,12 +295,12 @@ impl RosenpassSimulator { fn new(protocol_version: ProtocolVersion) -> anyhow::Result { // Set up the first server 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())?; + StaticKem.keygen(peer_a_sk.secret_mut(), peer_a_pk.deref_mut())?; let mut srv_a = CryptoServer::new(peer_a_sk, peer_a_pk.clone()); // …and the second server. 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())?; + StaticKem.keygen(peer_b_sk.secret_mut(), peer_b_pk.deref_mut())?; let mut srv_b = CryptoServer::new(peer_b_sk, peer_b_pk.clone()); // Generate a PSK and introduce the Peers to each other. diff --git a/rp/src/key.rs b/rp/src/key.rs index 0ec31219..a48ee3d5 100644 --- a/rp/src/key.rs +++ b/rp/src/key.rs @@ -66,7 +66,7 @@ pub fn genkey(private_keys_dir: &Path) -> Result<()> { if !pqsk_path.exists() && !pqpk_path.exists() { let mut pqsk = SSk::random(); let mut pqpk = SPk::random(); - StaticKem::keygen(pqsk.secret_mut(), pqpk.deref_mut())?; + StaticKem.keygen(pqsk.secret_mut(), pqpk.deref_mut())?; pqpk.store(pqpk_path)?; pqsk.store_secret(pqsk_path)?; } else { From b84e0beae8f5a4b390527aa9e32aecccba7faa97 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Wed, 26 Feb 2025 17:19:58 +0100 Subject: [PATCH 051/174] introduce traits for all the primitives and algorithms. A bit more cleanup. --- Cargo.lock | 3 +- cipher-traits/Cargo.toml | 1 + cipher-traits/src/algorithms.rs | 81 +++++++++++++++++ cipher-traits/src/lib.rs | 4 +- cipher-traits/src/primitives.rs | 9 +- cipher-traits/src/primitives/aead.rs | 73 ++++++++++++++++ cipher-traits/src/primitives/keyed_hash.rs | 37 ++++++++ ciphers/src/hash_domain.rs | 19 ++-- ciphers/src/lib.rs | 55 +++++------- .../subtle/custom/incorrect_hmac_blake2b.rs | 11 ++- ciphers/src/subtle/keyed_hash.rs | 2 +- ciphers/src/subtle/rust_crypto/blake2b.rs | 55 ++++-------- .../rust_crypto/chacha20poly1305_ietf.rs | 87 +++++++++++++++---- .../src/subtle/rust_crypto/keyed_shake256.rs | 2 +- .../rust_crypto/xchacha20poly1305_ietf.rs | 80 +++++++++++++---- fuzz/fuzz_targets/aead_enc_into.rs | 5 +- fuzz/fuzz_targets/blake2b.rs | 5 +- fuzz/fuzz_targets/handle_msg.rs | 4 +- fuzz/fuzz_targets/kyber_encaps.rs | 4 +- fuzz/fuzz_targets/mceliece_encaps.rs | 4 +- oqs/src/kem_macro.rs | 14 +-- oqs/src/lib.rs | 7 +- rosenpass/benches/handshake.rs | 4 +- rosenpass/src/cli.rs | 4 +- rosenpass/src/msgs.rs | 26 +++--- rosenpass/src/protocol/protocol.rs | 38 ++++---- rosenpass/tests/app_server_example.rs | 4 +- rosenpass/tests/poll_example.rs | 4 +- rp/src/key.rs | 4 +- 29 files changed, 469 insertions(+), 177 deletions(-) create mode 100644 cipher-traits/src/algorithms.rs create mode 100644 cipher-traits/src/primitives/aead.rs diff --git a/Cargo.lock b/Cargo.lock index a4ab6684..bb7fc0da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "addr2line" @@ -1828,6 +1828,7 @@ dependencies = [ "anyhow", "rosenpass-oqs", "rosenpass-secret-memory", + "rosenpass-to", "thiserror 2.0.11", ] diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml index 9d859588..7ba40c44 100644 --- a/cipher-traits/Cargo.toml +++ b/cipher-traits/Cargo.toml @@ -11,6 +11,7 @@ readme = "readme.md" [dependencies] thiserror = "2" +rosenpass-to = { workspace = true } [dev-dependencies] rosenpass-oqs = { workspace = true } diff --git a/cipher-traits/src/algorithms.rs b/cipher-traits/src/algorithms.rs new file mode 100644 index 00000000..25631209 --- /dev/null +++ b/cipher-traits/src/algorithms.rs @@ -0,0 +1,81 @@ +pub mod keyed_hash_incorrect_hmac_blake2b { + use crate::primitives::keyed_hash::*; + + pub const KEY_LEN: usize = 32; + pub const OUT_LEN: usize = 32; + + pub trait KeyedHashIncorrectHmacBlake2b: KeyedHash {} +} + +pub mod keyed_hash_blake2b { + use crate::primitives::keyed_hash::*; + + pub const KEY_LEN: usize = 32; + pub const OUT_LEN: usize = 32; + + pub trait KeyedHashBlake2b: KeyedHash {} +} + +pub mod keyed_hash_shake256 { + use crate::primitives::keyed_hash::*; + + pub const KEY_LEN: usize = 32; + pub const OUT_LEN: usize = 32; + + pub trait KeyedHashShake256: KeyedHash {} +} + +pub mod aead_chacha20poly1305 { + use crate::primitives::aead::*; + + pub const KEY_LEN: usize = 32; + pub const NONCE_LEN: usize = 12; + pub const TAG_LEN: usize = 16; + + pub trait AeadChaCha20Poly1305: Aead {} +} + +pub mod aead_xchacha20poly1305 { + use crate::primitives::aead::*; + + pub const KEY_LEN: usize = 32; + pub const NONCE_LEN: usize = 24; + pub const TAG_LEN: usize = 16; + + pub trait AeadXChaCha20Poly1305: Aead {} +} + +pub mod kem_kyber512 { + use crate::primitives::kem::*; + + // page 33 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf + // (which is ml-kem instead of kyber, but it's the same) + pub const SK_LEN: usize = 1632; + pub const PK_LEN: usize = 800; + pub const CT_LEN: usize = 768; + pub const SHK_LEN: usize = 32; + + pub trait KemKyber512: Kem {} +} + +pub mod kem_classic_mceliece460896 { + use crate::primitives::kem::*; + + // page 6 of https://classic.mceliece.org/mceliece-impl-20221023.pdf + pub const SK_LEN: usize = 13608; + pub const PK_LEN: usize = 524160; + pub const CT_LEN: usize = 156; + pub const SHK_LEN: usize = 32; + + pub trait KemClassicMceliece460896: Kem {} +} + +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; diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs index dbe4e5d5..b61036e9 100644 --- a/cipher-traits/src/lib.rs +++ b/cipher-traits/src/lib.rs @@ -1,2 +1,2 @@ -mod primitives; -pub use primitives::*; +pub mod algorithms; +pub mod primitives; diff --git a/cipher-traits/src/primitives.rs b/cipher-traits/src/primitives.rs index 24a8ead2..9a8116a7 100644 --- a/cipher-traits/src/primitives.rs +++ b/cipher-traits/src/primitives.rs @@ -1,2 +1,7 @@ -pub mod kem; -pub mod keyed_hash; +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::*; diff --git a/cipher-traits/src/primitives/aead.rs b/cipher-traits/src/primitives/aead.rs new file mode 100644 index 00000000..af44f408 --- /dev/null +++ b/cipher-traits/src/primitives/aead.rs @@ -0,0 +1,73 @@ +use thiserror::Error; + +pub trait Aead { + const KEY_LEN: usize = KEY_LEN; + const NONCE_LEN: usize = NONCE_LEN; + const TAG_LEN: usize = TAG_LEN; + + fn encrypt( + &self, + ciphertext: &mut [u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], + ad: &[u8], + plaintext: &[u8], + ) -> Result<(), Error>; + + fn decrypt( + &self, + plaintext: &mut [u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], + ad: &[u8], + ciphertext: &[u8], + ) -> Result<(), Error>; +} + +/// The API of XChaCha was a bit weird and moved the nonce into the ciphertext. Instead of changing +/// the protocol code, we recreate that API on top of the more normal API, but move it into a +/// separate crate. +pub trait AeadWithNonceInCiphertext< + const KEY_LEN: usize, + const NONCE_LEN: usize, + const TAG_LEN: usize, +>: Aead +{ + fn decrypt_with_nonce_in_ctxt( + &self, + plaintext: &mut [u8], + key: &[u8; KEY_LEN], + ad: &[u8], + ciphertext: &[u8], + ) -> Result<(), Error> { + if ciphertext.len() < plaintext.len() + TAG_LEN + NONCE_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, + > AeadWithNonceInCiphertext for T +{ +} + +#[derive(Debug, Error)] +pub enum Error { + #[error("internal error")] + InternalError, + #[error("decryption error")] + DecryptError, + #[error("buffers have invalid length")] + InvalidLengths, +} diff --git a/cipher-traits/src/primitives/keyed_hash.rs b/cipher-traits/src/primitives/keyed_hash.rs index 175bd9e5..1b06ce2b 100644 --- a/cipher-traits/src/primitives/keyed_hash.rs +++ b/cipher-traits/src/primitives/keyed_hash.rs @@ -109,3 +109,40 @@ where Static: KeyedHash, { } + +use rosenpass_to::{with_destination, To}; + +/// Extends the [`KeyedHash`] trait with a [`To`]-flavoured function. +pub trait KeyedHashTo: + KeyedHash +{ + 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> + KeyedHashTo for T +{ +} + +/// Extends the [`KeyedHashInstance`] trait with a [`To`]-flavoured function. +pub trait KeyedHashInstanceTo: + KeyedHashInstance +{ + 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> + KeyedHashInstanceTo for T +{ +} diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index e80215b6..49c51ffc 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -29,10 +29,11 @@ use anyhow::Result; use rosenpass_secret_memory::Secret; +use rosenpass_to::To as _; -pub use crate::keyed_hash::{KeyedHash, KEY_LEN}; +pub use crate::{KeyedHash, KEY_LEN}; -use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; +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. @@ -78,7 +79,7 @@ impl HashDomain { /// pub fn mix(self, v: &[u8]) -> Result { let mut new_key: [u8; KEY_LEN] = [0u8; KEY_LEN]; - self.1.keyed_hash(&self.0, v, &mut new_key)?; + self.1.keyed_hash_to(&self.0, v).to(&mut new_key)?; Ok(Self(new_key, self.1)) } @@ -101,7 +102,7 @@ impl HashDomainNamespace { /// as the `data` and uses the result as the key for the new [HashDomain]. pub fn mix(&self, v: &[u8]) -> Result { let mut new_key: [u8; KEY_LEN] = [0u8; KEY_LEN]; - self.1.keyed_hash(&self.0, v, &mut new_key)?; + self.1.keyed_hash_to(&self.0, v).to(&mut new_key)?; Ok(HashDomain(new_key, self.1.clone())) } @@ -129,9 +130,13 @@ impl SecretHashDomain { hash_choice: KeyedHash, ) -> Result { let mut new_secret_key = Secret::zero(); - hash_choice.keyed_hash(k.try_into()?, d, new_secret_key.secret_mut())?; + hash_choice + .keyed_hash_to(k.try_into()?, d) + .to(new_secret_key.secret_mut())?; let mut r = SecretHashDomain(new_secret_key, hash_choice); - KeyedHash::incorrect_hmac_blake2b().keyed_hash(k.try_into()?, d, r.0.secret_mut())?; + KeyedHash::incorrect_hmac_blake2b() + .keyed_hash_to(k.try_into()?, d) + .to(r.0.secret_mut())?; Ok(r) } @@ -186,7 +191,7 @@ impl SecretHashDomain { * let SecretHashDomain(secret, hash_choice) = &self; * * let mut new_secret = Secret::zero(); - * hash_choice.keyed_hash(secret.secret(), dst, new_secret.secret_mut())?; + * hash_choice.keyed_hash_to(secret.secret(), dst).to(new_secret.secret_mut())?; * self.0 = new_secret; * * Ok(()) diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index 77bf498d..a6a63c8c 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -1,11 +1,12 @@ +use rosenpass_cipher_traits::primitives::Aead as _; 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); /// Keyed hashing @@ -13,40 +14,30 @@ const_assert!(KEY_LEN == hash_domain::KEY_LEN); /// 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; +pub use crate::subtle::keyed_hash::KeyedHash; -/// Authenticated encryption with associated data +/// Authenticated encryption with associated data (AEAD) /// Chacha20poly1305 is used. -pub mod aead { - #[cfg(feature = "experiment_libcrux")] - pub use crate::subtle::libcrux::chacha20poly1305_ietf::{ - decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, - }; +#[cfg(feature = "experiment_libcrux")] +pub use subtle::libcrux::chacha20poly1305_ietf::Chacha20poly1305 as Aead; - #[cfg(not(feature = "experiment_libcrux"))] - pub use crate::subtle::rust_crypto::chacha20poly1305_ietf::{ - decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, - }; -} +/// Authenticated encryption with associated data (AEAD) +/// Chacha20poly1305 is used. +#[cfg(not(feature = "experiment_libcrux"))] +pub use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as Aead; -/// Authenticated encryption with associated data with a constant nonce +/// Authenticated encryption with associated data with a extended-length nonce (XAEAD) /// XChacha20poly1305 is used. -pub mod xaead { - pub use crate::subtle::rust_crypto::xchacha20poly1305_ietf::{ - decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN, - }; -} +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. +pub use rosenpass_oqs::Kyber512 as EphemeralKem; pub mod hash_domain; - -/// This crate includes two key encapsulation mechanisms. -/// Namely ClassicMceliece460896 (also referred to as `StaticKem` sometimes) and -/// Kyber512 (also referred to as `EphemeralKem` sometimes). -/// -/// See [rosenpass_oqs::ClassicMceliece460896] -/// and [rosenpass_oqs::Kyber512] for more details on the specific KEMS. -/// -pub mod kem { - pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem; - pub use rosenpass_oqs::Kyber512 as EphemeralKem; -} diff --git a/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs index ca1b3b96..d6aa3af1 100644 --- a/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs @@ -1,5 +1,8 @@ use anyhow::ensure; -use rosenpass_cipher_traits::keyed_hash::{InferKeyedHash, KeyedHash}; +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; @@ -59,14 +62,16 @@ impl KeyedHash for IncorrectHmacBlake2bCore { 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())?; + blake2b::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::hash(tmp_key.as_ref(), outer_data.as_ref()).to(out)?; + blake2b::Blake2b::keyed_hash_to(&tmp_key, outer_data.as_ref()).to(out)?; Ok(()) } } pub type IncorrectHmacBlake2b = InferKeyedHash; + +impl KeyedHashIncorrectHmacBlake2b for IncorrectHmacBlake2bCore {} diff --git a/ciphers/src/subtle/keyed_hash.rs b/ciphers/src/subtle/keyed_hash.rs index 9177c6e4..410f0e06 100644 --- a/ciphers/src/subtle/keyed_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; +use rosenpass_cipher_traits::primitives::KeyedHashInstance; pub const KEY_LEN: usize = 32; pub const HASH_LEN: usize = 32; diff --git a/ciphers/src/subtle/rust_crypto/blake2b.rs b/ciphers/src/subtle/rust_crypto/blake2b.rs index 4d4ff9d5..7651688e 100644 --- a/ciphers/src/subtle/rust_crypto/blake2b.rs +++ b/ciphers/src/subtle/rust_crypto/blake2b.rs @@ -2,54 +2,33 @@ 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::digest::{FixedOutput, Mac}; use blake2::Blake2bMac; -use rosenpass_to::{ops::copy_slice, with_destination, To}; -use rosenpass_util::typenum2const; +use rosenpass_cipher_traits::primitives::KeyedHash; +use rosenpass_to::{ops::copy_slice, To}; /// 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; -type KeyLen = ::KeySize; -type OutLen = ::OutputSize; - /// The key length for BLAKE2b supported by this API. Currently 32 Bytes. -const KEY_LEN: usize = typenum2const! { KeyLen }; +const KEY_LEN: usize = 32; /// The output length for BLAKE2b supported by this API. Currently 32 Bytes. -const OUT_LEN: usize = typenum2const! { OutLen }; - -/// Minimal key length supported by this API. -pub const KEY_MIN: usize = KEY_LEN; -/// maximal key length supported by this API. -pub const KEY_MAX: usize = KEY_LEN; -/// minimal output length supported by this API. -pub const OUT_MIN: usize = OUT_LEN; -/// maximal output length supported by this API. -pub const OUT_MAX: usize = OUT_LEN; +const OUT_LEN: usize = 32; /// 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). -/// -/// TODO: Adapt example -/// # Examples -/// -///```rust -/// # use rosenpass_ciphers::subtle::blake2b::hash; -/// use rosenpass_to::To; -/// let zero_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!(hash(&zero_key, &data).to(&mut hash_data).is_ok(), "Hashing has to return OK result"); -///``` -/// -#[inline] -pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a { - with_destination(|out: &mut [u8]| { +pub struct Blake2b; + +impl KeyedHash for Blake2b { + type Error = anyhow::Error; + + fn keyed_hash( + key: &[u8; KEY_LEN], + data: &[u8], + out: &mut [u8; OUT_LEN], + ) -> Result<(), Self::Error> { let mut h = Impl::new_from_slice(key)?; h.update(data); @@ -62,5 +41,7 @@ pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<( h.finalize_into(tmp); copy_slice(tmp.as_ref()).to(out); Ok(()) - }) + } } + +impl rosenpass_cipher_traits::algorithms::KeyedHashBlake2b for Blake2b {} diff --git a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs index b979aff1..1223e343 100644 --- a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs @@ -1,3 +1,4 @@ +use rosenpass_cipher_traits::primitives::{Aead, AeadError}; use rosenpass_to::ops::copy_slice; use rosenpass_to::To; use rosenpass_util::typenum2const; @@ -13,6 +14,66 @@ pub const TAG_LEN: usize = typenum2const! { ::TagSize }; /// The nonce length is 12 bytes or 96 bits. pub const NONCE_LEN: usize = typenum2const! { ::NonceSize }; +pub struct ChaCha20Poly1305; + +impl Aead for ChaCha20Poly1305 { + fn encrypt( + &self, + ciphertext: &mut [u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], + ad: &[u8], + plaintext: &[u8], + ) -> Result<(), AeadError> { + if ciphertext.len() < plaintext.len() + TAG_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> { + if ciphertext.len() < plaintext.len() + TAG_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(()) + } +} + /// Encrypts using ChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305). /// `key` MUST be chosen (pseudo-)randomly and `nonce` MOST NOT be reused. 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 @@ -42,17 +103,14 @@ pub const NONCE_LEN: usize = typenum2const! { ::NonceSize #[inline] pub fn encrypt( ciphertext: &mut [u8], - key: &[u8], - nonce: &[u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], 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(()) + ChaCha20Poly1305 + .encrypt(ciphertext, key, nonce, ad, plaintext) + .map_err(anyhow::Error::from) } /// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data @@ -85,15 +143,12 @@ pub fn encrypt( #[inline] pub fn decrypt( plaintext: &mut [u8], - key: &[u8], - nonce: &[u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], 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(()) + ChaCha20Poly1305 + .decrypt(plaintext, key, nonce, ad, ciphertext) + .map_err(anyhow::Error::from) } diff --git a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs index 297a78e3..9df9041d 100644 --- a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -1,5 +1,5 @@ use anyhow::ensure; -use rosenpass_cipher_traits::keyed_hash::{InferKeyedHash, KeyedHash}; +use rosenpass_cipher_traits::primitives::{InferKeyedHash, KeyedHash}; use sha3::digest::{ExtendableOutput, Update, XofReader}; use sha3::Shake256; diff --git a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs index 12a8ed8b..4aa839c8 100644 --- a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs @@ -1,3 +1,4 @@ +use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext}; use rosenpass_to::ops::copy_slice; use rosenpass_to::To; use rosenpass_util::typenum2const; @@ -13,6 +14,58 @@ pub const TAG_LEN: usize = typenum2const! { ::TagSize }; /// The nonce length is 24 bytes or 192 bits. pub const NONCE_LEN: usize = typenum2const! { ::NonceSize }; +pub struct XChaCha20Poly1305; + +impl Aead for XChaCha20Poly1305 { + fn encrypt( + &self, + ciphertext: &mut [u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], + ad: &[u8], + plaintext: &[u8], + ) -> Result<(), AeadError> { + 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); + + // 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> { + 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(()) + } +} + /// 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]. @@ -44,19 +97,14 @@ pub const NONCE_LEN: usize = typenum2const! { ::NonceSize #[inline] pub fn encrypt( ciphertext: &mut [u8], - key: &[u8], - nonce: &[u8], + key: &[u8; KEY_LEN], + nonce: &[u8; NONCE_LEN], 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(()) + XChaCha20Poly1305 + .encrypt(ciphertext, key, nonce, ad, plaintext) + .map_err(anyhow::Error::from) } /// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data @@ -94,15 +142,11 @@ pub fn encrypt( #[inline] pub fn decrypt( plaintext: &mut [u8], - key: &[u8], + key: &[u8; KEY_LEN], 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(()) + XChaCha20Poly1305 + .decrypt_with_nonce_in_ctxt(plaintext, key, ad, ciphertext) + .map_err(anyhow::Error::from) } diff --git a/fuzz/fuzz_targets/aead_enc_into.rs b/fuzz/fuzz_targets/aead_enc_into.rs index 8f529f7a..89c191d1 100644 --- a/fuzz/fuzz_targets/aead_enc_into.rs +++ b/fuzz/fuzz_targets/aead_enc_into.rs @@ -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, diff --git a/fuzz/fuzz_targets/blake2b.rs b/fuzz/fuzz_targets/blake2b.rs index f4349b01..a6b41edf 100644 --- a/fuzz/fuzz_targets/blake2b.rs +++ b/fuzz/fuzz_targets/blake2b.rs @@ -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(); }); diff --git a/fuzz/fuzz_targets/handle_msg.rs b/fuzz/fuzz_targets/handle_msg.rs index 6185ccac..0bfbaa25 100644 --- a/fuzz/fuzz_targets/handle_msg.rs +++ b/fuzz/fuzz_targets/handle_msg.rs @@ -4,8 +4,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; use rosenpass::protocol::CryptoServer; -use rosenpass_cipher_traits::kem::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; diff --git a/fuzz/fuzz_targets/kyber_encaps.rs b/fuzz/fuzz_targets/kyber_encaps.rs index 38a6887a..723003c9 100644 --- a/fuzz/fuzz_targets/kyber_encaps.rs +++ b/fuzz/fuzz_targets/kyber_encaps.rs @@ -4,8 +4,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass_cipher_traits::kem::Kem; -use rosenpass_ciphers::kem::EphemeralKem; +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::EphemeralKem; #[derive(arbitrary::Arbitrary, Debug)] pub struct Input { diff --git a/fuzz/fuzz_targets/mceliece_encaps.rs b/fuzz/fuzz_targets/mceliece_encaps.rs index 1eebf119..bbca4568 100644 --- a/fuzz/fuzz_targets/mceliece_encaps.rs +++ b/fuzz/fuzz_targets/mceliece_encaps.rs @@ -3,8 +3,8 @@ extern crate rosenpass; use libfuzzer_sys::fuzz_target; -use rosenpass_cipher_traits::kem::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]; diff --git a/oqs/src/kem_macro.rs b/oqs/src/kem_macro.rs index b3fd3cae..55c30943 100644 --- a/oqs/src/kem_macro.rs +++ b/oqs/src/kem_macro.rs @@ -2,10 +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_cipher_traits::primitives::{Kem, KemError}; #[doc = "Bindings for ::oqs_sys::kem::" [<"OQS_KEM" _ $name:snake>] "_*"] #[doc = ""] @@ -55,8 +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::Kem for [< $name:camel >] { - fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), kem::Error> { + impl Kem 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 >], @@ -68,7 +68,7 @@ macro_rules! oqs_kem { Ok(()) } - fn encaps(&self, shk: &mut [u8; SHK_LEN], ct: &mut [u8; CT_LEN], pk: &[u8; PK_LEN]) -> Result<(), kem::Error> { + 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 >], @@ -81,7 +81,7 @@ macro_rules! oqs_kem { Ok(()) } - fn decaps(&self, shk: &mut [u8; SHK_LEN], sk: &[u8; SK_LEN], ct: &[u8; CT_LEN]) -> Result<(), kem::Error> { + 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 >], @@ -102,6 +102,8 @@ macro_rules! oqs_kem { } } + impl $algo_trait for [< $name:camel >] {} + pub use [< $name:snake >] :: [< $name:camel >]; }} } diff --git a/oqs/src/lib.rs b/oqs/src/lib.rs index 842a8d5d..8117c681 100644 --- a/oqs/src/lib.rs +++ b/oqs/src/lib.rs @@ -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 +); diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index 8ba17a02..fc6b1fd3 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -4,8 +4,8 @@ use rosenpass::protocol::{ }; use std::ops::DerefMut; -use rosenpass_cipher_traits::kem::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; diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index eeb28fae..4d80761b 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -5,8 +5,8 @@ use anyhow::{bail, ensure, Context}; use clap::{Parser, Subcommand}; -use rosenpass_cipher_traits::kem::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::{ diff --git a/rosenpass/src/msgs.rs b/rosenpass/src/msgs.rs index 8ef7da5f..f6ea1b6f 100644 --- a/rosenpass/src/msgs.rs +++ b/rosenpass/src/msgs.rs @@ -9,13 +9,12 @@ //! To achieve this we utilize the zerocopy library. //! use std::mem::size_of; -use std::u8; use zerocopy::{AsBytes, FromBytes, FromZeroes}; use super::RosenpassError; -use rosenpass_cipher_traits::kem::Kem; -use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; -use rosenpass_ciphers::{aead, xaead, KEY_LEN}; +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; @@ -32,7 +31,7 @@ pub const MAX_MESSAGE_LEN: usize = 2500; // TODO fix this pub const BISCUIT_PT_LEN: usize = size_of::(); /// Length in bytes of an encrypted Biscuit (cipher text) -pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN; +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; @@ -136,9 +135,9 @@ 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 @@ -187,7 +186,7 @@ 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], } @@ -236,7 +235,7 @@ 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 @@ -292,7 +291,7 @@ pub struct EmptyData { /// 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] @@ -346,7 +345,7 @@ 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. @@ -437,7 +436,8 @@ impl From for u8 { #[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() { @@ -453,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 ); } } diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 6c0ba8cf..74fbfd05 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -20,12 +20,11 @@ use rand::Fill as Randomize; use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; -use rosenpass_cipher_traits::kem::Kem; -use rosenpass_cipher_traits::keyed_hash::KeyedHashInstance; +use rosenpass_cipher_traits::primitives::{ + Aead as _, AeadWithNonceInCiphertext, Kem, KeyedHashInstance, +}; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; -use rosenpass_ciphers::kem::{EphemeralKem, StaticKem}; -use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; -use rosenpass_ciphers::{aead, xaead, KEY_LEN}; +use rosenpass_ciphers::{Aead, EphemeralKem, KeyedHash, StaticKem, XAead, KEY_LEN}; use rosenpass_constant_time as constant_time; use rosenpass_secret_memory::{Public, PublicBox, Secret}; use rosenpass_to::ops::copy_slice; @@ -172,7 +171,7 @@ pub type SessionId = Public; pub type BiscuitId = Public; /// Nonce for use with random-nonce AEAD -pub type XAEADNonce = Public<{ xaead::NONCE_LEN }>; +pub type XAEADNonce = Public<{ XAead::NONCE_LEN }>; /// Buffer capably of holding any Rosenpass protocol message pub type MsgBuf = Public; @@ -2242,7 +2241,7 @@ impl CryptoServer { msg_out.inner.msg_type = MsgType::CookieReply.into(); msg_out.inner.sid = rx_sid; - xaead::encrypt( + XAead.encrypt( &mut msg_out.inner.cookie_encrypted[..], &cookie_key, &nonce.value, @@ -3310,7 +3309,7 @@ impl HandshakeState { .ck .mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)? .into_secret(); - aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?; + Aead.encrypt(ct, k.secret(), &[0u8; Aead::NONCE_LEN], &[], pt)?; self.mix(ct) } @@ -3323,7 +3322,7 @@ impl HandshakeState { .ck .mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)? .into_secret(); - aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?; + Aead.decrypt(pt, k.secret(), &[0u8; Aead::NONCE_LEN], &[], ct)?; self.mix(ct) } @@ -3449,7 +3448,7 @@ impl HandshakeState { let k = bk.get(srv).value.secret(); let pt = biscuit.as_bytes(); - xaead::encrypt(biscuit_ct, k, &*n, &ad, pt)?; + XAead.encrypt(biscuit_ct, k, &*n, &ad, pt)?; self.mix(biscuit_ct) } @@ -3476,7 +3475,7 @@ impl HandshakeState { let mut biscuit = Secret::::zero(); // pt buf let mut biscuit: Ref<&mut [u8], Biscuit> = Ref::new(biscuit.secret_mut().as_mut_slice()).unwrap(); - xaead::decrypt( + XAead.decrypt_with_nonce_in_ctxt( biscuit.as_bytes_mut(), bk.get(srv).value.secret(), &ad, @@ -3790,7 +3789,7 @@ impl CryptoServer { )?; // ICR2 - core.encrypt_and_mix(&mut [0u8; aead::TAG_LEN], &[])?; + core.encrypt_and_mix(&mut [0u8; Aead::TAG_LEN], &[])?; // ICR3 core.mix(&ic.sidi)?.mix(&ic.sidr)?; @@ -3855,9 +3854,9 @@ impl CryptoServer { rc.ctr.copy_from_slice(&ses.txnm.to_le_bytes()); ses.txnm += 1; // Increment nonce before encryption, just in case an error is raised - let n = cat!(aead::NONCE_LEN; &rc.ctr, &[0u8; 4]); + let n = cat!(Aead::NONCE_LEN; &rc.ctr, &[0u8; 4]); let k = ses.txkm.secret(); - aead::encrypt(&mut rc.auth, k, &n, &[], &[])?; // ct, k, n, ad, pt + Aead.encrypt(&mut rc.auth, k, &n, &[], &[])?; // ct, k, n, ad, pt Ok(peer) } @@ -3902,11 +3901,11 @@ impl CryptoServer { let n = u64::from_le_bytes(rc.ctr); ensure!(n >= s.txnt, "Stale nonce"); s.txnt = n; - aead::decrypt( + Aead.decrypt( // pt, k, n, ad, ct &mut [0u8; 0], s.txkt.secret(), - &cat!(aead::NONCE_LEN; &rc.ctr, &[0u8; 4]), + &cat!(Aead::NONCE_LEN; &rc.ctr, &[0u8; 4]), &[], &rc.auth, )?; @@ -3967,7 +3966,12 @@ impl CryptoServer { .into_value(); let cookie_value = peer.cv().update_mut(self).unwrap(); - xaead::decrypt(cookie_value, &cookie_key, &mac, &cr.inner.cookie_encrypted)?; + XAead.decrypt_with_nonce_in_ctxt( + cookie_value, + &cookie_key, + &mac, + &cr.inner.cookie_encrypted, + )?; // Immediately retransmit on recieving a cookie reply message peer.hs().register_immediate_retransmission(self)?; diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index 4897704f..555b96ca 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -12,8 +12,8 @@ use rosenpass::{ app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}, protocol::{SPk, SSk, SymKey}, }; -use rosenpass_cipher_traits::kem::Kem; -use rosenpass_ciphers::kem::StaticKem; +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::StaticKem; use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt}; #[test] diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index 7fc3add6..fcb4ffd3 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -5,8 +5,8 @@ use std::{ ops::DerefMut, }; -use rosenpass_cipher_traits::kem::Kem; -use rosenpass_ciphers::kem::StaticKem; +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::StaticKem; use rosenpass_util::result::OkExt; use rosenpass::protocol::{ diff --git a/rp/src/key.rs b/rp/src/key.rs index a48ee3d5..f8f7f3d1 100644 --- a/rp/src/key.rs +++ b/rp/src/key.rs @@ -10,8 +10,8 @@ use rosenpass_util::file::{LoadValueB64, StoreValue, StoreValueB64}; use zeroize::Zeroize; use rosenpass::protocol::{SPk, SSk}; -use rosenpass_cipher_traits::kem::Kem; -use rosenpass_ciphers::kem::StaticKem; +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::{file::StoreSecret as _, Public, Secret}; /// The length of wireguard keys as a length in base 64 encoding. From 01a14080443b49314474ac7dc36cd0683363741b Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 27 Feb 2025 11:30:51 +0100 Subject: [PATCH 052/174] address feedback --- cipher-traits/src/algorithms.rs | 7 ++++- cipher-traits/src/primitives/aead.rs | 29 ++++++++++++++++++- .../rust_crypto/chacha20poly1305_ietf.rs | 8 +++-- .../rust_crypto/xchacha20poly1305_ietf.rs | 19 +++++++++--- rosenpass/src/protocol/protocol.rs | 8 +++-- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/cipher-traits/src/algorithms.rs b/cipher-traits/src/algorithms.rs index 25631209..47470937 100644 --- a/cipher-traits/src/algorithms.rs +++ b/cipher-traits/src/algorithms.rs @@ -1,6 +1,7 @@ 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. pub const KEY_LEN: usize = 32; pub const OUT_LEN: usize = 32; @@ -10,6 +11,7 @@ pub mod keyed_hash_incorrect_hmac_blake2b { pub mod keyed_hash_blake2b { use crate::primitives::keyed_hash::*; + // These constants describe how they are used here, not what the algorithm defines. pub const KEY_LEN: usize = 32; pub const OUT_LEN: usize = 32; @@ -19,6 +21,7 @@ pub mod keyed_hash_blake2b { pub mod keyed_hash_shake256 { use crate::primitives::keyed_hash::*; + // These constants describe how they are used here, not what the algorithm defines. pub const KEY_LEN: usize = 32; pub const OUT_LEN: usize = 32; @@ -28,6 +31,7 @@ pub mod keyed_hash_shake256 { pub mod aead_chacha20poly1305 { use crate::primitives::aead::*; + // See https://datatracker.ietf.org/doc/html/rfc7539#section-2.8 pub const KEY_LEN: usize = 32; pub const NONCE_LEN: usize = 12; pub const TAG_LEN: usize = 16; @@ -38,6 +42,7 @@ pub mod aead_chacha20poly1305 { pub mod aead_xchacha20poly1305 { use crate::primitives::aead::*; + // See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03 pub const KEY_LEN: usize = 32; pub const NONCE_LEN: usize = 24; pub const TAG_LEN: usize = 16; @@ -48,7 +53,7 @@ pub mod aead_xchacha20poly1305 { pub mod kem_kyber512 { use crate::primitives::kem::*; - // page 33 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.ipd.pdf + // 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) pub const SK_LEN: usize = 1632; pub const PK_LEN: usize = 800; diff --git a/cipher-traits/src/primitives/aead.rs b/cipher-traits/src/primitives/aead.rs index af44f408..286bb48e 100644 --- a/cipher-traits/src/primitives/aead.rs +++ b/cipher-traits/src/primitives/aead.rs @@ -1,3 +1,4 @@ +use rosenpass_to::{ops::copy_slice, To as _}; use thiserror::Error; pub trait Aead { @@ -33,6 +34,28 @@ pub trait AeadWithNonceInCiphertext< const TAG_LEN: usize, >: Aead { + 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) + } + fn decrypt_with_nonce_in_ctxt( &self, plaintext: &mut [u8], @@ -40,7 +63,11 @@ pub trait AeadWithNonceInCiphertext< ad: &[u8], ciphertext: &[u8], ) -> Result<(), Error> { - if ciphertext.len() < plaintext.len() + TAG_LEN + NONCE_LEN { + // 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); } diff --git a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs index 1223e343..c085d03a 100644 --- a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs @@ -25,7 +25,9 @@ impl Aead for ChaCha20Poly1305 { ad: &[u8], plaintext: &[u8], ) -> Result<(), AeadError> { - if ciphertext.len() < plaintext.len() + TAG_LEN { + // 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); } @@ -53,7 +55,9 @@ impl Aead for ChaCha20Poly1305 { ad: &[u8], ciphertext: &[u8], ) -> Result<(), AeadError> { - if ciphertext.len() < plaintext.len() + TAG_LEN { + // 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); } diff --git a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs index 4aa839c8..2ba93fee 100644 --- a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs @@ -25,12 +25,17 @@ impl Aead for XChaCha20Poly1305 { ad: &[u8], plaintext: &[u8], ) -> Result<(), AeadError> { - 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); + // 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)?; @@ -50,6 +55,12 @@ impl Aead for XChaCha20Poly1305 { 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); diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 74fbfd05..5ac8d687 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -2241,7 +2241,7 @@ impl CryptoServer { msg_out.inner.msg_type = MsgType::CookieReply.into(); msg_out.inner.sid = rx_sid; - XAead.encrypt( + XAead.encrypt_with_nonce_in_ctxt( &mut msg_out.inner.cookie_encrypted[..], &cookie_key, &nonce.value, @@ -3348,6 +3348,7 @@ impl HandshakeState { self.mix(pk)?.mix(shk.secret())?.mix(ct) } + /// Calls [`Self::encaps_and_mix`] with the generic parameters that match [`StaticKem`]. pub fn encaps_and_mix_static( &mut self, ct: &mut [u8; StaticKem::CT_LEN], @@ -3356,6 +3357,7 @@ impl HandshakeState { self.encaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(ct, pk) } + /// Calls [`Self::encaps_and_mix`] with the generic parameters that match [`EphemeralKem`]. pub fn encaps_and_mix_ephemeral( &mut self, ct: &mut [u8; EphemeralKem::CT_LEN], @@ -3385,6 +3387,7 @@ impl HandshakeState { self.mix(pk)?.mix(shk.secret())?.mix(ct) } + /// Calls [`Self::decaps_and_mix`] with the generic parameters that match [`StaticKem`]. pub fn decaps_and_mix_static( &mut self, sk: &[u8; StaticKem::SK_LEN], @@ -3394,6 +3397,7 @@ impl HandshakeState { self.decaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(sk, pk, ct) } + /// Calls [`Self::decaps_and_mix`] with the generic parameters that match [`EphemeralKem`]. pub fn decaps_and_mix_ephemeral( &mut self, sk: &[u8; EphemeralKem::SK_LEN], @@ -3448,7 +3452,7 @@ impl HandshakeState { let k = bk.get(srv).value.secret(); let pt = biscuit.as_bytes(); - XAead.encrypt(biscuit_ct, k, &*n, &ad, pt)?; + XAead.encrypt_with_nonce_in_ctxt(biscuit_ct, k, &*n, &ad, pt)?; self.mix(biscuit_ct) } From 075d9ffff30719be8568315f0022a58f14a5516e Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 27 Feb 2025 16:56:05 +0100 Subject: [PATCH 053/174] update libcrux chachapoly to use libcrux-chacha20poly1305 --- Cargo.lock | 42 +++ Cargo.toml | 1 + ciphers/Cargo.toml | 8 +- ciphers/src/lib.rs | 2 +- .../subtle/libcrux/chacha20poly1305_ietf.rs | 354 +++++++++++++----- 5 files changed, 303 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb7fc0da..2fdedeb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1187,6 +1187,17 @@ dependencies = [ "rand", ] +[[package]] +name = "libcrux-chacha20poly1305" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04b666b490e714ff52d157da772a9a7faa3f394d619e79e64cdf1425599279ee" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-poly1305", +] + [[package]] name = "libcrux-hacl" version = "0.0.2-pre.2" @@ -1197,6 +1208,25 @@ dependencies = [ "libcrux-platform", ] +[[package]] +name = "libcrux-hacl-rs" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb04660b91e0be4185c3a9dd8e2c317c478cc0616f9cba2a4d523e033f852fa1" +dependencies = [ + "libcrux-macros", +] + +[[package]] +name = "libcrux-macros" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5848af0bd1aca5be28708945867adb2b841824a0a6f48779407a41287d5ca231" +dependencies = [ + "quote", + "syn 2.0.98", +] + [[package]] name = "libcrux-platform" version = "0.0.2-pre.2" @@ -1206,6 +1236,16 @@ dependencies = [ "libc", ] +[[package]] +name = "libcrux-poly1305" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82169ddf387e1912ebcde17979607e6a380dc758631757e7969652debe4dde9b" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", +] + [[package]] name = "libfuzzer-sys" version = "0.4.9" @@ -1840,6 +1880,8 @@ dependencies = [ "blake2", "chacha20poly1305", "libcrux", + "libcrux-chacha20poly1305", + "rand", "rosenpass-cipher-traits", "rosenpass-constant-time", "rosenpass-oqs", diff --git a/Cargo.toml b/Cargo.toml index e14a00fa..cc07b5b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ 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" } hex-literal = { version = "0.4.1" } hex = { version = "0.4.3" } heck = { version = "0.5.0" } diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index d794f373..b259d106 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -10,7 +10,10 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [features] -experiment_libcrux = ["dep:libcrux"] +experiment_libcrux = [ + "dep:libcrux", + "dep:libcrux-chacha20poly1305", +] [dependencies] anyhow = { workspace = true } @@ -26,3 +29,6 @@ chacha20poly1305 = { workspace = true } blake2 = { workspace = true } libcrux = { workspace = true, optional = true } sha3 = {workspace = true} +libcrux-chacha20poly1305 = { workspace = true, optional = true } +[dev-dependencies] +rand = { workspace = true } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index a6a63c8c..434c7d68 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -19,7 +19,7 @@ pub use crate::subtle::keyed_hash::KeyedHash; /// Authenticated encryption with associated data (AEAD) /// Chacha20poly1305 is used. #[cfg(feature = "experiment_libcrux")] -pub use subtle::libcrux::chacha20poly1305_ietf::Chacha20poly1305 as Aead; +pub use subtle::libcrux::chacha20poly1305_ietf::ChaCha20Poly1305 as Aead; /// Authenticated encryption with associated data (AEAD) /// Chacha20poly1305 is used. diff --git a/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs b/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs index 08082dd5..b266a55a 100644 --- a/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs @@ -1,8 +1,5 @@ -use std::fmt::format; -use rosenpass_to::ops::copy_slice; -use rosenpass_to::To; - -use zeroize::Zeroize; +use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305; +use rosenpass_cipher_traits::primitives::{Aead, AeadError}; /// The key length is 32 bytes or 256 bits. pub const KEY_LEN: usize = 32; // Grrrr! Libcrux, please provide me these constants. @@ -11,114 +8,267 @@ pub const TAG_LEN: usize = 16; /// The nonce length is 12 bytes or 96 bits. pub const NONCE_LEN: usize = 12; -/// 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); +/// An implementation of the ChaCha20Poly1305 AEAD from libcrux +pub struct ChaCha20Poly1305; - 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()); +impl Aead 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)?; - 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); + // 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); + } - match crux_key { - C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(), - _ => panic!(), + Ok(()) } - 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(()) + } } -/// 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); +impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {} - use libcrux::aead as C; - let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into()?)); - let crux_iv = C::Iv(nonce.try_into()?); - let crux_tag = match C::Tag::from_slice(mac) { - Ok(tag) => tag, - Err(err) => return Err(anyhow::anyhow!(format!("{:?}", err))), - }; +/// 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; - copy_slice(ciphertext).to(plaintext); - let dec_res = libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag); - if dec_res.is_err() { - return Err(anyhow::anyhow!("Decryption failed {:?}", dec_res.err())); + #[test] + fn fuzz_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); + } + } } - match crux_key { - C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(), - _ => panic!(), + #[test] + fn fuzz_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); + } + } } - Ok(()) + // 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(()) + } } From 253243a8c886e5ec284ddf67153e39ab80df6201 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 27 Feb 2025 16:58:31 +0100 Subject: [PATCH 054/174] add kyber512 from libcrux --- Cargo.lock | 198 +++++++++++++++++++++++-- Cargo.toml | 1 + ciphers/Cargo.toml | 6 +- ciphers/src/subtle/libcrux/kyber512.rs | 62 ++++++++ ciphers/src/subtle/libcrux/mod.rs | 1 + 5 files changed, 251 insertions(+), 17 deletions(-) create mode 100644 ciphers/src/subtle/libcrux/kyber512.rs diff --git a/Cargo.lock b/Cargo.lock index 2fdedeb4..8a113cdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,6 +999,44 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +[[package]] +name = "hax-lib" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd2dddf00d9120e8ff07ec0411cd48f6f419782b53c109d3984b6bf94345c822" +dependencies = [ + "hax-lib-macros", + "num-bigint", + "num-traits", +] + +[[package]] +name = "hax-lib-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "013ec0c6e58481b11658007e794ee09be35b97ef02c92102b9a5c01afd43a82f" +dependencies = [ + "hax-lib-macros-types", + "paste", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "hax-lib-macros-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e897f0a73b06263b106327db34e77b8df37a9a94a3fba759ee7c9b69493396" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "heapless" version = "0.7.17" @@ -1089,7 +1127,7 @@ dependencies = [ "lazy_static", "libc", "mio", - "rand", + "rand 0.8.5", "serde", "tempfile", "uuid", @@ -1184,7 +1222,7 @@ dependencies = [ "libcrux-hacl", "libcrux-platform", "libjade-sys", - "rand", + "rand 0.8.5", ] [[package]] @@ -1217,6 +1255,15 @@ dependencies = [ "libcrux-macros", ] +[[package]] +name = "libcrux-intrinsics" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256e25c0b16c98b715f7cc6b3ed268723a1158f78a236b1625ffe4a941cab41" +dependencies = [ + "hax-lib", +] + [[package]] name = "libcrux-macros" version = "0.0.2-beta.3" @@ -1227,6 +1274,19 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "libcrux-ml-kem" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cbf9aad4ad38d53cfdd7ffe9041cc4cf516c8c5a6f9c1a7bb8136a82b7b6d6" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-sha3", + "rand 0.9.0", +] + [[package]] name = "libcrux-platform" version = "0.0.2-pre.2" @@ -1246,6 +1306,17 @@ dependencies = [ "libcrux-macros", ] +[[package]] +name = "libcrux-sha3" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afd802f5c7862be77f1f320df6c0fea0f09a78ca94e79df26625c60d2d96de7" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", +] + [[package]] name = "libfuzzer-sys" version = "0.4.9" @@ -1508,6 +1579,25 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1668,7 +1758,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -1681,6 +1771,30 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -1730,8 +1844,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.2", + "zerocopy 0.8.20", ] [[package]] @@ -1741,7 +1866,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.2", ] [[package]] @@ -1753,6 +1888,16 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.20", +] + [[package]] name = "rayon" version = "1.10.0" @@ -1838,7 +1983,7 @@ dependencies = [ "mio", "paste", "procspawn", - "rand", + "rand 0.8.5", "rosenpass-cipher-traits", "rosenpass-ciphers", "rosenpass-constant-time", @@ -1857,7 +2002,7 @@ dependencies = [ "thiserror 1.0.69", "toml", "uds", - "zerocopy", + "zerocopy 0.7.35", "zeroize", ] @@ -1881,7 +2026,8 @@ dependencies = [ "chacha20poly1305", "libcrux", "libcrux-chacha20poly1305", - "rand", + "libcrux-ml-kem", + "rand 0.8.5", "rosenpass-cipher-traits", "rosenpass-constant-time", "rosenpass-oqs", @@ -1898,7 +2044,7 @@ name = "rosenpass-constant-time" version = "0.1.0" dependencies = [ "memsec", - "rand", + "rand 0.8.5", "rosenpass-to", ] @@ -1939,7 +2085,7 @@ dependencies = [ "log", "memsec", "procspawn", - "rand", + "rand 0.8.5", "rosenpass-to", "rosenpass-util", "tempfile", @@ -1966,7 +2112,7 @@ dependencies = [ "thiserror 1.0.69", "typenum", "uds", - "zerocopy", + "zerocopy 0.7.35", "zeroize", ] @@ -1983,7 +2129,7 @@ dependencies = [ "mio", "postcard", "procspawn", - "rand", + "rand 0.8.5", "rosenpass-secret-memory", "rosenpass-to", "rosenpass-util", @@ -1991,7 +2137,7 @@ dependencies = [ "thiserror 1.0.69", "tokio", "wireguard-uapi", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -2982,7 +3128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "serde", "zeroize", ] @@ -2994,7 +3140,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +dependencies = [ + "zerocopy-derive 0.8.20", ] [[package]] @@ -3008,6 +3163,17 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index cc07b5b6..d9809e41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,7 @@ 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" } hex-literal = { version = "0.4.1" } hex = { version = "0.4.3" } heck = { version = "0.5.0" } diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index b259d106..1ca707f6 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -13,6 +13,7 @@ readme = "readme.md" experiment_libcrux = [ "dep:libcrux", "dep:libcrux-chacha20poly1305", + "dep:libcrux-ml-kem", ] [dependencies] @@ -28,7 +29,10 @@ zeroize = { workspace = true } chacha20poly1305 = { workspace = true } blake2 = { workspace = true } libcrux = { workspace = true, optional = true } -sha3 = {workspace = true} libcrux-chacha20poly1305 = { workspace = true, optional = true } +libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] } +sha3 = { workspace = true } +rand = { workspace = true } + [dev-dependencies] rand = { workspace = true } diff --git a/ciphers/src/subtle/libcrux/kyber512.rs b/ciphers/src/subtle/libcrux/kyber512.rs new file mode 100644 index 00000000..a247bdcd --- /dev/null +++ b/ciphers/src/subtle/libcrux/kyber512.rs @@ -0,0 +1,62 @@ +use libcrux_ml_kem::kyber512; + +use rand::RngCore; + +use rosenpass_cipher_traits::algorithms::kem_kyber512::*; +use rosenpass_cipher_traits::primitives::{Kem, KemError}; + +pub struct Kyber512; + +impl Kem 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 {} diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs index b1c89034..623c28f6 100644 --- a/ciphers/src/subtle/libcrux/mod.rs +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -1,3 +1,4 @@ //! Implementations backed by libcrux, a verified crypto library pub mod chacha20poly1305_ietf; +pub mod kyber512; From 185e92108e34ff79f2052bee6d9ef098556316f0 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 27 Feb 2025 17:01:30 +0100 Subject: [PATCH 055/174] add blake2 from libcrux --- Cargo.lock | 13 ++++++++- Cargo.toml | 1 + ciphers/Cargo.toml | 4 ++- ciphers/src/subtle/libcrux/blake2b.rs | 39 +++++++++++++++++++++++++++ ciphers/src/subtle/libcrux/mod.rs | 1 + 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 ciphers/src/subtle/libcrux/blake2b.rs diff --git a/Cargo.lock b/Cargo.lock index 8a113cdf..706eaf0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -1225,6 +1225,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "libcrux-blake2" +version = "0.0.2-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce649c4eb25a6cae0f59d758052ffe9285e4eb5f3de36e67a584c04845fa92a" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", +] + [[package]] name = "libcrux-chacha20poly1305" version = "0.0.2-beta.3" @@ -2025,6 +2035,7 @@ dependencies = [ "blake2", "chacha20poly1305", "libcrux", + "libcrux-blake2", "libcrux-chacha20poly1305", "libcrux-ml-kem", "rand 0.8.5", diff --git a/Cargo.toml b/Cargo.toml index d9809e41..2ad21d90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ 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 = { version = "0.0.2-beta.3" } hex-literal = { version = "0.4.1" } hex = { version = "0.4.3" } heck = { version = "0.5.0" } diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 1ca707f6..8138f13f 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -12,6 +12,7 @@ readme = "readme.md" [features] experiment_libcrux = [ "dep:libcrux", + "dep:libcrux-blake2", "dep:libcrux-chacha20poly1305", "dep:libcrux-ml-kem", ] @@ -30,9 +31,10 @@ chacha20poly1305 = { workspace = true } blake2 = { workspace = true } libcrux = { workspace = true, optional = true } libcrux-chacha20poly1305 = { workspace = true, optional = true } +rand = { workspace = true } +libcrux-blake2 = { workspace = true, optional = true } libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] } sha3 = { workspace = true } -rand = { workspace = true } [dev-dependencies] rand = { workspace = true } diff --git a/ciphers/src/subtle/libcrux/blake2b.rs b/ciphers/src/subtle/libcrux/blake2b.rs new file mode 100644 index 00000000..04482c28 --- /dev/null +++ b/ciphers/src/subtle/libcrux/blake2b.rs @@ -0,0 +1,39 @@ +use rosenpass_cipher_traits::algorithms::KeyedHashBlake2b; +use rosenpass_cipher_traits::primitives::KeyedHash; + +use libcrux_blake2::Blake2bBuilder; + +pub enum Error { + InternalError, + DataTooLong, +} + +pub struct Blake2b; + +pub const KEY_LEN: usize = 32; +pub const HASH_LEN: usize = 32; + +impl KeyedHash 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 {} diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs index 623c28f6..7e39c429 100644 --- a/ciphers/src/subtle/libcrux/mod.rs +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -1,4 +1,5 @@ //! Implementations backed by libcrux, a verified crypto library +pub mod blake2b; pub mod chacha20poly1305_ietf; pub mod kyber512; From 64945184604bc166d801b45c60c37099b9758017 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 27 Feb 2025 17:58:49 +0100 Subject: [PATCH 056/174] add fine-grained features --- Cargo.lock | 28 ++++++++++++++-------------- ciphers/Cargo.toml | 16 ++++++++++------ ciphers/src/lib.rs | 7 +++++-- ciphers/src/subtle/libcrux/mod.rs | 5 +++++ ciphers/src/subtle/mod.rs | 6 +++++- fuzz/Cargo.toml | 2 +- rosenpass/Cargo.toml | 15 ++++++++++++--- rp/Cargo.toml | 2 +- 8 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 706eaf0a..9ff36e84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -369,9 +369,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -381,9 +381,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.45" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3040c8291884ddf39445dc033c70abc2bc44a42f0a3a00571a0f483a83f0cd" +checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98" dependencies = [ "clap", ] @@ -408,9 +408,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clap_mangen" -version = "0.2.24" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbae9cbfdc5d4fa8711c09bd7b83f644cb48281ac35bf97af3e47b0675864bdf" +checksum = "724842fa9b144f9b89b3f3d371a89f3455eea660361d13a554f68f8ae5d6c13a" dependencies = [ "clap", "roff", @@ -747,9 +747,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "embedded-io" @@ -1208,9 +1208,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libcrux" @@ -2673,9 +2673,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.14.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ "getrandom 0.3.1", ] diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 8138f13f..f3120c90 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -10,12 +10,16 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [features] -experiment_libcrux = [ - "dep:libcrux", - "dep:libcrux-blake2", - "dep:libcrux-chacha20poly1305", - "dep:libcrux-ml-kem", +#default = ["experiment_libcrux_all"] + +experiment_libcrux_all = [ + "experiment_libcrux_blake2", + "experiment_libcrux_chachapoly", + "experiment_libcrux_kyber", ] +experiment_libcrux_blake2 = ["dep:libcrux-blake2"] +experiment_libcrux_chachapoly = ["dep:libcrux-chacha20poly1305", "dep:libcrux"] +experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"] [dependencies] anyhow = { workspace = true } @@ -31,10 +35,10 @@ chacha20poly1305 = { workspace = true } blake2 = { workspace = true } libcrux = { workspace = true, optional = true } libcrux-chacha20poly1305 = { workspace = true, optional = true } -rand = { workspace = true } libcrux-blake2 = { workspace = true, optional = true } libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] } sha3 = { workspace = true } +rand = { workspace = true, optional = true } [dev-dependencies] rand = { workspace = true } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index 434c7d68..d8be6f5f 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -18,12 +18,12 @@ pub use crate::subtle::keyed_hash::KeyedHash; /// Authenticated encryption with associated data (AEAD) /// Chacha20poly1305 is used. -#[cfg(feature = "experiment_libcrux")] +#[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"))] +#[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) @@ -38,6 +38,9 @@ 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; diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs index 7e39c429..a2bad92b 100644 --- a/ciphers/src/subtle/libcrux/mod.rs +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -1,5 +1,10 @@ //! Implementations backed by libcrux, a verified crypto library +#[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; diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index 18a6b54c..1787af9f 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -6,5 +6,9 @@ pub use rust_crypto::{blake2b, keyed_shake256}; pub mod custom; pub mod rust_crypto; -#[cfg(feature = "experiment_libcrux")] +#[cfg(any( + feature = "experiment_libcrux_blake2", + feature = "experiment_libcrux_chachapoly", + feature = "experiment_libcrux_kyber" +))] pub mod libcrux; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f8ac42fb..073e91f2 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -5,7 +5,7 @@ publish = false edition = "2021" [features] -experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"] +experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux_all"] [package.metadata] cargo-fuzz = true diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index be0bf77f..e7a7fa45 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -28,7 +28,11 @@ required-features = ["experiment_api", "internal_testing"] [[test]] name = "gen-ipc-msg-types" -required-features = ["experiment_api", "internal_testing", "internal_bin_gen_ipc_msg_types"] +required-features = [ + "experiment_api", + "internal_testing", + "internal_bin_gen_ipc_msg_types", +] [[bench]] name = "handshake" @@ -81,9 +85,14 @@ tempfile = { workspace = true } rustix = { workspace = true } [features] -default = [] +#default = ["experiment_libcrux_all"] 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", diff --git a/rp/Cargo.toml b/rp/Cargo.toml index f31c34b3..82121dbc 100644 --- a/rp/Cargo.toml +++ b/rp/Cargo.toml @@ -43,4 +43,4 @@ stacker = { workspace = true } [features] experiment_memfd_secret = [] -experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"] +experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux_all"] From 576ad5f6d073eefe61734da918722df606c093ce Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Fri, 28 Feb 2025 08:49:34 +0100 Subject: [PATCH 057/174] respect experiment_libcrux_blake2 feature flag --- Cargo.lock | 1 + ciphers/Cargo.toml | 5 ++--- .../subtle/custom/incorrect_hmac_blake2b.rs | 19 ++++++++++--------- ciphers/src/subtle/keyed_hash.rs | 8 +++++--- ciphers/src/subtle/libcrux/blake2b.rs | 3 +++ 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ff36e84..aa5b5ebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2047,6 +2047,7 @@ dependencies = [ "rosenpass-util", "sha3", "static_assertions", + "thiserror 1.0.69", "zeroize", ] diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index f3120c90..891d3371 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -10,14 +10,12 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [features] -#default = ["experiment_libcrux_all"] - experiment_libcrux_all = [ "experiment_libcrux_blake2", "experiment_libcrux_chachapoly", "experiment_libcrux_kyber", ] -experiment_libcrux_blake2 = ["dep:libcrux-blake2"] +experiment_libcrux_blake2 = ["dep:libcrux-blake2", "dep:thiserror"] experiment_libcrux_chachapoly = ["dep:libcrux-chacha20poly1305", "dep:libcrux"] experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"] @@ -39,6 +37,7 @@ libcrux-blake2 = { workspace = true, optional = true } libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] } sha3 = { workspace = true } rand = { workspace = true, optional = true } +thiserror = { workspace = true, optional = true } [dev-dependencies] rand = { workspace = true } diff --git a/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs index d6aa3af1..cc7aeb13 100644 --- a/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs @@ -1,4 +1,3 @@ -use anyhow::ensure; use rosenpass_cipher_traits::{ algorithms::KeyedHashIncorrectHmacBlake2b, primitives::{InferKeyedHash, KeyedHash, KeyedHashTo}, @@ -7,7 +6,13 @@ use rosenpass_constant_time::xor; use rosenpass_to::{ops::copy_slice, To}; use zeroize::Zeroizing; -use crate::subtle::rust_crypto::blake2b; +#[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; @@ -42,7 +47,7 @@ pub const HASH_LEN: usize = 32; pub struct IncorrectHmacBlake2bCore; impl KeyedHash for IncorrectHmacBlake2bCore { - type Error = anyhow::Error; + type Error = Error; fn keyed_hash( key: &[u8; KEY_LEN], @@ -52,21 +57,17 @@ impl KeyedHash for IncorrectHmacBlake2bCore { const IPAD: [u8; KEY_LEN] = [0x36u8; KEY_LEN]; const OPAD: [u8; KEY_LEN] = [0x5Cu8; KEY_LEN]; - // 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::Blake2b::keyed_hash_to(&tmp_key, data).to(&mut outer_data)?; + 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::Blake2b::keyed_hash_to(&tmp_key, outer_data.as_ref()).to(out)?; + Blake2b::keyed_hash_to(&tmp_key, outer_data.as_ref()).to(out)?; Ok(()) } diff --git a/ciphers/src/subtle/keyed_hash.rs b/ciphers/src/subtle/keyed_hash.rs index 410f0e06..0ae02038 100644 --- a/ciphers/src/subtle/keyed_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -30,8 +30,10 @@ impl KeyedHashInstance for KeyedHash { 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), - } + Self::KeyedShake256(h) => h.keyed_hash(key, data, out)?, + Self::IncorrectHmacBlake2b(h) => h.keyed_hash(key, data, out)?, + }; + + Ok(()) } } diff --git a/ciphers/src/subtle/libcrux/blake2b.rs b/ciphers/src/subtle/libcrux/blake2b.rs index 04482c28..aa44de17 100644 --- a/ciphers/src/subtle/libcrux/blake2b.rs +++ b/ciphers/src/subtle/libcrux/blake2b.rs @@ -3,8 +3,11 @@ use rosenpass_cipher_traits::primitives::KeyedHash; use libcrux_blake2::Blake2bBuilder; +#[derive(Debug, thiserror::Error)] pub enum Error { + #[error("internal error")] InternalError, + #[error("data is too long")] DataTooLong, } From b16619b1d3cdce9aefe01c7b29c3336282d232d3 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Fri, 28 Feb 2025 09:27:03 +0100 Subject: [PATCH 058/174] fix doc example tests --- ciphers/src/hash_domain.rs | 2 +- .../subtle/custom/incorrect_hmac_blake2b.rs | 5 ++- ciphers/src/subtle/libcrux/blake2b.rs | 38 +++++++++++++++++++ .../rust_crypto/chacha20poly1305_ietf.rs | 12 +++--- .../src/subtle/rust_crypto/keyed_shake256.rs | 12 +++--- .../rust_crypto/xchacha20poly1305_ietf.rs | 14 +++---- 6 files changed, 61 insertions(+), 22 deletions(-) diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 49c51ffc..58a8f467 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -1,7 +1,7 @@ //! //!```rust //! # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace}; -//! use rosenpass_ciphers::keyed_hash::KeyedHash; +//! use rosenpass_ciphers::KeyedHash; //! use rosenpass_secret_memory::Secret; //! # rosenpass_secret_memory::secret_policy_use_only_malloc_secrets(); //! diff --git a/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs index cc7aeb13..93101d53 100644 --- a/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs +++ b/ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs @@ -30,14 +30,15 @@ pub const HASH_LEN: usize = 32; /// /// # Examples ///```rust -/// # use rosenpass_ciphers::subtle::incorrect_hmac_blake2b::hash; +/// # 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!(hash(&key, &data).to(&mut hash_data).is_ok(), "Hashing has to return OK result"); +/// 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); diff --git a/ciphers/src/subtle/libcrux/blake2b.rs b/ciphers/src/subtle/libcrux/blake2b.rs index aa44de17..76cdd375 100644 --- a/ciphers/src/subtle/libcrux/blake2b.rs +++ b/ciphers/src/subtle/libcrux/blake2b.rs @@ -40,3 +40,41 @@ impl KeyedHash for Blake2b { } 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); + } + } + } +} diff --git a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs index c085d03a..69db4de3 100644 --- a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs @@ -86,13 +86,13 @@ impl Aead for ChaCha20Poly1305 { /// /// # Examples ///```rust -/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN}; +/// # use rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_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] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY -/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE +/// 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;PLAINTEXT_LEN + TAG_LEN]; /// @@ -125,7 +125,7 @@ pub fn encrypt( /// /// # Examples ///```rust -/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN}; +/// # use rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::{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, @@ -133,8 +133,8 @@ pub fn encrypt( /// 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 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]; /// diff --git a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs index 9df9041d..91f4c6d6 100644 --- a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -23,8 +23,8 @@ impl KeyedHash /// TODO: Example/Test /// #Examples /// ```rust - /// # use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; - /// use rosenpass_cipher_traits::KeyedHash; + /// # 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]; @@ -78,11 +78,11 @@ impl Default for SHAKE256Core = /// TODO: Documentation and more interesting test /// ```rust /// # use rosenpass_ciphers::subtle::keyed_shake256::{SHAKE256_32}; -/// use rosenpass_cipher_traits::KeyedHashInstance; +/// use rosenpass_cipher_traits::primitives::KeyedHashInstance; /// const KEY_LEN: usize = 32; /// const HASH_LEN: usize = 32; /// let key: [u8; 32] = [0; KEY_LEN]; diff --git a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs index 2ba93fee..39a1573c 100644 --- a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs @@ -87,12 +87,12 @@ impl Aead for XChaCha20Poly1305 { /// /// # Examples ///```rust -/// # use rosenpass_ciphers::subtle::xchacha20poly1305_ietf::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN}; +/// # 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] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY -/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE +/// 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]; /// @@ -114,7 +114,7 @@ pub fn encrypt( plaintext: &[u8], ) -> anyhow::Result<()> { XChaCha20Poly1305 - .encrypt(ciphertext, key, nonce, ad, plaintext) + .encrypt_with_nonce_in_ctxt(ciphertext, key, nonce, ad, plaintext) .map_err(anyhow::Error::from) } @@ -130,7 +130,7 @@ pub fn encrypt( /// /// # Examples ///```rust -/// # use rosenpass_ciphers::subtle::xchacha20poly1305_ietf::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN}; +/// # 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, @@ -139,8 +139,8 @@ pub fn encrypt( /// const PLAINTEXT_LEN: usize = 43; /// assert_eq!(PLAINTEXT_LEN + TAG_LEN + NONCE_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 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]; /// From 30c3de3f87c72519cc9a0b65b014cc753cd1af6e Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Fri, 28 Feb 2025 14:44:56 +0100 Subject: [PATCH 059/174] undo add submodule --- initial | 1 - 1 file changed, 1 deletion(-) delete mode 160000 initial diff --git a/initial b/initial deleted file mode 160000 index f1122f1f..00000000 --- a/initial +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f1122f1f62fb210a02891ea2101e4f699adf772e From 2dba9205e7dc89ceb89e2737dc394bf793912529 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Fri, 28 Feb 2025 18:26:36 +0100 Subject: [PATCH 060/174] Address Feedback --- Cargo.lock | 2 +- cipher-traits/Cargo.toml | 2 +- cipher-traits/src/algorithms.rs | 63 +++- cipher-traits/src/lib.rs | 3 + cipher-traits/src/primitives.rs | 3 + cipher-traits/src/primitives/aead.rs | 81 ++++- cipher-traits/src/primitives/kem.rs | 48 +++ cipher-traits/src/primitives/keyed_hash.rs | 13 +- ciphers/Cargo.toml | 18 +- ciphers/src/subtle/keyed_hash.rs | 23 +- ciphers/src/subtle/libcrux/blake2b.rs | 18 +- .../subtle/libcrux/chacha20poly1305_ietf.rs | 304 +++++++++--------- ciphers/src/subtle/libcrux/kyber512.rs | 75 ++++- ciphers/src/subtle/libcrux/mod.rs | 6 +- ciphers/src/subtle/mod.rs | 2 + ciphers/src/subtle/rust_crypto/blake2b.rs | 14 +- .../rust_crypto/chacha20poly1305_ietf.rs | 95 +----- .../src/subtle/rust_crypto/keyed_shake256.rs | 1 + .../rust_crypto/xchacha20poly1305_ietf.rs | 20 +- rosenpass/src/protocol/protocol.rs | 65 +--- 20 files changed, 521 insertions(+), 335 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa5b5ebd..0870f965 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2024,7 +2024,7 @@ dependencies = [ "rosenpass-oqs", "rosenpass-secret-memory", "rosenpass-to", - "thiserror 2.0.11", + "thiserror 1.0.69", ] [[package]] diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml index 7ba40c44..15f24ff6 100644 --- a/cipher-traits/Cargo.toml +++ b/cipher-traits/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" [dependencies] -thiserror = "2" +thiserror = { workspace = true } rosenpass-to = { workspace = true } [dev-dependencies] diff --git a/cipher-traits/src/algorithms.rs b/cipher-traits/src/algorithms.rs index 47470937..03d763a2 100644 --- a/cipher-traits/src/algorithms.rs +++ b/cipher-traits/src/algorithms.rs @@ -1,77 +1,128 @@ +//! 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. - pub const KEY_LEN: usize = 32; - pub const OUT_LEN: usize = 32; - pub trait KeyedHashIncorrectHmacBlake2b: KeyedHash {} + /// 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 {} } +/// 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. - pub const KEY_LEN: usize = 32; - pub const OUT_LEN: usize = 32; - pub trait KeyedHashBlake2b: KeyedHash {} + /// 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 {} } +/// 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 OUT_LEN: usize = 32; + /// A [`KeyedHash`] that is SHAKE256. pub trait KeyedHashShake256: KeyedHash {} } +/// 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 {} } +/// 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 {} } +/// 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 {} } +/// 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 {} } diff --git a/cipher-traits/src/lib.rs b/cipher-traits/src/lib.rs index b61036e9..299e2a46 100644 --- a/cipher-traits/src/lib.rs +++ b/cipher-traits/src/lib.rs @@ -1,2 +1,5 @@ +//! 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; diff --git a/cipher-traits/src/primitives.rs b/cipher-traits/src/primitives.rs index 9a8116a7..2e279fe6 100644 --- a/cipher-traits/src/primitives.rs +++ b/cipher-traits/src/primitives.rs @@ -1,3 +1,6 @@ +//! 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; diff --git a/cipher-traits/src/primitives/aead.rs b/cipher-traits/src/primitives/aead.rs index 286bb48e..06deea75 100644 --- a/cipher-traits/src/primitives/aead.rs +++ b/cipher-traits/src/primitives/aead.rs @@ -1,11 +1,64 @@ 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( +/// aead: &AeadImpl, +/// msg: &str, +/// nonce: &[u8; NONCE_LEN], +/// encrypted: &mut [u8] +/// ) where AeadImpl: Aead { +/// 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( +/// msg: &str, +/// nonce: &[u8; NONCE_LEN], +/// encrypted: &mut [u8] +/// ) where AeadImpl: Default + Aead { +/// 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 = 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], @@ -15,6 +68,10 @@ pub trait Aead 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], @@ -25,15 +82,21 @@ pub trait Aead Result<(), Error>; } -/// The API of XChaCha was a bit weird and moved the nonce into the ciphertext. Instead of changing -/// the protocol code, we recreate that API on top of the more normal API, but move it into a -/// separate crate. +/// 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 { + /// 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], @@ -56,6 +119,10 @@ pub trait AeadWithNonceInCiphertext< 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], @@ -89,12 +156,20 @@ impl< { } +/// 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, } diff --git a/cipher-traits/src/primitives/kem.rs b/cipher-traits/src/primitives/kem.rs index 85b51b05..9c9b91f4 100644 --- a/cipher-traits/src/primitives/kem.rs +++ b/cipher-traits/src/primitives/kem.rs @@ -134,6 +134,54 @@ use thiserror::Error; /// /// 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( +/// kem: &KemImpl, +/// pk: &[u8l PK_LEN], +/// ct: &mut [u8; CT_LEN] +/// ) where KemImpl: Kem -> [u8; 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( +/// pk: &[u8l PK_LEN], +/// ct: &mut [u8; CT_LEN] +/// ) where KemImpl: Default + Kem -> [u8; SHK_LEN]{ +/// let mut shk = [0u8; SHK_LEN]; +/// KemImpl::default().encaps(&mut shk, ct, pk).unwrap(); +/// shk +/// } +/// ``` pub trait Kem { /// The length of the secret (decapsulation) key. const SK_LEN: usize = SK_LEN; diff --git a/cipher-traits/src/primitives/keyed_hash.rs b/cipher-traits/src/primitives/keyed_hash.rs index 1b06ce2b..d616cb20 100644 --- a/cipher-traits/src/primitives/keyed_hash.rs +++ b/cipher-traits/src/primitives/keyed_hash.rs @@ -1,6 +1,11 @@ +use std::marker::PhantomData; + +/// Models a keyed hash function using an associated function (i.e. without `&self` receiver). pub trait KeyedHash { + /// 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], @@ -8,9 +13,15 @@ pub trait KeyedHash { ) -> Result<(), Self::Error>; } +/// Models a keyed hash function using 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 { + /// 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], @@ -19,8 +30,6 @@ pub trait KeyedHashInstance { ) -> Result<(), Self::Error>; } -use std::marker::PhantomData; - /// This is a helper to allow for type parameter inference when calling functions /// that need a [KeyedHash]. /// diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 891d3371..c5fd3666 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -13,10 +13,15 @@ readme = "readme.md" 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", "dep:libcrux"] +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] @@ -31,13 +36,16 @@ static_assertions = { workspace = true } zeroize = { workspace = true } chacha20poly1305 = { workspace = true } blake2 = { workspace = true } -libcrux = { 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"] } 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 } diff --git a/ciphers/src/subtle/keyed_hash.rs b/ciphers/src/subtle/keyed_hash.rs index 0ae02038..b7e0f06b 100644 --- a/ciphers/src/subtle/keyed_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -1,16 +1,31 @@ +//! 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; -pub const KEY_LEN: usize = 32; -pub const HASH_LEN: usize = 32; +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 { - KeyedShake256(super::rust_crypto::keyed_shake256::SHAKE256), - IncorrectHmacBlake2b(super::custom::incorrect_hmac_blake2b::IncorrectHmacBlake2b), + /// 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()) } diff --git a/ciphers/src/subtle/libcrux/blake2b.rs b/ciphers/src/subtle/libcrux/blake2b.rs index 76cdd375..9bb4f1de 100644 --- a/ciphers/src/subtle/libcrux/blake2b.rs +++ b/ciphers/src/subtle/libcrux/blake2b.rs @@ -1,21 +1,29 @@ -use rosenpass_cipher_traits::algorithms::KeyedHashBlake2b; -use rosenpass_cipher_traits::primitives::KeyedHash; +//! 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; -pub const KEY_LEN: usize = 32; -pub const HASH_LEN: usize = 32; - impl KeyedHash for Blake2b { type Error = Error; diff --git a/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs b/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs index b266a55a..1a402be3 100644 --- a/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs @@ -1,14 +1,11 @@ +//! Implementation of the [`AeadChaCha20Poly1305`] trait based on the [`libcrux_chacha20poly1305`] crate. + use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305; use rosenpass_cipher_traits::primitives::{Aead, AeadError}; -/// The key length is 32 bytes or 256 bits. -pub const KEY_LEN: usize = 32; // Grrrr! Libcrux, please provide me these constants. -/// The MAC tag length is 16 bytes or 128 bits. -pub const TAG_LEN: usize = 16; -/// The nonce length is 12 bytes or 96 bits. -pub const NONCE_LEN: usize = 12; +pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN}; -/// An implementation of the ChaCha20Poly1305 AEAD from libcrux +/// An implementation of the ChaCha20Poly1305 AEAD based on libcrux pub struct ChaCha20Poly1305; impl Aead for ChaCha20Poly1305 { @@ -64,53 +61,7 @@ mod equivalence_tests { use rand::RngCore; #[test] - fn fuzz_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); - } - } - } - - #[test] - fn fuzz_equivalence_libcrux_rustcrypto() { + fn proptest_equivalence_libcrux_rustcrypto() { use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as RustCryptoChaCha20Poly1305; let ptxts: [&[u8]; 3] = [ b"".as_slice(), @@ -160,115 +111,164 @@ mod equivalence_tests { } } - // 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; + #[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(); - /// 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); + let mut ctxt_left = [0; 64 + TAG_LEN]; + let mut ctxt_right = [0; 64 + 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 mut ptxt_left = [0; 64]; + let mut ptxt_right = [0; 64]; - 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); + let nonce = [0; NONCE_LEN]; + let ad = b""; - match crux_key { - C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(), - _ => panic!(), + 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); + } } - Ok(()) - } + // The old libcrux functions: - /// 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); + // 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; - 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(); + /// 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); - copy_slice(ciphertext).to(plaintext); - libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap(); + 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()); - match crux_key { - C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(), - _ => panic!(), + 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(()) } - 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(()) + } } } diff --git a/ciphers/src/subtle/libcrux/kyber512.rs b/ciphers/src/subtle/libcrux/kyber512.rs index a247bdcd..34f7a80a 100644 --- a/ciphers/src/subtle/libcrux/kyber512.rs +++ b/ciphers/src/subtle/libcrux/kyber512.rs @@ -1,10 +1,14 @@ -use libcrux_ml_kem::kyber512; +//! 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::kem_kyber512::*; +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 for Kyber512 { @@ -60,3 +64,70 @@ impl Default for Kyber512 { } 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); + } + } +} diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs index a2bad92b..432bd5f3 100644 --- a/ciphers/src/subtle/libcrux/mod.rs +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -1,4 +1,8 @@ -//! Implementations backed by libcrux, a verified crypto library +//! 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; diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index 1787af9f..b3c3aa8a 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -1,3 +1,5 @@ +//! Contains the implementations of the crypto algorithms used throughout Rosenpass. + pub mod keyed_hash; pub use custom::incorrect_hmac_blake2b; diff --git a/ciphers/src/subtle/rust_crypto/blake2b.rs b/ciphers/src/subtle/rust_crypto/blake2b.rs index 7651688e..6f992101 100644 --- a/ciphers/src/subtle/rust_crypto/blake2b.rs +++ b/ciphers/src/subtle/rust_crypto/blake2b.rs @@ -8,26 +8,24 @@ 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; +pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::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; -/// The key length for BLAKE2b supported by this API. Currently 32 Bytes. -const KEY_LEN: usize = 32; -/// The output length for BLAKE2b supported by this API. Currently 32 Bytes. -const OUT_LEN: usize = 32; - /// 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 for Blake2b { +impl KeyedHash for Blake2b { type Error = anyhow::Error; fn keyed_hash( key: &[u8; KEY_LEN], data: &[u8], - out: &mut [u8; OUT_LEN], + out: &mut [u8; HASH_LEN], ) -> Result<(), Self::Error> { let mut h = Impl::new_from_slice(key)?; h.update(data); @@ -36,7 +34,7 @@ impl KeyedHash for Blake2b { // 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 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); diff --git a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs index 69db4de3..7f0a114c 100644 --- a/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs @@ -1,19 +1,17 @@ -use rosenpass_cipher_traits::primitives::{Aead, AeadError}; use rosenpass_to::ops::copy_slice; use rosenpass_to::To; -use rosenpass_util::typenum2const; + +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::{AeadCore, AeadInPlace, KeyInit, KeySizeUser}; +use chacha20poly1305::{AeadInPlace, KeyInit}; -/// The key length is 32 bytes or 256 bits. -pub const KEY_LEN: usize = typenum2const! { ::KeySize }; -/// The MAC tag length is 16 bytes or 128 bits. -pub const TAG_LEN: usize = typenum2const! { ::TagSize }; -/// The nonce length is 12 bytes or 96 bits. -pub const NONCE_LEN: usize = typenum2const! { ::NonceSize }; +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 for ChaCha20Poly1305 { @@ -78,81 +76,4 @@ impl Aead for ChaCha20Poly1305 { } } -/// Encrypts using ChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305). -/// `key` MUST be chosen (pseudo-)randomly and `nonce` MOST NOT be reused. 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::rust_crypto::chacha20poly1305_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;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; KEY_LEN], - nonce: &[u8; NONCE_LEN], - ad: &[u8], - plaintext: &[u8], -) -> anyhow::Result<()> { - ChaCha20Poly1305 - .encrypt(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 ChaCha20Poly1305 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]. -/// -/// # Examples -///```rust -/// # use rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::{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; 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, 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; KEY_LEN], - nonce: &[u8; NONCE_LEN], - ad: &[u8], - ciphertext: &[u8], -) -> anyhow::Result<()> { - ChaCha20Poly1305 - .decrypt(plaintext, key, nonce, ad, ciphertext) - .map_err(anyhow::Error::from) -} +impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {} diff --git a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs index 91f4c6d6..b9c60b3f 100644 --- a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -3,6 +3,7 @@ use rosenpass_cipher_traits::primitives::{InferKeyedHash, KeyedHash}; use sha3::digest::{ExtendableOutput, Update, XofReader}; use sha3::Shake256; +/// An implementation of the [`KeyedHash`] trait backed by the RustCrypto implementation of SHAKE256. #[derive(Clone, Debug, PartialEq, Eq)] pub struct SHAKE256Core; diff --git a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs index 39a1573c..a0e47302 100644 --- a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs @@ -1,19 +1,17 @@ -use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext}; use rosenpass_to::ops::copy_slice; use rosenpass_to::To; -use rosenpass_util::typenum2const; + +use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::{ + AeadXChaCha20Poly1305, KEY_LEN, NONCE_LEN, TAG_LEN, +}; +use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext}; use chacha20poly1305::aead::generic_array::GenericArray; use chacha20poly1305::XChaCha20Poly1305 as AeadImpl; -use chacha20poly1305::{AeadCore, AeadInPlace, KeyInit, KeySizeUser}; - -/// The key length is 32 bytes or 256 bits. -pub const KEY_LEN: usize = typenum2const! { ::KeySize }; -/// The MAC tag length is 16 bytes or 128 bits. -pub const TAG_LEN: usize = typenum2const! { ::TagSize }; -/// The nonce length is 24 bytes or 192 bits. -pub const NONCE_LEN: usize = typenum2const! { ::NonceSize }; +use chacha20poly1305::{AeadInPlace, KeyInit}; +/// Implements the [`Aead`] and [`AeadXChaCha20Poly1305`] traits backed by the RustCrypto +/// implementation. pub struct XChaCha20Poly1305; impl Aead for XChaCha20Poly1305 { @@ -77,6 +75,8 @@ impl Aead for XChaCha20Poly1305 { } } +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]. diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 5ac8d687..0709e539 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3337,35 +3337,18 @@ impl HandshakeState { const KEM_PK_LEN: usize, const KEM_CT_LEN: usize, const KEM_SHK_LEN: usize, - T: Default + Kem, + KemImpl: Kem, >( &mut self, + kem: &KemImpl, ct: &mut [u8; KEM_CT_LEN], pk: &[u8; KEM_PK_LEN], ) -> Result<&mut Self> { let mut shk = Secret::::zero(); - T::default().encaps(shk.secret_mut(), ct, pk)?; + kem.encaps(shk.secret_mut(), ct, pk)?; self.mix(pk)?.mix(shk.secret())?.mix(ct) } - /// Calls [`Self::encaps_and_mix`] with the generic parameters that match [`StaticKem`]. - pub fn encaps_and_mix_static( - &mut self, - ct: &mut [u8; StaticKem::CT_LEN], - pk: &[u8; StaticKem::PK_LEN], - ) -> Result<&mut Self> { - self.encaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(ct, pk) - } - - /// Calls [`Self::encaps_and_mix`] with the generic parameters that match [`EphemeralKem`]. - pub fn encaps_and_mix_ephemeral( - &mut self, - ct: &mut [u8; EphemeralKem::CT_LEN], - pk: &[u8; EphemeralKem::PK_LEN], - ) -> Result<&mut Self> { - self.encaps_and_mix::<{EphemeralKem::SK_LEN},{ EphemeralKem::PK_LEN}, {EphemeralKem::CT_LEN}, {EphemeralKem::SHK_LEN}, EphemeralKem>(ct, pk) - } - /// Decapsulation (decryption) counterpart to [Self::encaps_and_mix]. /// /// Makes sure that the same values are mixed into the chaining that where mixed in on the @@ -3375,38 +3358,19 @@ impl HandshakeState { const KEM_PK_LEN: usize, const KEM_CT_LEN: usize, const KEM_SHK_LEN: usize, - T: Default + Kem, + KemImpl: Kem, >( &mut self, + kem: &KemImpl, sk: &[u8; KEM_SK_LEN], pk: &[u8; KEM_PK_LEN], ct: &[u8; KEM_CT_LEN], ) -> Result<&mut Self> { let mut shk = Secret::::zero(); - T::default().decaps(shk.secret_mut(), sk, ct)?; + kem.decaps(shk.secret_mut(), sk, ct)?; self.mix(pk)?.mix(shk.secret())?.mix(ct) } - /// Calls [`Self::decaps_and_mix`] with the generic parameters that match [`StaticKem`]. - pub fn decaps_and_mix_static( - &mut self, - sk: &[u8; StaticKem::SK_LEN], - pk: &[u8; StaticKem::PK_LEN], - ct: &[u8; StaticKem::CT_LEN], - ) -> Result<&mut Self> { - self.decaps_and_mix::<{StaticKem::SK_LEN},{ StaticKem::PK_LEN}, {StaticKem::CT_LEN}, {StaticKem::SHK_LEN}, StaticKem>(sk, pk, ct) - } - - /// Calls [`Self::decaps_and_mix`] with the generic parameters that match [`EphemeralKem`]. - pub fn decaps_and_mix_ephemeral( - &mut self, - sk: &[u8; EphemeralKem::SK_LEN], - pk: &[u8; EphemeralKem::PK_LEN], - ct: &[u8; EphemeralKem::CT_LEN], - ) -> Result<&mut Self> { - self.decaps_and_mix::<{EphemeralKem::SK_LEN},{ EphemeralKem::PK_LEN}, {EphemeralKem::CT_LEN}, {EphemeralKem::SHK_LEN}, EphemeralKem>(sk, pk, ct) - } - /// Store the chaining key inside a cookie value called a "biscuit". /// /// This biscuit can be transmitted to the other party and must be returned @@ -3595,7 +3559,7 @@ impl CryptoServer { // IHI5 hs.core - .encaps_and_mix_static(&mut ih.sctr, peer.get(self).spkt.deref())?; + .encaps_and_mix(&StaticKem, &mut ih.sctr, peer.get(self).spkt.deref())?; // IHI6 hs.core.encrypt_and_mix( @@ -3638,7 +3602,7 @@ impl CryptoServer { core.mix(&ih.sidi)?.mix(&ih.epki)?; // IHR5 - core.decaps_and_mix_static(self.sskm.secret(), self.spkm.deref(), &ih.sctr)?; + core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &ih.sctr)?; // IHR6 let peer = { @@ -3664,10 +3628,10 @@ impl CryptoServer { core.mix(&rh.sidr)?.mix(&rh.sidi)?; // RHR4 - core.encaps_and_mix_ephemeral(&mut rh.ecti, &ih.epki)?; + core.encaps_and_mix(&EphemeralKem, &mut rh.ecti, &ih.epki)?; // RHR5 - core.encaps_and_mix_static(&mut rh.scti, peer.get(self).spkt.deref())?; + core.encaps_and_mix(&StaticKem, &mut rh.scti, peer.get(self).spkt.deref())?; // RHR6 core.store_biscuit(self, peer, &mut rh.biscuit)?; @@ -3727,10 +3691,15 @@ impl CryptoServer { core.mix(&rh.sidr)?.mix(&rh.sidi)?; // RHI4 - core.decaps_and_mix_ephemeral(hs!().eski.secret(), hs!().epki.deref(), &rh.ecti)?; + core.decaps_and_mix( + &EphemeralKem, + hs!().eski.secret(), + hs!().epki.deref(), + &rh.ecti, + )?; // RHI5 - core.decaps_and_mix_static(self.sskm.secret(), self.spkm.deref(), &rh.scti)?; + core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &rh.scti)?; // RHI6 core.mix(&rh.biscuit)?; From 38f371e3d7cb954644af6f83fb6219a82eadffda Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 3 Mar 2025 11:31:34 +0100 Subject: [PATCH 061/174] Fix examples in Doc-Comments --- cipher-traits/src/algorithms.rs | 4 +- cipher-traits/src/primitives/kem.rs | 74 +++++++------------ ciphers/src/subtle/rust_crypto/blake2b.rs | 3 +- .../src/subtle/rust_crypto/keyed_shake256.rs | 2 + .../rust_crypto/xchacha20poly1305_ietf.rs | 7 +- rosenpass/src/protocol/mod.rs | 8 +- rosenpass/src/protocol/protocol.rs | 26 +++---- 7 files changed, 53 insertions(+), 71 deletions(-) diff --git a/cipher-traits/src/algorithms.rs b/cipher-traits/src/algorithms.rs index 03d763a2..4ba989e4 100644 --- a/cipher-traits/src/algorithms.rs +++ b/cipher-traits/src/algorithms.rs @@ -40,10 +40,10 @@ pub mod keyed_hash_shake256 { /// The key length used in [`KeyedHashShake256`]. pub const KEY_LEN: usize = 32; /// The hash length used in [`KeyedHashShake256`]. - pub const OUT_LEN: usize = 32; + pub const HASH_LEN: usize = 32; /// A [`KeyedHash`] that is SHAKE256. - pub trait KeyedHashShake256: KeyedHash {} + pub trait KeyedHashShake256: KeyedHash {} } /// Constants and trait for the ChaCha20Poly1305 AEAD diff --git a/cipher-traits/src/primitives/kem.rs b/cipher-traits/src/primitives/kem.rs index 9c9b91f4..4df959ec 100644 --- a/cipher-traits/src/primitives/kem.rs +++ b/cipher-traits/src/primitives/kem.rs @@ -17,7 +17,7 @@ //! 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_cipher_traits::primitives::Kem; //! use rosenpass_oqs::Kyber512; //! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret}; //! @@ -25,14 +25,14 @@ //! 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)?; +//! 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::encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?; +//! MyKem::default().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)?; +//! 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>(()) @@ -43,13 +43,10 @@ //! 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; +//!# use rosenpass_cipher_traits::primitives::{Kem, KemError as Error}; //! //! struct DummyKem {} -//! impl Kem for DummyKem { -//! -//! // For this DummyKem, using String for errors is sufficient. -//! type Error = String; +//! impl Kem<1,1,1,1> for DummyKem { //! //! // For this DummyKem, we will use a single `u8` for everything //! const SK_LEN: usize = 1; @@ -57,74 +54,56 @@ //! 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()); -//! } +//! fn keygen(&self, sk: &mut [u8;1], pk: &mut [u8;1]) -> Result<(), Error> { //! 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()); -//! } +//! fn encaps(&self, shk: &mut [u8;1], ct: &mut [u8;1], pk: &[u8;1]) -> Result<(), Error> { //! if pk[0] != 21 { -//! return Err("Invalid public key!".to_string()); +//! return Err(Error::InvalidArgument); //! } //! 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()); -//! } +//! fn decaps(&self, shk: &mut [u8;1 ], sk: &[u8;1], ct: &[u8;1]) -> Result<(), Error> { //! if sk[0] != 42 { -//! return Err("Invalid public key!".to_string()); +//! return Err(Error::InvalidArgument); //! } //! if ct[0] != 7 { -//! return Err("Invalid ciphertext!".to_string()); +//! 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::keygen(alice_sk.secret_mut(), &mut alice_pk)?; +//! # 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::encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?; +//! # MyKem::default().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)?; +//! # MyKem::default().decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?; //! # //! # assert_eq!(alice_shk.secret(), bob_shk.secret()); //! # -//! # Ok::<(), String>(()) +//! # Ok::<(), Error>(()) //!``` //! @@ -154,9 +133,9 @@ use thiserror::Error; /// /// fn encaps_given_a_kem( /// kem: &KemImpl, -/// pk: &[u8l PK_LEN], +/// pk: &[u8; PK_LEN], /// ct: &mut [u8; CT_LEN] -/// ) where KemImpl: Kem -> [u8; SHK_LEN]{ +/// ) -> [u8; SHK_LEN] where KemImpl: Kem{ /// let mut shk = [0u8; SHK_LEN]; /// kem.encaps(&mut shk, ct, pk).unwrap(); /// shk @@ -174,9 +153,10 @@ use thiserror::Error; /// const SHK_LEN: usize = 32; /// /// fn encaps_without_kem( -/// pk: &[u8l PK_LEN], +/// pk: &[u8; PK_LEN], /// ct: &mut [u8; CT_LEN] -/// ) where KemImpl: Default + Kem -> [u8; SHK_LEN]{ +/// ) -> [u8; SHK_LEN] +/// where KemImpl: Default + Kem { /// let mut shk = [0u8; SHK_LEN]; /// KemImpl::default().encaps(&mut shk, ct, pk).unwrap(); /// shk diff --git a/ciphers/src/subtle/rust_crypto/blake2b.rs b/ciphers/src/subtle/rust_crypto/blake2b.rs index 6f992101..c391297f 100644 --- a/ciphers/src/subtle/rust_crypto/blake2b.rs +++ b/ciphers/src/subtle/rust_crypto/blake2b.rs @@ -8,8 +8,7 @@ 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; -pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::KEY_LEN; +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]). diff --git a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs index b9c60b3f..14d32ab9 100644 --- a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -3,6 +3,8 @@ 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; diff --git a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs index a0e47302..ce02f359 100644 --- a/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs +++ b/ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs @@ -1,15 +1,16 @@ use rosenpass_to::ops::copy_slice; use rosenpass_to::To; -use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::{ - AeadXChaCha20Poly1305, KEY_LEN, NONCE_LEN, TAG_LEN, -}; +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; diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index 9d50108e..f9ea0f3d 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -25,8 +25,8 @@ //! ``` //! use std::ops::DerefMut; //! use rosenpass_secret_memory::policy::*; -//! use rosenpass_cipher_traits::Kem; -//! use rosenpass_ciphers::kem::StaticKem; +//! use rosenpass_cipher_traits::primitives::Kem; +//! use rosenpass_ciphers::StaticKem; //! use rosenpass::{ //! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey}, //! }; @@ -38,11 +38,11 @@ //! //! // initialize secret and public key for peer a ... //! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero()); -//! StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.deref_mut())?; +//! 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())?; +//! StaticKem.keygen(peer_b_sk.secret_mut(), peer_b_pk.deref_mut())?; //! //! // initialize server and a pre-shared key //! let psk = SymKey::random(); diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 0709e539..8ebb5f49 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -398,16 +398,16 @@ impl From for ProtocolVersion { /// ``` /// use std::ops::DerefMut; /// use rosenpass::protocol::{SSk, SPk, SymKey, Peer, ProtocolVersion}; -/// use rosenpass_ciphers::kem::StaticKem; -/// use rosenpass_cipher_traits::Kem; +/// use rosenpass_ciphers::StaticKem; +/// use rosenpass_cipher_traits::primitives::Kem; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// /// let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); -/// StaticKem::keygen(sskt.secret_mut(), spkt.deref_mut())?; +/// StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; /// /// let (mut sskt2, mut spkt2) = (SSk::zero(), SPk::zero()); -/// StaticKem::keygen(sskt2.secret_mut(), spkt2.deref_mut())?; +/// StaticKem.keygen(sskt2.secret_mut(), spkt2.deref_mut())?; /// /// let psk = SymKey::random(); /// @@ -834,7 +834,7 @@ pub trait Mortal { /// /// ``` /// use std::ops::DerefMut; -/// use rosenpass_ciphers::kem::StaticKem; +/// use rosenpass_ciphers::StaticKem; /// use rosenpass::protocol::{SSk, SPk, testutils::ServerForTesting, ProtocolVersion}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); @@ -1376,13 +1376,13 @@ impl CryptoServer { /// ``` /// use std::ops::DerefMut; /// use rosenpass::protocol::{SSk, SPk, CryptoServer, ProtocolVersion}; - /// use rosenpass_ciphers::kem::StaticKem; - /// use rosenpass_cipher_traits::Kem; + /// use rosenpass_ciphers::StaticKem; + /// use rosenpass_cipher_traits::primitives::Kem; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// /// let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); - /// StaticKem::keygen(sskm.secret_mut(), spkm.deref_mut())?; + /// StaticKem.keygen(sskm.secret_mut(), spkm.deref_mut())?; /// /// let srv = CryptoServer::new(sskm, spkm.clone()); /// assert_eq!(srv.spkm, spkm); @@ -1441,17 +1441,17 @@ impl CryptoServer { /// ``` /// use std::ops::DerefMut; /// use rosenpass::protocol::{SSk, SPk, SymKey, CryptoServer, ProtocolVersion}; - /// use rosenpass_ciphers::kem::StaticKem; - /// use rosenpass_cipher_traits::Kem; + /// use rosenpass_ciphers::StaticKem; + /// use rosenpass_cipher_traits::primitives::Kem; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// /// let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); - /// StaticKem::keygen(sskm.secret_mut(), spkm.deref_mut())?; + /// StaticKem.keygen(sskm.secret_mut(), spkm.deref_mut())?; /// let mut srv = CryptoServer::new(sskm, spkm); /// /// let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); - /// StaticKem::keygen(sskt.secret_mut(), spkt.deref_mut())?; + /// StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; /// /// let psk = SymKey::random(); /// @@ -1698,7 +1698,7 @@ impl Session { /// /// ``` /// use rosenpass::protocol::{Session, HandshakeRole}; - /// use rosenpass_ciphers::keyed_hash::KeyedHash; + /// use rosenpass_ciphers::KeyedHash; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// From 6f7176752995d7246c1458d2857b003c21dd476a Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:00:47 +0100 Subject: [PATCH 062/174] dev(ciphers): remove keyed hash module --- ciphers/src/hash_domain.rs | 3 --- ciphers/src/lib.rs | 2 -- 2 files changed, 5 deletions(-) diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 58a8f467..522806a5 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -134,9 +134,6 @@ impl SecretHashDomain { .keyed_hash_to(k.try_into()?, d) .to(new_secret_key.secret_mut())?; let mut r = SecretHashDomain(new_secret_key, hash_choice); - KeyedHash::incorrect_hmac_blake2b() - .keyed_hash_to(k.try_into()?, d) - .to(r.0.secret_mut())?; Ok(r) } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index d8be6f5f..6a584e39 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -5,8 +5,6 @@ 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 == hash_domain::KEY_LEN); /// Keyed hashing From 23cf60c7ec0bf6ad1a43fc6e42ff6b7704710fde Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:02:42 +0100 Subject: [PATCH 063/174] dev(rosenpass): Make the cooke mechenism use SHA3 exclusively --- rosenpass/src/protocol/protocol.rs | 36 +++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 8ebb5f49..9aeec854 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -719,6 +719,7 @@ impl KnownResponseHasher { pub fn hash(&self, msg: &Envelope) -> KnownResponseHash { let data = &msg.as_bytes()[span_of!(Envelope, msg_type..cookie)]; // TODO: the hash choice hasn't been propagated here so far + // TODO: FIX DOCU AND OUT-COMMENTED_CODE_BELOW let hash_choice = rosenpass_ciphers::subtle::keyed_hash::KeyedHash::incorrect_hmac_blake2b(); @@ -2121,7 +2122,7 @@ impl CryptoServer { /// Keeps track of messages processed, and qualifies messages using /// cookie based DoS mitigation. /// - /// If recieving a InitHello message, it dispatches message for further processing + /// If receiving a InitHello message, it dispatches message for further processing /// to `process_msg` handler if cookie is valid otherwise sends a cookie reply /// message for sender to process and verify for messages part of the handshake phase /// @@ -2150,7 +2151,6 @@ impl CryptoServer { let mut rx_mac = [0u8; MAC_SIZE]; let mut rx_sid = [0u8; 4]; let msg_type: Result = rx_buf[0].try_into(); - // TODO: Th match msg_type { Ok(MsgType::InitConf) => { log::debug!( @@ -2172,9 +2172,11 @@ impl CryptoServer { } } + log::debug!("Checking all cookie secrets for match"); for cookie_secret in self.active_or_retired_cookie_secrets() { if let Some(cookie_secret) = cookie_secret { let cookie_secret = cookie_secret.get(self).value.secret(); + log::debug!("Checking cookie secret {:?}", cookie_secret); let mut cookie_value = [0u8; 16]; cookie_value.copy_from_slice( &hash_domains::cookie_value(KeyedHash::keyed_shake256())? @@ -2182,9 +2184,14 @@ impl CryptoServer { .mix(host_identification.encode())? .into_value()[..16], ); + log::debug!("Computed cookie_value: {:?}", cookie_value); - //Most recently filled value is active cookie value + // Most recently filled value is active cookie value if active_cookie_value.is_none() { + log::debug!( + "Active cookie_value was None, setting to {:?}", + cookie_value + ); active_cookie_value = Some(cookie_value); } @@ -2192,17 +2199,24 @@ impl CryptoServer { let msg_in = Ref::<&[u8], Envelope>::new(rx_buf) .ok_or(RosenpassError::BufferSizeMismatch)?; + log::debug!( + "Mixing with cookie from envelope: {:?}", + &msg_in.as_bytes()[span_of!(Envelope, msg_type..cookie)] + ); expected.copy_from_slice( &hash_domains::cookie(KeyedHash::keyed_shake256())? .mix(&cookie_value)? .mix(&msg_in.as_bytes()[span_of!(Envelope, msg_type..cookie)])? .into_value()[..16], ); + log::debug!("Computed expected cookie: {:?}", expected); rx_cookie.copy_from_slice(&msg_in.cookie); rx_mac.copy_from_slice(&msg_in.mac); rx_sid.copy_from_slice(&msg_in.payload.sidi); + log::debug!("Received cookie is: {:?}", rx_cookie); + //If valid cookie is found, process message if constant_time::memcmp(&rx_cookie, &expected) { log::debug!( @@ -2212,13 +2226,16 @@ impl CryptoServer { ); let result = self.handle_msg(rx_buf, tx_buf)?; return Ok(result); + } else { + log::debug!("Cookie did not match, continuing"); } } else { + log::debug!("Cookie secret was None for some reason"); break; } } - //Otherwise send cookie reply + // Otherwise send cookie reply if active_cookie_value.is_none() { bail!("No active cookie value found"); } @@ -3212,6 +3229,7 @@ where .mix(peer.get(srv).spkt.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); + log::debug!("Setting MAC for Envelope: {:?}", self.mac); self.seal_cookie(peer, srv)?; Ok(()) } @@ -3904,6 +3922,7 @@ impl CryptoServer { .map(|v| PeerPtr(v.0)) }); if let Some(peer) = peer_ptr { + log::debug!("Found peer for cookie reply: {:?}", peer); // Get last transmitted handshake message if let Some(ih) = &peer.get(self).handshake { let mut mac = [0u8; MAC_SIZE]; @@ -3932,11 +3951,17 @@ impl CryptoServer { pidr = cr.inner.sid ), }?; + log::debug!( + "Found last transmitted handshake message with mac: {:?}", + mac + ); let spkt = peer.get(self).spkt.deref(); let cookie_key = hash_domains::cookie_key(KeyedHash::keyed_shake256())? .mix(spkt)? .into_value(); + + log::debug!("Computed cookie key: {:?}", cookie_key); let cookie_value = peer.cv().update_mut(self).unwrap(); XAead.decrypt_with_nonce_in_ctxt( @@ -3945,8 +3970,9 @@ impl CryptoServer { &mac, &cr.inner.cookie_encrypted, )?; + log::debug!("Computed cookie value: {:?}", cookie_value); - // Immediately retransmit on recieving a cookie reply message + // Immediately retransmit on receiving a cookie reply message peer.hs().register_immediate_retransmission(self)?; Ok(peer) From 944be10bd2bd254b9edee1ae1b7c0ce49c2bf11f Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:16:57 +0100 Subject: [PATCH 064/174] dev(rp): Adapt rp to include set a protocol version. --- rp/src/exchange.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index 6deb9ba0..838824be 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use std::{net::SocketAddr, path::PathBuf, process::Command}; use anyhow::Result; - +use rosenpass::config::ProtocolVersion; #[cfg(any(target_os = "linux", target_os = "freebsd"))] use crate::key::WG_B64_LEN; @@ -24,6 +24,8 @@ pub struct ExchangePeer { pub persistent_keepalive: Option, /// The IPs that are allowed for this peer. pub allowed_ips: Option, + /// The protocol version used by the peer. + pub protocol_version: ProtocolVersion } /// Options for the exchange operation of the `rp` binary. @@ -356,6 +358,7 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> { None, broker_peer, peer.endpoint.map(|x| x.to_string()), + peer.protocol_version, )?; // Configure routes, equivalent to `ip route replace dev ` and set up From 33901d598a247e700ef20e1d1ff00c46655a95d4 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:18:10 +0100 Subject: [PATCH 065/174] test(ciphers): Adapt SHAKE256 tests to longer including the output length. --- .../src/subtle/rust_crypto/keyed_shake256.rs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs index 14d32ab9..1631c38e 100644 --- a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -14,7 +14,7 @@ impl KeyedHash { type Error = anyhow::Error; - /// TODO: Rework test + /// TODO: Check comment /// 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]). /// @@ -23,7 +23,6 @@ impl KeyedHash /// same collision resistance as SHAKE128, but 256 bits of preimage resistance. We therefore /// prefer a truncated SHAKE256 over SHAKE128. /// - /// TODO: Example/Test /// #Examples /// ```rust /// # use rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::SHAKE256Core; @@ -36,8 +35,8 @@ impl KeyedHash /// 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] = &[44, 102, 248, 251, 141, 145, 55, 194, 165, 228, 156, 42, 220, - /// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87]; + /// # 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( @@ -55,13 +54,9 @@ impl KeyedHash shake256.update(key); shake256.update(data); - // Following the NIST recommendations in Section A.2 of the FIPS 202 standard, - // (pages 24/25, i.e., 32/33 in the PDF) we append the length of the input to the end of - // the input. This prevents that if the same input is used with two different output lengths, - // the shorter output is a prefix of the longer output. See the Section A.2 of the FIPS 202 - // standard for more details. - // TODO: Explain why we do not include the length here - // shake256.update(&((HASH_LEN as u8).to_le_bytes())); + // 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(()) } @@ -91,8 +86,8 @@ impl Default for SHAKE256Core = @@ -110,8 +105,8 @@ pub type SHAKE256 = /// 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] = &[44, 102, 248, 251, 141, 145, 55, 194, 165, 228, 156, 42, 220, -/// 108, 50, 224, 48, 19, 197, 253, 105, 136, 95, 34, 95, 203, 149, 192, 124, 223, 243, 87]; +/// # 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>; From 006946442a67c3ef9d5a0a4c8b652d527b547a18 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Wed, 19 Mar 2025 18:28:41 +0100 Subject: [PATCH 066/174] Fix doc code examples in oqs Kem macro --- oqs/src/kem_macro.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/oqs/src/kem_macro.rs b/oqs/src/kem_macro.rs index 55c30943..9117dd77 100644 --- a/oqs/src/kem_macro.rs +++ b/oqs/src/kem_macro.rs @@ -13,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 = ""] @@ -22,19 +22,19 @@ 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 struct [< $name:camel >]; From b21a95dbbd29a90a13e966fb19c8965d10d57072 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:46:41 +0100 Subject: [PATCH 067/174] doc(rp+rosenpass+ciphers+cipher-traits): Apply cargo fmt formatting --- cipher-traits/src/primitives/keyed_hash.rs | 2 ++ ciphers/src/lib.rs | 1 - rosenpass/src/protocol/protocol.rs | 8 +++----- rosenpass/tests/gen-ipc-msg-types.rs | 2 +- rp/src/exchange.rs | 6 +++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/cipher-traits/src/primitives/keyed_hash.rs b/cipher-traits/src/primitives/keyed_hash.rs index d616cb20..da108806 100644 --- a/cipher-traits/src/primitives/keyed_hash.rs +++ b/cipher-traits/src/primitives/keyed_hash.rs @@ -66,10 +66,12 @@ where 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 } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index 6a584e39..aca6dbf4 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -1,4 +1,3 @@ -use rosenpass_cipher_traits::primitives::Aead as _; use static_assertions::const_assert; pub mod subtle; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 9aeec854..8a5edd35 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -718,13 +718,11 @@ impl KnownResponseHasher { /// Panics in case of a problem with this underlying hash function pub fn hash(&self, msg: &Envelope) -> KnownResponseHash { let data = &msg.as_bytes()[span_of!(Envelope, msg_type..cookie)]; - // TODO: the hash choice hasn't been propagated here so far - // TODO: FIX DOCU AND OUT-COMMENTED_CODE_BELOW - let hash_choice = - rosenpass_ciphers::subtle::keyed_hash::KeyedHash::incorrect_hmac_blake2b(); + // This function is only used internally and results are not propagated + // to outside the peer. Thus, it uses SHAKE256 exclusively. let mut hash = [0; 32]; - hash_choice + KeyedHash::keyed_shake256() .keyed_hash(self.key.secret(), data, &mut hash) .unwrap(); Public::from_slice(&hash[0..16]) // truncate to 16 bytes diff --git a/rosenpass/tests/gen-ipc-msg-types.rs b/rosenpass/tests/gen-ipc-msg-types.rs index 1b46144a..6b117dfb 100644 --- a/rosenpass/tests/gen-ipc-msg-types.rs +++ b/rosenpass/tests/gen-ipc-msg-types.rs @@ -1,4 +1,4 @@ -use std::{process::Command}; +use std::process::Command; #[test] fn test_gen_ipc_msg_types() -> anyhow::Result<()> { diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index 838824be..32b09ac7 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -6,10 +6,10 @@ use std::pin::Pin; use std::sync::Arc; use std::{net::SocketAddr, path::PathBuf, process::Command}; -use anyhow::Result; -use rosenpass::config::ProtocolVersion; #[cfg(any(target_os = "linux", target_os = "freebsd"))] use crate::key::WG_B64_LEN; +use anyhow::Result; +use rosenpass::config::ProtocolVersion; /// Used to define a peer for the rosenpass connection that consists of /// a directory for storing public keys and optionally an IP address and port of the endpoint, @@ -25,7 +25,7 @@ pub struct ExchangePeer { /// The IPs that are allowed for this peer. pub allowed_ips: Option, /// The protocol version used by the peer. - pub protocol_version: ProtocolVersion + pub protocol_version: ProtocolVersion, } /// Options for the exchange operation of the `rp` binary. From 3205f8c5722df81486554153a05fd21ed79707ef Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:53:05 +0100 Subject: [PATCH 068/174] doc(rosenpass): Remove already done TODO in handshake.rs --- rosenpass/benches/handshake.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index fc6b1fd3..6516a31f 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -54,7 +54,6 @@ fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer::new(ska, pka.clone()), CryptoServer::new(skb, pkb.clone()), ); - // TODO: Adapt this to both protocol versions a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?; b.add_peer(Some(psk), pka, protocol_version)?; Ok((a, b)) From 5e6c85d73d0c920ef065d77bf38351bda9df72e9 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 21 Mar 2025 15:54:11 +0100 Subject: [PATCH 069/174] test(rosenpass): Complete support for SHAKE256 in gen-ipc-msg-types.rs --- rosenpass/src/bin/gen-ipc-msg-types.rs | 9 +++++---- rosenpass/tests/gen-ipc-msg-types.rs | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index 3dfee4bd..4d6bc1e6 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -15,11 +15,11 @@ fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN] /// 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), path)?; + 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} -> "); } @@ -95,6 +95,7 @@ fn main() -> Result<()> { println!("type RawMsgType = u128;"); println!(); - tree.gen_code(EitherShakeOrBlake::Left(SHAKE256Core))?; - tree.gen_code(EitherShakeOrBlake::Right(Blake2bCore)) + tree.gen_code(KeyedHash::keyed_shake256())?; + println!(); + tree.gen_code(KeyedHash::incorrect_hmac_blake2b()) } diff --git a/rosenpass/tests/gen-ipc-msg-types.rs b/rosenpass/tests/gen-ipc-msg-types.rs index 6b117dfb..68abaeed 100644 --- a/rosenpass/tests/gen-ipc-msg-types.rs +++ b/rosenpass/tests/gen-ipc-msg-types.rs @@ -9,7 +9,10 @@ fn test_gen_ipc_msg_types() -> anyhow::Result<()> { // Smoke tests only assert!(stdout.contains("type RawMsgType = u128;")); + // For Blake2b: assert!(stdout.contains("const SUPPLY_KEYPAIR_RESPONSE : RawMsgType = RawMsgType::from_le_bytes(hex!(\"f2dc 49bd e261 5f10 40b7 3c16 ec61 edb9\"));")); + // For SHAKE256: + assert!(stdout.contains("const SUPPLY_KEYPAIR_RESPONSE : RawMsgType = RawMsgType::from_le_bytes(hex!(\"ff80 3886 68a4 47ce 2ae6 0915 0972 682f\"))")); // TODO: Also test SHAKE256 here Ok(()) From d1cf6af531c904164f072965ddd39bdd2ec83aca Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:11:29 +0100 Subject: [PATCH 070/174] test(rosenpass): Add test for protocol version in a toml configuration. --- rosenpass/src/config.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 82397d23..a18ae923 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -109,7 +109,7 @@ pub enum Verbosity { Verbose, } -/// TODO: Documentation +/// The protocol version to be used by a peer. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone, Default)] pub enum ProtocolVersion { #[default] @@ -781,6 +781,31 @@ mod test { Ok(()) } + + #[test] + 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); + + 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] fn test_cli_parse_multiple_peers() { From 62d408eade179ff3748e2ae6c7e5a70e4ea92ff9 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:12:55 +0100 Subject: [PATCH 071/174] dev(ciphers): implement the Display trait for the KeyedHash that allows to choose a hash. --- ciphers/src/subtle/keyed_hash.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ciphers/src/subtle/keyed_hash.rs b/ciphers/src/subtle/keyed_hash.rs index b7e0f06b..785011a5 100644 --- a/ciphers/src/subtle/keyed_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -1,6 +1,7 @@ //! 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 std::fmt::Display; use anyhow::Result; use rosenpass_cipher_traits::primitives::KeyedHashInstance; @@ -52,3 +53,13 @@ impl KeyedHashInstance for KeyedHash { 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"), + } + } + +} From ebf6403ea7a3ac3ee649b0c189b21a264822e0e1 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:13:23 +0100 Subject: [PATCH 072/174] doc(ciphers + rosenpass): improve the documentation --- ciphers/src/subtle/rust_crypto/keyed_shake256.rs | 13 +++++++++---- rosenpass/src/protocol/protocol.rs | 7 +++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs index 1631c38e..9474761f 100644 --- a/ciphers/src/subtle/rust_crypto/keyed_shake256.rs +++ b/ciphers/src/subtle/rust_crypto/keyed_shake256.rs @@ -14,7 +14,6 @@ impl KeyedHash { type Error = anyhow::Error; - /// TODO: Check comment /// 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]). /// @@ -74,7 +73,12 @@ impl Default for SHAKE256Core Default for SHAKE256Core Default for SHAKE256Core = InferKeyedHash, KEY_LEN, HASH_LEN>; -/// TODO: Documentation and more interesting test +/// 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; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 8a5edd35..36f16d43 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -1433,9 +1433,8 @@ impl CryptoServer { (0..l).map(move |i| PeerPtr((i + n) % l)) } - /// Add a peer with an optional pre shared key (`psk`) and its public key (`pk`) - /// - /// TODO: Adapt documentation + /// Add a peer with an optional pre shared key (`psk`), its public key (`pk`) and the peer's + /// protocol version (`protocol_version`). /// /// ``` /// use std::ops::DerefMut; @@ -1453,7 +1452,7 @@ impl CryptoServer { /// StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; /// /// let psk = SymKey::random(); - /// + /// // We use the latest protocol version for the example. /// let peer = srv.add_peer(Some(psk), spkt.clone(), ProtocolVersion::V03)?; /// /// assert_eq!(peer.get(&srv).spkt, spkt); From 7566eadef84bd8a97a03bff41101eba4b206f8c3 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:15:08 +0100 Subject: [PATCH 073/174] doc(rosenpass): correct formatting --- rosenpass/src/config.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index a18ae923..54695c36 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -781,7 +781,7 @@ mod test { Ok(()) } - + #[test] fn test_protocol_version() { let mut rosenpass = Rosenpass::empty(); @@ -791,9 +791,8 @@ mod test { let mut peer_v_03 = RosenpassPeer::default(); peer_v_03.protocol_version = ProtocolVersion::V03; rosenpass.peers.push(peer_v_03); - - let expected_toml = - r#"listen = [] + + let expected_toml = r#"listen = [] verbosity = "Quiet" [[peers]] From 8bb54b9ccaff799597d48fb2ed2a92c1ce5eb108 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:35:28 +0100 Subject: [PATCH 074/174] doc(ciphers): correct formatting --- ciphers/src/subtle/keyed_hash.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ciphers/src/subtle/keyed_hash.rs b/ciphers/src/subtle/keyed_hash.rs index 785011a5..c77b8e12 100644 --- a/ciphers/src/subtle/keyed_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -1,9 +1,9 @@ //! 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 std::fmt::Display; 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, @@ -61,5 +61,4 @@ impl Display for KeyedHash { Self::IncorrectHmacBlake2b(_) => write!(f, "IncorrectHmacBlake2b"), } } - } From 531ae0ef708c938ece508c747208e0823d826b70 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:44:19 +0100 Subject: [PATCH 075/174] test(rosenpass): Adapt test for protocol version of config files to tests being run with `--all-features` --- rosenpass/src/config.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 54695c36..20110645 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -791,9 +791,18 @@ mod test { let mut peer_v_03 = RosenpassPeer::default(); peer_v_03.protocol_version = ProtocolVersion::V03; rosenpass.peers.push(peer_v_03); + + rosenpass.api.listen_fd = vec![]; + rosenpass.api.listen_path = vec![]; + rosenpass.api.stream_fd = vec![]; let expected_toml = r#"listen = [] verbosity = "Quiet" + + [api] + listen_fd = [] + listen_path = [] + stream_fd = [] [[peers]] protocol_version = "V02" From dbb891a2edd48de3d50d78c3acfe3a097edd29bc Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:48:16 +0100 Subject: [PATCH 076/174] ci(supply-chain): Regenerate exemptions for cargo-vet --- supply-chain/config.toml | 380 +++++-------------------- supply-chain/imports.lock | 571 +++++++++++++++++++++++--------------- 2 files changed, 425 insertions(+), 526 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 78233367..4674c455 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -53,50 +53,26 @@ criteria = "safe-to-deploy" version = "0.2.15" criteria = "safe-to-run" -[[exemptions.anstream]] -version = "0.6.15" -criteria = "safe-to-deploy" - [[exemptions.anstream]] version = "0.6.18" criteria = "safe-to-deploy" -[[exemptions.anstyle]] -version = "1.0.8" -criteria = "safe-to-deploy" - [[exemptions.anstyle]] version = "1.0.10" criteria = "safe-to-deploy" -[[exemptions.anstyle-parse]] -version = "0.2.5" -criteria = "safe-to-deploy" - [[exemptions.anstyle-parse]] version = "0.2.6" criteria = "safe-to-deploy" -[[exemptions.anstyle-query]] -version = "1.1.1" -criteria = "safe-to-deploy" - [[exemptions.anstyle-query]] version = "1.1.2" criteria = "safe-to-deploy" -[[exemptions.anstyle-wincon]] -version = "3.0.4" -criteria = "safe-to-deploy" - [[exemptions.anstyle-wincon]] version = "3.0.7" criteria = "safe-to-deploy" -[[exemptions.anyhow]] -version = "1.0.95" -criteria = "safe-to-deploy" - [[exemptions.anyhow]] version = "1.0.96" criteria = "safe-to-deploy" @@ -117,10 +93,6 @@ criteria = "safe-to-deploy" version = "1.3.3" criteria = "safe-to-run" -[[exemptions.bitflags]] -version = "2.8.0" -criteria = "safe-to-deploy" - [[exemptions.blake2]] version = "0.10.6" criteria = "safe-to-deploy" @@ -129,22 +101,10 @@ criteria = "safe-to-deploy" version = "0.1.4" criteria = "safe-to-deploy" -[[exemptions.bumpalo]] -version = "3.17.0" -criteria = "safe-to-deploy" - -[[exemptions.bytes]] -version = "1.7.2" -criteria = "safe-to-deploy" - [[exemptions.bytes]] version = "1.10.0" criteria = "safe-to-deploy" -[[exemptions.cc]] -version = "1.1.30" -criteria = "safe-to-deploy" - [[exemptions.cc]] version = "1.2.15" criteria = "safe-to-deploy" @@ -157,48 +117,20 @@ criteria = "safe-to-deploy" version = "0.10.1" criteria = "safe-to-deploy" -[[exemptions.ciborium]] -version = "0.2.2" -criteria = "safe-to-run" - -[[exemptions.ciborium-io]] -version = "0.2.2" -criteria = "safe-to-run" - -[[exemptions.ciborium-ll]] -version = "0.2.2" -criteria = "safe-to-run" - [[exemptions.clang-sys]] version = "1.8.1" criteria = "safe-to-deploy" [[exemptions.clap]] -version = "4.5.23" -criteria = "safe-to-deploy" - -[[exemptions.clap]] -version = "4.5.30" +version = "4.5.31" criteria = "safe-to-deploy" [[exemptions.clap_builder]] -version = "4.5.23" -criteria = "safe-to-deploy" - -[[exemptions.clap_builder]] -version = "4.5.30" +version = "4.5.31" criteria = "safe-to-deploy" [[exemptions.clap_complete]] -version = "4.5.40" -criteria = "safe-to-deploy" - -[[exemptions.clap_complete]] -version = "4.5.45" -criteria = "safe-to-deploy" - -[[exemptions.clap_derive]] -version = "4.5.18" +version = "4.5.46" criteria = "safe-to-deploy" [[exemptions.clap_derive]] @@ -210,21 +142,13 @@ version = "0.7.4" criteria = "safe-to-deploy" [[exemptions.clap_mangen]] -version = "0.2.24" -criteria = "safe-to-deploy" - -[[exemptions.cmake]] -version = "0.1.51" +version = "0.2.26" criteria = "safe-to-deploy" [[exemptions.cmake]] version = "0.1.54" criteria = "safe-to-deploy" -[[exemptions.colorchoice]] -version = "1.0.2" -criteria = "safe-to-deploy" - [[exemptions.colorchoice]] version = "1.0.3" criteria = "safe-to-deploy" @@ -233,10 +157,6 @@ criteria = "safe-to-deploy" version = "0.2.3" criteria = "safe-to-deploy" -[[exemptions.cpufeatures]] -version = "0.2.14" -criteria = "safe-to-deploy" - [[exemptions.cpufeatures]] version = "0.2.17" criteria = "safe-to-deploy" @@ -253,26 +173,14 @@ criteria = "safe-to-run" version = "1.2.0" criteria = "safe-to-deploy" -[[exemptions.crossbeam-channel]] -version = "0.5.14" -criteria = "safe-to-deploy" - [[exemptions.crossbeam-deque]] version = "0.8.6" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.crossbeam-utils]] version = "0.8.20" criteria = "safe-to-run" -[[exemptions.crossbeam-utils]] -version = "0.8.21" -criteria = "safe-to-deploy" - -[[exemptions.crunchy]] -version = "0.2.3" -criteria = "safe-to-deploy" - [[exemptions.ctrlc-async]] version = "3.2.2" criteria = "safe-to-deploy" @@ -349,10 +257,6 @@ criteria = "safe-to-deploy" version = "0.10.2" criteria = "safe-to-deploy" -[[exemptions.equivalent]] -version = "1.0.2" -criteria = "safe-to-deploy" - [[exemptions.fastrand]] version = "2.3.0" criteria = "safe-to-deploy" @@ -381,22 +285,10 @@ criteria = "safe-to-deploy" version = "0.2.15" criteria = "safe-to-deploy" -[[exemptions.getrandom]] -version = "0.3.1" -criteria = "safe-to-deploy" - [[exemptions.gimli]] version = "0.31.1" criteria = "safe-to-deploy" -[[exemptions.glob]] -version = "0.3.2" -criteria = "safe-to-deploy" - -[[exemptions.half]] -version = "2.4.1" -criteria = "safe-to-run" - [[exemptions.hash32]] version = "0.2.1" criteria = "safe-to-deploy" @@ -405,6 +297,18 @@ criteria = "safe-to-deploy" version = "0.15.2" criteria = "safe-to-deploy" +[[exemptions.hax-lib]] +version = "0.1.0" +criteria = "safe-to-deploy" + +[[exemptions.hax-lib-macros]] +version = "0.1.0" +criteria = "safe-to-deploy" + +[[exemptions.hax-lib-macros-types]] +version = "0.1.0" +criteria = "safe-to-deploy" + [[exemptions.heapless]] version = "0.7.17" criteria = "safe-to-deploy" @@ -425,14 +329,6 @@ criteria = "safe-to-deploy" version = "2.1.0" criteria = "safe-to-deploy" -[[exemptions.indexmap]] -version = "2.6.0" -criteria = "safe-to-deploy" - -[[exemptions.indexmap]] -version = "2.7.1" -criteria = "safe-to-deploy" - [[exemptions.inout]] version = "0.1.4" criteria = "safe-to-deploy" @@ -441,10 +337,6 @@ criteria = "safe-to-deploy" version = "0.18.3" criteria = "safe-to-run" -[[exemptions.is-terminal]] -version = "0.4.13" -criteria = "safe-to-deploy" - [[exemptions.is-terminal]] version = "0.4.15" criteria = "safe-to-deploy" @@ -453,20 +345,16 @@ criteria = "safe-to-deploy" version = "1.70.1" criteria = "safe-to-deploy" -[[exemptions.itoa]] -version = "1.0.14" -criteria = "safe-to-deploy" - [[exemptions.jobserver]] version = "0.1.32" criteria = "safe-to-deploy" [[exemptions.js-sys]] -version = "0.3.72" +version = "0.3.77" criteria = "safe-to-deploy" -[[exemptions.js-sys]] -version = "0.3.77" +[[exemptions.keccak]] +version = "0.1.5" criteria = "safe-to-deploy" [[exemptions.lazycell]] @@ -474,27 +362,51 @@ version = "1.3.0" criteria = "safe-to-deploy" [[exemptions.libc]] -version = "0.2.168" -criteria = "safe-to-deploy" - -[[exemptions.libc]] -version = "0.2.169" +version = "0.2.170" criteria = "safe-to-deploy" [[exemptions.libcrux]] version = "0.0.2-pre.2" criteria = "safe-to-deploy" +[[exemptions.libcrux-blake2]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + +[[exemptions.libcrux-chacha20poly1305]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + [[exemptions.libcrux-hacl]] version = "0.0.2-pre.2" criteria = "safe-to-deploy" +[[exemptions.libcrux-hacl-rs]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + +[[exemptions.libcrux-intrinsics]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + +[[exemptions.libcrux-macros]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + +[[exemptions.libcrux-ml-kem]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + [[exemptions.libcrux-platform]] version = "0.0.2-pre.2" criteria = "safe-to-deploy" -[[exemptions.libfuzzer-sys]] -version = "0.4.8" +[[exemptions.libcrux-poly1305]] +version = "0.0.2-beta.3" +criteria = "safe-to-deploy" + +[[exemptions.libcrux-sha3]] +version = "0.0.2-beta.3" criteria = "safe-to-deploy" [[exemptions.libfuzzer-sys]] @@ -505,18 +417,10 @@ criteria = "safe-to-deploy" version = "0.0.2-pre.2" criteria = "safe-to-deploy" -[[exemptions.libloading]] -version = "0.8.5" -criteria = "safe-to-deploy" - [[exemptions.libloading]] version = "0.8.6" criteria = "safe-to-deploy" -[[exemptions.linux-raw-sys]] -version = "0.4.14" -criteria = "safe-to-deploy" - [[exemptions.linux-raw-sys]] version = "0.4.15" criteria = "safe-to-deploy" @@ -525,10 +429,6 @@ criteria = "safe-to-deploy" version = "0.4.12" criteria = "safe-to-deploy" -[[exemptions.log]] -version = "0.4.26" -criteria = "safe-to-deploy" - [[exemptions.memchr]] version = "2.7.4" criteria = "safe-to-deploy" @@ -549,10 +449,6 @@ criteria = "safe-to-deploy" version = "0.2.1" criteria = "safe-to-deploy" -[[exemptions.miniz_oxide]] -version = "0.8.5" -criteria = "safe-to-deploy" - [[exemptions.mio]] version = "1.0.3" criteria = "safe-to-deploy" @@ -561,10 +457,6 @@ criteria = "safe-to-deploy" version = "0.6.3" criteria = "safe-to-deploy" -[[exemptions.neli-proc-macros]] -version = "0.1.3" -criteria = "safe-to-deploy" - [[exemptions.neli-proc-macros]] version = "0.1.4" criteria = "safe-to-deploy" @@ -589,18 +481,10 @@ criteria = "safe-to-deploy" version = "0.2.3" criteria = "safe-to-deploy" -[[exemptions.netlink-proto]] -version = "0.11.3" -criteria = "safe-to-deploy" - [[exemptions.netlink-proto]] version = "0.11.5" criteria = "safe-to-deploy" -[[exemptions.netlink-sys]] -version = "0.8.6" -criteria = "safe-to-deploy" - [[exemptions.netlink-sys]] version = "0.8.7" criteria = "safe-to-deploy" @@ -613,8 +497,8 @@ criteria = "safe-to-deploy" version = "0.27.1" criteria = "safe-to-deploy" -[[exemptions.object]] -version = "0.36.5" +[[exemptions.num-bigint]] +version = "0.4.6" criteria = "safe-to-deploy" [[exemptions.object]] @@ -625,10 +509,6 @@ criteria = "safe-to-deploy" version = "1.20.2" criteria = "safe-to-deploy" -[[exemptions.once_cell]] -version = "1.20.3" -criteria = "safe-to-deploy" - [[exemptions.oqs-sys]] version = "0.9.1+liboqs-0.9.0" criteria = "safe-to-deploy" @@ -677,58 +557,42 @@ criteria = "safe-to-deploy" version = "0.2.20" criteria = "safe-to-deploy" -[[exemptions.prettyplease]] -version = "0.2.22" -criteria = "safe-to-deploy" - [[exemptions.prettyplease]] version = "0.2.29" criteria = "safe-to-deploy" -[[exemptions.proc-macro2]] -version = "1.0.93" +[[exemptions.proc-macro-error]] +version = "1.0.4" criteria = "safe-to-deploy" [[exemptions.procspawn]] version = "1.0.1" criteria = "safe-to-run" -[[exemptions.psm]] -version = "0.1.23" -criteria = "safe-to-deploy" - [[exemptions.psm]] version = "0.1.25" criteria = "safe-to-deploy" -[[exemptions.quote]] -version = "1.0.38" -criteria = "safe-to-deploy" - [[exemptions.rand]] -version = "0.8.5" +version = "0.9.0" criteria = "safe-to-deploy" -[[exemptions.redox_syscall]] -version = "0.5.7" +[[exemptions.rand_chacha]] +version = "0.9.0" +criteria = "safe-to-deploy" + +[[exemptions.rand_core]] +version = "0.9.2" criteria = "safe-to-deploy" [[exemptions.redox_syscall]] version = "0.5.9" criteria = "safe-to-deploy" -[[exemptions.regex]] -version = "1.11.0" -criteria = "safe-to-deploy" - [[exemptions.regex]] version = "1.11.1" criteria = "safe-to-deploy" -[[exemptions.regex-automata]] -version = "0.4.8" -criteria = "safe-to-deploy" - [[exemptions.regex-automata]] version = "0.4.9" criteria = "safe-to-deploy" @@ -741,57 +605,25 @@ criteria = "safe-to-deploy" version = "0.14.1" criteria = "safe-to-deploy" -[[exemptions.rustix]] -version = "0.38.42" -criteria = "safe-to-deploy" - [[exemptions.rustix]] version = "0.38.44" criteria = "safe-to-deploy" -[[exemptions.rustversion]] -version = "1.0.19" -criteria = "safe-to-deploy" - -[[exemptions.ryu]] -version = "1.0.18" -criteria = "safe-to-run" - [[exemptions.ryu]] version = "1.0.19" criteria = "safe-to-deploy" -[[exemptions.scc]] -version = "2.2.1" -criteria = "safe-to-run" - [[exemptions.scc]] version = "2.3.3" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.scopeguard]] version = "1.2.0" criteria = "safe-to-deploy" -[[exemptions.sdd]] -version = "3.0.4" -criteria = "safe-to-run" - [[exemptions.sdd]] version = "3.0.7" -criteria = "safe-to-deploy" - -[[exemptions.semver]] -version = "1.0.25" -criteria = "safe-to-deploy" - -[[exemptions.serde]] -version = "1.0.218" -criteria = "safe-to-deploy" - -[[exemptions.serde_derive]] -version = "1.0.218" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.serde_json]] version = "1.0.139" @@ -821,14 +653,6 @@ criteria = "safe-to-deploy" version = "0.4.9" criteria = "safe-to-deploy" -[[exemptions.smallvec]] -version = "1.14.0" -criteria = "safe-to-deploy" - -[[exemptions.socket2]] -version = "0.5.7" -criteria = "safe-to-deploy" - [[exemptions.socket2]] version = "0.5.8" criteria = "safe-to-deploy" @@ -837,10 +661,6 @@ criteria = "safe-to-deploy" version = "0.9.8" criteria = "safe-to-deploy" -[[exemptions.stacker]] -version = "0.1.17" -criteria = "safe-to-deploy" - [[exemptions.stacker]] version = "0.1.19" criteria = "safe-to-deploy" @@ -849,10 +669,6 @@ criteria = "safe-to-deploy" version = "1.0.109" criteria = "safe-to-deploy" -[[exemptions.syn]] -version = "2.0.87" -criteria = "safe-to-deploy" - [[exemptions.syn]] version = "2.0.98" criteria = "safe-to-deploy" @@ -861,10 +677,6 @@ criteria = "safe-to-deploy" version = "0.1.0" criteria = "safe-to-deploy" -[[exemptions.tempfile]] -version = "3.14.0" -criteria = "safe-to-deploy" - [[exemptions.tempfile]] version = "3.17.1" criteria = "safe-to-deploy" @@ -877,34 +689,18 @@ criteria = "safe-to-deploy" version = "0.4.0" criteria = "safe-to-run" -[[exemptions.thiserror]] -version = "1.0.69" -criteria = "safe-to-deploy" - [[exemptions.thiserror]] version = "2.0.11" criteria = "safe-to-deploy" -[[exemptions.thiserror-impl]] -version = "1.0.69" -criteria = "safe-to-deploy" - [[exemptions.thiserror-impl]] version = "2.0.11" criteria = "safe-to-deploy" -[[exemptions.tokio]] -version = "1.42.0" -criteria = "safe-to-deploy" - [[exemptions.tokio]] version = "1.43.0" criteria = "safe-to-deploy" -[[exemptions.tokio-macros]] -version = "2.4.0" -criteria = "safe-to-deploy" - [[exemptions.tokio-macros]] version = "2.5.0" criteria = "safe-to-deploy" @@ -921,10 +717,6 @@ criteria = "safe-to-deploy" version = "0.19.15" criteria = "safe-to-deploy" -[[exemptions.typenum]] -version = "1.17.0" -criteria = "safe-to-deploy" - [[exemptions.typenum]] version = "1.18.0" criteria = "safe-to-deploy" @@ -942,11 +734,7 @@ version = "0.2.2" criteria = "safe-to-deploy" [[exemptions.uuid]] -version = "1.10.0" -criteria = "safe-to-run" - -[[exemptions.uuid]] -version = "1.14.0" +version = "1.15.1" criteria = "safe-to-deploy" [[exemptions.version_check]] @@ -965,53 +753,29 @@ criteria = "safe-to-deploy" version = "0.13.3+wasi-0.2.2" criteria = "safe-to-deploy" -[[exemptions.wasm-bindgen]] -version = "0.2.95" -criteria = "safe-to-deploy" - [[exemptions.wasm-bindgen]] version = "0.2.100" criteria = "safe-to-deploy" -[[exemptions.wasm-bindgen-backend]] -version = "0.2.95" -criteria = "safe-to-deploy" - [[exemptions.wasm-bindgen-backend]] version = "0.2.100" criteria = "safe-to-deploy" -[[exemptions.wasm-bindgen-macro]] -version = "0.2.95" -criteria = "safe-to-deploy" - [[exemptions.wasm-bindgen-macro]] version = "0.2.100" criteria = "safe-to-deploy" -[[exemptions.wasm-bindgen-macro-support]] -version = "0.2.95" -criteria = "safe-to-deploy" - [[exemptions.wasm-bindgen-macro-support]] version = "0.2.100" criteria = "safe-to-deploy" -[[exemptions.wasm-bindgen-shared]] -version = "0.2.95" -criteria = "safe-to-deploy" - [[exemptions.wasm-bindgen-shared]] version = "0.2.100" criteria = "safe-to-deploy" -[[exemptions.web-sys]] -version = "0.3.72" -criteria = "safe-to-run" - [[exemptions.web-sys]] version = "0.3.77" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.which]] version = "4.4.2" @@ -1181,10 +945,6 @@ criteria = "safe-to-deploy" version = "3.0.0" criteria = "safe-to-deploy" -[[exemptions.wit-bindgen-rt]] -version = "0.33.0" -criteria = "safe-to-deploy" - [[exemptions.x25519-dalek]] version = "2.0.1" criteria = "safe-to-deploy" @@ -1193,6 +953,14 @@ criteria = "safe-to-deploy" version = "0.7.35" criteria = "safe-to-deploy" +[[exemptions.zerocopy]] +version = "0.8.20" +criteria = "safe-to-deploy" + [[exemptions.zerocopy-derive]] version = "0.7.35" criteria = "safe-to-deploy" + +[[exemptions.zerocopy-derive]] +version = "0.8.20" +criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 9f7c927a..ec2ceed7 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -2,8 +2,8 @@ # cargo-vet imports lock [[publisher.bumpalo]] -version = "3.16.0" -when = "2024-04-08" +version = "3.17.0" +when = "2025-01-28" user-id = 696 user-login = "fitzgen" user-name = "Nick Fitzgerald" @@ -15,6 +15,12 @@ user-id = 3788 user-login = "emilio" user-name = "Emilio Cobos Álvarez" +[[publisher.wit-bindgen-rt]] +version = "0.33.0" +when = "2024-09-30" +user-id = 73222 +user-login = "wasmtime-publish" + [audits.actix.audits] [[audits.bytecode-alliance.wildcard-audits.bumpalo]] @@ -24,6 +30,18 @@ user-id = 696 # Nick Fitzgerald (fitzgen) start = "2019-03-16" end = "2025-07-30" +[[audits.bytecode-alliance.wildcard-audits.wit-bindgen-rt]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +user-id = 73222 # wasmtime-publish +start = "2023-01-01" +end = "2025-05-08" +notes = """ +The Bytecode Alliance uses the `wasmtime-publish` crates.io account to automate +publication of this crate from CI. This repository requires all PRs are reviewed +by a Bytecode Alliance maintainer and it owned by the Bytecode Alliance itself. +""" + [[audits.bytecode-alliance.audits.adler2]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -103,12 +121,6 @@ who = "Benjamin Bouvier " criteria = "safe-to-deploy" version = "0.1.3" -[[audits.bytecode-alliance.audits.either]] -who = "Alex Crichton " -criteria = "safe-to-deploy" -delta = "1.8.1 -> 1.13.0" -notes = "More utilities and such for the `Either` type, no `unsafe` code." - [[audits.bytecode-alliance.audits.embedded-io]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -132,15 +144,6 @@ who = "Dan Gohman " criteria = "safe-to-deploy" delta = "0.3.9 -> 0.3.10" -[[audits.bytecode-alliance.audits.fastrand]] -who = "Alex Crichton " -criteria = "safe-to-deploy" -delta = "2.0.0 -> 2.0.1" -notes = """ -This update had a few doc updates but no otherwise-substantial source code -updates. -""" - [[audits.bytecode-alliance.audits.futures]] who = "Joel Dice " criteria = "safe-to-deploy" @@ -193,11 +196,10 @@ criteria = "safe-to-deploy" delta = "0.4.1 -> 0.5.0" notes = "Minor changes for a `no_std` upgrade but otherwise everything looks as expected." -[[audits.bytecode-alliance.audits.inout]] -who = "Andrew Brown " +[[audits.bytecode-alliance.audits.itoa]] +who = "Dan Gohman " criteria = "safe-to-deploy" -version = "0.1.3" -notes = "A part of RustCrypto/utils, this crate is designed to handle unsafe buffers and carefully documents the safety concerns throughout. Older versions of this tally up to ~130k daily downloads." +delta = "1.0.11 -> 1.0.14" [[audits.bytecode-alliance.audits.miniz_oxide]] who = "Alex Crichton " @@ -219,6 +221,16 @@ criteria = "safe-to-deploy" delta = "0.7.1 -> 0.8.0" notes = "Minor updates, using new Rust features like `const`, no major changes." +[[audits.bytecode-alliance.audits.miniz_oxide]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.8.0 -> 0.8.5" +notes = """ +Lots of small updates here and there, for example around modernizing Rust +idioms. No new `unsafe` code and everything looks like what you'd expect a +compression library to be doing. +""" + [[audits.bytecode-alliance.audits.num-traits]] who = "Andrew Brown " criteria = "safe-to-deploy" @@ -231,12 +243,6 @@ criteria = "safe-to-deploy" version = "1.0.0" notes = "I am the author of this crate." -[[audits.bytecode-alliance.audits.pin-project-lite]] -who = "Alex Crichton " -criteria = "safe-to-deploy" -delta = "0.2.13 -> 0.2.14" -notes = "No substantive changes in this update" - [[audits.bytecode-alliance.audits.pin-utils]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -277,6 +283,18 @@ criteria = "safe-to-deploy" version = "1.0.1" notes = "No unsafe usage or ambient capabilities" +[[audits.embark-studios.audits.thiserror]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "1.0.40" +notes = "Wrapper over implementation crate, found no unsafe or ambient capabilities used" + +[[audits.embark-studios.audits.thiserror-impl]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "1.0.40" +notes = "Found no unsafe or ambient capabilities used" + [[audits.fermyon.audits.oorandom]] who = "Radu Matei " criteria = "safe-to-run" @@ -305,6 +323,13 @@ Additional review comments can be found at https://crrev.com/c/4723145/31 """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.bitflags]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "2.6.0 -> 2.8.0" +notes = "No changes related to `unsafe impl ... bytemuck` pieces from `src/external.rs`." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.byteorder]] who = "danakj " criteria = "safe-to-deploy" @@ -318,6 +343,24 @@ criteria = "safe-to-run" version = "0.3.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.ciborium]] +who = "Daniel Verkamp " +criteria = "safe-to-run" +version = "0.2.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.ciborium-io]] +who = "Daniel Verkamp " +criteria = "safe-to-run" +version = "0.2.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.ciborium-ll]] +who = "Daniel Verkamp " +criteria = "safe-to-run" +version = "0.2.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.crossbeam-channel]] who = "George Burgess IV " criteria = "safe-to-run" @@ -330,12 +373,6 @@ criteria = "safe-to-run" delta = "0.5.7 -> 0.5.8" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.crossbeam-deque]] -who = "George Burgess IV " -criteria = "safe-to-run" -version = "0.8.3" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" - [[audits.google.audits.crossbeam-epoch]] who = "George Burgess IV " criteria = "safe-to-run" @@ -348,21 +385,39 @@ criteria = "safe-to-run" delta = "0.9.14 -> 0.9.15" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.either]] +who = "Manish Goregaokar " +criteria = "safe-to-deploy" +version = "1.13.0" +notes = "Unsafe code pertaining to wrapping Pin APIs. Mostly passes invariants down." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.either]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "1.13.0 -> 1.14.0" +notes = """ +Inheriting ub-risk-1 from the baseline review of 1.13.0. While the delta has some diffs in unsafe code, they are either: +- migrating code to use helper macros +- migrating match patterns to take advantage of default bindings mode from RFC 2005 +Either way, the result is code that does exactly the same thing and does not change the risk of UB. + +See https://crrev.com/c/6323164 for more audit details. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.equivalent]] who = "George Burgess IV " criteria = "safe-to-deploy" version = "1.0.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.fastrand]] -who = "George Burgess IV " +[[audits.google.audits.equivalent]] +who = "Jonathan Hao " criteria = "safe-to-deploy" -version = "1.9.0" -notes = """ -`does-not-implement-crypto` is certified because this crate explicitly says -that the RNG here is not cryptographically secure. -""" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +delta = "1.0.1 -> 1.0.2" +notes = "No changes to any .rs files or Rust code." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" [[audits.google.audits.glob]] who = "George Burgess IV " @@ -370,6 +425,19 @@ criteria = "safe-to-deploy" version = "0.3.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.glob]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "0.3.1 -> 0.3.2" +notes = "Still no unsafe" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.half]] +who = "Daniel Verkamp " +criteria = "safe-to-run" +version = "2.4.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.heck]] who = "Lukasz Anforowicz " criteria = "safe-to-deploy" @@ -383,6 +451,19 @@ https://source.chromium.org/chromium/chromium/src/+/28841c33c77833cc30b286f9ae24 """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.indexmap]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "2.7.1" +notes = ''' +Grepped for `-i cipher`, `-i crypto`, `'\bfs\b'`, `'\bnet\b'` +and there were no hits. + +There is a little bit of `unsafe` Rust code - the audit can be found at +https://chromium-review.googlesource.com/c/chromium/src/+/6187726/2 +''' +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.itertools]] who = "ChromeOS" criteria = "safe-to-run" @@ -451,6 +532,20 @@ describe in the review doc. """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.log]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "0.4.22 -> 0.4.25" +notes = "No impact on `unsafe` usage in `lib.rs`." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.log]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "0.4.25 -> 0.4.26" +notes = "Only trivial code and documentation changes." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.nom]] who = "danakj@chromium.org" criteria = "safe-to-deploy" @@ -460,19 +555,18 @@ Reviewed in https://chromium-review.googlesource.com/c/chromium/src/+/5046153 """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.pin-project-lite]] -who = "David Koloski " +[[audits.google.audits.num-integer]] +who = "Manish Goregaokar " criteria = "safe-to-deploy" -version = "0.2.9" -notes = "Reviewed on https://fxrev.dev/824504" -aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" +version = "0.1.46" +notes = "Contains no unsafe" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.pin-project-lite]] -who = "David Koloski " +[[audits.google.audits.proc-macro-error-attr]] +who = "George Burgess IV " criteria = "safe-to-deploy" -delta = "0.2.9 -> 0.2.13" -notes = "Audited at https://fxrev.dev/946396" -aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" +version = "1.0.4" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.proc-macro2]] who = "Lukasz Anforowicz " @@ -551,6 +645,35 @@ delta = "1.0.86 -> 1.0.87" notes = "No new unsafe interactions." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.proc-macro2]] +who = "Liza Burakova Qualifiers::Unsafe, + ``` + +* Using `std::fs` in `build/build.rs` to write `${OUT_DIR}/version.expr` + which is later read back via `include!` used in `src/lib.rs`. + +Version `1.0.6` of this crate has been added to Chromium in +https://source.chromium.org/chromium/chromium/src/+/28841c33c77833cc30b286f9ae24c97e7a8f4057 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.rustversion]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.0.14 -> 1.0.15" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.rustversion]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.0.15 -> 1.0.16" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.rustversion]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.16 -> 1.0.17" +notes = "Just updates windows compat" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.rustversion]] +who = "Liza Burakova " +criteria = "safe-to-deploy" +delta = "1.0.17 -> 1.0.18" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.rustversion]] +who = "Dustin J. Mitchell " +criteria = "safe-to-deploy" +delta = "1.0.18 -> 1.0.19" +notes = "No unsafe, just doc changes" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.same-file]] who = "Android Legacy" criteria = "safe-to-run" @@ -704,6 +904,13 @@ delta = "1.0.216 -> 1.0.217" notes = "Minimal changes, nothing unsafe" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.serde]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "1.0.217 -> 1.0.218" +notes = "No changes outside comments and documentation." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.serde_derive]] who = "Lukasz Anforowicz " criteria = "safe-to-deploy" @@ -797,51 +1004,11 @@ delta = "1.0.216 -> 1.0.217" notes = "No changes" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.serde_json]] -who = "danakj@chromium.org" -criteria = "safe-to-run" -version = "1.0.108" -notes = """ -Reviewed in https://crrev.com/c/5171063 - -Previously reviewed during security review and the audit is grandparented in. -""" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_json]] -who = "danakj " -criteria = "safe-to-run" -delta = "1.0.116 -> 1.0.117" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_json]] -who = "Adrian Taylor " -criteria = "safe-to-run" -delta = "1.0.117 -> 1.0.120" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_json]] -who = "Lukasz Anforowicz " -criteria = "safe-to-run" -delta = "1.0.120 -> 1.0.122" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_json]] -who = "Lukasz Anforowicz " -criteria = "safe-to-run" -delta = "1.0.122 -> 1.0.124" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_json]] -who = "Lukasz Anforowicz " -criteria = "safe-to-run" -delta = "1.0.124 -> 1.0.127" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.serde_json]] -who = "danakj " -criteria = "safe-to-run" -delta = "1.0.127 -> 1.0.128" +[[audits.google.audits.serde_derive]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "1.0.217 -> 1.0.218" +notes = "No changes outside comments and documentation." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" [[audits.google.audits.small_ctor]] @@ -868,6 +1035,20 @@ criteria = "safe-to-deploy" version = "1.13.2" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.smallvec]] +who = "Jonathan Hao " +criteria = "safe-to-deploy" +delta = "1.13.2 -> 1.14.0" +notes = """ +WARNING: This certification is a result of a **partial** audit. The +`malloc_size_of` feature has **not** been audited. This feature does +not explicitly document its safety requirements. +See also https://chromium-review.googlesource.com/c/chromium/src/+/6275133/comment/ea0d7a93_98051a2e/ +and https://github.com/servo/malloc_size_of/issues/8. +This feature is banned in gnrt_config.toml. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.stable_deref_trait]] who = "Manish Goregaokar " criteria = "safe-to-deploy" @@ -892,44 +1073,11 @@ criteria = "safe-to-run" version = "1.2.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.unicode-ident]] -who = "Lukasz Anforowicz " -criteria = "safe-to-deploy" -version = "1.0.12" -notes = ''' -I grepped for \"crypt\", \"cipher\", \"fs\", \"net\" - there were no hits. - -All two functions from the public API of this crate use `unsafe` to avoid bound -checks for an array access. Cross-module analysis shows that the offsets can -be statically proven to be within array bounds. More details can be found in -the unsafe review CL at https://crrev.com/c/5350386. - -This crate has been added to Chromium in https://crrev.com/c/3891618. -''' -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - -[[audits.google.audits.unicode-ident]] -who = "Dustin J. Mitchell " -criteria = "safe-to-deploy" -delta = "1.0.12 -> 1.0.13" -notes = "Lots of table updates, and tables are assumed correct with unsafe `.get_unchecked()`, so ub-risk-2 is appropriate" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - [[audits.isrg.audits.block-buffer]] who = "David Cook " criteria = "safe-to-deploy" version = "0.9.0" -[[audits.isrg.audits.crunchy]] -who = "David Cook " -criteria = "safe-to-deploy" -version = "0.2.2" - -[[audits.isrg.audits.either]] -who = "David Cook " -criteria = "safe-to-deploy" -version = "1.6.1" - [[audits.isrg.audits.fiat-crypto]] who = "David Cook " criteria = "safe-to-deploy" @@ -1055,11 +1203,36 @@ who = "Ameer Ghani " criteria = "safe-to-deploy" version = "1.12.1" +[[audits.isrg.audits.sha3]] +who = "David Cook " +criteria = "safe-to-deploy" +version = "0.10.6" + +[[audits.isrg.audits.sha3]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "0.10.6 -> 0.10.7" + +[[audits.isrg.audits.sha3]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "0.10.7 -> 0.10.8" + [[audits.isrg.audits.subtle]] who = "David Cook " criteria = "safe-to-deploy" delta = "2.5.0 -> 2.6.1" +[[audits.isrg.audits.thiserror]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "1.0.40 -> 1.0.43" + +[[audits.isrg.audits.thiserror-impl]] +who = "Brandon Pitman " +criteria = "safe-to-deploy" +delta = "1.0.40 -> 1.0.43" + [[audits.isrg.audits.universal-hash]] who = "David Cook " criteria = "safe-to-deploy" @@ -1173,6 +1346,18 @@ criteria = "safe-to-deploy" delta = "0.5.12 -> 0.5.13" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.crossbeam-channel]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.5.13 -> 0.5.14" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.audits.crunchy]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +version = "0.2.3" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.crypto-common]] who = "Mike Hommey " criteria = "safe-to-deploy" @@ -1189,42 +1374,12 @@ comments on older versions of rustc. """ aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" -[[audits.mozilla.audits.either]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.6.1 -> 1.7.0" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.either]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.7.0 -> 1.8.0" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.either]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.8.0 -> 1.8.1" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - [[audits.mozilla.audits.errno]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.3.1 -> 0.3.3" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" -[[audits.mozilla.audits.fastrand]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "1.9.0 -> 2.0.0" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - -[[audits.mozilla.audits.fastrand]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "2.0.1 -> 2.1.0" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - [[audits.mozilla.audits.fnv]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -1244,12 +1399,29 @@ criteria = "safe-to-deploy" delta = "0.3.27 -> 0.3.28" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.getrandom]] +who = "Chris Martin " +criteria = "safe-to-deploy" +delta = "0.2.15 -> 0.3.1" +notes = """ +I've looked over all unsafe code, and it appears to be safe, fully initializing the rng buffers. +In addition, I've checked Linux, Windows, Mac, and Android more thoroughly against API +documentation. +""" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.hex]] who = "Simon Friedberger " criteria = "safe-to-deploy" version = "0.4.3" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.once_cell]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +delta = "1.20.2 -> 1.20.3" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.peeking_take_while]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -1283,6 +1455,12 @@ version = "1.1.0" notes = "Straightforward crate with no unsafe code, does what it says on the tin." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.semver]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "1.0.17 -> 1.0.25" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.shlex]] who = "Max Inden " criteria = "safe-to-deploy" @@ -1302,6 +1480,18 @@ version = "2.5.0" notes = "The goal is to provide some constant-time correctness for cryptographic implementations. The approach is reasonable, it is known to be insufficient but this is pointed out in the documentation." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.thiserror]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "1.0.43 -> 1.0.69" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.audits.thiserror-impl]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "1.0.43 -> 1.0.69" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.zeroize]] who = "Benjamin Beurdouche " criteria = "safe-to-deploy" @@ -1325,17 +1515,10 @@ delta = "0.10.3 -> 0.10.4" notes = "Adds panics to prevent a block size of zero from causing unsoundness." aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.crossbeam-deque]] +[[audits.zcash.audits.crossbeam-utils]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.8.3 -> 0.8.4" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - -[[audits.zcash.audits.crossbeam-deque]] -who = "Daira-Emma Hopwood " -criteria = "safe-to-deploy" -delta = "0.8.4 -> 0.8.5" -notes = "Changes to `unsafe` code look okay." +delta = "0.8.20 -> 0.8.21" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" [[audits.zcash.audits.errno]] @@ -1350,12 +1533,6 @@ criteria = "safe-to-deploy" delta = "0.3.8 -> 0.3.9" aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" -[[audits.zcash.audits.fastrand]] -who = "Jack Grigg " -criteria = "safe-to-deploy" -delta = "2.1.0 -> 2.1.1" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - [[audits.zcash.audits.oorandom]] who = "Jack Grigg " criteria = "safe-to-run" @@ -1390,52 +1567,6 @@ delta = "0.4.0 -> 0.4.1" notes = "Changes to `Command` usage are to add support for `RUSTC_WRAPPER`." aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.semver]] -who = "Jack Grigg " -criteria = "safe-to-deploy" -delta = "1.0.17 -> 1.0.18" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - -[[audits.zcash.audits.semver]] -who = "Jack Grigg " -criteria = "safe-to-deploy" -delta = "1.0.18 -> 1.0.19" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - -[[audits.zcash.audits.semver]] -who = "Jack Grigg " -criteria = "safe-to-deploy" -delta = "1.0.19 -> 1.0.20" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - -[[audits.zcash.audits.semver]] -who = "Daira-Emma Hopwood " -criteria = "safe-to-deploy" -delta = "1.0.20 -> 1.0.22" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - -[[audits.zcash.audits.semver]] -who = "Jack Grigg " -criteria = "safe-to-deploy" -delta = "1.0.22 -> 1.0.23" -notes = """ -`build.rs` change is to enable checking for expected `#[cfg]` names if compiling -with Rust 1.80 or later. -""" -aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" - -[[audits.zcash.audits.serde_json]] -who = "Jack Grigg " -criteria = "safe-to-deploy" -delta = "1.0.108 -> 1.0.110" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - -[[audits.zcash.audits.serde_json]] -who = "Daira-Emma Hopwood " -criteria = "safe-to-deploy" -delta = "1.0.110 -> 1.0.116" -aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" - [[audits.zcash.audits.universal-hash]] who = "Daira Hopwood " criteria = "safe-to-deploy" From de0022f092c379206d937ae75605991976384acf Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:54:50 +0100 Subject: [PATCH 077/174] test(rosenpass): Adapt test for protocol_version in config to work with and without feature "experiment_api" --- rosenpass/src/config.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 20110645..5dc925ce 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -791,11 +791,13 @@ mod test { let mut peer_v_03 = RosenpassPeer::default(); peer_v_03.protocol_version = ProtocolVersion::V03; rosenpass.peers.push(peer_v_03); - - rosenpass.api.listen_fd = vec![]; - rosenpass.api.listen_path = vec![]; - rosenpass.api.stream_fd = vec![]; - + #[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" @@ -808,6 +810,18 @@ mod test { protocol_version = "V02" public_key = "" + [[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 = "" From 1a8e220aa878d4bfd2d69f50f564513a2bb87c33 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 27 Mar 2025 12:58:16 +0100 Subject: [PATCH 078/174] ci(supply-chain): Add exceptions for advisories RUSTSEC-2024-0436 and RUSTSEC-2024-0370 to cargo-deny --- deny.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deny.toml b/deny.toml index 295d685d..e41c593d 100644 --- a/deny.toml +++ b/deny.toml @@ -25,6 +25,8 @@ feature-depth = 1 # 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", ] # 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. From 9dd00e04c1468568ae765e4d54973d6b3f81582d Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 31 Mar 2025 15:32:34 +0200 Subject: [PATCH 079/174] Use libcrux-blake2 with std This way we don't require the error_in_core feature of the Rust compiler --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2ad21d90..f737d9b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ 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 = { version = "0.0.2-beta.3" } +libcrux-blake2 = { git = "https://github.com/cryspen/libcrux.git", rev = "36d7d392e1c2"} hex-literal = { version = "0.4.1" } hex = { version = "0.4.3" } heck = { version = "0.5.0" } From 417df7aa7f956e0e59b9b2c14e5b16e29791f7c1 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 31 Mar 2025 15:38:10 +0200 Subject: [PATCH 080/174] update the lock file --- Cargo.lock | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0870f965..2c57bfa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1227,12 +1227,11 @@ dependencies = [ [[package]] name = "libcrux-blake2" -version = "0.0.2-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce649c4eb25a6cae0f59d758052ffe9285e4eb5f3de36e67a584c04845fa92a" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux.git?rev=36d7d392e1c2#36d7d392e1c2a6f90c9f5224d360d43895c4ac8e" dependencies = [ - "libcrux-hacl-rs", - "libcrux-macros", + "libcrux-hacl-rs 0.0.2", + "libcrux-macros 0.0.2", ] [[package]] @@ -1241,8 +1240,8 @@ version = "0.0.2-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04b666b490e714ff52d157da772a9a7faa3f394d619e79e64cdf1425599279ee" dependencies = [ - "libcrux-hacl-rs", - "libcrux-macros", + "libcrux-hacl-rs 0.0.2-beta.3", + "libcrux-macros 0.0.2-beta.3", "libcrux-poly1305", ] @@ -1262,7 +1261,15 @@ version = "0.0.2-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb04660b91e0be4185c3a9dd8e2c317c478cc0616f9cba2a4d523e033f852fa1" dependencies = [ - "libcrux-macros", + "libcrux-macros 0.0.2-beta.3", +] + +[[package]] +name = "libcrux-hacl-rs" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux.git?rev=36d7d392e1c2#36d7d392e1c2a6f90c9f5224d360d43895c4ac8e" +dependencies = [ + "libcrux-macros 0.0.2", ] [[package]] @@ -1284,6 +1291,15 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "libcrux-macros" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux.git?rev=36d7d392e1c2#36d7d392e1c2a6f90c9f5224d360d43895c4ac8e" +dependencies = [ + "quote", + "syn 2.0.98", +] + [[package]] name = "libcrux-ml-kem" version = "0.0.2-beta.3" @@ -1312,8 +1328,8 @@ version = "0.0.2-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82169ddf387e1912ebcde17979607e6a380dc758631757e7969652debe4dde9b" dependencies = [ - "libcrux-hacl-rs", - "libcrux-macros", + "libcrux-hacl-rs 0.0.2-beta.3", + "libcrux-macros 0.0.2-beta.3", ] [[package]] From d023108d3b84c3f4ec1628cae6f0dcaec5918f1e Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 31 Mar 2025 17:47:02 +0200 Subject: [PATCH 081/174] attempt to work around the importCargoLock bugs --- Cargo.lock | 154 ++++++++++++++++++++++++++++++----------------------- Cargo.toml | 2 +- 2 files changed, 88 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c57bfa3..ab585319 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "addr2line" @@ -359,9 +359,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -369,9 +369,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -381,9 +381,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.46" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98" +checksum = "1e3040c8291884ddf39445dc033c70abc2bc44a42f0a3a00571a0f483a83f0cd" dependencies = [ "clap", ] @@ -408,9 +408,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clap_mangen" -version = "0.2.26" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "724842fa9b144f9b89b3f3d371a89f3455eea660361d13a554f68f8ae5d6c13a" +checksum = "fbae9cbfdc5d4fa8711c09bd7b83f644cb48281ac35bf97af3e47b0675864bdf" dependencies = [ "clap", "roff", @@ -747,9 +747,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.14.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "embedded-io" @@ -1005,7 +1005,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd2dddf00d9120e8ff07ec0411cd48f6f419782b53c109d3984b6bf94345c822" dependencies = [ - "hax-lib-macros", + "hax-lib-macros 0.1.0", + "num-bigint", + "num-traits", +] + +[[package]] +name = "hax-lib" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61933dbb676f11311378720e1ee97a511813edb7044255381ba0d625cac6be7b" +dependencies = [ + "hax-lib-macros 0.2.0", "num-bigint", "num-traits", ] @@ -1016,7 +1027,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "013ec0c6e58481b11658007e794ee09be35b97ef02c92102b9a5c01afd43a82f" dependencies = [ - "hax-lib-macros-types", + "hax-lib-macros-types 0.1.0", + "paste", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "hax-lib-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a8d32921c3f535e973f72053d20bc8c7f74028911a269748440952157807" +dependencies = [ + "hax-lib-macros-types 0.2.0", "paste", "proc-macro-error", "proc-macro2", @@ -1037,6 +1062,19 @@ dependencies = [ "uuid", ] +[[package]] +name = "hax-lib-macros-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a22f64cb35f8363892df6285e7edbe96885cd660d85bfd6765c95886647b77" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "heapless" version = "0.7.17" @@ -1208,9 +1246,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libcrux" @@ -1227,21 +1265,21 @@ dependencies = [ [[package]] name = "libcrux-blake2" -version = "0.0.2" -source = "git+https://github.com/cryspen/libcrux.git?rev=36d7d392e1c2#36d7d392e1c2a6f90c9f5224d360d43895c4ac8e" +version = "0.0.3-pre" +source = "git+https://github.com/cryspen/libcrux.git?rev=10ce653e9476#10ce653e94761352b657b6cecdcc0c85675813df" dependencies = [ - "libcrux-hacl-rs 0.0.2", - "libcrux-macros 0.0.2", + "libcrux-hacl-rs", + "libcrux-macros", ] [[package]] name = "libcrux-chacha20poly1305" -version = "0.0.2-beta.3" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04b666b490e714ff52d157da772a9a7faa3f394d619e79e64cdf1425599279ee" +checksum = "78d522fb626847390ea4b776c7eca179ecec363c6c4730b61b0c0feb797b8d92" dependencies = [ - "libcrux-hacl-rs 0.0.2-beta.3", - "libcrux-macros 0.0.2-beta.3", + "libcrux-hacl-rs", + "libcrux-macros", "libcrux-poly1305", ] @@ -1255,46 +1293,29 @@ dependencies = [ "libcrux-platform", ] -[[package]] -name = "libcrux-hacl-rs" -version = "0.0.2-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb04660b91e0be4185c3a9dd8e2c317c478cc0616f9cba2a4d523e033f852fa1" -dependencies = [ - "libcrux-macros 0.0.2-beta.3", -] - [[package]] name = "libcrux-hacl-rs" version = "0.0.2" -source = "git+https://github.com/cryspen/libcrux.git?rev=36d7d392e1c2#36d7d392e1c2a6f90c9f5224d360d43895c4ac8e" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bba0885296a72555a5d77056c39cc9b04edd9ab1afa3025ef3dbd96220705c" dependencies = [ - "libcrux-macros 0.0.2", + "libcrux-macros", ] [[package]] name = "libcrux-intrinsics" -version = "0.0.2-beta.3" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256e25c0b16c98b715f7cc6b3ed268723a1158f78a236b1625ffe4a941cab41" +checksum = "f4f764ef781467a75b92f4df575911f1cdcf77a7beb316d8054a233fed53a7ab" dependencies = [ - "hax-lib", -] - -[[package]] -name = "libcrux-macros" -version = "0.0.2-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5848af0bd1aca5be28708945867adb2b841824a0a6f48779407a41287d5ca231" -dependencies = [ - "quote", - "syn 2.0.98", + "hax-lib 0.2.0", ] [[package]] name = "libcrux-macros" version = "0.0.2" -source = "git+https://github.com/cryspen/libcrux.git?rev=36d7d392e1c2#36d7d392e1c2a6f90c9f5224d360d43895c4ac8e" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3021bc24fb679408d4d7175e21cf808f49816c599733ebf4a97e5bd39c3ce7c0" dependencies = [ "quote", "syn 2.0.98", @@ -1306,7 +1327,7 @@ version = "0.0.2-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89cbf9aad4ad38d53cfdd7ffe9041cc4cf516c8c5a6f9c1a7bb8136a82b7b6d6" dependencies = [ - "hax-lib", + "hax-lib 0.1.0", "libcrux-intrinsics", "libcrux-platform", "libcrux-sha3", @@ -1324,12 +1345,12 @@ dependencies = [ [[package]] name = "libcrux-poly1305" -version = "0.0.2-beta.3" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82169ddf387e1912ebcde17979607e6a380dc758631757e7969652debe4dde9b" +checksum = "80143d78ae14ab51ceb2c8a9514fb60af6645d42a9c951bc511792c19c974fca" dependencies = [ - "libcrux-hacl-rs 0.0.2-beta.3", - "libcrux-macros 0.0.2-beta.3", + "libcrux-hacl-rs", + "libcrux-macros", ] [[package]] @@ -1338,7 +1359,7 @@ version = "0.0.2-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6afd802f5c7862be77f1f320df6c0fea0f09a78ca94e79df26625c60d2d96de7" dependencies = [ - "hax-lib", + "hax-lib 0.1.0", "libcrux-intrinsics", "libcrux-platform", ] @@ -1881,8 +1902,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.2", - "zerocopy 0.8.20", + "rand_core 0.9.3", + "zerocopy 0.8.24", ] [[package]] @@ -1902,7 +1923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.2", + "rand_core 0.9.3", ] [[package]] @@ -1916,12 +1937,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.20", ] [[package]] @@ -2690,9 +2710,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.15.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" +checksum = "93d59ca99a559661b96bf898d8fce28ed87935fd2bea9f05983c1464dd6c71b1" dependencies = [ "getrandom 0.3.1", ] @@ -3173,11 +3193,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.20" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ - "zerocopy-derive 0.8.20", + "zerocopy-derive 0.8.24", ] [[package]] @@ -3193,9 +3213,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.20" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index f737d9b4..bb8274d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ 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 = "36d7d392e1c2"} +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" } From 80885d81d779c1304f587dcf5472bb9a83694697 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Thu, 3 Apr 2025 15:17:14 +0200 Subject: [PATCH 082/174] fix: Missing nix hashes for libcrux_blake --- pkgs/rosenpass.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/rosenpass.nix b/pkgs/rosenpass.nix index ff4e0ec3..37b467d0 100644 --- a/pkgs/rosenpass.nix +++ b/pkgs/rosenpass.nix @@ -57,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="; }; }; From c65abe7bd9435f59f5345624edacb7fca365b931 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 3 Apr 2025 16:54:30 +0200 Subject: [PATCH 083/174] fix dos test: hardcode use of shake in seal_cookie --- rosenpass/src/protocol/protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 36f16d43..1a4e7227 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3236,7 +3236,7 @@ where /// This is called inside [Self::seal] and does not need to be called again separately. pub fn seal_cookie(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> { if let Some(cookie_key) = &peer.cv().get(srv) { - let cookie = hash_domains::cookie(peer.get(srv).protocol_version.keyed_hash())? + let cookie = hash_domains::cookie(KeyedHash::keyed_shake256())? .mix(cookie_key.value.secret())? .mix(&self.as_bytes()[span_of!(Self, msg_type..cookie)])?; self.cookie From 954162b61f2ab357cab51e4dab4e6b49e2520144 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Thu, 3 Apr 2025 17:04:00 +0200 Subject: [PATCH 084/174] cleanup --- cipher-traits/src/primitives/keyed_hash.rs | 2 +- ciphers/src/hash_domain.rs | 23 +----- ciphers/src/lib.rs | 3 + ciphers/src/subtle/keyed_hash.rs | 1 + rosenpass/src/bin/gen-ipc-msg-types.rs | 3 +- rosenpass/src/protocol/protocol.rs | 84 ++++++++-------------- 6 files changed, 38 insertions(+), 78 deletions(-) diff --git a/cipher-traits/src/primitives/keyed_hash.rs b/cipher-traits/src/primitives/keyed_hash.rs index da108806..93ecaf17 100644 --- a/cipher-traits/src/primitives/keyed_hash.rs +++ b/cipher-traits/src/primitives/keyed_hash.rs @@ -13,7 +13,7 @@ pub trait KeyedHash { ) -> Result<(), Self::Error>; } -/// Models a keyed hash function using using a method (i.e. with a `&self` receiver). +/// 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. diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 522806a5..4b084bca 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -117,8 +117,6 @@ impl HashDomainNamespace { } impl SecretHashDomain { - // XXX: Why is the old hash still used unconditionally? - // /// 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]. @@ -133,7 +131,7 @@ impl SecretHashDomain { hash_choice .keyed_hash_to(k.try_into()?, d) .to(new_secret_key.secret_mut())?; - let mut r = SecretHashDomain(new_secret_key, hash_choice); + let r = SecretHashDomain(new_secret_key, hash_choice); Ok(r) } @@ -177,23 +175,6 @@ impl SecretHashDomain { self.0 } - /* XXX: This code was calling the specific hmac-blake2b code as well as the new KeyedHash enum - * (f.k.a. EitherHash). I was confused by the way the code used the local variables, because it - * didn't match the code. I made the code match the documentation, but I'm not sure that is - * correct. Either way, it doesn't look like this is used anywhere. Maybe just remove it? - * - * /// Evaluate [hash::hash] with this [SecretHashDomain]'s data as the `key` and - * /// `dst` as the `data` and stores the result as the new data for this [SecretHashDomain]. - * pub fn into_secret_slice(mut self, v: &[u8; KEY_LEN], dst: &[u8; KEY_LEN]) -> Result<()> { - * let SecretHashDomain(secret, hash_choice) = &self; - * - * let mut new_secret = Secret::zero(); - * hash_choice.keyed_hash_to(secret.secret(), dst).to(new_secret.secret_mut())?; - * self.0 = new_secret; - * - * Ok(()) - * } - */ } impl SecretHashDomainNamespace { @@ -223,7 +204,7 @@ impl SecretHashDomainNamespace { self.0 } - pub fn shake_or_blake(&self) -> &KeyedHash { + pub fn keyed_hash(&self) -> &KeyedHash { &self.1 } } diff --git a/ciphers/src/lib.rs b/ciphers/src/lib.rs index aca6dbf4..392f0f4e 100644 --- a/ciphers/src/lib.rs +++ b/ciphers/src/lib.rs @@ -1,9 +1,12 @@ +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 == hash_domain::KEY_LEN); /// Keyed hashing diff --git a/ciphers/src/subtle/keyed_hash.rs b/ciphers/src/subtle/keyed_hash.rs index c77b8e12..1a223418 100644 --- a/ciphers/src/subtle/keyed_hash.rs +++ b/ciphers/src/subtle/keyed_hash.rs @@ -31,6 +31,7 @@ impl KeyedHash { Self::KeyedShake256(Default::default()) } + /// Creates an [`KeyedHash`] backed by Blake2B. pub fn incorrect_hmac_blake2b() -> Self { Self::IncorrectHmacBlake2b(Default::default()) } diff --git a/rosenpass/src/bin/gen-ipc-msg-types.rs b/rosenpass/src/bin/gen-ipc-msg-types.rs index 4d6bc1e6..64eb27bd 100644 --- a/rosenpass/src/bin/gen-ipc-msg-types.rs +++ b/rosenpass/src/bin/gen-ipc-msg-types.rs @@ -2,7 +2,6 @@ use anyhow::{Context, Result}; use heck::ToShoutySnakeCase; use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; -use rosenpass_ciphers::subtle::keyed_shake256::SHAKE256Core; use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN}; /// Recursively calculate a concrete hash value for an API message type @@ -32,7 +31,7 @@ fn print_literal(path: &[&str], shake_or_blake: KeyedHash) -> Result<()> { .map(|chunk| chunk.iter().collect::()) .collect::>(); println!("const {var_name} : RawMsgType = RawMsgType::from_le_bytes(hex!(\"{} {} {} {} {} {} {} {}\"));", - c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); Ok(()) } diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 1a4e7227..6352bf37 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -362,6 +362,7 @@ pub enum IndexKey { KnownInitConfResponse(KnownResponseHash), } +/// Specifies the protocol version used by a peer. #[derive(Debug, Clone)] pub enum ProtocolVersion { V02, @@ -369,6 +370,7 @@ pub enum ProtocolVersion { } impl ProtocolVersion { + /// Returns the [KeyedHash] used by a protocol version. pub fn keyed_hash(&self) -> KeyedHash { match self { ProtocolVersion::V02 => KeyedHash::incorrect_hmac_blake2b(), @@ -475,7 +477,7 @@ pub struct Peer { /// on the network without having to account for it in the cryptographic code itself. pub known_init_conf_response: Option, - /// TODO: Documentation + /// The protocol version used by with this peer. pub protocol_version: ProtocolVersion, } @@ -718,10 +720,9 @@ impl KnownResponseHasher { /// Panics in case of a problem with this underlying hash function pub fn hash(&self, msg: &Envelope) -> KnownResponseHash { let data = &msg.as_bytes()[span_of!(Envelope, msg_type..cookie)]; - // This function is only used internally and results are not propagated // to outside the peer. Thus, it uses SHAKE256 exclusively. - let mut hash = [0; 32]; + let mut hash = [0u8; 32]; KeyedHash::keyed_shake256() .keyed_hash(self.key.secret(), data, &mut hash) .unwrap(); @@ -784,7 +785,7 @@ pub enum Lifecycle { /// /// If a secret, it must be zeroized and disposed. Dead, - /// Soon to be dead. Do not use any more. + /// Soon to be dead. Do not use anymore. /// /// If a secret, it might be used for decoding (decrypting) /// data, but must not be used for encryption of cryptographic values. @@ -1418,9 +1419,9 @@ impl CryptoServer { /// Calculate the peer ID of this CryptoServer #[rustfmt::skip] - pub fn pidm(&self, shake_or_blake: KeyedHash) -> Result { + pub fn pidm(&self, keyed_hash: KeyedHash) -> Result { Ok(Public::new( - hash_domains::peerid(shake_or_blake)? + hash_domains::peerid(keyed_hash)? .mix(self.spkm.deref())? .into_value())) } @@ -1704,13 +1705,13 @@ impl Session { /// assert_eq!(s.created_at, 0.0); /// assert_eq!(s.handshake_role, HandshakeRole::Initiator); /// ``` - pub fn zero(shake_or_blake: KeyedHash) -> Self { + pub fn zero(keyed_hash: KeyedHash) -> Self { Self { created_at: 0.0, sidm: SessionId::zero(), sidt: SessionId::zero(), handshake_role: HandshakeRole::Initiator, - ck: SecretHashDomain::zero(shake_or_blake).dup(), + ck: SecretHashDomain::zero(keyed_hash).dup(), txkm: SymKey::zero(), txkt: SymKey::zero(), txnm: 0, @@ -2169,11 +2170,9 @@ impl CryptoServer { } } - log::debug!("Checking all cookie secrets for match"); for cookie_secret in self.active_or_retired_cookie_secrets() { if let Some(cookie_secret) = cookie_secret { let cookie_secret = cookie_secret.get(self).value.secret(); - log::debug!("Checking cookie secret {:?}", cookie_secret); let mut cookie_value = [0u8; 16]; cookie_value.copy_from_slice( &hash_domains::cookie_value(KeyedHash::keyed_shake256())? @@ -2181,14 +2180,9 @@ impl CryptoServer { .mix(host_identification.encode())? .into_value()[..16], ); - log::debug!("Computed cookie_value: {:?}", cookie_value); // Most recently filled value is active cookie value if active_cookie_value.is_none() { - log::debug!( - "Active cookie_value was None, setting to {:?}", - cookie_value - ); active_cookie_value = Some(cookie_value); } @@ -2196,24 +2190,17 @@ impl CryptoServer { let msg_in = Ref::<&[u8], Envelope>::new(rx_buf) .ok_or(RosenpassError::BufferSizeMismatch)?; - log::debug!( - "Mixing with cookie from envelope: {:?}", - &msg_in.as_bytes()[span_of!(Envelope, msg_type..cookie)] - ); expected.copy_from_slice( &hash_domains::cookie(KeyedHash::keyed_shake256())? .mix(&cookie_value)? .mix(&msg_in.as_bytes()[span_of!(Envelope, msg_type..cookie)])? .into_value()[..16], ); - log::debug!("Computed expected cookie: {:?}", expected); rx_cookie.copy_from_slice(&msg_in.cookie); rx_mac.copy_from_slice(&msg_in.mac); rx_sid.copy_from_slice(&msg_in.payload.sidi); - log::debug!("Received cookie is: {:?}", rx_cookie); - //If valid cookie is found, process message if constant_time::memcmp(&rx_cookie, &expected) { log::debug!( @@ -2223,11 +2210,8 @@ impl CryptoServer { ); let result = self.handle_msg(rx_buf, tx_buf)?; return Ok(result); - } else { - log::debug!("Cookie did not match, continuing"); } } else { - log::debug!("Cookie secret was None for some reason"); break; } } @@ -2470,7 +2454,8 @@ impl CryptoServer { }) } - /// TODO documentation + /// Given a peer and a [KeyedHash] `peer_hash_choice`, this function checks whether the + /// `peer_hash_choice` matches the hash function that is expected for the peer. fn verify_hash_choice_match(&self, peer: PeerPtr, peer_hash_choice: KeyedHash) -> Result<()> { match peer.get(self).protocol_version.keyed_hash() { KeyedHash::KeyedShake256(_) => match peer_hash_choice { @@ -3226,7 +3211,6 @@ where .mix(peer.get(srv).spkt.deref())? .mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?; self.mac.copy_from_slice(mac.into_value()[..16].as_ref()); - log::debug!("Setting MAC for Envelope: {:?}", self.mac); self.seal_cookie(peer, srv)?; Ok(()) } @@ -3264,11 +3248,11 @@ where impl InitiatorHandshake { /// Zero initialization of an InitiatorHandshake, with up to date timestamp - pub fn zero_with_timestamp(srv: &CryptoServer, shake_or_blake: KeyedHash) -> Self { + pub fn zero_with_timestamp(srv: &CryptoServer, keyed_hash: KeyedHash) -> Self { InitiatorHandshake { created_at: srv.timebase.now(), next: HandshakeStateMachine::RespHello, - core: HandshakeState::zero(shake_or_blake), + core: HandshakeState::zero(keyed_hash), eski: ESk::zero(), epki: EPk::zero(), tx_at: 0.0, @@ -3283,23 +3267,23 @@ impl InitiatorHandshake { impl HandshakeState { /// Zero initialization of an HandshakeState - pub fn zero(shake_or_blake: KeyedHash) -> Self { + pub fn zero(keyed_hash: KeyedHash) -> Self { Self { sidi: SessionId::zero(), sidr: SessionId::zero(), - ck: SecretHashDomain::zero(shake_or_blake).dup(), + ck: SecretHashDomain::zero(keyed_hash).dup(), } } /// Securely erase the chaining key pub fn erase(&mut self) { - self.ck = SecretHashDomain::zero(self.ck.shake_or_blake().clone()).dup(); + self.ck = SecretHashDomain::zero(self.ck.keyed_hash().clone()).dup(); } /// Initialize the handshake state with the responder public key and the protocol domain /// separator pub fn init(&mut self, spkr: &[u8]) -> Result<&mut Self> { - self.ck = hash_domains::ckinit(self.ck.shake_or_blake().clone())? + self.ck = hash_domains::ckinit(self.ck.keyed_hash().clone())? .turn_secret() .mix(spkr)? .dup(); @@ -3311,7 +3295,7 @@ impl HandshakeState { pub fn mix(&mut self, a: &[u8]) -> Result<&mut Self> { self.ck = self .ck - .mix(&hash_domains::mix(self.ck.shake_or_blake().clone())?)? + .mix(&hash_domains::mix(self.ck.keyed_hash().clone())?)? .mix(a)? .dup(); Ok(self) @@ -3322,9 +3306,10 @@ impl HandshakeState { pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> { let k = self .ck - .mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)? + .mix(&hash_domains::hs_enc(self.ck.keyed_hash().clone())?)? .into_secret(); - Aead.encrypt(ct, k.secret(), &[0u8; Aead::NONCE_LEN], &[], pt)?; + ensure!(Aead::NONCE_LEN == 12); + Aead.encrypt(ct, k.secret(), &[0u8; 12], &[], pt)?; self.mix(ct) } @@ -3335,9 +3320,10 @@ impl HandshakeState { pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> { let k = self .ck - .mix(&hash_domains::hs_enc(self.ck.shake_or_blake().clone())?)? + .mix(&hash_domains::hs_enc(self.ck.keyed_hash().clone())?)? .into_secret(); - Aead.decrypt(pt, k.secret(), &[0u8; Aead::NONCE_LEN], &[], ct)?; + ensure!(Aead::NONCE_LEN == 12); + Aead.decrypt(pt, k.secret(), &[0u8; 12], &[], ct)?; self.mix(ct) } @@ -3599,14 +3585,13 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a /// [RespHello] message on the responder side. - /// TODO: Document Hash Functon usage pub fn handle_init_hello( &mut self, ih: &InitHello, rh: &mut RespHello, - shake_or_blake: KeyedHash, + keyed_hash: KeyedHash, ) -> Result { - let mut core = HandshakeState::zero(shake_or_blake); + let mut core = HandshakeState::zero(keyed_hash); core.sidi = SessionId::from_slice(&ih.sidi); @@ -3759,12 +3744,11 @@ impl CryptoServer { /// /// This concludes the handshake on the cryptographic level; the [EmptyData] message is just /// an acknowledgement message telling the initiator to stop performing retransmissions. - /// TODO: documentation pub fn handle_init_conf( &mut self, ic: &InitConf, rc: &mut EmptyData, - shake_or_blake: KeyedHash, + keyed_hash: KeyedHash, ) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 @@ -3773,7 +3757,7 @@ impl CryptoServer { &ic.biscuit, SessionId::from_slice(&ic.sidi), SessionId::from_slice(&ic.sidr), - shake_or_blake, + keyed_hash, )?; // ICR2 @@ -3919,7 +3903,6 @@ impl CryptoServer { .map(|v| PeerPtr(v.0)) }); if let Some(peer) = peer_ptr { - log::debug!("Found peer for cookie reply: {:?}", peer); // Get last transmitted handshake message if let Some(ih) = &peer.get(self).handshake { let mut mac = [0u8; MAC_SIZE]; @@ -3948,17 +3931,11 @@ impl CryptoServer { pidr = cr.inner.sid ), }?; - log::debug!( - "Found last transmitted handshake message with mac: {:?}", - mac - ); let spkt = peer.get(self).spkt.deref(); let cookie_key = hash_domains::cookie_key(KeyedHash::keyed_shake256())? .mix(spkt)? .into_value(); - - log::debug!("Computed cookie key: {:?}", cookie_key); let cookie_value = peer.cv().update_mut(self).unwrap(); XAead.decrypt_with_nonce_in_ctxt( @@ -3967,7 +3944,6 @@ impl CryptoServer { &mac, &cr.inner.cookie_encrypted, )?; - log::debug!("Computed cookie value: {:?}", cookie_value); // Immediately retransmit on receiving a cookie reply message peer.hs().register_immediate_retransmission(self)?; @@ -4009,7 +3985,7 @@ pub mod testutils { pub srv: CryptoServer, } - /// TODO: Document that the protocol verson s only used for creating the peer for testing + /// TODO: Document that the protocol version is only used for creating the peer for testing impl ServerForTesting { pub fn new(protocol_version: ProtocolVersion) -> anyhow::Result { let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); @@ -4414,7 +4390,7 @@ mod test { let retx_init_hello_len = loop { match a.poll().unwrap() { PollResult::SendRetransmission(peer) => { - break (a.retransmit_handshake(peer, &mut *a_to_b_buf).unwrap()); + break a.retransmit_handshake(peer, &mut *a_to_b_buf).unwrap(); } PollResult::Sleep(time) => { sleep(Duration::from_secs_f64(time)); From 8f519b042dac005d416154057328526bf83c72b5 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:09:25 +0200 Subject: [PATCH 085/174] dev(rosenpass): adapt protocol identifier for protocol version v 0.2 to be backwards compatible with current main branch --- rosenpass/src/hash_domains.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index 5d23e637..3828cf9b 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -115,7 +115,7 @@ pub fn protocol(hash_choice: KeyedHash) -> Result { 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 Blake2b".as_bytes()), + .mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes()), } } From db6530ef77dd969c61e277f22dde538b4aa81306 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:14:18 +0200 Subject: [PATCH 086/174] doc(rosenpass): properly document protocol function for hash domains --- rosenpass/src/hash_domains.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index 3828cf9b..a1ac2b22 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -100,9 +100,8 @@ macro_rules! hash_domain { /// 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. See -/// -/// TODO: Update documentation +/// This is generally used to create further hash-domains for specific purposes. Depending on +/// the used hash function, the protocol string is different. /// /// # Examples /// @@ -110,7 +109,7 @@ macro_rules! hash_domain { /// /// See the [module](self) documentation on how to use the hash domains in general pub fn protocol(hash_choice: KeyedHash) -> Result { - // TODO: Update this string that is mixed in? + // 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()), From f7fb09bc44683883b01c4d7628a9667bd154d2e2 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:24:08 +0200 Subject: [PATCH 087/174] ci(supply-chain): update exemptions for cargo-vet --- supply-chain/config.toml | 44 +++++++++++++++++++++++---------------- supply-chain/imports.lock | 14 ------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 4674c455..7236af2e 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -122,15 +122,15 @@ version = "1.8.1" criteria = "safe-to-deploy" [[exemptions.clap]] -version = "4.5.31" +version = "4.5.30" criteria = "safe-to-deploy" [[exemptions.clap_builder]] -version = "4.5.31" +version = "4.5.30" criteria = "safe-to-deploy" [[exemptions.clap_complete]] -version = "4.5.46" +version = "4.5.45" criteria = "safe-to-deploy" [[exemptions.clap_derive]] @@ -142,7 +142,7 @@ version = "0.7.4" criteria = "safe-to-deploy" [[exemptions.clap_mangen]] -version = "0.2.26" +version = "0.2.24" criteria = "safe-to-deploy" [[exemptions.cmake]] @@ -301,14 +301,26 @@ criteria = "safe-to-deploy" version = "0.1.0" criteria = "safe-to-deploy" +[[exemptions.hax-lib]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.hax-lib-macros]] version = "0.1.0" criteria = "safe-to-deploy" +[[exemptions.hax-lib-macros]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.hax-lib-macros-types]] version = "0.1.0" criteria = "safe-to-deploy" +[[exemptions.hax-lib-macros-types]] +version = "0.2.0" +criteria = "safe-to-deploy" + [[exemptions.heapless]] version = "0.7.17" criteria = "safe-to-deploy" @@ -362,19 +374,15 @@ version = "1.3.0" criteria = "safe-to-deploy" [[exemptions.libc]] -version = "0.2.170" +version = "0.2.169" criteria = "safe-to-deploy" [[exemptions.libcrux]] version = "0.0.2-pre.2" criteria = "safe-to-deploy" -[[exemptions.libcrux-blake2]] -version = "0.0.2-beta.3" -criteria = "safe-to-deploy" - [[exemptions.libcrux-chacha20poly1305]] -version = "0.0.2-beta.3" +version = "0.0.2" criteria = "safe-to-deploy" [[exemptions.libcrux-hacl]] @@ -382,15 +390,15 @@ version = "0.0.2-pre.2" criteria = "safe-to-deploy" [[exemptions.libcrux-hacl-rs]] -version = "0.0.2-beta.3" +version = "0.0.2" criteria = "safe-to-deploy" [[exemptions.libcrux-intrinsics]] -version = "0.0.2-beta.3" +version = "0.0.2" criteria = "safe-to-deploy" [[exemptions.libcrux-macros]] -version = "0.0.2-beta.3" +version = "0.0.2" criteria = "safe-to-deploy" [[exemptions.libcrux-ml-kem]] @@ -402,7 +410,7 @@ version = "0.0.2-pre.2" criteria = "safe-to-deploy" [[exemptions.libcrux-poly1305]] -version = "0.0.2-beta.3" +version = "0.0.2" criteria = "safe-to-deploy" [[exemptions.libcrux-sha3]] @@ -582,7 +590,7 @@ version = "0.9.0" criteria = "safe-to-deploy" [[exemptions.rand_core]] -version = "0.9.2" +version = "0.9.3" criteria = "safe-to-deploy" [[exemptions.redox_syscall]] @@ -734,7 +742,7 @@ version = "0.2.2" criteria = "safe-to-deploy" [[exemptions.uuid]] -version = "1.15.1" +version = "1.14.0" criteria = "safe-to-deploy" [[exemptions.version_check]] @@ -954,7 +962,7 @@ version = "0.7.35" criteria = "safe-to-deploy" [[exemptions.zerocopy]] -version = "0.8.20" +version = "0.8.24" criteria = "safe-to-deploy" [[exemptions.zerocopy-derive]] @@ -962,5 +970,5 @@ version = "0.7.35" criteria = "safe-to-deploy" [[exemptions.zerocopy-derive]] -version = "0.8.20" +version = "0.8.24" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index ec2ceed7..111b9c11 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -392,20 +392,6 @@ version = "1.13.0" notes = "Unsafe code pertaining to wrapping Pin APIs. Mostly passes invariants down." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" -[[audits.google.audits.either]] -who = "Daniel Cheng " -criteria = "safe-to-deploy" -delta = "1.13.0 -> 1.14.0" -notes = """ -Inheriting ub-risk-1 from the baseline review of 1.13.0. While the delta has some diffs in unsafe code, they are either: -- migrating code to use helper macros -- migrating match patterns to take advantage of default bindings mode from RFC 2005 -Either way, the result is code that does exactly the same thing and does not change the risk of UB. - -See https://crrev.com/c/6323164 for more audit details. -""" -aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" - [[audits.google.audits.equivalent]] who = "George Burgess IV " criteria = "safe-to-deploy" From b47d3a9deb5b3727a27f0306c6e1e8e470d00683 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:31:32 +0200 Subject: [PATCH 088/174] style(ciphers): fix formatting --- ciphers/src/hash_domain.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 4b084bca..4a56cfbc 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -174,7 +174,6 @@ impl SecretHashDomain { pub fn into_secret(self) -> Secret { self.0 } - } impl SecretHashDomainNamespace { From e8fb7206fc6c635f6d6f87ed8c99eaccf5659016 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 16:29:28 +0200 Subject: [PATCH 089/174] fix: Wrong host identification in poll_example --- rosenpass/tests/poll_example.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index fcb4ffd3..d3c0e30f 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -510,8 +510,11 @@ impl ServerPtr { // Let the crypto server handle the message now let mut tx_buf = MsgBuf::zero(); let handle_msg_result = if self.get(sim).under_load { - self.srv_mut(sim) - .handle_msg_under_load(rx_msg.borrow(), tx_buf.borrow_mut(), &self) + self.srv_mut(sim).handle_msg_under_load( + rx_msg.borrow(), + tx_buf.borrow_mut(), + &self.other(), + ) } else { self.srv_mut(sim) .handle_msg(rx_msg.borrow(), tx_buf.borrow_mut()) From d558bdb633a2542628e3877a62561ec818e76a25 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 16:35:45 +0200 Subject: [PATCH 090/174] fix: Add a feature flag for the cookie reply mechanism This is a stopgap measure against #539 --- rosenpass/Cargo.toml | 1 + rosenpass/src/protocol/protocol.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index e7a7fa45..62f21496 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -86,6 +86,7 @@ rustix = { workspace = true } [features] #default = ["experiment_libcrux_all"] +experiment_cookie_dos_mitigation = [] experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"] experiment_libcrux_all = ["rosenpass-ciphers/experiment_libcrux_all"] experiment_libcrux_blake2 = ["rosenpass-ciphers/experiment_libcrux_blake2"] diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 6352bf37..c23a9c84 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -2138,6 +2138,18 @@ impl CryptoServer { /// /// - test::cookie_reply_mechanism_responder_under_load /// - test::cookie_reply_mechanism_initiator_bails_on_message_under_load + #[cfg(not(feature = "experiment_cookie_dos_mitigation"))] + #[inline] + pub fn handle_msg_under_load( + &mut self, + rx_buf: &[u8], + tx_buf: &mut [u8], + host_identification: &H, + ) -> Result { + self.handle_msg(rx_buf, tx_buf) + } + + #[cfg(feature = "experiment_cookie_dos_mitigation")] pub fn handle_msg_under_load( &mut self, rx_buf: &[u8], @@ -4313,16 +4325,19 @@ mod test { #[test] #[serial] + #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_responder_under_load_v02() { cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) } #[test] #[serial] + #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_responder_under_load_v03() { cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) } + #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) { setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); @@ -4420,16 +4435,19 @@ mod test { #[test] #[serial] + #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v02() { cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) } #[test] #[serial] + #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v03() { cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) } + #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_initiator_bails_on_message_under_load( protocol_version: ProtocolVersion, ) { From e81612d2e33dcf7ad3bf9b9062ce1138774826b4 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 16:45:36 +0200 Subject: [PATCH 091/174] fix: Incorrect timeouts for poll_example - Raised first timeout under load to fourty seconds - Corrected discrepancies between debug prints and numeric checks --- rosenpass/tests/poll_example.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index d3c0e30f..9fb8bbfc 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -51,9 +51,9 @@ fn test_successful_exchange_with_poll(protocol_version: ProtocolVersion) -> anyh ); #[cfg(not(coverage))] assert!( - _completions[0].0 < 20.0, + _completions[0].0 < 60.0, "\ - First key exchange should happen in under twenty seconds!\n\ + First key exchange should happen in under 60 seconds!\n\ Transcript: {transcript:?}\n\ Completions: {_completions:?}\ " @@ -119,7 +119,7 @@ fn test_successful_exchange_under_packet_loss( event: ServerEvent::Transmit(_, _), } = ev { - // Drop every fifth package + // Drop every tenth package if pkg_counter % 10 == 0 { source.drop_outgoing_packet(&mut sim); } @@ -145,9 +145,9 @@ fn test_successful_exchange_under_packet_loss( ); #[cfg(not(coverage))] assert!( - _completions[0].0 < 10.0, + _completions[0].0 < 60.0, "\ - First key exchange should happen in under twenty seconds!\n\ + First key exchange should happen in under 60 seconds!\n\ Transcript: {transcript:?}\n\ Completions: {_completions:?}\ " From d453002230e98f55b34651b6e1f5acb53c2575ad Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 16:49:18 +0200 Subject: [PATCH 092/174] fix: Security update for tokio --- Cargo.lock | 6 +++--- supply-chain/config.toml | 22 +++++++++------------- supply-chain/imports.lock | 12 ++++++++++++ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab585319..aeeec36a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1391,7 +1391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -2610,9 +2610,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 7236af2e..e344e32e 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -341,10 +341,6 @@ criteria = "safe-to-deploy" version = "2.1.0" criteria = "safe-to-deploy" -[[exemptions.inout]] -version = "0.1.4" -criteria = "safe-to-deploy" - [[exemptions.ipc-channel]] version = "0.18.3" criteria = "safe-to-run" @@ -706,7 +702,7 @@ version = "2.0.11" criteria = "safe-to-deploy" [[exemptions.tokio]] -version = "1.43.0" +version = "1.44.2" criteria = "safe-to-deploy" [[exemptions.tokio-macros]] @@ -851,7 +847,7 @@ criteria = "safe-to-deploy" [[exemptions.windows-targets]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows-targets]] version = "0.52.6" @@ -863,7 +859,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_aarch64_gnullvm]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_aarch64_gnullvm]] version = "0.52.6" @@ -875,7 +871,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_aarch64_msvc]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_aarch64_msvc]] version = "0.52.6" @@ -887,7 +883,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_i686_gnu]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_i686_gnu]] version = "0.52.6" @@ -903,7 +899,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_i686_msvc]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_i686_msvc]] version = "0.52.6" @@ -915,7 +911,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnu]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnu]] version = "0.52.6" @@ -927,7 +923,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnullvm]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnullvm]] version = "0.52.6" @@ -939,7 +935,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_x86_64_msvc]] version = "0.48.5" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.windows_x86_64_msvc]] version = "0.52.6" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 111b9c11..44053021 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -196,6 +196,12 @@ criteria = "safe-to-deploy" delta = "0.4.1 -> 0.5.0" notes = "Minor changes for a `no_std` upgrade but otherwise everything looks as expected." +[[audits.bytecode-alliance.audits.inout]] +who = "Andrew Brown " +criteria = "safe-to-deploy" +version = "0.1.3" +notes = "A part of RustCrypto/utils, this crate is designed to handle unsafe buffers and carefully documents the safety concerns throughout. Older versions of this tally up to ~130k daily downloads." + [[audits.bytecode-alliance.audits.itoa]] who = "Dan Gohman " criteria = "safe-to-deploy" @@ -1519,6 +1525,12 @@ criteria = "safe-to-deploy" delta = "0.3.8 -> 0.3.9" aggregated-from = "https://raw.githubusercontent.com/zcash/librustzcash/main/supply-chain/audits.toml" +[[audits.zcash.audits.inout]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.1.3 -> 0.1.4" +aggregated-from = "https://raw.githubusercontent.com/zcash/wallet/main/supply-chain/audits.toml" + [[audits.zcash.audits.oorandom]] who = "Jack Grigg " criteria = "safe-to-run" From c84bbed3bd22250153b16677711c2ccb636393a7 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 18:02:12 +0200 Subject: [PATCH 093/174] fix: Increase time outs for integration tests --- rosenpass/tests/integration_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/tests/integration_test.rs b/rosenpass/tests/integration_test.rs index b45dbd6a..d5b5f22a 100644 --- a/rosenpass/tests/integration_test.rs +++ b/rosenpass/tests/integration_test.rs @@ -129,7 +129,7 @@ fn run_server_client_exchange( }); // give them some time to do the key exchange under load - std::thread::sleep(Duration::from_secs(10)); + std::thread::sleep(Duration::from_secs(30)); // time's up, kill the childs server_terminate.send(()).unwrap(); From ceff8b711ae9efed197c46e7d3b956cddabc26fa Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 23:12:58 +0200 Subject: [PATCH 094/174] feat(ci): Use ubicloud based, paid for runners --- .github/workflows/nix.yaml | 40 +++++++++++++++---------------- .github/workflows/qc.yaml | 24 +++++++++---------- .github/workflows/regressions.yml | 4 ++-- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 093a35e9..432cd8ff 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -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,7 +65,7 @@ jobs: i686-linux---check: name: Run Nix checks on i686-linux runs-on: - - ubuntu-latest + - ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 @@ -180,7 +180,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 +197,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 +214,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 +230,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 +249,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 +273,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,7 +289,7 @@ jobs: aarch64-linux---rosenpass: name: Build aarch64-linux.rosenpass runs-on: - - ubuntu-latest + - ubicloud-standard-2-arm-ubuntu-2204 needs: [] steps: - run: | @@ -310,7 +310,7 @@ jobs: aarch64-linux---rp: name: Build aarch64-linux.rp runs-on: - - ubuntu-latest + - ubicloud-standard-2-arm-ubuntu-2204 needs: [] steps: - run: | @@ -331,7 +331,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,7 +348,7 @@ 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: @@ -370,7 +370,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 +386,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 +402,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 +419,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 +435,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 +449,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 diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 5b4a9c29..0696cbc5 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -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, macos-13] # - 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,7 @@ jobs: - run: nix develop --command cargo test --workspace --all-features cargo-fuzz: - runs-on: ubuntu-latest + runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 @@ -191,7 +191,7 @@ 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 diff --git a/.github/workflows/regressions.yml b/.github/workflows/regressions.yml index a370e2f4..1f19746f 100644 --- a/.github/workflows/regressions.yml +++ b/.github/workflows/regressions.yml @@ -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 From 54fc904c15e3a810f2bdda70da1dfaae85b25e9d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 23:27:03 +0200 Subject: [PATCH 095/174] fix(rp): Protocol version field should be optional --- rp/src/exchange.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index 32b09ac7..bbbac0dd 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -25,6 +25,7 @@ pub struct ExchangePeer { /// The IPs that are allowed for this peer. pub allowed_ips: Option, /// The protocol version used by the peer. + #[serde(default)] pub protocol_version: ProtocolVersion, } From 03464e1be70efb0960e9a95b4d02f1f1ed0bd6ea Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 8 Apr 2025 23:39:43 +0200 Subject: [PATCH 096/174] feat(ci): Use warpbuild based runners for mac os --- .github/workflows/nix.yaml | 56 +++++++++++++++++++------------------- .github/workflows/qc.yaml | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 432cd8ff..0d0b849b 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -77,12 +77,12 @@ jobs: authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Check run: nix flake check . --print-build-logs - x86_64-darwin---default: - name: Build x86_64-darwin.default + aarch64-darwin---default: + name: Build aarch64-darwin.default runs-on: - - macos-13 + - warp-macos-13-arm64-6x needs: - - x86_64-darwin---rosenpass + - aarch64-darwin---rosenpass steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 @@ -93,15 +93,15 @@ jobs: 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 + run: nix build .#packages.aarch64-darwin.default --print-build-logs + aarch64-darwin---release-package: + name: Build aarch64-darwin.release-package runs-on: - - macos-13 + - warp-macos-13-arm64-6x needs: - - x86_64-darwin---rosenpass - - x86_64-darwin---rp - - x86_64-darwin---rosenpass-oci-image + - aarch64-darwin---rosenpass + - aarch64-darwin---rp + - aarch64-darwin---rosenpass-oci-image steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 @@ -112,11 +112,11 @@ jobs: 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 + run: nix build .#packages.aarch64-darwin.release-package --print-build-logs + aarch64-darwin---rosenpass: + name: Build aarch64-darwin.rosenpass runs-on: - - macos-13 + - warp-macos-13-arm64-6x needs: [] steps: - uses: actions/checkout@v4 @@ -128,11 +128,11 @@ jobs: 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 + run: nix build .#packages.aarch64-darwin.rosenpass --print-build-logs + aarch64-darwin---rp: + name: Build aarch64-darwin.rp runs-on: - - macos-13 + - warp-macos-13-arm64-6x needs: [] steps: - uses: actions/checkout@v4 @@ -144,13 +144,13 @@ jobs: 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 + run: nix build .#packages.aarch64-darwin.rp --print-build-logs + aarch64-darwin---rosenpass-oci-image: + name: Build aarch64-darwin.rosenpass-oci-image runs-on: - - macos-13 + - warp-macos-13-arm64-6x needs: - - x86_64-darwin---rosenpass + - aarch64-darwin---rosenpass steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 @@ -161,11 +161,11 @@ jobs: 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 + run: nix build .#packages.aarch64-darwin.rosenpass-oci-image --print-build-logs + aarch64-darwin---check: + name: Run Nix checks on aarch64-darwin runs-on: - - macos-13 + - warp-macos-13-arm64-6x steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 0696cbc5..7264ce3e 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -115,7 +115,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubicloud-standard-2-ubuntu-2204, 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: From abd5210ae4c412d69fb7534d6a36f9166086301f Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 00:07:24 +0200 Subject: [PATCH 097/174] fix(ci): Memcmp not constant time on apple sillicon, stopgap https://github.com/rosenpass/rosenpass/issues/634 --- constant-time/src/memcmp.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/constant-time/src/memcmp.rs b/constant-time/src/memcmp.rs index 1df87094..2f72a089 100644 --- a/constant-time/src/memcmp.rs +++ b/constant-time/src/memcmp.rs @@ -32,6 +32,8 @@ pub fn memcmp(a: &[u8], b: &[u8]) -> bool { /// For discussion on how to (further) ensure the constant-time execution of this function, /// see #[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; From b3403e712021f2f55e99d7a98cd52c836af2c004 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 00:18:47 +0200 Subject: [PATCH 098/174] fix(ci): Do not run mac os CI jobs on pull requests Warpbuild is quite expensive --- .github/workflows/nix-mac.yaml | 113 +++++++++++++++++++++++++++++++++ .github/workflows/nix.yaml | 100 ----------------------------- .github/workflows/qc-mac.yaml | 31 +++++++++ 3 files changed, 144 insertions(+), 100 deletions(-) create mode 100644 .github/workflows/nix-mac.yaml create mode 100644 .github/workflows/qc-mac.yaml diff --git a/.github/workflows/nix-mac.yaml b/.github/workflows/nix-mac.yaml new file mode 100644 index 00000000..4abd6dcb --- /dev/null +++ b/.github/workflows/nix-mac.yaml @@ -0,0 +1,113 @@ +name: Nix on Mac +permissions: + contents: write +on: + push: + branches: + - main + +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 diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 0d0b849b..8efda2f0 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -77,106 +77,6 @@ jobs: authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} - name: Check run: nix flake check . --print-build-logs - 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 x86_64-linux---default: name: Build x86_64-linux.default runs-on: diff --git a/.github/workflows/qc-mac.yaml b/.github/workflows/qc-mac.yaml new file mode 100644 index 00000000..da9029d9 --- /dev/null +++ b/.github/workflows/qc-mac.yaml @@ -0,0 +1,31 @@ +name: QC +on: + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + checks: write + contents: read + +jobs: + cargo-test: + 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 From 9ab754eb0bbc409d91ee3817a75f33ac1d2e0359 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 01:21:05 +0200 Subject: [PATCH 099/174] fix(docker): Used name of author not of org for docker upload --- .github/workflows/docker.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 5daf4517..cee9afca 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -135,7 +135,7 @@ jobs: uses: docker/metadata-action@v5 with: - images: ghcr.io/${{ github.actor }}/rp + images: ghcr.io/${{ github.repository_owner }}/rp labels: | maintainer=Karolin Varner , wucke13 org.opencontainers.image.authors=Karolin Varner , wucke13 @@ -148,7 +148,7 @@ jobs: 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.actor }} --password-stdin + 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 @@ -161,7 +161,7 @@ jobs: file: docker/Dockerfile push: ${{ github.event_name != 'pull_request' }} labels: ${{ steps.meta.outputs.labels }} - tags: ghcr.io/${{ github.actor }}/rp + 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 @@ -199,7 +199,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/${{ github.actor }}/rosenpass + images: ghcr.io/${{ github.repository_owner }}/rosenpass labels: | maintainer=Karolin Varner , wucke13 org.opencontainers.image.authors=Karolin Varner , wucke13 @@ -212,7 +212,7 @@ jobs: 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.actor }} --password-stdin + 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 @@ -225,7 +225,7 @@ jobs: file: docker/Dockerfile push: ${{ github.event_name != 'pull_request' }} labels: ${{ steps.meta.outputs.labels }} - tags: ghcr.io/${{ github.actor }}/rosenpass + 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 @@ -262,7 +262,7 @@ jobs: merge-multiple: true - name: Log in to registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + 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 @@ -271,7 +271,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: ghcr.io/${{ github.actor }}/${{ matrix.target }} + images: ghcr.io/${{ github.repository_owner }}/${{ matrix.target }} tags: | type=edge,branch=main type=sha,branch=main @@ -281,8 +281,8 @@ jobs: working-directory: ${{ runner.temp }}/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf 'ghcr.io/${{ github.actor }}/${{ matrix.target }}@sha256:%s ' *) + $(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}@sha256:%s ' *) - name: Inspect image run: | - docker buildx imagetools inspect ghcr.io/${{ github.actor }}/${{ matrix.target }}:${{ steps.meta.outputs.version }} + docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}:${{ steps.meta.outputs.version }} From e3f7773bac4a69ebe7f25a4a81674c26cae929a4 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 01:29:04 +0200 Subject: [PATCH 100/174] 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. --- util/src/time.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/util/src/time.rs b/util/src/time.rs index 0aabeff2..6a0c6c69 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -39,13 +39,6 @@ mod tests { use std::thread::sleep; use std::time::Duration; - #[test] - fn test_timebase() { - let timebase = Timebase::default(); - let now = timebase.now(); - assert!(now > 0.0); - } - #[test] fn test_timebase_clone() { let timebase = Timebase::default(); From ae418ffba7646725a370073bc7b0c520355894be Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 3 Apr 2025 15:42:45 +0200 Subject: [PATCH 101/174] ci(supply-chain+dependabot): Automatically create exemptions for cargo-crev for dependa-bot PRs --- .../cargo-crev-exemptions-dependabot.yml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/cargo-crev-exemptions-dependabot.yml diff --git a/.github/workflows/cargo-crev-exemptions-dependabot.yml b/.github/workflows/cargo-crev-exemptions-dependabot.yml new file mode 100644 index 00000000..552b56aa --- /dev/null +++ b/.github/workflows/cargo-crev-exemptions-dependabot.yml @@ -0,0 +1,58 @@ +name: Dependabot Vet Exemptions +on: + pull_request: + branches: + - main + paths: + - "Cargo.toml" + - "Cargo.lock" + +jobs: + dependabot-cargo-crev-exceptions: + if: github.actor == 'dependabot[bot]' # Run only for Dependabot PRs + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} # Ensure push access + + - 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 + run: cargo vet regenerate exemptions + + - name: Check for changes + run: git diff --exit-code || echo "Changes detected, committing..." + + - name: Commit and push changes + if: success() + run: | + 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 }} From 070d29932913907e7146dada941c0d791ca4149d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 08:28:45 +0200 Subject: [PATCH 102/174] fix(ci): Separate names of cargo test jobs on linux and mac --- .github/workflows/qc-mac.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/qc-mac.yaml b/.github/workflows/qc-mac.yaml index da9029d9..76f37a88 100644 --- a/.github/workflows/qc-mac.yaml +++ b/.github/workflows/qc-mac.yaml @@ -1,4 +1,4 @@ -name: QC +name: QC Mac on: push: branches: [main] @@ -12,7 +12,7 @@ permissions: contents: read jobs: - cargo-test: + cargo-test-mac: runs-on: warp-macos-13-arm64-6x steps: - uses: actions/checkout@v4 From 4266cbfb72ed364ecdd27eefc72da7a1257dff6e Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 08:39:10 +0200 Subject: [PATCH 103/174] fix(time): Fix another non-functional test for Timebase --- util/src/time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/time.rs b/util/src/time.rs index 6a0c6c69..8e190470 100644 --- a/util/src/time.rs +++ b/util/src/time.rs @@ -13,7 +13,7 @@ use std::time::Instant; /// /// let timebase = Timebase::default(); /// let now = timebase.now(); -/// assert!(now > 0.0); +/// assert!(now >= 0.0); /// ``` #[derive(Clone, Debug)] From 7218b0a3f48712895cdd8dada727b652a8c46413 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:11:36 +0200 Subject: [PATCH 104/174] feat: Ability to manually run CI for pull requests --- .github/workflows/manual-mac-pr.yaml | 8 ++++++++ .github/workflows/nix-mac.yaml | 1 + .github/workflows/qc-mac.yaml | 1 + 3 files changed, 10 insertions(+) create mode 100644 .github/workflows/manual-mac-pr.yaml diff --git a/.github/workflows/manual-mac-pr.yaml b/.github/workflows/manual-mac-pr.yaml new file mode 100644 index 00000000..2e533360 --- /dev/null +++ b/.github/workflows/manual-mac-pr.yaml @@ -0,0 +1,8 @@ +name: PR Validation on Mac +on: + workflow_dispatch: +jobs: + qc: + uses: ./.github/workflows/qc-mac.yaml + nix: + uses: ./.github/workflows/nix-mac.yaml diff --git a/.github/workflows/nix-mac.yaml b/.github/workflows/nix-mac.yaml index 4abd6dcb..73e77cde 100644 --- a/.github/workflows/nix-mac.yaml +++ b/.github/workflows/nix-mac.yaml @@ -5,6 +5,7 @@ on: push: branches: - main + workflow_call: concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/qc-mac.yaml b/.github/workflows/qc-mac.yaml index 76f37a88..390e56e4 100644 --- a/.github/workflows/qc-mac.yaml +++ b/.github/workflows/qc-mac.yaml @@ -2,6 +2,7 @@ name: QC Mac on: push: branches: [main] + workflow_call: concurrency: group: ${{ github.workflow }}-${{ github.ref }} From 77632d0725693560b863e2bb475efadd370222a0 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:18:54 +0200 Subject: [PATCH 105/174] fix: Incorrect permissions for manual mac CI workflow --- .github/workflows/manual-mac-pr.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/manual-mac-pr.yaml b/.github/workflows/manual-mac-pr.yaml index 2e533360..46b6d8e3 100644 --- a/.github/workflows/manual-mac-pr.yaml +++ b/.github/workflows/manual-mac-pr.yaml @@ -1,6 +1,9 @@ name: PR Validation on Mac on: workflow_dispatch: +permissions: + checks: write + contents: write jobs: qc: uses: ./.github/workflows/qc-mac.yaml From faa45a8540be09df6cf3adfd295dfba8ac62242d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:25:56 +0200 Subject: [PATCH 106/174] fix: Incorrect permissions for manual mac CI workflow try 2 --- .github/workflows/manual-mac-pr.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/manual-mac-pr.yaml b/.github/workflows/manual-mac-pr.yaml index 46b6d8e3..a9d0d70c 100644 --- a/.github/workflows/manual-mac-pr.yaml +++ b/.github/workflows/manual-mac-pr.yaml @@ -7,5 +7,10 @@ permissions: jobs: qc: uses: ./.github/workflows/qc-mac.yaml + permissions: + checks: write + contents: read nix: uses: ./.github/workflows/nix-mac.yaml + permissions: + contents: write From 3fc3083a543371f15d944c19b0aa1d9fa02fe9e7 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:35:20 +0200 Subject: [PATCH 107/174] feat: Manual Mac CI runs parallelism --- .github/workflows/manual-mac-pr.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/manual-mac-pr.yaml b/.github/workflows/manual-mac-pr.yaml index a9d0d70c..d9e58853 100644 --- a/.github/workflows/manual-mac-pr.yaml +++ b/.github/workflows/manual-mac-pr.yaml @@ -4,6 +4,9 @@ on: permissions: checks: write contents: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: qc: uses: ./.github/workflows/qc-mac.yaml From 508d46f2bca0ae93560c0b8bbe1967036f9feaa9 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:44:25 +0200 Subject: [PATCH 108/174] fix: Deadlock for manual Mac CI runs parallelism --- .github/workflows/manual-mac-pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/manual-mac-pr.yaml b/.github/workflows/manual-mac-pr.yaml index d9e58853..c11d6592 100644 --- a/.github/workflows/manual-mac-pr.yaml +++ b/.github/workflows/manual-mac-pr.yaml @@ -5,7 +5,7 @@ permissions: checks: write contents: write concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: manual-mac-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: qc: From a83589d76ad4ee5734338f3df7354658c01d46bc Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Apr 2025 09:23:58 +0200 Subject: [PATCH 109/174] feat: Cargo-msrv in full development package --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index d232bfa8..1e84d67c 100644 --- a/flake.nix +++ b/flake.nix @@ -129,6 +129,7 @@ nativeBuildInputs = with pkgs; [ cargo-audit cargo-release + cargo-msrv rustfmt nodePackages.prettier nushell # for the .ci/gen-workflow-files.nu script From f22f4aad7dd901a690675e76ae9ca33e6826a474 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 12:09:12 +0200 Subject: [PATCH 110/174] 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. --- .github/workflows/qc.yaml | 4 +++- cipher-traits/Cargo.toml | 1 + ciphers/Cargo.toml | 1 + constant-time/Cargo.toml | 1 + fuzz/Cargo.toml | 1 + oqs/Cargo.toml | 1 + rosenpass/Cargo.toml | 1 + rp/Cargo.toml | 1 + rust-toolchain.toml | 2 ++ secret-memory/Cargo.toml | 1 + to/Cargo.toml | 1 + util/Cargo.toml | 1 + wireguard-broker/Cargo.toml | 1 + 13 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 7264ce3e..5a251114 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -159,6 +159,7 @@ jobs: cargo-fuzz: 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 @@ -209,4 +210,5 @@ jobs: files: ./target/grcov/lcov verbose: true env: + RUSTUP_TOOLCHAIN: 1.81 CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml index 15f24ff6..f2656a18 100644 --- a/cipher-traits/Cargo.toml +++ b/cipher-traits/Cargo.toml @@ -8,6 +8,7 @@ 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 } diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index c5fd3666..9e34a084 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -8,6 +8,7 @@ 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_all = [ diff --git a/constant-time/Cargo.toml b/constant-time/Cargo.toml index 9c85e0d3..a4faab54 100644 --- a/constant-time/Cargo.toml +++ b/constant-time/Cargo.toml @@ -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 diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 073e91f2..b4c1eb92 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -3,6 +3,7 @@ name = "rosenpass-fuzzing" version = "0.0.1" publish = false edition = "2021" +rust-version = "1.77" [features] experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux_all"] diff --git a/oqs/Cargo.toml b/oqs/Cargo.toml index 6a69563b..b562e74c 100644 --- a/oqs/Cargo.toml +++ b/oqs/Cargo.toml @@ -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 } diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 62f21496..2e65b957 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -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" diff --git a/rp/Cargo.toml b/rp/Cargo.toml index 82121dbc..918e331d 100644 --- a/rp/Cargo.toml +++ b/rp/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" description = "Build post-quantum-secure VPNs with WireGuard!" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" +rust-version = "1.77" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..fcc85b9e --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.77" diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml index e186ec70..53c14da8 100644 --- a/secret-memory/Cargo.toml +++ b/secret-memory/Cargo.toml @@ -8,6 +8,7 @@ description = "Rosenpass internal utilities for storing secrets in memory" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" +rust-version = "1.77" [dependencies] anyhow = { workspace = true } diff --git a/to/Cargo.toml b/to/Cargo.toml index bda13f3a..1692a0ba 100644 --- a/to/Cargo.toml +++ b/to/Cargo.toml @@ -8,6 +8,7 @@ description = "Flexible destination parameters" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" +rust-version = "1.77" [dev-dependencies] doc-comment = { workspace = true } diff --git a/util/Cargo.toml b/util/Cargo.toml index d6bdf003..49d66fb8 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -8,6 +8,7 @@ description = "Rosenpass internal utilities" 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 diff --git a/wireguard-broker/Cargo.toml b/wireguard-broker/Cargo.toml index cfc74e9f..2ee2d47a 100644 --- a/wireguard-broker/Cargo.toml +++ b/wireguard-broker/Cargo.toml @@ -8,6 +8,7 @@ description = "Rosenpass internal broker that runs as root and supplies exchange homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" +rust-version = "1.77" [dependencies] thiserror = { workspace = true } From b46cd636d24ab16a2d79c92059cb4110473eb950 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:00:43 +0200 Subject: [PATCH 111/174] =?UTF-8?q?fix:=20Security=20update=20=E2=80=93=20?= =?UTF-8?q?crossbeam-channel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 4 ++-- supply-chain/imports.lock | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeeec36a..98e590da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,9 +500,9 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 44053021..babb9a4f 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1344,6 +1344,13 @@ criteria = "safe-to-deploy" delta = "0.5.13 -> 0.5.14" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" +[[audits.mozilla.audits.crossbeam-channel]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.5.14 -> 0.5.15" +notes = "Fixes a regression from an earlier version which could lead to a double free" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + [[audits.mozilla.audits.crunchy]] who = "Erich Gubler " criteria = "safe-to-deploy" From f6971aa5adf8029c900f38da11ed1dc0e97a417d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 13 Apr 2025 13:40:07 +0200 Subject: [PATCH 112/174] 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 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fcc85b9e..83025f97 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.77" +channel = "1.77.0" From 67f387a19025e3ea1ca848785409abfe14b14007 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:35:35 +0200 Subject: [PATCH 113/174] ci(cargo-crev): Fix regeneration of cargo-crev-exemptions --- .github/workflows/cargo-crev-exemptions-dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cargo-crev-exemptions-dependabot.yml b/.github/workflows/cargo-crev-exemptions-dependabot.yml index 552b56aa..f253ac2a 100644 --- a/.github/workflows/cargo-crev-exemptions-dependabot.yml +++ b/.github/workflows/cargo-crev-exemptions-dependabot.yml @@ -53,6 +53,6 @@ jobs: run: | git config --global user.name "github-actions[bot]" git config --global user.email "github-actions@github.com" - git add supply-chain./* + git add supply-chain/* git commit -m "Regenerate cargo vet exemptions" git push origin ${{ github.head_ref }} From 108ca440feff87ae8cc7664a09307c9abee98421 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:30:36 +0200 Subject: [PATCH 114/174] enable github workflow for creating crev-exemptions for dependabots to push to the repository --- .github/workflows/cargo-crev-exemptions-dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cargo-crev-exemptions-dependabot.yml b/.github/workflows/cargo-crev-exemptions-dependabot.yml index f253ac2a..a8f97c0a 100644 --- a/.github/workflows/cargo-crev-exemptions-dependabot.yml +++ b/.github/workflows/cargo-crev-exemptions-dependabot.yml @@ -11,6 +11,8 @@ jobs: dependabot-cargo-crev-exceptions: if: github.actor == 'dependabot[bot]' # Run only for Dependabot PRs runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout repository uses: actions/checkout@v4 @@ -56,3 +58,5 @@ jobs: git add supply-chain/* git commit -m "Regenerate cargo vet exemptions" git push origin ${{ github.head_ref }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 4896cd61309ce658d35742e581e1b57ead0de32d Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:19:08 +0200 Subject: [PATCH 115/174] ci(cargo-vet): merge regeneration of exemptions for cargo-vet for dependabot into main cargo-vet job --- .../cargo-crev-exemptions-dependabot.yml | 62 ------------------- .github/workflows/supply-chain.yml | 18 ++++++ 2 files changed, 18 insertions(+), 62 deletions(-) delete mode 100644 .github/workflows/cargo-crev-exemptions-dependabot.yml diff --git a/.github/workflows/cargo-crev-exemptions-dependabot.yml b/.github/workflows/cargo-crev-exemptions-dependabot.yml deleted file mode 100644 index a8f97c0a..00000000 --- a/.github/workflows/cargo-crev-exemptions-dependabot.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Dependabot Vet Exemptions -on: - pull_request: - branches: - - main - paths: - - "Cargo.toml" - - "Cargo.lock" - -jobs: - dependabot-cargo-crev-exceptions: - if: github.actor == 'dependabot[bot]' # Run only for Dependabot PRs - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - token: ${{ secrets.GITHUB_TOKEN }} # Ensure push access - - - 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 - run: cargo vet regenerate exemptions - - - name: Check for changes - run: git diff --exit-code || echo "Changes detected, committing..." - - - name: Commit and push changes - if: success() - run: | - 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 }} diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index 024ecbcc..f6889ea3 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -46,6 +46,8 @@ jobs: cargo-vet: name: Vet Dependencies runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 @@ -67,5 +69,21 @@ jobs: 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 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 From e3d3584adb9375c2ae8a2168f16b568d4596592e Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:03:34 +0200 Subject: [PATCH 116/174] fix(ci+supply-chain+dependabot): Checkout correct branch in the supply chain checks for cargo-vet --- .github/workflows/supply-chain.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index f6889ea3..effbb594 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -78,6 +78,8 @@ jobs: - 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/* From b8e9519e26f7454a848c981f99a9aec579ae2479 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 9 May 2025 18:15:55 +0200 Subject: [PATCH 117/174] chore: Ignore rust advisory RUSTSEC-2023-0089 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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](https://github.com/embassy-rs/atomic-polyfill/blob/48e55c166684f37af0b00fbee5a0809b1a2bae8e/README.md). ## Possible alternatives * [portable-atomic](https://crates.io/crates/portable-atomic) ├ Announcement: https://github.com/embassy-rs/atomic-polyfill/commit/48e55c166684f37af0b00fbee5a0809b1a2bae8e ├ 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 (*) --- deny.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/deny.toml b/deny.toml index e41c593d..c39fc949 100644 --- a/deny.toml +++ b/deny.toml @@ -27,6 +27,7 @@ feature-depth = 1 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. From a45812b2cd2da300867eeef6c53422dee47a31fa Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 12:52:41 +0200 Subject: [PATCH 118/174] feat: add treefmt.nix setup Add a treefmt setup for a single-entry point format-everything system. To use it, simply run `nix fmt`. This will in term run nixfmt, prettier and rustfmt. Signed-off-by: wucke13 --- flake.lock | 23 ++++++++++++++++++++++- flake.nix | 24 +++++++++--------------- treefmt.nix | 30 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 treefmt.nix diff --git a/flake.lock b/flake.lock index e8d4a2e1..bf07e4d3 100644 --- a/flake.lock +++ b/flake.lock @@ -80,7 +80,8 @@ "fenix": "fenix", "flake-utils": "flake-utils", "nix-vm-test": "nix-vm-test", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "treefmt-nix": "treefmt-nix" } }, "rust-analyzer-src": { @@ -114,6 +115,26 @@ "repo": "default", "type": "github" } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1743748085, + "narHash": "sha256-uhjnlaVTWo5iD3LXics1rp9gaKgDRQj6660+gbUU3cE=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "815e4121d6a5d504c0f96e5be2dd7f871e4fd99d", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 1e84d67c..26864342 100644 --- a/flake.nix +++ b/flake.nix @@ -10,9 +10,12 @@ 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"; + + treefmt-nix.url = "github:numtide/treefmt-nix"; + treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = { self, nixpkgs, flake-utils, nix-vm-test, ... }@inputs: + outputs = { self, nixpkgs, flake-utils, nix-vm-test, treefmt-nix, ... }@inputs: nixpkgs.lib.foldl (a: b: nixpkgs.lib.recursiveUpdate a b) { } [ @@ -86,6 +89,8 @@ nix-vm-test.overlays.default ]; }; + + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; in { packages.package-deb = pkgs.callPackage ./pkgs/package-deb.nix { @@ -152,26 +157,15 @@ 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 - ''; - nixpkgs-fmt = pkgs.runCommand "check-nixpkgs-fmt" - { nativeBuildInputs = [ pkgs.nixpkgs-fmt ]; } '' - nixpkgs-fmt --check ${./.} && touch $out - ''; - prettier-check = pkgs.runCommand "check-with-prettier" - { nativeBuildInputs = [ pkgs.nodePackages.prettier ]; } '' - cd ${./.} && prettier --check . && touch $out - ''; + formatting = treefmtEval.config.build.check self; } // 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; + # for `nix fmt` + formatter = treefmtEval.config.build.wrapper; })) ]; } diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 00000000..a79c8f3b --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,30 @@ +{ pkgs, ... }: +{ + # Used to find the project root + projectRootFile = "flake.nix"; + programs.nixfmt.enable = true; + programs.prettier = { + enable = true; + includes = [ + "*.css" + "*.html" + "*.js" + "*.json" + "*.json5" + "*.md" + "*.mdx" + "*.toml" + "*.yaml" + "*.yml" + ]; + excludes = [ + "supply-chain/*" + ]; + settings = { + plugins = [ + "${pkgs.nodePackages.prettier-plugin-toml}/lib/node_modules/prettier-plugin-toml/lib/index.js" + ]; + }; + }; + programs.rustfmt.enable = true; +} From 22b980a61f41f9e6bc91773e0fc02189451f7caa Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 12:57:57 +0200 Subject: [PATCH 119/174] chore: format everything This implicates a change from nixpkgs-fmt to nixfmt. Nixfmt will become the new standard on nix formatting, sanctioned by the nixpkgs. To verify that these changes are purely in whitespace, but not semantic: git diff --ignore-all-space -w HEAD^! That will only show newline changes, make the diffing somewhat easier. Signed-off-by: wucke13 --- Cargo.toml | 35 ++--- ciphers/Cargo.toml | 12 +- deny.toml | 34 ++-- flake.nix | 252 ++++++++++++++++-------------- overlay.nix | 6 +- pkgs/package-deb.nix | 6 +- pkgs/package-rpm.nix | 13 +- pkgs/release-package.nix | 23 +-- pkgs/rosenpass-oci-image.nix | 6 +- pkgs/rosenpass.nix | 60 ++++--- pkgs/whitepaper.nix | 55 ++++++- rosenpass/Cargo.toml | 20 +-- tests/legacy-distro-packaging.nix | 53 ++++--- tests/systemd/rosenpass.nix | 216 ++++++++++++++----------- tests/systemd/rp.nix | 175 ++++++++++++--------- treefmt.nix | 4 +- util/Cargo.toml | 1 - 17 files changed, 558 insertions(+), 413 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bb8274d7..8583095d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,17 +2,17 @@ resolver = "2" members = [ - "rosenpass", - "cipher-traits", - "ciphers", - "util", - "constant-time", - "oqs", - "to", - "fuzz", - "secret-memory", - "rp", - "wireguard-broker", + "rosenpass", + "cipher-traits", + "ciphers", + "util", + "constant-time", + "oqs", + "to", + "fuzz", + "secret-memory", + "rp", + "wireguard-broker", ] default-members = ["rosenpass", "rp", "wireguard-broker"] @@ -42,7 +42,7 @@ toml = "0.7.8" static_assertions = "1.1.0" allocator-api2 = "0.2.14" memsec = { git = "https://github.com/rosenpass/memsec.git", rev = "aceb9baee8aec6844125bd6612f92e9a281373df", features = [ - "alloc_ext", + "alloc_ext", ] } rand = "0.8.5" typenum = "1.17.0" @@ -55,14 +55,14 @@ arbitrary = { version = "1.4.1", features = ["derive"] } 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', + 'classic_mceliece', + 'kyber', ] } blake2 = "0.10.6" sha3 = "0.10.8" chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ - "std", - "heapless", + "std", + "heapless", ] } zerocopy = { version = "0.7.35", features = ["derive"] } home = "=0.5.9" # 5.11 requires rustc 1.81 @@ -72,7 +72,7 @@ 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"} +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" } @@ -90,7 +90,6 @@ criterion = "0.5.1" allocator-api2-tests = "0.2.15" 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" diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 9e34a084..cee798cf 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -12,16 +12,16 @@ rust-version = "1.77" [features] experiment_libcrux_all = [ - "experiment_libcrux_blake2", - "experiment_libcrux_chachapoly", - "experiment_libcrux_chachapoly_test", - "experiment_libcrux_kyber", + "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_chachapoly", + "dep:libcrux", ] experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"] diff --git a/deny.toml b/deny.toml index c39fc949..794f7ee3 100644 --- a/deny.toml +++ b/deny.toml @@ -24,11 +24,7 @@ feature-depth = 1 [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", -] +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. @@ -43,11 +39,11 @@ ignore = [ # 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", + "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 @@ -57,10 +53,10 @@ 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" }, + # 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" }, ] @@ -94,15 +90,11 @@ workspace-default-features = "allow" # on a crate-by-crate basis if desired. external-default-features = "allow" # List of crates that are allowed. Use with care! -allow = [ -] +allow = [] # List of crates to deny -deny = [ -] +deny = [] -skip-tree = [ - -] +skip-tree = [] # This section is considered when running `cargo deny check sources`. # More documentation about the 'sources' section can be found here: diff --git a/flake.nix b/flake.nix index 26864342..6031bb3f 100644 --- a/flake.nix +++ b/flake.nix @@ -15,32 +15,38 @@ treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = { self, nixpkgs, flake-utils, nix-vm-test, treefmt-nix, ... }@inputs: + outputs = + { + self, + nixpkgs, + flake-utils, + nix-vm-test, + treefmt-nix, + ... + }@inputs: nixpkgs.lib.foldl (a: b: nixpkgs.lib.recursiveUpdate a b) { } [ - # ### Export the overlay.nix from this flake ### # - { - overlays.default = import ./overlay.nix; - } - + { overlays.default = import ./overlay.nix; } # ### Actual Rosenpass Package and Docker Container Images ### # - (flake-utils.lib.eachSystem [ - "x86_64-linux" - "aarch64-linux" + (flake-utils.lib.eachSystem + [ + "x86_64-linux" + "aarch64-linux" - # unsuported best-effort - "i686-linux" - "x86_64-darwin" - "aarch64-darwin" - # "x86_64-windows" - ] - (system: + # unsuported best-effort + "i686-linux" + "x86_64-darwin" + "aarch64-darwin" + # "x86_64-windows" + ] + ( + system: let # normal nixpkgs pkgs = import nixpkgs { @@ -51,121 +57,131 @@ }; in { - packages = { - default = pkgs.rosenpass; - rosenpass = pkgs.rosenpass; - rosenpass-oci-image = pkgs.rosenpass-oci-image; - rp = pkgs.rp; + packages = + { + default = pkgs.rosenpass; + rosenpass = pkgs.rosenpass; + rosenpass-oci-image = pkgs.rosenpass-oci-image; + rp = pkgs.rp; - release-package = pkgs.release-package; + release-package = pkgs.release-package; - # for good measure, we also offer to cross compile to Linux on Arm - aarch64-linux-rosenpass-static = - pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rosenpass; - aarch64-linux-rp-static = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rp; - } - // - # We only offer static builds for linux, as this is not supported on OS X - (nixpkgs.lib.attrsets.optionalAttrs pkgs.stdenv.isLinux { - rosenpass-static = pkgs.pkgsStatic.rosenpass; - rosenpass-static-oci-image = pkgs.pkgsStatic.rosenpass-oci-image; - rp-static = pkgs.pkgsStatic.rp; - }); + # for good measure, we also offer to cross compile to Linux on Arm + aarch64-linux-rosenpass-static = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rosenpass; + aarch64-linux-rp-static = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rp; + } + // + # We only offer static builds for linux, as this is not supported on OS X + (nixpkgs.lib.attrsets.optionalAttrs pkgs.stdenv.isLinux { + rosenpass-static = pkgs.pkgsStatic.rosenpass; + rosenpass-static-oci-image = pkgs.pkgsStatic.rosenpass-oci-image; + rp-static = pkgs.pkgsStatic.rp; + }); } - )) - + ) + ) # ### Linux specifics ### # - (flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system: - let - pkgs = import nixpkgs { - inherit system; + (flake-utils.lib.eachSystem + [ + "x86_64-linux" + "aarch64-linux" + ] + ( + system: + let + pkgs = import nixpkgs { + inherit system; - # apply our own overlay, overriding/inserting our packages as defined in ./pkgs - overlays = [ - self.overlays.default - nix-vm-test.overlays.default - ]; - }; + # apply our own overlay, overriding/inserting our packages as defined in ./pkgs + overlays = [ + self.overlays.default + nix-vm-test.overlays.default + ]; + }; - treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; - 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; - }; + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + 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 ### - # - packages.whitepaper = pkgs.whitepaper; + # + ### Reading materials ### + # + packages.whitepaper = pkgs.whitepaper; - # - ### Proof and Proof Tools ### - # - packages.proverif-patched = pkgs.proverif-patched; - packages.proof-proverif = pkgs.proof-proverif; + # + ### Proof and Proof Tools ### + # + packages.proverif-patched = pkgs.proverif-patched; + packages.proof-proverif = pkgs.proof-proverif; + # + ### Devshells ### + # + devShells.default = pkgs.mkShell { + inherit (pkgs.proof-proverif) CRYPTOVERIF_LIB; + inputsFrom = [ pkgs.rosenpass ]; + nativeBuildInputs = with pkgs; [ + cargo-release + clippy + rustfmt + nodePackages.prettier + nushell # for the .ci/gen-workflow-files.nu script + 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 + ]; + }; - # - ### Devshells ### - # - devShells.default = pkgs.mkShell { - inherit (pkgs.proof-proverif) CRYPTOVERIF_LIB; - inputsFrom = [ pkgs.rosenpass ]; - nativeBuildInputs = with pkgs; [ - cargo-release - clippy - rustfmt - nodePackages.prettier - nushell # for the .ci/gen-workflow-files.nu script - 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; + formatting = treefmtEval.config.build.check self; + } + // 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; + } + ); - - checks = { - systemd-rosenpass = pkgs.testers.runNixOSTest ./tests/systemd/rosenpass.nix; - systemd-rp = pkgs.testers.runNixOSTest ./tests/systemd/rp.nix; - formatting = treefmtEval.config.build.check self; - } // 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; - }); - - # for `nix fmt` - formatter = treefmtEval.config.build.wrapper; - })) + # for `nix fmt` + formatter = treefmtEval.config.build.wrapper; + } + ) + ) ]; } diff --git a/overlay.nix b/overlay.nix index 5e98dfcf..25505288 100644 --- a/overlay.nix +++ b/overlay.nix @@ -1,6 +1,5 @@ final: prev: { - # ### Actual rosenpass software ### # @@ -27,7 +26,10 @@ final: prev: { "marzipan(/marzipan.awk)?" "analysis(/.*)?" ]; - nativeBuildInputs = [ final.proverif final.graphviz ]; + nativeBuildInputs = [ + final.proverif + final.graphviz + ]; CRYPTOVERIF_LIB = final.proverif-patched + "/lib/cryptoverif.pvl"; installPhase = '' mkdir -p $out diff --git a/pkgs/package-deb.nix b/pkgs/package-deb.nix index d7194d6f..c3db31ed 100644 --- a/pkgs/package-deb.nix +++ b/pkgs/package-deb.nix @@ -1,4 +1,8 @@ -{ runCommand, dpkg, rosenpass }: +{ + runCommand, + dpkg, + rosenpass, +}: let inherit (rosenpass) version; diff --git a/pkgs/package-rpm.nix b/pkgs/package-rpm.nix index f446bfc9..b60269a2 100644 --- a/pkgs/package-rpm.nix +++ b/pkgs/package-rpm.nix @@ -1,12 +1,15 @@ -{ lib, system, runCommand, rosenpass, rpm }: +{ + 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; + release = if builtins.length splitVersion != 2 then "release" else builtins.elemAt splitVersion 1; arch = builtins.head (builtins.split "-" system); in diff --git a/pkgs/release-package.nix b/pkgs/release-package.nix index 2de60298..f147c758 100644 --- a/pkgs/release-package.nix +++ b/pkgs/release-package.nix @@ -1,21 +1,24 @@ -{ lib, stdenvNoCC, runCommandNoCC, pkgsStatic, rosenpass, rosenpass-oci-image, rp } @ args: +{ + lib, + stdenvNoCC, + runCommandNoCC, + pkgsStatic, + rosenpass, + rosenpass-oci-image, + rp, +}@args: let version = rosenpass.version; # select static packages on Linux, default packages otherwise - package = - if stdenvNoCC.hostPlatform.isLinux then - pkgsStatic.rosenpass - else args.rosenpass; - rp = - if stdenvNoCC.hostPlatform.isLinux then - pkgsStatic.rp - else args.rp; + package = if stdenvNoCC.hostPlatform.isLinux then pkgsStatic.rosenpass else args.rosenpass; + rp = if stdenvNoCC.hostPlatform.isLinux then pkgsStatic.rp else args.rp; oci-image = if stdenvNoCC.hostPlatform.isLinux then pkgsStatic.rosenpass-oci-image - else args.rosenpass-oci-image; + else + args.rosenpass-oci-image; in runCommandNoCC "lace-result" { } '' mkdir {bin,$out} diff --git a/pkgs/rosenpass-oci-image.nix b/pkgs/rosenpass-oci-image.nix index f68e0377..3008e7a6 100644 --- a/pkgs/rosenpass-oci-image.nix +++ b/pkgs/rosenpass-oci-image.nix @@ -1,4 +1,8 @@ -{ dockerTools, buildEnv, rosenpass }: +{ + dockerTools, + buildEnv, + rosenpass, +}: dockerTools.buildImage { name = rosenpass.name + "-oci"; diff --git a/pkgs/rosenpass.nix b/pkgs/rosenpass.nix index 37b467d0..30f560fc 100644 --- a/pkgs/rosenpass.nix +++ b/pkgs/rosenpass.nix @@ -1,4 +1,13 @@ -{ lib, stdenv, rustPlatform, cmake, mandoc, removeReferencesTo, bash, package ? "rosenpass" }: +{ + lib, + stdenv, + rustPlatform, + cmake, + mandoc, + removeReferencesTo, + bash, + package ? "rosenpass", +}: let # whether we want to build a statically linked binary @@ -17,24 +26,30 @@ let "toml" ]; # Files to explicitly include - files = [ - "to/README.md" - ]; + files = [ "to/README.md" ]; src = ../.; - filter = (path: type: scoped rec { - inherit (lib) any id removePrefix hasSuffix; - anyof = (any id); + filter = ( + path: type: + scoped rec { + inherit (lib) + any + id + removePrefix + hasSuffix + ; + anyof = (any id); - basename = baseNameOf (toString path); - relative = removePrefix (toString src + "/") (toString path); + basename = baseNameOf (toString path); + relative = removePrefix (toString src + "/") (toString path); - result = anyof [ - (type == "directory") - (any (ext: hasSuffix ".${ext}" basename) extensions) - (any (file: file == relative) files) - ]; - }); + result = anyof [ + (type == "directory") + (any (ext: hasSuffix ".${ext}" basename) extensions) + (any (file: file == relative) files) + ]; + } + ); result = lib.sources.cleanSourceWith { inherit src filter; }; }; @@ -47,8 +62,14 @@ rustPlatform.buildRustPackage { version = cargoToml.package.version; inherit src; - cargoBuildOptions = [ "--package" package ]; - cargoTestOptions = [ "--package" package ]; + cargoBuildOptions = [ + "--package" + package + ]; + cargoTestOptions = [ + "--package" + package + ]; doCheck = true; @@ -81,7 +102,10 @@ rustPlatform.buildRustPackage { meta = { inherit (cargoToml.package) description homepage; - license = with lib.licenses; [ mit asl20 ]; + license = with lib.licenses; [ + mit + asl20 + ]; maintainers = [ lib.maintainers.wucke13 ]; platforms = lib.platforms.all; }; diff --git a/pkgs/whitepaper.nix b/pkgs/whitepaper.nix index 558e9673..b01f8023 100644 --- a/pkgs/whitepaper.nix +++ b/pkgs/whitepaper.nix @@ -1,13 +1,52 @@ -{ stdenvNoCC, texlive, ncurses, python3Packages, which }: +{ + stdenvNoCC, + texlive, + ncurses, + python3Packages, + which, +}: let - customTexLiveSetup = (texlive.combine { - inherit (texlive) acmart amsfonts biber biblatex biblatex-software - biblatex-trad ccicons csquotes csvsimple doclicense eso-pic fancyvrb - fontspec gitinfo2 gobble ifmtarg koma-script latexmk lm lualatex-math - markdown mathtools minted noto nunito paralist pgf scheme-basic soul - unicode-math upquote xifthen xkeyval xurl; - }); + customTexLiveSetup = ( + texlive.combine { + inherit (texlive) + acmart + amsfonts + biber + biblatex + biblatex-software + biblatex-trad + ccicons + csquotes + csvsimple + doclicense + eso-pic + fancyvrb + fontspec + gitinfo2 + gobble + ifmtarg + koma-script + latexmk + lm + lualatex-math + markdown + mathtools + minted + noto + nunito + paralist + pgf + scheme-basic + soul + unicode-math + upquote + xifthen + xkeyval + xurl + ; + } + ); in stdenvNoCC.mkDerivation { name = "whitepaper"; diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 2e65b957..d967e524 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -30,9 +30,9 @@ required-features = ["experiment_api", "internal_testing"] [[test]] name = "gen-ipc-msg-types" required-features = [ - "experiment_api", - "internal_testing", - "internal_bin_gen_ipc_msg_types", + "experiment_api", + "internal_testing", + "internal_bin_gen_ipc_msg_types", ] [[bench]] @@ -92,16 +92,16 @@ experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"] experiment_libcrux_all = ["rosenpass-ciphers/experiment_libcrux_all"] experiment_libcrux_blake2 = ["rosenpass-ciphers/experiment_libcrux_blake2"] experiment_libcrux_chachapoly = [ - "rosenpass-ciphers/experiment_libcrux_chachapoly", + "rosenpass-ciphers/experiment_libcrux_chachapoly", ] experiment_libcrux_kyber = ["rosenpass-ciphers/experiment_libcrux_kyber"] experiment_api = [ - "hex-literal", - "uds", - "command-fds", - "rustix", - "rosenpass-util/experiment_file_descriptor_passing", - "rosenpass-wireguard-broker/experiment_api", + "hex-literal", + "uds", + "command-fds", + "rustix", + "rosenpass-util/experiment_file_descriptor_passing", + "rosenpass-wireguard-broker/experiment_api", ] internal_signal_handling_for_coverage_reports = ["signal-hook"] internal_testing = [] diff --git a/tests/legacy-distro-packaging.nix b/tests/legacy-distro-packaging.nix index 1d358069..619dbfa2 100644 --- a/tests/legacy-distro-packaging.nix +++ b/tests/legacy-distro-packaging.nix @@ -1,4 +1,8 @@ -{ pkgs, rosenpass-deb, rosenpass-rpm }: +{ + pkgs, + rosenpass-deb, + rosenpass-rpm, +}: let wg-deb = pkgs.fetchurl { @@ -23,31 +27,38 @@ let cp ${./prepare-test.sh} $out/prepare-test.sh ''; - test = { tester, installPrefix, suffix, source }: (tester { - sharedDirs.share = { - inherit source; - target = "/mnt/share"; - }; - testScript = '' - vm.wait_for_unit("multi-user.target") - vm.succeed("${installPrefix} /mnt/share/wireguard.${suffix}") - vm.succeed("${installPrefix} /mnt/share/rosenpass.${suffix}") - vm.succeed("bash /mnt/share/prepare-test.sh") + test = + { + tester, + installPrefix, + suffix, + source, + }: + (tester { + sharedDirs.share = { + inherit source; + target = "/mnt/share"; + }; + testScript = '' + vm.wait_for_unit("multi-user.target") + vm.succeed("${installPrefix} /mnt/share/wireguard.${suffix}") + vm.succeed("${installPrefix} /mnt/share/rosenpass.${suffix}") + vm.succeed("bash /mnt/share/prepare-test.sh") - vm.succeed(f"systemctl start rp@server") - vm.succeed(f"systemctl start rp@client") + vm.succeed(f"systemctl start rp@server") + vm.succeed(f"systemctl start rp@client") - vm.wait_for_unit("rp@server.service") - vm.wait_for_unit("rp@client.service") + vm.wait_for_unit("rp@server.service") + vm.wait_for_unit("rp@client.service") - vm.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + vm.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); - psk_server = vm.succeed("wg show rp-server preshared-keys").strip().split()[-1] - psk_client = vm.succeed("wg show rp-client preshared-keys").strip().split()[-1] + psk_server = vm.succeed("wg show rp-server preshared-keys").strip().split()[-1] + psk_client = vm.succeed("wg show rp-client preshared-keys").strip().split()[-1] - assert psk_server == psk_client, "preshared-key exchange must be successful" - ''; - }).sandboxed; + assert psk_server == psk_client, "preshared-key exchange must be successful" + ''; + }).sandboxed; in { package-deb-debian-13 = test { diff --git a/tests/systemd/rosenpass.nix b/tests/systemd/rosenpass.nix index 18ca5d10..d1eddf82 100644 --- a/tests/systemd/rosenpass.nix +++ b/tests/systemd/rosenpass.nix @@ -32,29 +32,33 @@ let public_key = "/etc/rosenpass/rp0/pqpk"; secret_key = "/run/credentials/rosenpass@rp0.service/pqsk"; verbosity = "Verbose"; - peers = [{ - device = "rp0"; - peer = client.wg.public; - public_key = "/etc/rosenpass/rp0/peers/client/pqpk"; - }]; + peers = [ + { + device = "rp0"; + peer = client.wg.public; + public_key = "/etc/rosenpass/rp0/peers/client/pqpk"; + } + ]; }; client_config = { listen = [ ]; public_key = "/etc/rosenpass/rp0/pqpk"; secret_key = "/run/credentials/rosenpass@rp0.service/pqsk"; verbosity = "Verbose"; - peers = [{ - device = "rp0"; - peer = server.wg.public; - public_key = "/etc/rosenpass/rp0/peers/server/pqpk"; - endpoint = "${server.ip4}:9999"; - }]; + peers = [ + { + device = "rp0"; + peer = server.wg.public; + public_key = "/etc/rosenpass/rp0/peers/server/pqpk"; + endpoint = "${server.ip4}:9999"; + } + ]; }; config = pkgs.runCommand "config" { } '' mkdir -pv $out - cp -v ${(pkgs.formats.toml {}).generate "rp0.toml" server_config} $out/server - cp -v ${(pkgs.formats.toml {}).generate "rp0.toml" client_config} $out/client + cp -v ${(pkgs.formats.toml { }).generate "rp0.toml" server_config} $out/server + cp -v ${(pkgs.formats.toml { }).generate "rp0.toml" client_config} $out/client ''; in { @@ -62,50 +66,71 @@ in nodes = let - shared = peer: { config, modulesPath, pkgs, ... }: { - # Need to work around a problem in recent systemd changes. - # It won't be necessary in other distros (for which the systemd file was designed), this is NixOS specific - # https://github.com/NixOS/nixpkgs/issues/258371#issuecomment-1925672767 - # This can potentially be removed in future nixpkgs updates - systemd.packages = [ - (pkgs.runCommand "rosenpass" { } '' - mkdir -p $out/lib/systemd/system - < ${pkgs.rosenpass}/lib/systemd/system/rosenpass.target > $out/lib/systemd/system/rosenpass.target - < ${pkgs.rosenpass}/lib/systemd/system/rosenpass@.service \ - sed 's@^\(\[Service]\)$@\1\nEnvironment=PATH=${pkgs.wireguard-tools}/bin@' | - sed 's@^ExecStartPre=envsubst @ExecStartPre='"${pkgs.envsubst}"'/bin/envsubst @' | - sed 's@^ExecStart=rosenpass @ExecStart='"${pkgs.rosenpass}"'/bin/rosenpass @' > $out/lib/systemd/system/rosenpass@.service - '') - ]; - networking.wireguard = { - enable = true; - interfaces.rp0 = { - ips = [ "${peer.wg.ip4}/32" "${peer.wg.ip6}/128" ]; - privateKeyFile = "/etc/wireguard/wgsk"; + shared = + peer: + { + config, + modulesPath, + pkgs, + ... + }: + { + # Need to work around a problem in recent systemd changes. + # It won't be necessary in other distros (for which the systemd file was designed), this is NixOS specific + # https://github.com/NixOS/nixpkgs/issues/258371#issuecomment-1925672767 + # This can potentially be removed in future nixpkgs updates + systemd.packages = [ + (pkgs.runCommand "rosenpass" { } '' + mkdir -p $out/lib/systemd/system + < ${pkgs.rosenpass}/lib/systemd/system/rosenpass.target > $out/lib/systemd/system/rosenpass.target + < ${pkgs.rosenpass}/lib/systemd/system/rosenpass@.service \ + sed 's@^\(\[Service]\)$@\1\nEnvironment=PATH=${pkgs.wireguard-tools}/bin@' | + sed 's@^ExecStartPre=envsubst @ExecStartPre='"${pkgs.envsubst}"'/bin/envsubst @' | + sed 's@^ExecStart=rosenpass @ExecStart='"${pkgs.rosenpass}"'/bin/rosenpass @' > $out/lib/systemd/system/rosenpass@.service + '') + ]; + networking.wireguard = { + enable = true; + interfaces.rp0 = { + ips = [ + "${peer.wg.ip4}/32" + "${peer.wg.ip6}/128" + ]; + privateKeyFile = "/etc/wireguard/wgsk"; + }; + }; + environment.etc."wireguard/wgsk".text = peer.wg.secret; + networking.interfaces.eth1 = { + ipv4.addresses = [ + { + address = peer.ip4; + prefixLength = 24; + } + ]; + ipv6.addresses = [ + { + address = peer.ip6; + prefixLength = 64; + } + ]; }; }; - environment.etc."wireguard/wgsk".text = peer.wg.secret; - networking.interfaces.eth1 = { - ipv4.addresses = [{ - address = peer.ip4; - prefixLength = 24; - }]; - ipv6.addresses = [{ - address = peer.ip6; - prefixLength = 64; - }]; - }; - }; in { server = { imports = [ (shared server) ]; - networking.firewall.allowedUDPPorts = [ 9999 server.wg.listen ]; + networking.firewall.allowedUDPPorts = [ + 9999 + server.wg.listen + ]; networking.wireguard.interfaces.rp0 = { listenPort = server.wg.listen; peers = [ { - allowedIPs = [ client.wg.ip4 client.wg.ip6 ]; + allowedIPs = [ + client.wg.ip4 + client.wg.ip6 + ]; publicKey = client.wg.public; } ]; @@ -116,7 +141,10 @@ in networking.wireguard.interfaces.rp0 = { peers = [ { - allowedIPs = [ "10.23.42.0/24" "fc00::/64" ]; + allowedIPs = [ + "10.23.42.0/24" + "fc00::/64" + ]; publicKey = server.wg.public; endpoint = "${server.ip4}:${toString server.wg.listen}"; } @@ -124,60 +152,62 @@ in }; }; }; - testScript = { ... }: '' - from os import system - rosenpass = "${pkgs.rosenpass}/bin/rosenpass" + testScript = + { ... }: + '' + from os import system + rosenpass = "${pkgs.rosenpass}/bin/rosenpass" - start_all() - - for machine in [server, client]: - machine.wait_for_unit("multi-user.target") - machine.wait_for_unit("network-online.target") - - with subtest("Key, Config, and Service Setup"): - for name, machine, remote in [("server", server, client), ("client", client, server)]: - # generate all the keys - system(f"{rosenpass} gen-keys --public-key {name}-pqpk --secret-key {name}-pqsk") - - # copy private keys to our side - machine.copy_from_host(f"{name}-pqsk", "/etc/rosenpass/rp0/pqsk") - machine.copy_from_host(f"{name}-pqpk", "/etc/rosenpass/rp0/pqpk") - - # copy public keys to other side - remote.copy_from_host(f"{name}-pqpk", f"/etc/rosenpass/rp0/peers/{name}/pqpk") - - machine.copy_from_host(f"${config}/{name}", "/etc/rosenpass/rp0.toml") + start_all() for machine in [server, client]: - machine.wait_for_unit("wireguard-rp0.service") + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("network-online.target") - with subtest("wg network test"): - client.succeed("wg show all preshared-keys | grep none", timeout=5); - client.succeed("ping -c5 ${server.wg.ip4}") - server.succeed("ping -c5 ${client.wg.ip6}") + with subtest("Key, Config, and Service Setup"): + for name, machine, remote in [("server", server, client), ("client", client, server)]: + # generate all the keys + system(f"{rosenpass} gen-keys --public-key {name}-pqpk --secret-key {name}-pqsk") - with subtest("Set up rosenpass"): - for machine in [server, client]: - machine.succeed("systemctl start rosenpass@rp0.service") + # copy private keys to our side + machine.copy_from_host(f"{name}-pqsk", "/etc/rosenpass/rp0/pqsk") + machine.copy_from_host(f"{name}-pqpk", "/etc/rosenpass/rp0/pqpk") - for machine in [server, client]: - machine.wait_for_unit("rosenpass@rp0.service") + # copy public keys to other side + remote.copy_from_host(f"{name}-pqpk", f"/etc/rosenpass/rp0/peers/{name}/pqpk") + + machine.copy_from_host(f"${config}/{name}", "/etc/rosenpass/rp0.toml") + + for machine in [server, client]: + machine.wait_for_unit("wireguard-rp0.service") + + with subtest("wg network test"): + client.succeed("wg show all preshared-keys | grep none", timeout=5); + client.succeed("ping -c5 ${server.wg.ip4}") + server.succeed("ping -c5 ${client.wg.ip6}") + + with subtest("Set up rosenpass"): + for machine in [server, client]: + machine.succeed("systemctl start rosenpass@rp0.service") + + for machine in [server, client]: + machine.wait_for_unit("rosenpass@rp0.service") - with subtest("compare preshared keys"): - client.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); - server.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + with subtest("compare preshared keys"): + client.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + server.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); - def get_psk(m): - psk = m.succeed("wg show rp0 preshared-keys | awk '{print $2}'") - psk = psk.strip() - assert len(psk.split()) == 1, "Only one PSK" - return psk + def get_psk(m): + psk = m.succeed("wg show rp0 preshared-keys | awk '{print $2}'") + psk = psk.strip() + assert len(psk.split()) == 1, "Only one PSK" + return psk - assert get_psk(client) == get_psk(server), "preshared keys need to match" + assert get_psk(client) == get_psk(server), "preshared keys need to match" - with subtest("rosenpass network test"): - client.succeed("ping -c5 ${server.wg.ip4}") - server.succeed("ping -c5 ${client.wg.ip6}") - ''; + with subtest("rosenpass network test"): + client.succeed("ping -c5 ${server.wg.ip4}") + server.succeed("ping -c5 ${client.wg.ip6}") + ''; } diff --git a/tests/systemd/rp.nix b/tests/systemd/rp.nix index 7218d155..c0acdb8a 100644 --- a/tests/systemd/rp.nix +++ b/tests/systemd/rp.nix @@ -24,27 +24,31 @@ let verbose = true; dev = "test-rp-device0"; ip = "fc00::1/64"; - peers = [{ - public_keys_dir = "/etc/rosenpass/test-rp-device0/peers/client"; - allowed_ips = "fc00::2"; - }]; + peers = [ + { + public_keys_dir = "/etc/rosenpass/test-rp-device0/peers/client"; + allowed_ips = "fc00::2"; + } + ]; }; client_config = { private_keys_dir = "/run/credentials/rp@test-rp-device0.service"; verbose = true; dev = "test-rp-device0"; ip = "fc00::2/128"; - peers = [{ - public_keys_dir = "/etc/rosenpass/test-rp-device0/peers/server"; - endpoint = "${server.ip4}:9999"; - allowed_ips = "fc00::/64"; - }]; + peers = [ + { + public_keys_dir = "/etc/rosenpass/test-rp-device0/peers/server"; + endpoint = "${server.ip4}:9999"; + allowed_ips = "fc00::/64"; + } + ]; }; config = pkgs.runCommand "config" { } '' mkdir -pv $out - cp -v ${(pkgs.formats.toml {}).generate "test-rp-device0.toml" server_config} $out/server - cp -v ${(pkgs.formats.toml {}).generate "test-rp-device0.toml" client_config} $out/client + cp -v ${(pkgs.formats.toml { }).generate "test-rp-device0.toml" server_config} $out/server + cp -v ${(pkgs.formats.toml { }).generate "test-rp-device0.toml" client_config} $out/client ''; in { @@ -52,88 +56,105 @@ in nodes = let - shared = peer: { config, modulesPath, pkgs, ... }: { - # Need to work around a problem in recent systemd changes. - # It won't be necessary in other distros (for which the systemd file was designed), this is NixOS specific - # https://github.com/NixOS/nixpkgs/issues/258371#issuecomment-1925672767 - # This can potentially be removed in future nixpkgs updates - systemd.packages = [ - (pkgs.runCommand "rp@.service" { } '' - mkdir -p $out/lib/systemd/system - < ${pkgs.rosenpass}/lib/systemd/system/rosenpass.target > $out/lib/systemd/system/rosenpass.target - < ${pkgs.rosenpass}/lib/systemd/system/rp@.service \ - sed 's@^\(\[Service]\)$@\1\nEnvironment=PATH=${pkgs.iproute2}/bin:${pkgs.wireguard-tools}/bin@' | - sed 's@^ExecStartPre=envsubst @ExecStartPre='"${pkgs.envsubst}"'/bin/envsubst @' | - sed 's@^ExecStart=rp @ExecStart='"${pkgs.rosenpass}"'/bin/rp @' > $out/lib/systemd/system/rp@.service - '') - ]; - environment.systemPackages = [ pkgs.wireguard-tools ]; - networking.interfaces.eth1 = { - ipv4.addresses = [{ - address = peer.ip4; - prefixLength = 24; - }]; - ipv6.addresses = [{ - address = peer.ip6; - prefixLength = 64; - }]; + shared = + peer: + { + config, + modulesPath, + pkgs, + ... + }: + { + # Need to work around a problem in recent systemd changes. + # It won't be necessary in other distros (for which the systemd file was designed), this is NixOS specific + # https://github.com/NixOS/nixpkgs/issues/258371#issuecomment-1925672767 + # This can potentially be removed in future nixpkgs updates + systemd.packages = [ + (pkgs.runCommand "rp@.service" { } '' + mkdir -p $out/lib/systemd/system + < ${pkgs.rosenpass}/lib/systemd/system/rosenpass.target > $out/lib/systemd/system/rosenpass.target + < ${pkgs.rosenpass}/lib/systemd/system/rp@.service \ + sed 's@^\(\[Service]\)$@\1\nEnvironment=PATH=${pkgs.iproute2}/bin:${pkgs.wireguard-tools}/bin@' | + sed 's@^ExecStartPre=envsubst @ExecStartPre='"${pkgs.envsubst}"'/bin/envsubst @' | + sed 's@^ExecStart=rp @ExecStart='"${pkgs.rosenpass}"'/bin/rp @' > $out/lib/systemd/system/rp@.service + '') + ]; + environment.systemPackages = [ pkgs.wireguard-tools ]; + networking.interfaces.eth1 = { + ipv4.addresses = [ + { + address = peer.ip4; + prefixLength = 24; + } + ]; + ipv6.addresses = [ + { + address = peer.ip6; + prefixLength = 64; + } + ]; + }; }; - }; in { server = { imports = [ (shared server) ]; - networking.firewall.allowedUDPPorts = [ 9999 server.wg.listen ]; + networking.firewall.allowedUDPPorts = [ + 9999 + server.wg.listen + ]; }; client = { imports = [ (shared client) ]; }; }; - testScript = { ... }: '' - from os import system - rp = "${pkgs.rosenpass}/bin/rp" + testScript = + { ... }: + '' + from os import system + rp = "${pkgs.rosenpass}/bin/rp" - start_all() - - for machine in [server, client]: - machine.wait_for_unit("multi-user.target") - machine.wait_for_unit("network-online.target") - - with subtest("Key, Config, and Service Setup"): - for name, machine, remote in [("server", server, client), ("client", client, server)]: - # create all the keys - system(f"{rp} genkey {name}-sk") - system(f"{rp} pubkey {name}-sk {name}-pk") - - # copy secret keys to our side - for file in ["pqpk", "pqsk", "wgsk"]: - machine.copy_from_host(f"{name}-sk/{file}", f"/etc/rosenpass/test-rp-device0/{file}") - # copy public keys to other side - for file in ["pqpk", "wgpk"]: - remote.copy_from_host(f"{name}-pk/{file}", f"/etc/rosenpass/test-rp-device0/peers/{name}/{file}") - - machine.copy_from_host(f"${config}/{name}", "/etc/rosenpass/test-rp-device0.toml") + start_all() for machine in [server, client]: - machine.succeed("systemctl start rp@test-rp-device0.service") + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("network-online.target") - for machine in [server, client]: - machine.wait_for_unit("rp@test-rp-device0.service") + with subtest("Key, Config, and Service Setup"): + for name, machine, remote in [("server", server, client), ("client", client, server)]: + # create all the keys + system(f"{rp} genkey {name}-sk") + system(f"{rp} pubkey {name}-sk {name}-pk") - with subtest("compare preshared keys"): - client.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); - server.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + # copy secret keys to our side + for file in ["pqpk", "pqsk", "wgsk"]: + machine.copy_from_host(f"{name}-sk/{file}", f"/etc/rosenpass/test-rp-device0/{file}") + # copy public keys to other side + for file in ["pqpk", "wgpk"]: + remote.copy_from_host(f"{name}-pk/{file}", f"/etc/rosenpass/test-rp-device0/peers/{name}/{file}") - def get_psk(m): - psk = m.succeed("wg show test-rp-device0 preshared-keys | awk '{print $2}'") - psk = psk.strip() - assert len(psk.split()) == 1, "Only one PSK" - return psk + machine.copy_from_host(f"${config}/{name}", "/etc/rosenpass/test-rp-device0.toml") - assert get_psk(client) == get_psk(server), "preshared keys need to match" + for machine in [server, client]: + machine.succeed("systemctl start rp@test-rp-device0.service") - with subtest("network test"): - client.succeed("ping -c5 ${server.wg.ip6}") - server.succeed("ping -c5 ${client.wg.ip6}") - ''; + for machine in [server, client]: + machine.wait_for_unit("rp@test-rp-device0.service") + + with subtest("compare preshared keys"): + client.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + server.wait_until_succeeds("wg show all preshared-keys | grep --invert-match none", timeout=5); + + def get_psk(m): + psk = m.succeed("wg show test-rp-device0 preshared-keys | awk '{print $2}'") + psk = psk.strip() + assert len(psk.split()) == 1, "Only one PSK" + return psk + + assert get_psk(client) == get_psk(server), "preshared keys need to match" + + with subtest("network test"): + client.succeed("ping -c5 ${server.wg.ip6}") + server.succeed("ping -c5 ${client.wg.ip6}") + ''; } diff --git a/treefmt.nix b/treefmt.nix index a79c8f3b..49e6c733 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -17,9 +17,7 @@ "*.yaml" "*.yml" ]; - excludes = [ - "supply-chain/*" - ]; + excludes = [ "supply-chain/*" ]; settings = { plugins = [ "${pkgs.nodePackages.prettier-plugin-toml}/lib/node_modules/prettier-plugin-toml/lib/index.js" diff --git a/util/Cargo.toml b/util/Cargo.toml index 49d66fb8..273c0c80 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -25,6 +25,5 @@ mio = { workspace = true } tempfile = { workspace = true } uds = { workspace = true, optional = true, features = ["mio_1xx"] } - [features] experiment_file_descriptor_passing = ["uds"] From 740489544dbc7a95cb6928824d44be3f53abfa3c Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 13:42:24 +0200 Subject: [PATCH 120/174] fix: remove fenix flake input By now it is possible to use cargo-llvm-cov with the nixpkgs built-in llvm tools, thus no need for a nightly rust with the llvm-tools-preview. Therefore, fenix as a dependency is removed. Signed-off-by: wucke13 --- flake.lock | 39 --------------------------------------- flake.nix | 11 +++++------ 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/flake.lock b/flake.lock index bf07e4d3..f275c359 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,5 @@ { "nodes": { - "fenix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1728282832, - "narHash": "sha256-I7AbcwGggf+CHqpyd/9PiAjpIBGTGx5woYHqtwxaV7I=", - "owner": "nix-community", - "repo": "fenix", - "rev": "1ec71be1f4b8f3105c5d38da339cb061fefc43f4", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, "flake-utils": { "inputs": { "systems": "systems" @@ -77,30 +56,12 @@ }, "root": { "inputs": { - "fenix": "fenix", "flake-utils": "flake-utils", "nix-vm-test": "nix-vm-test", "nixpkgs": "nixpkgs", "treefmt-nix": "treefmt-nix" } }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1728249780, - "narHash": "sha256-J269DvCI5dzBmPrXhAAtj566qt0b22TJtF3TIK+tMsI=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "2b750da1a1a2c1d2c70896108d7096089842d877", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index 6031bb3f..a9a35310 100644 --- a/flake.nix +++ b/flake.nix @@ -3,10 +3,6 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; flake-utils.url = "github:numtide/flake-utils"; - # 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"; @@ -150,18 +146,21 @@ 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 + pkgs.rust-bin.stable.latest.complete ]; }; devShells.coverage = pkgs.mkShell { inputsFrom = [ pkgs.rosenpass ]; nativeBuildInputs = [ - inputs.fenix.packages.${system}.complete.toolchain pkgs.cargo-llvm-cov pkgs.grcov + pkgs.rustc.llvmPackages.llvm ]; + env = { + inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA; + }; }; checks = From d496490916e1e88a6af519824524483d56df129c Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 13:54:09 +0200 Subject: [PATCH 121/174] fix: set crate MSRVs to a precise version Before this change, the patch release was left open. This patch pinpoints it exactly, down to the patch release. Signed-off-by: wucke13 --- cipher-traits/Cargo.toml | 2 +- ciphers/Cargo.toml | 2 +- constant-time/Cargo.toml | 2 +- fuzz/Cargo.toml | 2 +- oqs/Cargo.toml | 2 +- rosenpass/Cargo.toml | 2 +- rp/Cargo.toml | 2 +- secret-memory/Cargo.toml | 2 +- to/Cargo.toml | 2 +- util/Cargo.toml | 2 +- wireguard-broker/Cargo.toml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cipher-traits/Cargo.toml b/cipher-traits/Cargo.toml index f2656a18..73a19b83 100644 --- a/cipher-traits/Cargo.toml +++ b/cipher-traits/Cargo.toml @@ -8,7 +8,7 @@ description = "Rosenpass internal traits for cryptographic primitives" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" -rust-version = "1.77" +rust-version = "1.77.0" [dependencies] thiserror = { workspace = true } diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index cee798cf..2e8e5c75 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -8,7 +8,7 @@ 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" +rust-version = "1.77.0" [features] experiment_libcrux_all = [ diff --git a/constant-time/Cargo.toml b/constant-time/Cargo.toml index a4faab54..d9683874 100644 --- a/constant-time/Cargo.toml +++ b/constant-time/Cargo.toml @@ -8,7 +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" +rust-version = "1.77.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index b4c1eb92..f22ff9cc 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -3,7 +3,7 @@ name = "rosenpass-fuzzing" version = "0.0.1" publish = false edition = "2021" -rust-version = "1.77" +rust-version = "1.77.0" [features] experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux_all"] diff --git a/oqs/Cargo.toml b/oqs/Cargo.toml index b562e74c..20fe2ebf 100644 --- a/oqs/Cargo.toml +++ b/oqs/Cargo.toml @@ -8,7 +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" +rust-version = "1.77.0" [dependencies] rosenpass-cipher-traits = { workspace = true } diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index d967e524..2975a1f2 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -8,7 +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" +rust-version = "1.77.0" [[bin]] name = "rosenpass" diff --git a/rp/Cargo.toml b/rp/Cargo.toml index 918e331d..5b3091f3 100644 --- a/rp/Cargo.toml +++ b/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" description = "Build post-quantum-secure VPNs with WireGuard!" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" -rust-version = "1.77" +rust-version = "1.77.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/secret-memory/Cargo.toml b/secret-memory/Cargo.toml index 53c14da8..50f5881f 100644 --- a/secret-memory/Cargo.toml +++ b/secret-memory/Cargo.toml @@ -8,7 +8,7 @@ description = "Rosenpass internal utilities for storing secrets in memory" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" -rust-version = "1.77" +rust-version = "1.77.0" [dependencies] anyhow = { workspace = true } diff --git a/to/Cargo.toml b/to/Cargo.toml index 1692a0ba..69712723 100644 --- a/to/Cargo.toml +++ b/to/Cargo.toml @@ -8,7 +8,7 @@ description = "Flexible destination parameters" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" -rust-version = "1.77" +rust-version = "1.77.0" [dev-dependencies] doc-comment = { workspace = true } diff --git a/util/Cargo.toml b/util/Cargo.toml index 273c0c80..ccc91c87 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -8,7 +8,7 @@ description = "Rosenpass internal utilities" homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" -rust-version = "1.77" +rust-version = "1.77.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/wireguard-broker/Cargo.toml b/wireguard-broker/Cargo.toml index 2ee2d47a..e43585c1 100644 --- a/wireguard-broker/Cargo.toml +++ b/wireguard-broker/Cargo.toml @@ -8,7 +8,7 @@ description = "Rosenpass internal broker that runs as root and supplies exchange homepage = "https://rosenpass.eu/" repository = "https://github.com/rosenpass/rosenpass" readme = "readme.md" -rust-version = "1.77" +rust-version = "1.77.0" [dependencies] thiserror = { workspace = true } From 3ea1a824ccda22165ae4d9cc51573b1056075ac5 Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 13:43:59 +0200 Subject: [PATCH 122/174] feat: add rosenpass MSRV check This check requires a specific toolchain version, and to get that, we introduce oxalica's rust-overlay. Signed-off-by: wucke13 --- flake.lock | 21 +++++++++++++++++++++ flake.nix | 22 +++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/flake.lock b/flake.lock index f275c359..a9dacb80 100644 --- a/flake.lock +++ b/flake.lock @@ -59,9 +59,30 @@ "flake-utils": "flake-utils", "nix-vm-test": "nix-vm-test", "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", "treefmt-nix": "treefmt-nix" } }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1744513456, + "narHash": "sha256-NLVluTmK8d01Iz+WyarQhwFcXpHEwU7m5hH3YQQFJS0=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "730fd8e82799219754418483fabe1844262fd1e2", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index a9a35310..e54f9675 100644 --- a/flake.nix +++ b/flake.nix @@ -7,6 +7,10 @@ nix-vm-test.inputs.nixpkgs.follows = "nixpkgs"; nix-vm-test.inputs.flake-utils.follows = "flake-utils"; + # for rust nightly with llvm-tools-preview + rust-overlay.url = "github:oxalica/rust-overlay"; + rust-overlay.inputs.nixpkgs.follows = "nixpkgs"; + treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; }; @@ -17,6 +21,7 @@ nixpkgs, flake-utils, nix-vm-test, + rust-overlay, treefmt-nix, ... }@inputs: @@ -91,10 +96,14 @@ pkgs = import nixpkgs { inherit system; - # apply our own overlay, overriding/inserting our packages as defined in ./pkgs overlays = [ + # apply our own overlay, overriding/inserting our packages as defined in ./pkgs self.overlays.default + nix-vm-test.overlays.default + + # apply rust-overlay to get specific versions of the rust toolchain for a MSRV check + (import rust-overlay) ]; }; @@ -168,6 +177,17 @@ systemd-rosenpass = pkgs.testers.runNixOSTest ./tests/systemd/rosenpass.nix; systemd-rp = pkgs.testers.runNixOSTest ./tests/systemd/rp.nix; formatting = treefmtEval.config.build.check self; + rosenpass-msrv-check = + let + rosenpassCargoToml = pkgs.lib.trivial.importTOML ./rosenpass/Cargo.toml; + + rustToolchain = pkgs.rust-bin.stable.${rosenpassCargoToml.package.rust-version}.default; + rustPlatform = pkgs.makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; + }; + in + pkgs.rosenpass.override { inherit rustPlatform; }; } // pkgs.lib.optionalAttrs (system == "x86_64-linux") ( import ./tests/legacy-distro-packaging.nix { From 39f99fbfea35714402cede7a13a9631821de3d43 Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 14:02:03 +0200 Subject: [PATCH 123/174] feat: add cargo vet It was missing from the fullEnv nativeBuildInputs. Also, reorder the cargo subcommands in that list alphabetically. Signed-off-by: wucke13 --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index e54f9675..68ae5909 100644 --- a/flake.nix +++ b/flake.nix @@ -149,8 +149,9 @@ inputsFrom = [ pkgs.rosenpass ]; nativeBuildInputs = with pkgs; [ cargo-audit - cargo-release cargo-msrv + cargo-release + cargo-vet rustfmt nodePackages.prettier nushell # for the .ci/gen-workflow-files.nu script From 50501f37fde1fda941219b9d0392f317682441cf Mon Sep 17 00:00:00 2001 From: wucke13 Date: Sun, 13 Apr 2025 14:55:40 +0200 Subject: [PATCH 124/174] chore: update versions in gen-ci script There still is ambiguity between the script's output and the current CI pipelines, usage not recommend. Signed-off-by: wucke13 --- .ci/gen-workflow-files.nu | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.ci/gen-workflow-files.nu b/.ci/gen-workflow-files.nu index f1eaeece..f3b3ff8e 100755 --- a/.ci/gen-workflow-files.nu +++ b/.ci/gen-workflow-files.nu @@ -32,9 +32,9 @@ let systems_map = { # aarch64-darwin # aarch64-linux - i686-linux: ubuntu-latest, + i686-linux: ubicloud-standard-2-ubuntu-2204, x86_64-darwin: macos-13, - x86_64-linux: ubuntu-latest + x86_64-linux: ubicloud-standard-2-ubuntu-2204 } let targets = (get-attr-names ".#packages" @@ -61,14 +61,13 @@ mut release_workflow = { let runner_setup = [ { - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" } { - uses: "cachix/install-nix-action@v22", - with: { nix_path: "nixpkgs=channel:nixos-unstable" } + uses: "cachix/install-nix-action@v30", } { - uses: "cachix/cachix-action@v12", + uses: "cachix/cachix-action@v15", with: { name: rosenpass, authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" @@ -154,7 +153,7 @@ for system in ($targets | columns) { } { name: Release, - uses: "softprops/action-gh-release@v1", + uses: "softprops/action-gh-release@v2", with: { draft: "${{ contains(github.ref_name, 'rc') }}", prerelease: "${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}", @@ -182,7 +181,7 @@ $cachix_workflow.jobs = ($cachix_workflow.jobs | insert $"($system)---whitepaper } { name: "Deploy PDF artifacts", - uses: "peaceiris/actions-gh-pages@v3", + uses: "peaceiris/actions-gh-pages@v4", with: { github_token: "${{ secrets.GITHUB_TOKEN }}", publish_dir: result/, From 3d724f04d418b45e7ba28cb9176bfaf1167d3674 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Wed, 14 May 2025 16:51:54 +0200 Subject: [PATCH 125/174] fix: make CI workflows run after pushing excemptions for cargo-vet This commits changes the CI for dependabot PRs such that initially, only the exemptions for cargo vet are regenerated and pushed to the PR. Only after that, all other workflows are triggered. This ensures that the CI result for dependabot PRs is properly presented on github. --- .github/workflows/dependent-issues.yml | 6 +++ .github/workflows/docker.yaml | 21 ++++++-- .github/workflows/nix-mac.yaml | 12 +++++ .github/workflows/nix.yaml | 43 ++++++++++++++- .github/workflows/qc-mac.yaml | 2 + .github/workflows/qc.yaml | 28 ++++++++++ .../regenerate-cargo-vet-exemptions.yml | 54 +++++++++++++++++++ .github/workflows/regressions.yml | 8 +++ .github/workflows/supply-chain.yml | 30 ++++------- 9 files changed, 178 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/regenerate-cargo-vet-exemptions.yml diff --git a/.github/workflows/dependent-issues.yml b/.github/workflows/dependent-issues.yml index 11a8eeb2..2009b1bd 100644 --- a/.github/workflows/dependent-issues.yml +++ b/.github/workflows/dependent-issues.yml @@ -17,6 +17,10 @@ on: # this action is required to pass before merging. Otherwise, it # can be removed. - synchronize + workflow_run: + workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] + types: + - completed # Schedule a daily check. Useful if you reference cross-repository # issues or pull requests. Otherwise, it can be removed. @@ -25,6 +29,8 @@ on: jobs: check: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} permissions: issues: write pull-requests: write diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index cee9afca..63def6db 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -14,6 +14,15 @@ on: - ".github/workflows/docker.yaml" branches: - "main" + workflow_run: + workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] + types: + - completed + paths: + - "docker/Dockerfile" + - ".github/workflows/docker.yaml" + branches: + - "main" permissions: contents: read @@ -24,6 +33,8 @@ jobs: # 1. BUILD & TEST # -------------------------------- build-and-test-rp: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} strategy: matrix: arch: [amd64, arm64] @@ -118,8 +129,8 @@ jobs: 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' }} + # Only run this job if it s triggered by by a push to the main branch or a version tag. + if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_run' }} # Use a matrix to build for both AMD64 and ARM64 strategy: matrix: @@ -183,8 +194,8 @@ jobs: 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' }} + # Only run this job if it s triggered by by a push to the main branch or a version tag. + if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_run' }} # Use a matrix to build for both AMD64 and ARM64 strategy: matrix: @@ -249,7 +260,7 @@ jobs: needs: - docker-image-rosenpass - docker-image-rp - if: ${{ github.event_name != 'pull_request' }} + if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_run' }} strategy: matrix: target: [rp, rosenpass] diff --git a/.github/workflows/nix-mac.yaml b/.github/workflows/nix-mac.yaml index 73e77cde..0feba925 100644 --- a/.github/workflows/nix-mac.yaml +++ b/.github/workflows/nix-mac.yaml @@ -13,6 +13,8 @@ concurrency: jobs: aarch64-darwin---default: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.default runs-on: - warp-macos-13-arm64-6x @@ -30,6 +32,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.default --print-build-logs aarch64-darwin---release-package: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.release-package runs-on: - warp-macos-13-arm64-6x @@ -49,6 +53,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.release-package --print-build-logs aarch64-darwin---rosenpass: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.rosenpass runs-on: - warp-macos-13-arm64-6x @@ -65,6 +71,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.rosenpass --print-build-logs aarch64-darwin---rp: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.rp runs-on: - warp-macos-13-arm64-6x @@ -81,6 +89,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.rp --print-build-logs aarch64-darwin---rosenpass-oci-image: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.rosenpass-oci-image runs-on: - warp-macos-13-arm64-6x @@ -98,6 +108,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.rosenpass-oci-image --print-build-logs aarch64-darwin---check: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Run Nix checks on aarch64-darwin runs-on: - warp-macos-13-arm64-6x diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 8efda2f0..ced55319 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -6,6 +6,10 @@ on: push: branches: - main + workflow_run: + workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] + types: + - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -13,6 +17,8 @@ concurrency: jobs: i686-linux---default: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build i686-linux.default runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -30,6 +36,8 @@ jobs: - name: Build run: nix build .#packages.i686-linux.default --print-build-logs i686-linux---rosenpass: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build i686-linux.rosenpass runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -46,6 +54,8 @@ jobs: - name: Build run: nix build .#packages.i686-linux.rosenpass --print-build-logs i686-linux---rosenpass-oci-image: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build i686-linux.rosenpass-oci-image runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -63,6 +73,8 @@ jobs: - name: Build run: nix build .#packages.i686-linux.rosenpass-oci-image --print-build-logs i686-linux---check: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Run Nix checks on i686-linux runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -78,6 +90,8 @@ jobs: - name: Check run: nix flake check . --print-build-logs x86_64-linux---default: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.default runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -95,6 +109,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.default --print-build-logs x86_64-linux---proof-proverif: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.proof-proverif runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -112,6 +128,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.proof-proverif --print-build-logs x86_64-linux---proverif-patched: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.proverif-patched runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -128,6 +146,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.proverif-patched --print-build-logs x86_64-linux---release-package: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.release-package runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -171,6 +191,8 @@ jobs: # - name: Build # run: nix build .#packages.aarch64-linux.release-package --print-build-logs x86_64-linux---rosenpass: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -187,6 +209,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass --print-build-logs aarch64-linux---rosenpass: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build aarch64-linux.rosenpass runs-on: - ubicloud-standard-2-arm-ubuntu-2204 @@ -208,6 +232,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-linux.rosenpass --print-build-logs aarch64-linux---rp: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build aarch64-linux.rp runs-on: - ubicloud-standard-2-arm-ubuntu-2204 @@ -229,6 +255,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-linux.rp --print-build-logs x86_64-linux---rosenpass-oci-image: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass-oci-image runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -246,6 +274,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass-oci-image --print-build-logs aarch64-linux---rosenpass-oci-image: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build aarch64-linux.rosenpass-oci-image runs-on: - ubicloud-standard-2-arm-ubuntu-2204 @@ -268,6 +298,8 @@ jobs: - name: Build run: nix build .#packages.aarch64-linux.rosenpass-oci-image --print-build-logs x86_64-linux---rosenpass-static: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass-static runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -284,6 +316,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass-static --print-build-logs x86_64-linux---rp-static: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rp-static runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -300,6 +334,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rp-static --print-build-logs x86_64-linux---rosenpass-static-oci-image: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass-static-oci-image runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -317,6 +353,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass-static-oci-image --print-build-logs x86_64-linux---whitepaper: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.whitepaper runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -333,6 +371,8 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.whitepaper --print-build-logs x86_64-linux---check: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Run Nix checks on x86_64-linux runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -350,7 +390,8 @@ jobs: x86_64-linux---whitepaper-upload: name: Upload whitepaper x86_64-linux runs-on: ubicloud-standard-2-ubuntu-2204 - if: ${{ github.ref == 'refs/heads/main' }} + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ (github.ref == 'refs/heads/main') && (github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run') }} steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 diff --git a/.github/workflows/qc-mac.yaml b/.github/workflows/qc-mac.yaml index 390e56e4..b25ac6de 100644 --- a/.github/workflows/qc-mac.yaml +++ b/.github/workflows/qc-mac.yaml @@ -14,6 +14,8 @@ permissions: jobs: cargo-test-mac: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} runs-on: warp-macos-13-arm64-6x steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 5a251114..435f92a8 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -3,6 +3,10 @@ on: pull_request: push: branches: [main] + workflow_run: + workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] + types: + - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -14,6 +18,8 @@ permissions: jobs: prettier: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -22,6 +28,8 @@ jobs: args: --check . shellcheck: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Shellcheck runs-on: ubicloud-standard-2-ubuntu-2204 steps: @@ -30,6 +38,8 @@ jobs: uses: ludeeus/action-shellcheck@master rustfmt: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Rust Format runs-on: ubicloud-standard-2-ubuntu-2204 steps: @@ -38,6 +48,8 @@ jobs: run: bash format_rust_code.sh --mode check cargo-bench: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -56,6 +68,8 @@ jobs: - run: RUST_MIN_STACK=8388608 cargo bench --workspace --exclude rosenpass-fuzzing mandoc: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: mandoc runs-on: ubicloud-standard-2-ubuntu-2204 steps: @@ -66,6 +80,8 @@ jobs: run: doc/check.sh doc/rp.1 cargo-audit: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -74,6 +90,8 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} cargo-clippy: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -93,6 +111,8 @@ jobs: args: --all-features cargo-doc: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -112,6 +132,8 @@ jobs: - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items cargo-test: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ${{ matrix.os }} strategy: matrix: @@ -135,6 +157,8 @@ jobs: - run: RUST_MIN_STACK=8388608 cargo test --workspace --all-features cargo-test-nix-devshell-x86_64-linux: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: - ubicloud-standard-2-ubuntu-2204 steps: @@ -158,6 +182,8 @@ jobs: - run: nix develop --command cargo test --workspace --all-features cargo-fuzz: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 env: steps: @@ -192,6 +218,8 @@ jobs: cargo fuzz run fuzz_vec_secret_alloc_memfdsec_mallocfb -- -max_total_time=5 codecov: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/regenerate-cargo-vet-exemptions.yml b/.github/workflows/regenerate-cargo-vet-exemptions.yml new file mode 100644 index 00000000..fd71e82a --- /dev/null +++ b/.github/workflows/regenerate-cargo-vet-exemptions.yml @@ -0,0 +1,54 @@ +name: Regenerate cargo-vet exemptions for dependabot-PRs +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + regen-cargo-vet-exemptions: + if: ${{ github.actor == 'dependabot[bot]' }} + name: Regenerate exemptions for cargo-vet for dependabot-PRs + 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 + run: cargo vet regenerate exemptions + - name: Check for changes in case of dependabot PR + run: git diff --exit-code || echo "Changes detected, committing..." + - name: Commit and push changes for dependabot PRs + if: ${{ success() }} + 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 }} diff --git a/.github/workflows/regressions.yml b/.github/workflows/regressions.yml index 1f19746f..c57f2921 100644 --- a/.github/workflows/regressions.yml +++ b/.github/workflows/regressions.yml @@ -3,6 +3,10 @@ on: pull_request: push: branches: [main] + workflow_run: + workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] + types: + - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -14,6 +18,8 @@ permissions: jobs: multi-peer: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -25,6 +31,8 @@ jobs: [ $(ls -1 output/ate/out | wc -l) -eq 100 ] boot-race: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index effbb594..e83e9c2f 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -3,6 +3,10 @@ on: pull_request: push: branches: [main] + workflow_run: + workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] + types: + - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -10,12 +14,16 @@ concurrency: jobs: cargo-deny: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} 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: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Supply Chain Report runs-on: ubuntu-latest steps: @@ -44,10 +52,10 @@ jobs: 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: + # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions + if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Vet Dependencies runs-on: ubuntu-latest - permissions: - contents: write steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 @@ -69,23 +77,5 @@ jobs: 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 From e90bc1b636705107b5c1cee9c36cba8f9957c10f Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 13 May 2025 17:26:53 +0200 Subject: [PATCH 126/174] feat(sha+paper): Add reference for SHAKE256 to biblography --- papers/references.bib | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/papers/references.bib b/papers/references.bib index 96f524a9..16e788b9 100644 --- a/papers/references.bib +++ b/papers/references.bib @@ -196,3 +196,13 @@ Vadim Lyubashevsky and John M. Schanck and Peter Schwabe and Gregor Seiler and D type = {NIST Post-Quantum Cryptography Selected Algorithm}, url = {https://pq-crystals.org/kyber/} } + + +@misc{SHAKE256, + author = "National Institute of Standards and Technology", + title = "FIPS PUB 202: SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions", + year = {2015}, + month = {August}, + doi = {10.6028/NIST.FIPS.202} +} + From 37e71a4051495f2bf346dc08e23df601f2431ff0 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 22 May 2025 14:04:30 +0200 Subject: [PATCH 127/174] feat(sha3+paper): add information on how SHAKE256 is used in rosenpass to the whitepaper --- papers/whitepaper.md | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/papers/whitepaper.md b/papers/whitepaper.md index 266dcbf6..6397e367 100644 --- a/papers/whitepaper.md +++ b/papers/whitepaper.md @@ -34,7 +34,7 @@ abstract: | Rosenpass inherits most security properties from Post-Quantum WireGuard (PQWG). The security properties mentioned here are covered by the symbolic analysis in the Rosenpass repository. ## Secrecy -Three key encapsulations using the keypairs `sski`/`spki`, `sskr`/`spkr`, and `eski`/`epki` provide secrecy (see Section \ref{variables} for an introduction of the variables). Their respective ciphertexts are called `scti`, `sctr`, and `ectr` and the resulting keys are called `spti`, `sptr`, `epti`. A single secure encapsulation is sufficient to provide secrecy. We use two different KEMs (Key Encapsulation Mechanisms; see section \ref{skem}): Kyber and Classic McEliece. +Three key encapsulations using the keypairs `sski`/`spki`, `sskr`/`spkr`, and `eski`/`epki` provide secrecy (see Section \ref{variables} for an introduction of the variables). Their respective ciphertexts are called `scti`, `sctr`, and `ectr` and the resulting keys are called `spti`, `sptr`, `epti`. A single secure encapsulation is sufficient to provide secrecy. We use two different KEMs (Key Encapsulation Mechanisms; see Section \ref{skem}): Kyber and Classic McEliece. ## Authenticity @@ -64,9 +64,12 @@ Note that while Rosenpass is secure against state disruption, using it does not All symmetric keys and hash values used in Rosenpass are 32 bytes long. -### Hash +### Hash {#hash} -A keyed hash function with one 32-byte input, one variable-size input, and one 32-byte output. As keyed hash function we use the HMAC construction [@rfc_hmac] with BLAKE2s [@rfc_blake2] as the inner hash function. +A keyed hash function with one 32-byte input, one variable-size input, and one 32-byte output. As keyed hash function we offer two options that can be configured on a peer-basis, with Blake2s being the default: + +1. the HMAC construction [@rfc_hmac] with BLAKE2s [@rfc_blake2] as the inner hash function. +2. the SHAKE256 extendable output function (XOF) [@SHAKE256] truncated to a 32-byte output. The result is produced be concatenating the 32-byte input with the variable-size input in this order. ```pseudorust hash(key, data) -> key @@ -172,6 +175,8 @@ Rosenpass uses a cryptographic hash function for multiple purposes: * Key derivation during and after the handshake * Computing the additional data for the biscuit encryption, to provide some privacy for its contents +Recall from Section \ref{hash} that rosenpass supports using either BLAKE2s or SHAKE256 as hash function, which can be configured for each peer ID. However, as noted above, rosenpass uses a hash function to compute the peer ID and thus also to access the configuration for a peer ID. This is an issue when receiving an `InitHello`-message, because the correct hash function is not known when a responder receives this message and at the same the responders needs it in order to compute the peer ID and by that also identfy the hash function for that peer. The reference implementation resolves this issue by first trying to derive the peer ID using SHAKE256. If that does not work (i.e. leads to an AEAD decryption error), the reference implementation tries again with BLAKE2s. The reference implementation verifies that the hash function matches the one confgured for the peer. Similarly, if the correct peer ID is not cached when receiving an InitConf message, the reference implementation proceeds in the same manner. + Using one hash function for multiple purposes can cause real-world security issues and even key recovery attacks [@oraclecloning]. We choose a tree-based domain separation scheme based on a keyed hash function – the previously introduced primitive `hash` – to make sure all our hash function calls can be seen as distinct. \setupimage{landscape,fullpage,label=img:HashingTree} @@ -179,13 +184,13 @@ Using one hash function for multiple purposes can cause real-world security issu Each tree node $\circ{}$ in Figure 3 represents the application of the keyed hash function, using the previous chaining key value as first parameter. The root of the tree is the zero key. In level one, the `PROTOCOL` identifier is applied to the zero key to generate a label unique across cryptographic protocols (unless the same label is deliberately used elsewhere). In level two, purpose identifiers are applied to the protocol label to generate labels to use with each separate hash function application within the Rosenpass protocol. The following layers contain the inputs used in each separate usage of the hash function: Beneath the identifiers `"mac"`, `"cookie"`, `"peer id"`, and `"biscuit additional data"` are hash functions or message authentication codes with a small number of inputs. The second, third, and fourth column in Figure 3 cover the long sequential branch beneath the identifier `"chaining key init"` representing the entire protocol execution, one column for each message processed during the handshake. The leaves beneath `"chaining key extract"` in the left column represent pseudo-random labels for use when extracting values from the chaining key during the protocol execution. These values such as `mix >` appear as outputs in the left column, and then as inputs `< mix` in the other three columns. -The protocol identifier is defined as follows: +The protocol identifier depends on the hash function used with the respective peer is defined as follows if BLAKE2s [@rfc_blake2] is used: ```pseudorust PROTOCOL = "rosenpass 1 rosenpass.eu aead=chachapoly1305 hash=blake2s ekem=kyber512 skem=mceliece460896 xaead=xchachapoly1305" ``` -Since every tree node represents a sequence of `hash` calls, the node beneath `"handshake encryption"` called `hs_enc` can be written as follows: +If SHAKE256 [@SHAKE256] is used, `blake2s` is replaced by `shake256` in `PROTOCOL`. Since every tree node represents a sequence of `hash` calls, the node beneath `"handshake encryption"` called `hs_enc` can be written as follows: ```pseudorust hs_enc = hash(hash(hash(0, PROTOCOL), "chaining key extract"), "handshake encryption") @@ -233,6 +238,7 @@ For each peer, the server stores: * `psk` – The pre-shared key used with the peer * `spkt` – The peer's public key * `biscuit_used` – The `biscuit_no` from the last biscuit accepted for the peer as part of InitConf processing +* `hash_function` – The hash function, SHAKE256 or BLAKE2s, used with the peer. ### Handshake State and Biscuits @@ -452,14 +458,14 @@ For an initiator, Rosenpass ignores all messages when under load. #### Cookie Reply Message -The cookie reply message is sent by the responder on receiving an InitHello message when under load. It consists of the `sidi` of the initiator, a random 24-byte bitstring `nonce` and encrypting `cookie_value` into a `cookie_encrypted` reply field which consists of the following: +The cookie reply message is sent by the responder on receiving an InitHello message when under load. It consists of the `sidi` of the initiator, a random 24-byte bitstring `nonce` and encrypting `cookie_value` into a `cookie_encrypted` reply field, which consists of the following: ```pseudorust cookie_value = lhash("cookie-value", cookie_secret, initiator_host_info)[0..16] cookie_encrypted = XAEAD(lhash("cookie-key", spkm), nonce, cookie_value, mac_peer) ``` -where `cookie_secret` is a secret variable that changes every two minutes to a random value. `initiator_host_info` is used to identify the initiator host, and is implementation-specific for the client. This paramaters used to identify the host must be carefully chosen to ensure there is a unique mapping, especially when using IPv4 and IPv6 addresses to identify the host (such as taking care of IPv6 link-local addresses). `cookie_value` is a truncated 16 byte value from the above hash operation. `mac_peer` is the `mac` field of the peer's handshake message to which message is the reply. +where `cookie_secret` is a secret variable that changes every two minutes to a random value. Moreover, `lhash` is always instantiated with SHAKE256 when computing `cookie_value` for compatability reasons. `initiator_host_info` is used to identify the initiator host, and is implementation-specific for the client. This paramaters used to identify the host must be carefully chosen to ensure there is a unique mapping, especially when using IPv4 and IPv6 addresses to identify the host (such as taking care of IPv6 link-local addresses). `cookie_value` is a truncated 16 byte value from the above hash operation. `mac_peer` is the `mac` field of the peer's handshake message to which message is the reply. #### Envelope `mac` Field @@ -495,7 +501,7 @@ However, when the responder is under load, it may reject InitHello messages with This whitepaper does not mandate any specific mechanism to detect responder contention (also mentioned as the under load condition) that would trigger use of the cookie mechanism. -For the reference implemenation, Rosenpass has derived inspiration from the linux implementation of Wireguard. This implementation suggests that the reciever keep track of the number of messages it is processing at a given time. +For the reference implemenation, Rosenpass has derived inspiration from the Linux implementation of Wireguard. This implementation suggests that the reciever keep track of the number of messages it is processing at a given time. On receiving an incoming message, if the length of the message queue to be processed exceeds a threshold `MAX_QUEUED_INCOMING_HANDSHAKES_THRESHOLD`, the client is considered under load and its state is stored as under load. In addition, the timestamp of this instant when the client was last under load is stored. When recieving subsequent messages, if the client is still in an under load state, the client will check if the time ellpased since the client was last under load has exceeded `LAST_UNDER_LOAD_WINDOW` seconds. If this is the case, the client will update its state to normal operation, and process the message in a normal fashion. @@ -526,6 +532,24 @@ When the responder is under load and it recieves an InitConf message, the messag ### 0.3.x +#### 2025-05-22 - SHAKE256 keyed hash +\vspace{0.5em} + +Author: David Niehues +PR: [#653](https://github.com/rosenpass/rosenpass/pull/653) + +\vspace{0.5em} + +We document the support for SHAKE256 with prepended key as an alternative to BLAKE2s with HMAC. + +Previously, BLAKE2s with HMAC was the only supported keyed hash function. Recently, SHAKE256 was added as an option. SHAKE256 is used as a keyed hash function by prepending the key to the variable-length data and then evaluating SHAKE256. +In order to maintain compatablity without introducing an explcit version number in the protocol messages, SHAKE256 is truncated to 32 bytes. In the update to the whitepaper, we explain where and how SHAKE256 is used. That is: + +1. We explain that SHAKE256 or BLAKE2s can be configured to be used on a peer basis. +2. We explain under which circumstances, the reference implementation tries both hash functions for messages in order to determine the correct hash function. +3. We document that the cookie mechanism always uses SHAKE256. + + #### 2024-10-30 – InitConf retransmission updates \vspace{0.5em} From dd105a4491d8026869cd9264fdbe65c0dc4352ec Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Fri, 30 May 2025 13:15:37 +0200 Subject: [PATCH 128/174] Revert "fix: make CI workflows run after pushing excemptions for cargo-vet (#652)" This reverts commit bbd7e7bb729af55040684ba0a998c9523a7fa898, reversing changes made to db9d0b642b213fb4d0d1759d2c8cbffed669f922. --- .github/workflows/dependent-issues.yml | 6 --- .github/workflows/docker.yaml | 21 ++------ .github/workflows/nix-mac.yaml | 12 ----- .github/workflows/nix.yaml | 43 +-------------- .github/workflows/qc-mac.yaml | 2 - .github/workflows/qc.yaml | 28 ---------- .../regenerate-cargo-vet-exemptions.yml | 54 ------------------- .github/workflows/regressions.yml | 8 --- .github/workflows/supply-chain.yml | 30 +++++++---- 9 files changed, 26 insertions(+), 178 deletions(-) delete mode 100644 .github/workflows/regenerate-cargo-vet-exemptions.yml diff --git a/.github/workflows/dependent-issues.yml b/.github/workflows/dependent-issues.yml index 2009b1bd..11a8eeb2 100644 --- a/.github/workflows/dependent-issues.yml +++ b/.github/workflows/dependent-issues.yml @@ -17,10 +17,6 @@ on: # this action is required to pass before merging. Otherwise, it # can be removed. - synchronize - workflow_run: - workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] - types: - - completed # Schedule a daily check. Useful if you reference cross-repository # issues or pull requests. Otherwise, it can be removed. @@ -29,8 +25,6 @@ on: jobs: check: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} permissions: issues: write pull-requests: write diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 63def6db..cee9afca 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -14,15 +14,6 @@ on: - ".github/workflows/docker.yaml" branches: - "main" - workflow_run: - workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] - types: - - completed - paths: - - "docker/Dockerfile" - - ".github/workflows/docker.yaml" - branches: - - "main" permissions: contents: read @@ -33,8 +24,6 @@ jobs: # 1. BUILD & TEST # -------------------------------- build-and-test-rp: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} strategy: matrix: arch: [amd64, arm64] @@ -129,8 +118,8 @@ jobs: docker-image-rp: needs: - build-and-test-rp - # Only run this job if it s triggered by by a push to the main branch or a version tag. - if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_run' }} + # 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: @@ -194,8 +183,8 @@ jobs: docker-image-rosenpass: needs: - build-and-test-rp - # Only run this job if it s triggered by by a push to the main branch or a version tag. - if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_run' }} + # 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: @@ -260,7 +249,7 @@ jobs: needs: - docker-image-rosenpass - docker-image-rp - if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_run' }} + if: ${{ github.event_name != 'pull_request' }} strategy: matrix: target: [rp, rosenpass] diff --git a/.github/workflows/nix-mac.yaml b/.github/workflows/nix-mac.yaml index 0feba925..73e77cde 100644 --- a/.github/workflows/nix-mac.yaml +++ b/.github/workflows/nix-mac.yaml @@ -13,8 +13,6 @@ concurrency: jobs: aarch64-darwin---default: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.default runs-on: - warp-macos-13-arm64-6x @@ -32,8 +30,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.default --print-build-logs aarch64-darwin---release-package: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.release-package runs-on: - warp-macos-13-arm64-6x @@ -53,8 +49,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.release-package --print-build-logs aarch64-darwin---rosenpass: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.rosenpass runs-on: - warp-macos-13-arm64-6x @@ -71,8 +65,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.rosenpass --print-build-logs aarch64-darwin---rp: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.rp runs-on: - warp-macos-13-arm64-6x @@ -89,8 +81,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.rp --print-build-logs aarch64-darwin---rosenpass-oci-image: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Build aarch64-darwin.rosenpass-oci-image runs-on: - warp-macos-13-arm64-6x @@ -108,8 +98,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-darwin.rosenpass-oci-image --print-build-logs aarch64-darwin---check: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} name: Run Nix checks on aarch64-darwin runs-on: - warp-macos-13-arm64-6x diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index ced55319..8efda2f0 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -6,10 +6,6 @@ on: push: branches: - main - workflow_run: - workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] - types: - - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -17,8 +13,6 @@ concurrency: jobs: i686-linux---default: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build i686-linux.default runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -36,8 +30,6 @@ jobs: - name: Build run: nix build .#packages.i686-linux.default --print-build-logs i686-linux---rosenpass: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build i686-linux.rosenpass runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -54,8 +46,6 @@ jobs: - name: Build run: nix build .#packages.i686-linux.rosenpass --print-build-logs i686-linux---rosenpass-oci-image: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build i686-linux.rosenpass-oci-image runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -73,8 +63,6 @@ jobs: - name: Build run: nix build .#packages.i686-linux.rosenpass-oci-image --print-build-logs i686-linux---check: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Run Nix checks on i686-linux runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -90,8 +78,6 @@ jobs: - name: Check run: nix flake check . --print-build-logs x86_64-linux---default: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.default runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -109,8 +95,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.default --print-build-logs x86_64-linux---proof-proverif: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.proof-proverif runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -128,8 +112,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.proof-proverif --print-build-logs x86_64-linux---proverif-patched: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.proverif-patched runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -146,8 +128,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.proverif-patched --print-build-logs x86_64-linux---release-package: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.release-package runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -191,8 +171,6 @@ jobs: # - name: Build # run: nix build .#packages.aarch64-linux.release-package --print-build-logs x86_64-linux---rosenpass: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -209,8 +187,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass --print-build-logs aarch64-linux---rosenpass: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build aarch64-linux.rosenpass runs-on: - ubicloud-standard-2-arm-ubuntu-2204 @@ -232,8 +208,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-linux.rosenpass --print-build-logs aarch64-linux---rp: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build aarch64-linux.rp runs-on: - ubicloud-standard-2-arm-ubuntu-2204 @@ -255,8 +229,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-linux.rp --print-build-logs x86_64-linux---rosenpass-oci-image: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass-oci-image runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -274,8 +246,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass-oci-image --print-build-logs aarch64-linux---rosenpass-oci-image: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build aarch64-linux.rosenpass-oci-image runs-on: - ubicloud-standard-2-arm-ubuntu-2204 @@ -298,8 +268,6 @@ jobs: - name: Build run: nix build .#packages.aarch64-linux.rosenpass-oci-image --print-build-logs x86_64-linux---rosenpass-static: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass-static runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -316,8 +284,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass-static --print-build-logs x86_64-linux---rp-static: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rp-static runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -334,8 +300,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rp-static --print-build-logs x86_64-linux---rosenpass-static-oci-image: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.rosenpass-static-oci-image runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -353,8 +317,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.rosenpass-static-oci-image --print-build-logs x86_64-linux---whitepaper: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Build x86_64-linux.whitepaper runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -371,8 +333,6 @@ jobs: - name: Build run: nix build .#packages.x86_64-linux.whitepaper --print-build-logs x86_64-linux---check: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Run Nix checks on x86_64-linux runs-on: - ubicloud-standard-2-ubuntu-2204 @@ -390,8 +350,7 @@ jobs: x86_64-linux---whitepaper-upload: name: Upload whitepaper x86_64-linux runs-on: ubicloud-standard-2-ubuntu-2204 - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ (github.ref == 'refs/heads/main') && (github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run') }} + if: ${{ github.ref == 'refs/heads/main' }} steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v30 diff --git a/.github/workflows/qc-mac.yaml b/.github/workflows/qc-mac.yaml index b25ac6de..390e56e4 100644 --- a/.github/workflows/qc-mac.yaml +++ b/.github/workflows/qc-mac.yaml @@ -14,8 +14,6 @@ permissions: jobs: cargo-test-mac: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions or explicitly called - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' || github.event_name == 'workflow_call'}} runs-on: warp-macos-13-arm64-6x steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 435f92a8..5a251114 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -3,10 +3,6 @@ on: pull_request: push: branches: [main] - workflow_run: - workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] - types: - - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -18,8 +14,6 @@ permissions: jobs: prettier: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -28,8 +22,6 @@ jobs: args: --check . shellcheck: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Shellcheck runs-on: ubicloud-standard-2-ubuntu-2204 steps: @@ -38,8 +30,6 @@ jobs: uses: ludeeus/action-shellcheck@master rustfmt: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Rust Format runs-on: ubicloud-standard-2-ubuntu-2204 steps: @@ -48,8 +38,6 @@ jobs: run: bash format_rust_code.sh --mode check cargo-bench: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -68,8 +56,6 @@ jobs: - run: RUST_MIN_STACK=8388608 cargo bench --workspace --exclude rosenpass-fuzzing mandoc: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: mandoc runs-on: ubicloud-standard-2-ubuntu-2204 steps: @@ -80,8 +66,6 @@ jobs: run: doc/check.sh doc/rp.1 cargo-audit: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -90,8 +74,6 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} cargo-clippy: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -111,8 +93,6 @@ jobs: args: --all-features cargo-doc: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -132,8 +112,6 @@ jobs: - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items cargo-test: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ${{ matrix.os }} strategy: matrix: @@ -157,8 +135,6 @@ jobs: - run: RUST_MIN_STACK=8388608 cargo test --workspace --all-features cargo-test-nix-devshell-x86_64-linux: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: - ubicloud-standard-2-ubuntu-2204 steps: @@ -182,8 +158,6 @@ jobs: - run: nix develop --command cargo test --workspace --all-features cargo-fuzz: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 env: steps: @@ -218,8 +192,6 @@ jobs: cargo fuzz run fuzz_vec_secret_alloc_memfdsec_mallocfb -- -max_total_time=5 codecov: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/regenerate-cargo-vet-exemptions.yml b/.github/workflows/regenerate-cargo-vet-exemptions.yml deleted file mode 100644 index fd71e82a..00000000 --- a/.github/workflows/regenerate-cargo-vet-exemptions.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Regenerate cargo-vet exemptions for dependabot-PRs -on: - pull_request: - push: - branches: [main] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - regen-cargo-vet-exemptions: - if: ${{ github.actor == 'dependabot[bot]' }} - name: Regenerate exemptions for cargo-vet for dependabot-PRs - 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 - run: cargo vet regenerate exemptions - - name: Check for changes in case of dependabot PR - run: git diff --exit-code || echo "Changes detected, committing..." - - name: Commit and push changes for dependabot PRs - if: ${{ success() }} - 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 }} diff --git a/.github/workflows/regressions.yml b/.github/workflows/regressions.yml index c57f2921..1f19746f 100644 --- a/.github/workflows/regressions.yml +++ b/.github/workflows/regressions.yml @@ -3,10 +3,6 @@ on: pull_request: push: branches: [main] - workflow_run: - workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] - types: - - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -18,8 +14,6 @@ permissions: jobs: multi-peer: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 @@ -31,8 +25,6 @@ jobs: [ $(ls -1 output/ate/out | wc -l) -eq 100 ] boot-race: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index e83e9c2f..effbb594 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -3,10 +3,6 @@ on: pull_request: push: branches: [main] - workflow_run: - workflows: [Regenerate cargo-vet exemptions for dependabot-PRs] - types: - - completed concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -14,16 +10,12 @@ concurrency: jobs: cargo-deny: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} 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: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Supply Chain Report runs-on: ubuntu-latest steps: @@ -52,10 +44,10 @@ jobs: 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: - # Only run this for dependabot PRs if it's triggered by the workflow to regenerate cargo-vet exemptions - if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'workflow_run' }} name: Vet Dependencies runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 @@ -77,5 +69,23 @@ jobs: 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 From 7e590dd30e55311934667bc3a7abefc52966fde4 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:32:52 +0200 Subject: [PATCH 129/174] fix(CI+dependabot): add instructions on how to set up a repository to work with the supply-chain+dependabot accomodations --- CI.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 CI.md diff --git a/CI.md b/CI.md new file mode 100644 index 00000000..f0edb3fd --- /dev/null +++ b/CI.md @@ -0,0 +1,20 @@ +# Continuous Integration + +This repository's CI uses non-standard mechanisms to harmonize the usage of `dependabot` together with [`cargo vet`](https://mozilla.github.io/cargo-vet/). Since cargo-vet audits for new versions of crates are rarely immediately available once dependabots bumps the version, +the exemptions for `cargo vet` have to be regenerated for each push request opened by dependabot. To make this work, some setup is neccessary to setup the CI. The required steps are as follows: + +1. Create a mew user on github. For the purpose of these instructions, we will assume that its mail address is `ci@example.com` and that its username is `ci-bot`. Protect this user account as you would any other user account that you intend to gve write permissions to. For example, setup MFA or protect the emal address of the user. Make sure to verify your e-mail. +2. Grant `ci-bot` write access to the repository. +3. Create a new personal access token for `ci-bot`. That is, in the settings for `ci-bot`, select "Developer settings" -> "Personal Access tokens" -> "Fine-grained tokens". Then click on "Generate new token". Enter a name of your choosing and choose an expiration date that you feel comfortable with. A shorter expiration period will requrie more manual management by you but is more secure than a longer one. Under "Repository permissions", grant "Read and write"-access to the "Contens" premission for the token. Grant no other permissions to the token, except for the read-only access to the "Metadata" permission, which is mandatory. Then generate the token and copy it for the next step. +4. Now, with your account that has administrative permissions for the repository, open the settings page for the repository and select "Secrets and variables" -> "Actions" and click "New repository secret". In the name field enter "CI_BOT_PAT". This name is mandatory, since it is explicitly referenced in the supply-chain workflow. Below, enter the token that was generated in the previous step. + +## What this does + +For the `cargo vet` check in the CI for dependabot, the `cargo vet`-exemptions have to automatically be regenerated, because otherwise this CI job will always fail for dependabot PRs. After the exemptions have been regenerated, they need to be commited and pushed to the PR. This invalidates the CI run that pushed the commit so that it does not show up in the PR anymore but does not trigger a new CI run. This is a [protection by Github](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow) to prevent infinite loops. However, in this case it prevents us from having a proper CI run for dependabot PRs. The solution to this is to execute `push` operation with a personal access token. + +## Preventing infinite loops + +The CI is configured to avoid infinite loops by only regenerating and pushing the `cargo vet` exemptions if at least one of the following conditions is met: + +- The last commit was performed by dependabot +- The last commit message ends in `--regenerate-exemptions` From 49f384c38003f3978aa216fa4d1aa2bc7811f5c7 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:34:48 +0200 Subject: [PATCH 130/174] fix(CI+dependabot): adapt the supply-chain workflow for cargo-vet to work with dependabot, i.e. regenerating exemptions for dependabot and restart the CI afterwards --- .github/workflows/supply-chain.yml | 63 ++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index effbb594..d785e6fc 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -28,6 +28,10 @@ jobs: ~/.cargo/registry/cache/ ~/.cache/cargo-supply-chain/ key: cargo-supply-chain-cache + - name: Install nightly toolchain # Since we are running/compiling cargo-vet, we should rely on the stable toolchain. + run: | + rustup toolchain install nightly + rustup override set nightly - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-supply-chain @@ -50,6 +54,8 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + with: + token: ${{ secrets.CI_BOT_PAT }} - uses: actions/cache@v4 with: path: | @@ -57,10 +63,10 @@ jobs: ~/.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. + - name: Install nightly toolchain # Since we are running/compiling cargo-vet, we should rely on the stable toolchain. run: | - rustup toolchain install stable - rustup default stable + rustup toolchain install nightly + rustup override set nightly - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-vet @@ -69,23 +75,48 @@ jobs: 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]' + - name: Check if last commit was by Dependabot 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 }} + COMMIT_AUTHOR=$(gh api repos/${{ github.repository }}/commits/${{ github.sha }} --jq .author.login) + if [[ "$COMMIT_AUTHOR" == "dependabot[bot]" ]]; then + echo "The last commit was made by dependabot" + IS_DEPENDABOT=true + else + echo "The last commit was not made by dependabot" + IS_DEPENDABOT=false + fi + echo "IS_DEPENDABOT=$IS_DEPENDABOT" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check if the last commit's message ends in "--regenerate-exemptions" + run: | + # Get commit message + COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s") + if [[ "$COMMIT_MESSAGE" == *"--regenerate-exemptions" ]]; then + echo "The last commit message ends in --regenerate-exemptions" + REGEN_EXEMP=true + else + echo "The last commit message does not end in --regenerate-exemptions" + REGEN_EXEMP=false + fi + echo "REGEN_EXEMP=$REGEN_EXEMP" >> $GITHUB_ENV + - name: Regenerate vet exemptions for dependabot PRs + if: github.actor == 'dependabot[bot]' && (env.IS_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested + run: cargo vet regenerate exemptions + - name: Check for changes in case of dependabot PR + if: github.actor == 'dependabot[bot]' && (env.IS_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested + run: git diff --exit-code || echo "Changes detected, committing..." + - name: Commit and push changes for dependabot PRs + if: success() && github.actor == 'dependabot[bot]' && (env.IS_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: Regenerate cargo vet exemptions + commit_user_name: rosenpass-ci-bot[bot] + commit_user_email: noreply@rosenpass.eu + commit_author: Rosenpass CI Bot + env: + GITHUB_TOKEN: ${{ secrets.CI_BOT_PAT }} - name: Invoke cargo-vet run: cargo vet --locked From 6c49f38e298cad73e1555b6b68da5341d1128cb4 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 18 Jun 2025 19:42:30 +0200 Subject: [PATCH 131/174] Revert "Make the CI restart once cargo-vet exemptions for dependabot have been pushed (#658)" This reverts commit e021b9f11d9935ae0e452730f8a7d92944ab4006, reversing changes made to d98815fa7f8dbe6fd2ea2e024e17447bf587d134. --- .github/workflows/supply-chain.yml | 63 ++++++++---------------------- CI.md | 20 ---------- 2 files changed, 16 insertions(+), 67 deletions(-) delete mode 100644 CI.md diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index d785e6fc..effbb594 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -28,10 +28,6 @@ jobs: ~/.cargo/registry/cache/ ~/.cache/cargo-supply-chain/ key: cargo-supply-chain-cache - - name: Install nightly toolchain # Since we are running/compiling cargo-vet, we should rely on the stable toolchain. - run: | - rustup toolchain install nightly - rustup override set nightly - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-supply-chain @@ -54,8 +50,6 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - with: - token: ${{ secrets.CI_BOT_PAT }} - uses: actions/cache@v4 with: path: | @@ -63,10 +57,10 @@ jobs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ key: cargo-vet-cache - - name: Install nightly toolchain # Since we are running/compiling cargo-vet, we should rely on the stable toolchain. + - name: Install stable toolchain # Since we are running/compiling cargo-vet, we should rely on the stable toolchain. run: | - rustup toolchain install nightly - rustup override set nightly + rustup toolchain install stable + rustup default stable - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-vet @@ -75,48 +69,23 @@ jobs: 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: Check if last commit was by Dependabot + - 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 }} - COMMIT_AUTHOR=$(gh api repos/${{ github.repository }}/commits/${{ github.sha }} --jq .author.login) - if [[ "$COMMIT_AUTHOR" == "dependabot[bot]" ]]; then - echo "The last commit was made by dependabot" - IS_DEPENDABOT=true - else - echo "The last commit was not made by dependabot" - IS_DEPENDABOT=false - fi - echo "IS_DEPENDABOT=$IS_DEPENDABOT" >> $GITHUB_ENV + 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: Check if the last commit's message ends in "--regenerate-exemptions" - run: | - # Get commit message - COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s") - if [[ "$COMMIT_MESSAGE" == *"--regenerate-exemptions" ]]; then - echo "The last commit message ends in --regenerate-exemptions" - REGEN_EXEMP=true - else - echo "The last commit message does not end in --regenerate-exemptions" - REGEN_EXEMP=false - fi - echo "REGEN_EXEMP=$REGEN_EXEMP" >> $GITHUB_ENV - - name: Regenerate vet exemptions for dependabot PRs - if: github.actor == 'dependabot[bot]' && (env.IS_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested - run: cargo vet regenerate exemptions - - name: Check for changes in case of dependabot PR - if: github.actor == 'dependabot[bot]' && (env.IS_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested - run: git diff --exit-code || echo "Changes detected, committing..." - - name: Commit and push changes for dependabot PRs - if: success() && github.actor == 'dependabot[bot]' && (env.IS_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: Regenerate cargo vet exemptions - commit_user_name: rosenpass-ci-bot[bot] - commit_user_email: noreply@rosenpass.eu - commit_author: Rosenpass CI Bot - env: - GITHUB_TOKEN: ${{ secrets.CI_BOT_PAT }} - name: Invoke cargo-vet run: cargo vet --locked diff --git a/CI.md b/CI.md deleted file mode 100644 index f0edb3fd..00000000 --- a/CI.md +++ /dev/null @@ -1,20 +0,0 @@ -# Continuous Integration - -This repository's CI uses non-standard mechanisms to harmonize the usage of `dependabot` together with [`cargo vet`](https://mozilla.github.io/cargo-vet/). Since cargo-vet audits for new versions of crates are rarely immediately available once dependabots bumps the version, -the exemptions for `cargo vet` have to be regenerated for each push request opened by dependabot. To make this work, some setup is neccessary to setup the CI. The required steps are as follows: - -1. Create a mew user on github. For the purpose of these instructions, we will assume that its mail address is `ci@example.com` and that its username is `ci-bot`. Protect this user account as you would any other user account that you intend to gve write permissions to. For example, setup MFA or protect the emal address of the user. Make sure to verify your e-mail. -2. Grant `ci-bot` write access to the repository. -3. Create a new personal access token for `ci-bot`. That is, in the settings for `ci-bot`, select "Developer settings" -> "Personal Access tokens" -> "Fine-grained tokens". Then click on "Generate new token". Enter a name of your choosing and choose an expiration date that you feel comfortable with. A shorter expiration period will requrie more manual management by you but is more secure than a longer one. Under "Repository permissions", grant "Read and write"-access to the "Contens" premission for the token. Grant no other permissions to the token, except for the read-only access to the "Metadata" permission, which is mandatory. Then generate the token and copy it for the next step. -4. Now, with your account that has administrative permissions for the repository, open the settings page for the repository and select "Secrets and variables" -> "Actions" and click "New repository secret". In the name field enter "CI_BOT_PAT". This name is mandatory, since it is explicitly referenced in the supply-chain workflow. Below, enter the token that was generated in the previous step. - -## What this does - -For the `cargo vet` check in the CI for dependabot, the `cargo vet`-exemptions have to automatically be regenerated, because otherwise this CI job will always fail for dependabot PRs. After the exemptions have been regenerated, they need to be commited and pushed to the PR. This invalidates the CI run that pushed the commit so that it does not show up in the PR anymore but does not trigger a new CI run. This is a [protection by Github](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow) to prevent infinite loops. However, in this case it prevents us from having a proper CI run for dependabot PRs. The solution to this is to execute `push` operation with a personal access token. - -## Preventing infinite loops - -The CI is configured to avoid infinite loops by only regenerating and pushing the `cargo vet` exemptions if at least one of the following conditions is met: - -- The last commit was performed by dependabot -- The last commit message ends in `--regenerate-exemptions` From d5eb996423e9127324eb965f4706455a2406a684 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 18 Jun 2025 20:50:49 +0200 Subject: [PATCH 132/174] fix: CI failures due to older rustc version --- .github/workflows/supply-chain.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index effbb594..de26200f 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -28,6 +28,10 @@ jobs: ~/.cargo/registry/cache/ ~/.cache/cargo-supply-chain/ key: cargo-supply-chain-cache + - name: Install stable toolchain # Cargo-supply-chain is incompatible with older versions + run: | + rustup toolchain install stable + rustup default stable - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-supply-chain @@ -35,7 +39,7 @@ jobs: - 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 + run: cargo +stable 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 @@ -68,7 +72,7 @@ jobs: - 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 + run: cargo +stable 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 From 5097d9fce1dbcb109883a417afcbcd2cc3e8b6bb Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 14 Apr 2025 18:13:13 +0200 Subject: [PATCH 133/174] Add benchmarking for cryptographic primitives and protocol performance This commit introduces two kinds of benchmarks: 1. Cryptographic Primitives. Measures the performance of all available implementations of cryptographic algorithms using traditional benchmarking. Uses criterion. 2. Protocol Runs. Measures the time each step in the protocol takes. Measured using a tracing-based approach. The benchmarks are run on CI and an interactive visual overview is written to the gh-pages branch. If a benchmark takes more than twice the time than the reference commit (for PR: the main branch), the action fails. --- .github/workflows/bench-primitives.yml | 106 +++++++ .github/workflows/bench-protocol.yml | 96 +++++++ Cargo.lock | 29 +- Cargo.toml | 2 + ciphers/Cargo.toml | 14 + ciphers/benches/primitives.rs | 346 +++++++++++++++++++++++ ciphers/src/subtle/libcrux/mod.rs | 6 +- ciphers/src/subtle/mod.rs | 3 +- flake.nix | 10 + pkgs/rosenpass.nix | 1 + readme.md | 42 ++- rosenpass/Cargo.toml | 7 + rosenpass/benches/trace_handshake.rs | 371 +++++++++++++++++++++++++ rosenpass/src/protocol/protocol.rs | 259 +++++++++++------ util/Cargo.toml | 3 + util/src/lib.rs | 3 + util/src/trace_bench.rs | 19 ++ 17 files changed, 1225 insertions(+), 92 deletions(-) create mode 100644 .github/workflows/bench-primitives.yml create mode 100644 .github/workflows/bench-protocol.yml create mode 100644 ciphers/benches/primitives.rs create mode 100644 rosenpass/benches/trace_handshake.rs create mode 100644 util/src/trace_bench.rs diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml new file mode 100644 index 00000000..c2a6234b --- /dev/null +++ b/.github/workflows/bench-primitives.yml @@ -0,0 +1,106 @@ +name: rosenpass-ciphers - primitives - benchmark + +on: + pull_request: + push: + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + prim-benchmark: + strategy: + fail-fast: true + matrix: + system: ["x86_64-linux", "i686-linux"] + + runs-on: ubuntu-latest + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v4 + + # Install nix + + - name: Install Nix + uses: cachix/install-nix-action@v27 # A popular action for installing Nix + with: + extra_nix_config: | + experimental-features = nix-command flakes + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + + # Set up environment + + - name: 🛠️ Config Linux x64 + run: echo "RUST_TARGET_FLAG=--target=x86_64-unknown-linux-gnu" > $GITHUB_ENV + if: ${{ matrix.system == 'x86_64-linux' }} + + - name: 🛠️ Config Linux x86 + run: echo "RUST_TARGET_FLAG=--target=i686-unknown-linux-gnu" > $GITHUB_ENV + if: ${{ matrix.system == 'i686-linux' }} + + - name: 🛠️ Prepare Benchmark Path + env: + EVENT_NAME: ${{ github.event_name }} + BRANCH_NAME: ${{ github.ref_name }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + case "$EVENT_NAME" in + "push") + echo "BENCH_PATH=branch/$BRANCH_NAME" >> $GITHUB_ENV + ;; + "pull_request") + echo "BENCH_PATH=pull/$PR_NUMBER" >> $GITHUB_ENV + ;; + *) + echo "don't know benchmark path for event of type $EVENT_NAME, aborting" + exit 1 + esac + + # Benchmarks ... + + - name: 🏃🏻‍♀️ Benchmarks (using Nix as shell) + working-directory: ciphers + run: nix develop ".#devShells.${{ matrix.system }}.benchmark" --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt + + - name: Extract benchmarks + uses: cryspen/benchmark-data-extract-transform@v2 + with: + name: rosenpass-ciphers primitives benchmarks + tool: "cargo" + os: ${{ matrix.system }} + output-file-path: bench-primitives.txt + data-out-path: bench-primitives.json + + - name: Upload benchmarks + uses: cryspen/benchmark-upload-and-plot-action@v3 + with: + name: Crypto Primitives Benchmarks + group-by: "os,primitive,algorithm" + schema: "os,primitive,algorithm,implementation,operation,length" + input-data-path: bench-primitives.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # NOTE: pushes to current repository + gh-repository: github.com/${{ github.repository }} + # use the default (gh-pages) for the demo + #gh-pages-branch: benchmarks + auto-push: true + fail-on-alert: true + + ciphers-primitives-bench-status: + if: ${{ always() }} + needs: [prim-benchmark] + runs-on: ubuntu-latest + steps: + - name: Successful + if: ${{ !(contains(needs.*.result, 'failure')) }} + run: exit 0 + - name: Failing + if: ${{ (contains(needs.*.result, 'failure')) }} + run: exit 1 diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml new file mode 100644 index 00000000..881d12d5 --- /dev/null +++ b/.github/workflows/bench-protocol.yml @@ -0,0 +1,96 @@ +name: rosenpass - protocol - benchmark + +on: + pull_request: + push: + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + proto-benchmark: + strategy: + fail-fast: true + matrix: + system: ["x86_64-linux", "i686-linux"] + + runs-on: ubuntu-latest + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v4 + + # Install nix + + - name: Install Nix + uses: cachix/install-nix-action@v27 # A popular action for installing Nix + with: + extra_nix_config: | + experimental-features = nix-command flakes + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + + # Set up environment + + - name: 🛠️ Config Linux x64 + run: echo "RUST_TARGET_FLAG=--target=x86_64-unknown-linux-gnu" > $GITHUB_ENV + if: ${{ matrix.system == 'x86_64-linux' }} + + - name: 🛠️ Config Linux x86 + run: echo "RUST_TARGET_FLAG=--target=i686-unknown-linux-gnu" > $GITHUB_ENV + if: ${{ matrix.system == 'i686-linux' }} + + - name: 🛠️ Prepare Benchmark Path + env: + EVENT_NAME: ${{ github.event_name }} + BRANCH_NAME: ${{ github.ref_name }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + case "$EVENT_NAME" in + "push") + echo "BENCH_PATH=branch/$BRANCH_NAME" >> $GITHUB_ENV + ;; + "pull_request") + echo "BENCH_PATH=pull/$PR_NUMBER" >> $GITHUB_ENV + ;; + *) + echo "don't know benchmark path for event of type $EVENT_NAME, aborting" + exit 1 + esac + + # Benchmarks ... + + - name: 🏃🏻‍♀️ Benchmarks + run: nix develop ".#devShells.${{ matrix.system }}.benchmark" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json + + - name: Upload benchmarks + uses: cryspen/benchmark-upload-and-plot-action@v3 + with: + name: Protocol Benchmarks + group-by: "os,arch,protocol version,run time" + schema: "os,arch,protocol version,run time,name" + input-data-path: bench-protocol.json + github-token: ${{ secrets.GITHUB_TOKEN }} + # NOTE: pushes to current repository + gh-repository: github.com/${{ github.repository }} + # use the default (gh-pages) for the demo + #gh-pages-branch: benchmarks + auto-push: true + fail-on-alert: true + + ciphers-protocol-bench-status: + if: ${{ always() }} + needs: [proto-benchmark] + runs-on: ubuntu-latest + steps: + - name: Successful + if: ${{ !(contains(needs.*.result, 'failure')) }} + run: exit 0 + - name: Failing + if: ${{ (contains(needs.*.result, 'failure')) }} + run: exit 1 diff --git a/Cargo.lock b/Cargo.lock index 98e590da..b356525c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1269,7 +1269,7 @@ version = "0.0.3-pre" source = "git+https://github.com/cryspen/libcrux.git?rev=10ce653e9476#10ce653e94761352b657b6cecdcc0c85675813df" dependencies = [ "libcrux-hacl-rs", - "libcrux-macros", + "libcrux-macros 0.0.2", ] [[package]] @@ -1279,7 +1279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78d522fb626847390ea4b776c7eca179ecec363c6c4730b61b0c0feb797b8d92" dependencies = [ "libcrux-hacl-rs", - "libcrux-macros", + "libcrux-macros 0.0.2", "libcrux-poly1305", ] @@ -1299,7 +1299,7 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8bba0885296a72555a5d77056c39cc9b04edd9ab1afa3025ef3dbd96220705c" dependencies = [ - "libcrux-macros", + "libcrux-macros 0.0.2", ] [[package]] @@ -1321,6 +1321,15 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "libcrux-macros" +version = "0.0.3" +source = "git+https://github.com/cryspen/libcrux.git?rev=0ab6d2dd9c1f#0ab6d2dd9c1f39c82b1125a566d6befb38feea28" +dependencies = [ + "quote", + "syn 2.0.98", +] + [[package]] name = "libcrux-ml-kem" version = "0.0.2-beta.3" @@ -1350,7 +1359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80143d78ae14ab51ceb2c8a9514fb60af6645d42a9c951bc511792c19c974fca" dependencies = [ "libcrux-hacl-rs", - "libcrux-macros", + "libcrux-macros 0.0.2", ] [[package]] @@ -1364,6 +1373,14 @@ dependencies = [ "libcrux-platform", ] +[[package]] +name = "libcrux-test-utils" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux.git?rev=0ab6d2dd9c1f#0ab6d2dd9c1f39c82b1125a566d6befb38feea28" +dependencies = [ + "libcrux-macros 0.0.3", +] + [[package]] name = "libfuzzer-sys" version = "0.4.9" @@ -2024,6 +2041,7 @@ dependencies = [ "hex", "hex-literal", "home", + "libcrux-test-utils", "log", "memoffset 0.9.1", "mio", @@ -2070,6 +2088,7 @@ dependencies = [ "anyhow", "blake2", "chacha20poly1305", + "criterion", "libcrux", "libcrux-blake2", "libcrux-chacha20poly1305", @@ -2153,6 +2172,8 @@ version = "0.1.0" dependencies = [ "anyhow", "base64ct", + "lazy_static", + "libcrux-test-utils", "mio", "rustix", "static_assertions", diff --git a/Cargo.toml b/Cargo.toml index 8583095d..368b8917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,12 +73,14 @@ 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" } +libcrux-test-utils = { git = "https://github.com/cryspen/libcrux.git", rev = "0ab6d2dd9c1f" } 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" +lazy_static = "1.5" #Dev dependencies serial_test = "3.2.0" diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 2e8e5c75..080deeb0 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -24,6 +24,19 @@ experiment_libcrux_chachapoly_test = [ "dep:libcrux", ] experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"] +bench = [ + "dep:thiserror", + "dep:rand", + "dep:libcrux", + "dep:libcrux-blake2", + "dep:libcrux-ml-kem", + "dep:libcrux-chacha20poly1305", +] + +[[bench]] +name = "primitives" +harness = false +required-features = ["bench"] [dependencies] anyhow = { workspace = true } @@ -50,3 +63,4 @@ libcrux = { workspace = true, optional = true } [dev-dependencies] rand = { workspace = true } +criterion = { workspace = true } diff --git a/ciphers/benches/primitives.rs b/ciphers/benches/primitives.rs new file mode 100644 index 00000000..eb81c66e --- /dev/null +++ b/ciphers/benches/primitives.rs @@ -0,0 +1,346 @@ +criterion::criterion_main!(keyed_hash::benches, aead::benches, kem::benches); + +fn benchid(base: KvPairs, last: KvPairs) -> String { + format!("{base},{last}") +} + +#[derive(Clone, Copy, Debug)] +struct KvPair<'a>(&'a str, &'a str); + +impl std::fmt::Display for KvPair<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{k}={v}", k = self.0, v = self.1) + } +} + +#[derive(Clone, Copy, Debug)] +struct KvPairs<'a>(&'a [KvPair<'a>]); + +impl std::fmt::Display for KvPairs<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.0.len() { + 0 => Ok(()), + 1 => write!(f, "{}", &self.0[0]), + _ => { + let mut delim = ""; + for pair in self.0 { + write!(f, "{delim}{pair}")?; + delim = ","; + } + Ok(()) + } + } + } +} + +mod kem { + criterion::criterion_group!( + benches, + bench_kyber512_libcrux, + bench_kyber512_oqs, + bench_classicmceliece460896_oqs + ); + + use criterion::Criterion; + + fn bench_classicmceliece460896_oqs(c: &mut Criterion) { + template( + c, + "classicmceliece460896", + "oqs", + rosenpass_oqs::ClassicMceliece460896, + ); + } + + fn bench_kyber512_libcrux(c: &mut Criterion) { + template( + c, + "kyber512", + "libcrux", + rosenpass_ciphers::subtle::libcrux::kyber512::Kyber512, + ); + } + + fn bench_kyber512_oqs(c: &mut Criterion) { + template(c, "kyber512", "oqs", rosenpass_oqs::Kyber512); + } + + use rosenpass_cipher_traits::primitives::Kem; + + fn template< + const SK_LEN: usize, + const PK_LEN: usize, + const CT_LEN: usize, + const SHK_LEN: usize, + T: Kem, + >( + c: &mut Criterion, + alg_name: &str, + impl_name: &str, + scheme: T, + ) { + use super::{benchid, KvPair, KvPairs}; + + let base = [ + KvPair("primitive", "kem"), + KvPair("algorithm", alg_name), + KvPair("implementation", impl_name), + KvPair("length", "-1"), + ]; + + let kem_benchid = |op| benchid(KvPairs(&base), KvPairs(&[KvPair("operation", op)])); + + c.bench_function(&kem_benchid("keygen"), |bench| { + let mut sk = [0; SK_LEN]; + let mut pk = [0; PK_LEN]; + + bench.iter(|| { + scheme.keygen(&mut sk, &mut pk).unwrap(); + }); + }); + + c.bench_function(&kem_benchid("encaps"), |bench| { + let mut sk = [0; SK_LEN]; + let mut pk = [0; PK_LEN]; + let mut ct = [0; CT_LEN]; + let mut shk = [0; SHK_LEN]; + + scheme.keygen(&mut sk, &mut pk).unwrap(); + + bench.iter(|| { + scheme.encaps(&mut shk, &mut ct, &pk).unwrap(); + }); + }); + + c.bench_function(&kem_benchid("decaps"), |bench| { + let mut sk = [0; SK_LEN]; + let mut pk = [0; PK_LEN]; + let mut ct = [0; CT_LEN]; + let mut shk = [0; SHK_LEN]; + let mut shk2 = [0; SHK_LEN]; + + scheme.keygen(&mut sk, &mut pk).unwrap(); + scheme.encaps(&mut shk, &mut ct, &pk).unwrap(); + + bench.iter(|| { + scheme.decaps(&mut shk2, &sk, &ct).unwrap(); + }); + }); + } +} +mod aead { + criterion::criterion_group!( + benches, + bench_chachapoly_libcrux, + bench_chachapoly_rustcrypto, + bench_xchachapoly_rustcrypto, + ); + + use criterion::Criterion; + + const KEY_LEN: usize = rosenpass_ciphers::Aead::KEY_LEN; + const TAG_LEN: usize = rosenpass_ciphers::Aead::TAG_LEN; + + fn bench_xchachapoly_rustcrypto(c: &mut Criterion) { + template( + c, + "xchacha20poly1305", + "rustcrypto", + rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::XChaCha20Poly1305, + ); + } + + fn bench_chachapoly_rustcrypto(c: &mut Criterion) { + template( + c, + "chacha20poly1305", + "rustcrypto", + rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305, + ); + } + + fn bench_chachapoly_libcrux(c: &mut Criterion) { + template( + c, + "chacha20poly1305", + "libcrux", + rosenpass_ciphers::subtle::libcrux::chacha20poly1305_ietf::ChaCha20Poly1305, + ); + } + + use rosenpass_cipher_traits::primitives::Aead; + + fn template>( + c: &mut Criterion, + alg_name: &str, + impl_name: &str, + scheme: T, + ) { + use crate::{benchid, KvPair, KvPairs}; + + let base = [ + KvPair("primitive", "aead"), + KvPair("algorithm", alg_name), + KvPair("implementation", impl_name), + ]; + let aead_benchid = |op, len| { + benchid( + KvPairs(&base), + KvPairs(&[KvPair("operation", op), KvPair("length", len)]), + ) + }; + + let key = [12; KEY_LEN]; + let nonce = [23; NONCE_LEN]; + let ad = []; + + c.bench_function(&aead_benchid("encrypt", "32byte"), |bench| { + const DATA_LEN: usize = 32; + + let ptxt = [34u8; DATA_LEN]; + let mut ctxt = [0; DATA_LEN + TAG_LEN]; + + bench.iter(|| { + scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap(); + }); + }); + + c.bench_function(&aead_benchid("decrypt", "32byte"), |bench| { + const DATA_LEN: usize = 32; + + let ptxt = [34u8; DATA_LEN]; + let mut ctxt = [0; DATA_LEN + TAG_LEN]; + let mut ptxt_out = [0u8; DATA_LEN]; + + scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap(); + + bench.iter(|| { + scheme + .decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt) + .unwrap() + }) + }); + + c.bench_function(&aead_benchid("encrypt", "1024byte"), |bench| { + const DATA_LEN: usize = 1024; + + let ptxt = [34u8; DATA_LEN]; + let mut ctxt = [0; DATA_LEN + TAG_LEN]; + + bench.iter(|| { + scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap(); + }); + }); + c.bench_function(&aead_benchid("decrypt", "1024byte"), |bench| { + const DATA_LEN: usize = 1024; + + let ptxt = [34u8; DATA_LEN]; + let mut ctxt = [0; DATA_LEN + TAG_LEN]; + let mut ptxt_out = [0u8; DATA_LEN]; + + scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap(); + + bench.iter(|| { + scheme + .decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt) + .unwrap() + }) + }); + } +} + +mod keyed_hash { + criterion::criterion_group!( + benches, + bench_blake2b_rustcrypto, + bench_blake2b_libcrux, + bench_shake256_rustcrypto, + ); + + const KEY_LEN: usize = 32; + const HASH_LEN: usize = 32; + + use criterion::Criterion; + + fn bench_shake256_rustcrypto(c: &mut Criterion) { + template( + c, + "shake256", + "rustcrypto", + &rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::SHAKE256Core, + ); + } + + fn bench_blake2b_rustcrypto(c: &mut Criterion) { + template( + c, + "blake2b", + "rustcrypto", + &rosenpass_ciphers::subtle::rust_crypto::blake2b::Blake2b, + ); + } + + fn bench_blake2b_libcrux(c: &mut Criterion) { + template( + c, + "blake2b", + "libcrux", + &rosenpass_ciphers::subtle::libcrux::blake2b::Blake2b, + ); + } + + use rosenpass_cipher_traits::primitives::KeyedHash; + + fn template>( + c: &mut Criterion, + alg_name: &str, + impl_name: &str, + _: &H, + ) where + H::Error: std::fmt::Debug, + { + use crate::{benchid, KvPair, KvPairs}; + + let key = [12u8; KEY_LEN]; + let mut out = [0u8; HASH_LEN]; + + let base = [ + KvPair("primitive", "keyedhash"), + KvPair("algorithm", alg_name), + KvPair("implementation", impl_name), + KvPair("operation", "hash"), + ]; + let keyedhash_benchid = |len| benchid(KvPairs(&base), KvPairs(&[KvPair("length", len)])); + + c.bench_function(&keyedhash_benchid("32byte"), |bench| { + let bytes = [34u8; 32]; + + bench.iter(|| { + H::keyed_hash(&key, &bytes, &mut out).unwrap(); + }) + }) + .bench_function(&keyedhash_benchid("64byte"), |bench| { + let bytes = [34u8; 64]; + + bench.iter(|| { + H::keyed_hash(&key, &bytes, &mut out).unwrap(); + }) + }) + .bench_function(&keyedhash_benchid("128byte"), |bench| { + let bytes = [34u8; 128]; + + bench.iter(|| { + H::keyed_hash(&key, &bytes, &mut out).unwrap(); + }) + }) + .bench_function(&keyedhash_benchid("1024byte"), |bench| { + let bytes = [34u8; 1024]; + + bench.iter(|| { + H::keyed_hash(&key, &bytes, &mut out).unwrap(); + }) + }); + } +} + +mod templates {} diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs index 432bd5f3..210bc61a 100644 --- a/ciphers/src/subtle/libcrux/mod.rs +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -4,11 +4,11 @@ //! //! [Github](https://github.com/cryspen/libcrux) -#[cfg(feature = "experiment_libcrux_blake2")] +#[cfg(any(feature = "experiment_libcrux_blake2", feature = "bench"))] pub mod blake2b; -#[cfg(feature = "experiment_libcrux_chachapoly")] +#[cfg(any(feature = "experiment_libcrux_chachapoly", feature = "bench"))] pub mod chacha20poly1305_ietf; -#[cfg(feature = "experiment_libcrux_kyber")] +#[cfg(any(feature = "experiment_libcrux_kyber", feature = "bench"))] pub mod kyber512; diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index b3c3aa8a..6221324f 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -11,6 +11,7 @@ pub mod rust_crypto; #[cfg(any( feature = "experiment_libcrux_blake2", feature = "experiment_libcrux_chachapoly", - feature = "experiment_libcrux_kyber" + feature = "experiment_libcrux_kyber", + feature = "bench" ))] pub mod libcrux; diff --git a/flake.nix b/flake.nix index 68ae5909..4b3413f6 100644 --- a/flake.nix +++ b/flake.nix @@ -89,6 +89,7 @@ [ "x86_64-linux" "aarch64-linux" + "i686-linux" ] ( system: @@ -172,6 +173,15 @@ inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA; }; }; + devShells.benchmark = pkgs.mkShell { + inputsFrom = [ pkgs.rosenpass ]; + nativeBuildInputs = let + rustToolchain = (inputs.fenix.packages.${system}.toolchainOf { + channel = "1.77.0"; + sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8="; + }); + in [ rustToolchain.toolchain ]; + }; checks = { diff --git a/pkgs/rosenpass.nix b/pkgs/rosenpass.nix index 30f560fc..c25809f1 100644 --- a/pkgs/rosenpass.nix +++ b/pkgs/rosenpass.nix @@ -79,6 +79,7 @@ rustPlatform.buildRustPackage { "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="; + "libcrux-macros-0.0.3" = "sha256-Tb5uRirwhRhoFEK8uu1LvXl89h++40pxzZ+7kXe8RAI="; }; }; diff --git a/readme.md b/readme.md index aa4c8814..c4ceb1af 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,37 @@ +# Changes on This Branch + +This branch adds facilities for benchmarking both the Rosenpass protocol +code and the implementations of the primitives behind it. The primitives +are benchmarked using criterion. For the protocol code, we use a custom +library for instrumenting the code such that events are written to a +trace, which is then inspected after a run. + +## Protocol Benchmark + +The trace that is being written to lives in a new module +`trace_bench` in the util crate. A basic benchmark that +performs some minor statistical analysis of the trace can be run using + +``` +cargo bench -p rosenpass --bench trace_handshake -F trace_bench +``` + +## Primitive Benchmark + +Benchmarks for the functions of the traits `Kem`, `Aead` and `KeyedHash` +have been added and are run for all implementations in the `primitives` +benchmark of `rosenpass-ciphers`. Run the benchmarks using + +``` +cargo bench -p rosenpass-ciphers --bench primitives -F bench +``` + +Note that the `bench` feature enables the inclusion of the libcrux-backed +trait implementations in the module tree, but does not enable them +as default. + +--- + # Rosenpass README ![Nix](https://github.com/rosenpass/rosenpass/actions/workflows/nix.yaml/badge.svg) @@ -14,7 +48,7 @@ This repository contains ## Getting started -First, [install rosenpass](#Getting-Rosenpass). Then, check out the help functions of `rp` & `rosenpass`: +First, [install rosenpass](#getting-rosenpass). Then, check out the help functions of `rp` & `rosenpass`: ```sh rp help @@ -64,11 +98,7 @@ The analysis is implemented according to modern software engineering principles: The code uses a variety of optimizations to speed up analysis such as using secret functions to model trusted/malicious setup. We split the model into two separate entry points which can be analyzed in parallel. Each is much faster than both models combined. A wrapper script provides instant feedback about which queries execute as expected in color: A red cross if a query fails and a green check if it succeeds. -[^liboqs]: https://openquantumsafe.org/liboqs/ -[^wg]: https://www.wireguard.com/ -[^pqwg]: https://eprint.iacr.org/2020/379 -[^pqwg-statedis]: Unless supplied with a pre-shared-key, but this defeats the purpose of a key exchange protocol -[^wg-statedis]: https://lists.zx2c4.com/pipermail/wireguard/2021-August/006916.htmlA +[^liboqs]: # Getting Rosenpass diff --git a/rosenpass/Cargo.toml b/rosenpass/Cargo.toml index 2975a1f2..c23d1f38 100644 --- a/rosenpass/Cargo.toml +++ b/rosenpass/Cargo.toml @@ -35,6 +35,11 @@ required-features = [ "internal_bin_gen_ipc_msg_types", ] +[[bench]] +name = "trace_handshake" +harness = false +required-features = ["trace_bench"] + [[bench]] name = "handshake" harness = false @@ -72,6 +77,7 @@ 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 } +libcrux-test-utils = { workspace = true, optional = true } [build-dependencies] anyhow = { workspace = true } @@ -106,6 +112,7 @@ experiment_api = [ internal_signal_handling_for_coverage_reports = ["signal-hook"] internal_testing = [] internal_bin_gen_ipc_msg_types = ["hex", "heck"] +trace_bench = ["rosenpass-util/trace_bench", "dep:libcrux-test-utils"] [lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] } diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs new file mode 100644 index 00000000..9adaafe8 --- /dev/null +++ b/rosenpass/benches/trace_handshake.rs @@ -0,0 +1,371 @@ +// Standard library imports +use std::{ + collections::HashMap, + hint::black_box, + io::{self, Write}, + ops::DerefMut, + time::{Duration, Instant}, +}; + +// External crate imports +use anyhow::Result; +use libcrux_test_utils::tracing::{EventType, Trace as _}; +use rosenpass::protocol::{ + CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, +}; +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::StaticKem; +use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; +use rosenpass_util::trace_bench::{RpEventType, TRACE}; + +const ITERATIONS: usize = 100; + +fn handle( + tx: &mut CryptoServer, + msgb: &mut MsgBuf, + msgl: usize, + rx: &mut CryptoServer, + resb: &mut MsgBuf, +) -> Result<(Option, Option)> { + let HandleMsgResult { + exchanged_with: xch, + resp, + } = rx.handle_msg(&msgb[..msgl], &mut **resb)?; + + assert!(matches!(xch, None | Some(PeerPtr(0)))); + + let xch = xch.map(|p| rx.osk(p).unwrap()); + + let (rxk, txk) = resp + .map(|resl| handle(rx, resb, resl, tx, msgb)) + .transpose()? + .unwrap_or((None, None)); + + assert!(rxk.is_none() || xch.is_none()); + + Ok((txk, rxk.or(xch))) +} + +fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> { + let (mut inib, mut resb) = (MsgBuf::zero(), MsgBuf::zero()); + let sz = ini.initiate_handshake(PeerPtr(0), &mut *inib)?; + let (kini, kres) = handle(ini, &mut inib, sz, res, &mut resb)?; + assert!(kini.unwrap().secret() == kres.unwrap().secret()); + Ok(()) +} + +fn keygen() -> Result<(SSk, SPk)> { + let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); + StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?; + Ok((sk, pk)) +} + +fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> { + let psk = SymKey::random(); + let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?); + let (mut a, mut b) = ( + CryptoServer::new(ska, pka.clone()), + CryptoServer::new(skb, pkb.clone()), + ); + a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?; + b.add_peer(Some(psk), pka, protocol_version)?; + Ok((a, b)) +} + +fn main() { + // Attempt to use memfd_secrets for storing sensitive key material + secret_policy_try_use_memfd_secrets(); + + // Run protocol for V02 + let (mut a_v02, mut b_v02) = make_server_pair(ProtocolVersion::V02).unwrap(); + for _ in 0..ITERATIONS { + hs(black_box(&mut a_v02), black_box(&mut b_v02)).unwrap(); + } + + // Emit a marker event to separate V02 and V03 trace sections + TRACE.emit_on_the_fly("start-hs-v03"); + + // Run protocol for V03 + let (mut a_v03, mut b_v03) = make_server_pair(ProtocolVersion::V03).unwrap(); + for _ in 0..ITERATIONS { + hs(black_box(&mut a_v03), black_box(&mut b_v03)).unwrap(); + } + + // Collect the trace events generated during the handshakes + let trace: Vec<_> = TRACE.clone().report(); + + // Split the trace into V02 and V03 sections based on the marker + let (trace_v02, trace_v03) = { + let cutoff = trace + .iter() + .position(|entry| entry.label == "start-hs-v03") + .unwrap(); + // Exclude the marker itself from the V03 trace + let (v02, v03_with_marker) = trace.split_at(cutoff); + (v02, &v03_with_marker[1..]) + }; + + // Perform statistical analysis on both trace sections and write results as JSON + write_json_arrays( + &mut std::io::stdout(), // Write to standard output + vec![ + ("V02", statistical_analysis(trace_v02.to_vec())), + ("V03", statistical_analysis(trace_v03.to_vec())), + ], + ) + .expect("error writing json data"); +} + +/// Takes a vector of trace events, bins them by label, extracts durations, +/// filters empty bins, calculates aggregate statistics (mean, std dev), and returns them. +fn statistical_analysis(trace: Vec) -> Vec<(&'static str, AggregateStat)> { + bin_events(trace) + .into_iter() + .map(|(label, spans)| (label, extract_span_durations(label, spans.as_slice()))) + .filter(|(_, durations)| !durations.is_empty()) + .map(|(label, durations)| (label, AggregateStat::analyze_durations(&durations))) + .collect() +} + +/// Takes an iterator of ("protocol_version", iterator_of_stats) pairs and writes them +/// as a single flat JSON array to the provided writer. +/// +/// # Arguments +/// * `w` - The writer to output JSON to (e.g., stdout, file). +/// * `item_groups` - An iterator producing tuples of (`&'static str`, `II`), where +/// `II` is itself an iterator producing (`&'static str`, `AggregateStat`). +/// Represents the protocol_version name and the statistics items within that protocol_version. +/// +/// # Type Parameters +/// * `W` - A type that implements `std::io::Write`. +/// * `II` - An iterator type yielding (`&'static str`, `AggregateStat`). +fn write_json_arrays)>>( + w: &mut W, + item_groups: impl IntoIterator, +) -> io::Result<()> { + // Flatten the groups into a single iterator of (protocol_version, label, stats) + let iter = item_groups.into_iter().flat_map(|(version, items)| { + items + .into_iter() + .map(move |(label, agg_stat)| (version, label, agg_stat)) + }); + let mut delim = ""; // Start with no delimiter + + // Start the JSON array + write!(w, "[")?; + + // Write the flattened statistics as JSON objects, separated by commas. + for (version, label, agg_stat) in iter { + write!(w, "{delim}")?; // Write delimiter (empty for first item, "," for subsequent) + agg_stat.write_json_ns(label, version, w)?; // Write the JSON object for the stat entry + delim = ","; // Set delimiter for the next iteration + } + + // End the JSON array + write!(w, "]") +} + +/// Used to group benchmark results in visualizations +enum RunTimeGroup { + /// For particularly long operations. + Long, + /// Operations of moderate duration. + Medium, + /// Operations expected to complete under a millisecond. + BelowMillisec, + /// Very fast operations, likely under a microsecond. + BelowMicrosec, +} + +impl std::fmt::Display for RunTimeGroup { + /// Used when writing the group information to JSON output. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let txt = match self { + RunTimeGroup::Long => "long", + RunTimeGroup::Medium => "medium", + RunTimeGroup::BelowMillisec => "below_ms", + RunTimeGroup::BelowMicrosec => "below_us", + }; + write!(f, "{txt}") + } +} + +/// Maps specific internal timing labels (likely from rosenpass internals) +/// to the broader SpanGroup categories. +fn run_time_group(label: &str) -> RunTimeGroup { + match label { + // Explicitly categorized labels based on expected performance characteristics + "handle_init_hello" | "handle_resp_hello" | "RHI5" | "IHR5" => RunTimeGroup::Long, + "RHR1" | "IHI2" | "ICR6" => RunTimeGroup::BelowMicrosec, + "RHI6" | "ICI7" | "ICR7" | "RHR3" | "ICR3" | "IHR8" | "ICI4" | "RHI3" | "RHI4" | "RHR4" + | "RHR7" | "ICI3" | "IHI3" | "IHI8" | "ICR2" | "ICR4" | "IHR4" | "IHR6" | "IHI4" + | "RHI7" => RunTimeGroup::BelowMillisec, + // Default protocol_version for any other labels + _ => RunTimeGroup::Medium, + } +} + +/// Used temporarily within `extract_span_durations` to track open spans +/// and calculated durations. +#[derive(Debug, Clone)] +enum StatEntry { + /// Represents an unmatched SpanOpen event with its timestamp. + Start(Instant), + /// Represents a completed span with its calculated duration. + Duration(Duration), +} + +/// Takes a flat list of events and organizes them into a HashMap where keys +/// are event labels and values are vectors of events with that label. +fn bin_events(events: Vec) -> HashMap<&'static str, Vec> { + let mut spans = HashMap::<_, Vec<_>>::new(); + for event in events { + // Get the vector for the event's label, or create a new one + let spans_for_label = spans.entry(event.label).or_default(); + // Add the event to the vector + spans_for_label.push(event); + } + spans +} + +/// Processes a list of events (assumed to be for the same label), matching +/// `SpanOpen` and `SpanClose` events to calculate the duration of each span. +/// It handles potentially interleaved spans correctly. +fn extract_span_durations(label: &str, events: &[RpEventType]) -> Vec { + let mut processing_list: Vec = vec![]; // List to track open spans and final durations + + for entry in events { + match &entry.ty { + EventType::SpanOpen => { + // Record the start time of a new span + processing_list.push(StatEntry::Start(entry.at)); + } + EventType::SpanClose => { + // Find the most recent unmatched 'Start' entry + let start_index = processing_list + .iter() + .rposition(|span| matches!(span, StatEntry::Start(_))); // Find last Start + + match start_index { + Some(index) => { + // Retrieve the start time + let start_time = match processing_list[index] { + StatEntry::Start(t) => t, + _ => unreachable!(), // Should always be Start based on rposition logic + }; + // Calculate duration and replace the 'Start' entry with 'Duration' + processing_list[index] = StatEntry::Duration(entry.at - start_time); + } + None => { + // This should not happen with well-formed traces + eprintln!( + "Warning: Found SpanClose without a matching SpanOpen for label '{}': {:?}", + label, entry + ); + } + } + } + EventType::OnTheFly => { + // Ignore OnTheFly events for duration calculation + } + } + } + + // Collect all calculated durations, reporting any unmatched starts + processing_list + .into_iter() + .filter_map(|span| match span { + StatEntry::Start(at) => { + // Report error if a span was opened but never closed + eprintln!( + "Warning: Unmatched SpanOpen at {:?} for label '{}'", + at, label + ); + None // Discard unmatched starts + } + StatEntry::Duration(dur) => Some(dur), // Keep calculated durations + }) + .collect() +} + +/// Stores the mean, standard deviation, relative standard deviation (sd/mean), +/// and the number of samples used for calculation. +#[derive(Debug)] +struct AggregateStat { + /// Average duration. + mean_duration: T, + /// Standard deviation of durations. + sd_duration: T, + /// Standard deviation as a percentage of the mean. + sd_by_mean: String, + /// Number of duration measurements. + sample_size: usize, +} + +impl AggregateStat { + /// Calculates mean, variance, and standard deviation for a slice of Durations. + fn analyze_durations(durations: &[Duration]) -> Self { + let sample_size = durations.len(); + assert!(sample_size > 0, "Cannot analyze empty duration slice"); + + // Calculate the sum of durations + let sum: Duration = durations.iter().sum(); + // Calculate the mean duration + let mean = sum / (sample_size as u32); + + // Calculate mean in nanoseconds, adding 1 to avoid potential division by zero later + // (though highly unlikely with realistic durations) + let mean_ns = mean.as_nanos().saturating_add(1); + + // Calculate variance (sum of squared differences from the mean) / N + let variance = durations + .iter() + .map(Duration::as_nanos) + .map(|d_ns| d_ns.abs_diff(mean_ns).pow(2)) // (duration_ns - mean_ns)^2 + .sum::() // Sum of squares + / (sample_size as u128); // Divide by sample size + + // Calculate standard deviation (sqrt of variance) + let sd_ns = (variance as f64).sqrt() as u128; + let sd = Duration::from_nanos(sd_ns as u64); // Convert back to Duration + + // Calculate relative standard deviation (sd / mean) as a percentage string + let sd_rel_permille = (10000 * sd_ns).checked_div(mean_ns).unwrap_or(0); // Calculate sd/mean * 10000 + let sd_rel_formatted = format!("{}.{:02}%", sd_rel_permille / 100, sd_rel_permille % 100); + + AggregateStat { + mean_duration: mean, + sd_duration: sd, + sd_by_mean: sd_rel_formatted, + sample_size, + } + } + + /// Writes the statistics as a JSON object to the provided writer. + /// Includes metadata like label, protocol_version, OS, architecture, and run time group. + /// + /// # Arguments + /// * `label` - The specific benchmark/span label. + /// * `protocol_version` - Version of the protocol that is benchmarked. + /// * `w` - The output writer (must implement `std::io::Write`). + fn write_json_ns( + &self, + label: &str, + protocol_version: &str, + w: &mut impl io::Write, + ) -> io::Result<()> { + // Format the JSON string using measured values and environment constants + writeln!( + w, + r#"{{"name":"{name}", "unit":"ns/iter", "value":"{value}", "range":"± {range}", "protocol version":"{protocol_version}", "sample size":"{sample_size}", "os":"{os}", "arch":"{arch}", "run time":"{run_time}"}}"#, + name = label, // Benchmark name + value = self.mean_duration.as_nanos(), // Mean duration in nanoseconds + range = self.sd_duration.as_nanos(), // Standard deviation in nanoseconds + sample_size = self.sample_size, // Number of samples + os = std::env::consts::OS, // Operating system + arch = std::env::consts::ARCH, // CPU architecture + run_time = run_time_group(label), // Run time group category (long, medium, etc.) + protocol_version = protocol_version // Overall protocol_version (e.g., protocol version) + ) + } +} diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index c23a9c84..5a5e7c54 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -16,7 +16,6 @@ use std::{ }; use anyhow::{bail, ensure, Context, Result}; -use rand::Fill as Randomize; use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; @@ -3547,9 +3546,27 @@ impl CryptoServer { } } +/// Marks a section of the protocol using the same identifiers as are used in the whitepaper. +/// When building with the trace benchmarking feature enabled, this also emits span events into the +/// trace, which allows reconstructing the run times of the individual sections for performace +/// measurement. +macro_rules! protocol_section { + ($label:expr, $body:tt) => {{ + #[cfg(feature = "trace_bench")] + let _span_raii_handle = rosenpass_util::trace_bench::TRACE.emit_span($label); + + #[allow(unused_braces)] + $body + }}; +} + impl CryptoServer { /// Core cryptographic protocol implementation: Kicks of the handshake /// on the initiator side, producing the InitHello message. + #[cfg_attr( + feature = "trace_bench", + rosenpass_util::trace_bench::trace_span("handle_initiation", rosenpass_util::trace_bench::TRACE) + )] pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { let mut hs = InitiatorHandshake::zero_with_timestamp( self, @@ -3557,37 +3574,53 @@ impl CryptoServer { ); // IHI1 - hs.core.init(peer.get(self).spkt.deref())?; + protocol_section!("IHI1", { + hs.core.init(peer.get(self).spkt.deref())?; + }); // IHI2 - hs.core.sidi.randomize(); - ih.sidi.copy_from_slice(&hs.core.sidi.value); + protocol_section!("IHI2", { + hs.core.sidi.randomize(); + ih.sidi.copy_from_slice(&hs.core.sidi.value); + }); // IHI3 - EphemeralKem.keygen(hs.eski.secret_mut(), &mut *hs.epki)?; - ih.epki.copy_from_slice(&hs.epki.value); + protocol_section!("IHI3", { + EphemeralKem.keygen(hs.eski.secret_mut(), &mut *hs.epki)?; + ih.epki.copy_from_slice(&hs.epki.value); + }); // IHI4 - hs.core.mix(ih.sidi.as_slice())?.mix(ih.epki.as_slice())?; + protocol_section!("IHI4", { + hs.core.mix(ih.sidi.as_slice())?.mix(ih.epki.as_slice())?; + }); // IHI5 - hs.core - .encaps_and_mix(&StaticKem, &mut ih.sctr, peer.get(self).spkt.deref())?; + protocol_section!("IHI5", { + hs.core + .encaps_and_mix(&StaticKem, &mut ih.sctr, peer.get(self).spkt.deref())?; + }); // IHI6 - hs.core.encrypt_and_mix( - ih.pidic.as_mut_slice(), - self.pidm(peer.get(self).protocol_version.keyed_hash())? - .as_ref(), - )?; + protocol_section!("IHI6", { + hs.core.encrypt_and_mix( + ih.pidic.as_mut_slice(), + self.pidm(peer.get(self).protocol_version.keyed_hash())? + .as_ref(), + )?; + }); // IHI7 - hs.core - .mix(self.spkm.deref())? - .mix(peer.get(self).psk.secret())?; + protocol_section!("IHI7", { + hs.core + .mix(self.spkm.deref())? + .mix(peer.get(self).psk.secret())?; + }); // IHI8 - hs.core.encrypt_and_mix(ih.auth.as_mut_slice(), &[])?; + protocol_section!("IHI8", { + hs.core.encrypt_and_mix(ih.auth.as_mut_slice(), &[])?; + }); // Update the handshake hash last (not changing any state on prior error peer.hs().insert(self, hs)?; @@ -3597,6 +3630,10 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a /// [RespHello] message on the responder side. + #[cfg_attr( + feature = "trace_bench", + rosenpass_util::trace_bench::trace_span("handle_init_hello", rosenpass_util::trace_bench::TRACE) + )] pub fn handle_init_hello( &mut self, ih: &InitHello, @@ -3608,54 +3645,80 @@ impl CryptoServer { core.sidi = SessionId::from_slice(&ih.sidi); // IHR1 - core.init(self.spkm.deref())?; + protocol_section!("IHR1", { + core.init(self.spkm.deref())?; + }); // IHR4 - core.mix(&ih.sidi)?.mix(&ih.epki)?; + protocol_section!("IHR4", { + core.mix(&ih.sidi)?.mix(&ih.epki)?; + }); // IHR5 - core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &ih.sctr)?; + protocol_section!("IHR5", { + core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &ih.sctr)?; + }); // IHR6 - let peer = { + let peer = protocol_section!("IHR6", { let mut peerid = PeerId::zero(); core.decrypt_and_mix(&mut *peerid, &ih.pidic)?; self.find_peer(peerid) .with_context(|| format!("No such peer {peerid:?}."))? - }; + }); // IHR7 - core.mix(peer.get(self).spkt.deref())? - .mix(peer.get(self).psk.secret())?; + protocol_section!("IHR7", { + core.mix(peer.get(self).spkt.deref())? + .mix(peer.get(self).psk.secret())?; + }); // IHR8 - core.decrypt_and_mix(&mut [0u8; 0], &ih.auth)?; + protocol_section!("IHR8", { + core.decrypt_and_mix(&mut [0u8; 0], &ih.auth)?; + }); // RHR1 - core.sidr.randomize(); - rh.sidi.copy_from_slice(core.sidi.as_ref()); - rh.sidr.copy_from_slice(core.sidr.as_ref()); + protocol_section!("RHR1", { + core.sidr.randomize(); + rh.sidi.copy_from_slice(core.sidi.as_ref()); + rh.sidr.copy_from_slice(core.sidr.as_ref()); + }); // RHR3 - core.mix(&rh.sidr)?.mix(&rh.sidi)?; + protocol_section!("RHR3", { + core.mix(&rh.sidr)?.mix(&rh.sidi)?; + }); // RHR4 - core.encaps_and_mix(&EphemeralKem, &mut rh.ecti, &ih.epki)?; + protocol_section!("RHR4", { + core.encaps_and_mix(&EphemeralKem, &mut rh.ecti, &ih.epki)?; + }); // RHR5 - core.encaps_and_mix(&StaticKem, &mut rh.scti, peer.get(self).spkt.deref())?; + protocol_section!("RHR5", { + core.encaps_and_mix(&StaticKem, &mut rh.scti, peer.get(self).spkt.deref())?; + }); // RHR6 - core.store_biscuit(self, peer, &mut rh.biscuit)?; + protocol_section!("RHR6", { + core.store_biscuit(self, peer, &mut rh.biscuit)?; + }); // RHR7 - core.encrypt_and_mix(&mut rh.auth, &[])?; + protocol_section!("RHR7", { + core.encrypt_and_mix(&mut rh.auth, &[])?; + }); Ok(peer) } /// Core cryptographic protocol implementation: Parses an [RespHello] message and produces an /// [InitConf] message on the initiator side. + #[cfg_attr( + feature = "trace_bench", + rosenpass_util::trace_bench::trace_span("handle_resp_hello", rosenpass_util::trace_bench::TRACE) + )] pub fn handle_resp_hello(&mut self, rh: &RespHello, ic: &mut InitConf) -> Result { // RHI2 let peer = self @@ -3700,24 +3763,34 @@ impl CryptoServer { // to save us from the repetitive secret unwrapping // RHI3 - core.mix(&rh.sidr)?.mix(&rh.sidi)?; + protocol_section!("RHI3", { + core.mix(&rh.sidr)?.mix(&rh.sidi)?; + }); // RHI4 - core.decaps_and_mix( - &EphemeralKem, - hs!().eski.secret(), - hs!().epki.deref(), - &rh.ecti, - )?; + protocol_section!("RHI4", { + core.decaps_and_mix( + &EphemeralKem, + hs!().eski.secret(), + hs!().epki.deref(), + &rh.ecti, + )?; + }); // RHI5 - core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &rh.scti)?; + protocol_section!("RHI5", { + core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &rh.scti)?; + }); // RHI6 - core.mix(&rh.biscuit)?; + protocol_section!("RHI6", { + core.mix(&rh.biscuit)?; + }); // RHI7 - core.decrypt_and_mix(&mut [0u8; 0], &rh.auth)?; + protocol_section!("RHI7", { + core.decrypt_and_mix(&mut [0u8; 0], &rh.auth)?; + }); // TODO: We should just authenticate the entire network package up to the auth // tag as a pattern instead of mixing in fields separately @@ -3726,27 +3799,33 @@ impl CryptoServer { ic.sidr.copy_from_slice(&rh.sidr); // ICI3 - core.mix(&ic.sidi)?.mix(&ic.sidr)?; - ic.biscuit.copy_from_slice(&rh.biscuit); + protocol_section!("ICI3", { + core.mix(&ic.sidi)?.mix(&ic.sidr)?; + ic.biscuit.copy_from_slice(&rh.biscuit); + }); // ICI4 - core.encrypt_and_mix(&mut ic.auth, &[])?; + protocol_section!("ICI4", { + core.encrypt_and_mix(&mut ic.auth, &[])?; + }); // Split() – We move the secrets into the session; we do not // delete the InitiatorHandshake, just clear it's secrets because // we still need it for InitConf message retransmission to function. // ICI7 - peer.session().insert( - self, - core.enter_live( + protocol_section!("ICI7", { + peer.session().insert( self, - HandshakeRole::Initiator, - peer.get(self).protocol_version.keyed_hash(), - )?, - )?; - hs_mut!().core.erase(); - hs_mut!().next = HandshakeStateMachine::RespConf; + core.enter_live( + self, + HandshakeRole::Initiator, + peer.get(self).protocol_version.keyed_hash(), + )?, + )?; + hs_mut!().core.erase(); + hs_mut!().next = HandshakeStateMachine::RespConf; + }); Ok(peer) } @@ -3756,6 +3835,10 @@ impl CryptoServer { /// /// This concludes the handshake on the cryptographic level; the [EmptyData] message is just /// an acknowledgement message telling the initiator to stop performing retransmissions. + #[cfg_attr( + feature = "trace_bench", + rosenpass_util::trace_bench::trace_span("handle_init_conf", rosenpass_util::trace_bench::TRACE) + )] pub fn handle_init_conf( &mut self, ic: &InitConf, @@ -3764,22 +3847,30 @@ impl CryptoServer { ) -> Result { // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 - let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit( - self, - &ic.biscuit, - SessionId::from_slice(&ic.sidi), - SessionId::from_slice(&ic.sidr), - keyed_hash, - )?; + let (peer, biscuit_no, mut core) = protocol_section!("ICR1", { + HandshakeState::load_biscuit( + self, + &ic.biscuit, + SessionId::from_slice(&ic.sidi), + SessionId::from_slice(&ic.sidr), + keyed_hash, + )? + }); // ICR2 - core.encrypt_and_mix(&mut [0u8; Aead::TAG_LEN], &[])?; + protocol_section!("ICR2", { + core.encrypt_and_mix(&mut [0u8; Aead::TAG_LEN], &[])?; + }); // ICR3 - core.mix(&ic.sidi)?.mix(&ic.sidr)?; + protocol_section!("ICR3", { + core.mix(&ic.sidi)?.mix(&ic.sidr)?; + }); // ICR4 - core.decrypt_and_mix(&mut [0u8; 0], &ic.auth)?; + protocol_section!("ICR4", { + core.decrypt_and_mix(&mut [0u8; 0], &ic.auth)?; + }); // ICR5 // Defense against replay attacks; implementations may accept @@ -3791,20 +3882,24 @@ impl CryptoServer { ); // ICR6 - peer.get_mut(self).biscuit_used = biscuit_no; + protocol_section!("ICR6", { + peer.get_mut(self).biscuit_used = biscuit_no; + }); // ICR7 - peer.session().insert( - self, - core.enter_live( + protocol_section!("ICR7", { + peer.session().insert( self, - HandshakeRole::Responder, - peer.get(self).protocol_version.keyed_hash(), - )?, - )?; - // TODO: This should be part of the protocol specification. - // Abort any ongoing handshake from initiator role - peer.hs().take(self); + core.enter_live( + self, + HandshakeRole::Responder, + peer.get(self).protocol_version.keyed_hash(), + )?, + )?; + // TODO: This should be part of the protocol specification. + // Abort any ongoing handshake from initiator role + peer.hs().take(self); + }); // TODO: Implementing RP should be possible without touching the live session stuff // TODO: I fear that this may lead to race conditions; the acknowledgement may be @@ -3849,6 +3944,10 @@ impl CryptoServer { /// message then terminates the handshake. /// /// The EmptyData message is just there to tell the initiator to abort retransmissions. + #[cfg_attr( + feature = "trace_bench", + rosenpass_util::trace_bench::trace_span("handle_resp_conf", rosenpass_util::trace_bench::TRACE) + )] pub fn handle_resp_conf( &mut self, msg_in: &Ref<&[u8], Envelope>, @@ -3906,6 +4005,10 @@ impl CryptoServer { /// DOS mitigation features. /// /// See more on DOS mitigation in Rosenpass in the [whitepaper](https://rosenpass.eu/whitepaper.pdf). + #[cfg_attr( + feature = "trace_bench", + rosenpass_util::trace_bench::trace_span("handle_cookie_reply", rosenpass_util::trace_bench::TRACE) + )] pub fn handle_cookie_reply(&mut self, cr: &CookieReply) -> Result { let peer_ptr: Option = self .lookup_session(Public::new(cr.inner.sid)) @@ -4030,7 +4133,7 @@ pub mod testutils { #[cfg(test)] mod test { - use std::{borrow::BorrowMut, net::SocketAddrV4, ops::DerefMut, thread::sleep, time::Duration}; + use std::{borrow::BorrowMut, net::SocketAddrV4, ops::DerefMut}; use super::*; use serial_test::serial; diff --git a/util/Cargo.toml b/util/Cargo.toml index ccc91c87..99d5baf7 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -24,6 +24,9 @@ thiserror = { workspace = true } mio = { workspace = true } tempfile = { workspace = true } uds = { workspace = true, optional = true, features = ["mio_1xx"] } +libcrux-test-utils = { workspace = true, optional = true } +lazy_static = { workspace = true, optional = true } [features] experiment_file_descriptor_passing = ["uds"] +trace_bench = ["dep:libcrux-test-utils", "dep:lazy_static"] diff --git a/util/src/lib.rs b/util/src/lib.rs index 66d2387f..43acbac1 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -36,3 +36,6 @@ pub mod typenum; pub mod zerocopy; /// Memory wiping utilities. pub mod zeroize; +/// Trace benchmarking utilities +#[cfg(feature = "trace_bench")] +pub mod trace_bench; diff --git a/util/src/trace_bench.rs b/util/src/trace_bench.rs new file mode 100644 index 00000000..d659dec2 --- /dev/null +++ b/util/src/trace_bench.rs @@ -0,0 +1,19 @@ +use std::time::Instant; + +use libcrux_test_utils::tracing; + +lazy_static::lazy_static! { + /// The trace value used in all Rosepass crates. + pub static ref TRACE: RpTrace = RpTrace::default(); +} + +/// The trace type used to trace Rosenpass for performance measurement. +pub type RpTrace = tracing::MutexTrace<&'static str, Instant>; + +/// The trace event type used to trace Rosenpass for performance measurement. +pub type RpEventType = tracing::TraceEvent<&'static str, Instant>; + +// Re-export to make functionality availalable and callers don't need to also directly depend on +// [`libcrux_test_utils`]. +pub use libcrux_test_utils::tracing::trace_span; +pub use tracing::Trace; From 196d459a2b93973ae59988875e9cc8f1e5497c60 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 20 May 2025 10:51:49 +0200 Subject: [PATCH 134/174] fix flake.nix: no more fenix --- .github/workflows/bench-primitives.yml | 2 +- .github/workflows/bench-protocol.yml | 2 +- flake.nix | 9 --------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml index c2a6234b..b0edaf14 100644 --- a/.github/workflows/bench-primitives.yml +++ b/.github/workflows/bench-primitives.yml @@ -67,7 +67,7 @@ jobs: - name: 🏃🏻‍♀️ Benchmarks (using Nix as shell) working-directory: ciphers - run: nix develop ".#devShells.${{ matrix.system }}.benchmark" --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt + run: nix develop --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt - name: Extract benchmarks uses: cryspen/benchmark-data-extract-transform@v2 diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml index 881d12d5..6a66f74b 100644 --- a/.github/workflows/bench-protocol.yml +++ b/.github/workflows/bench-protocol.yml @@ -66,7 +66,7 @@ jobs: # Benchmarks ... - name: 🏃🏻‍♀️ Benchmarks - run: nix develop ".#devShells.${{ matrix.system }}.benchmark" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json + run: nix develop --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json - name: Upload benchmarks uses: cryspen/benchmark-upload-and-plot-action@v3 diff --git a/flake.nix b/flake.nix index 4b3413f6..8ae3fad9 100644 --- a/flake.nix +++ b/flake.nix @@ -173,15 +173,6 @@ inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA; }; }; - devShells.benchmark = pkgs.mkShell { - inputsFrom = [ pkgs.rosenpass ]; - nativeBuildInputs = let - rustToolchain = (inputs.fenix.packages.${system}.toolchainOf { - channel = "1.77.0"; - sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8="; - }); - in [ rustToolchain.toolchain ]; - }; checks = { From cf061bd0f5457b678ddb8b285011cefac2521f98 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 20 May 2025 11:03:12 +0200 Subject: [PATCH 135/174] workflows: use arch-specific dev shell --- .github/workflows/bench-primitives.yml | 2 +- .github/workflows/bench-protocol.yml | 2 +- ciphers/Cargo.toml | 12 ++++++------ util/src/lib.rs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml index b0edaf14..395bdeaf 100644 --- a/.github/workflows/bench-primitives.yml +++ b/.github/workflows/bench-primitives.yml @@ -67,7 +67,7 @@ jobs: - name: 🏃🏻‍♀️ Benchmarks (using Nix as shell) working-directory: ciphers - run: nix develop --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt + run: nix develop ".#devShells.${{ matrix.system }}.default" --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt - name: Extract benchmarks uses: cryspen/benchmark-data-extract-transform@v2 diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml index 6a66f74b..752ed908 100644 --- a/.github/workflows/bench-protocol.yml +++ b/.github/workflows/bench-protocol.yml @@ -66,7 +66,7 @@ jobs: # Benchmarks ... - name: 🏃🏻‍♀️ Benchmarks - run: nix develop --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json + run: nix develop ".#devShells.${{ matrix.system }}.default" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json - name: Upload benchmarks uses: cryspen/benchmark-upload-and-plot-action@v3 diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 080deeb0..5a1c375d 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -25,12 +25,12 @@ experiment_libcrux_chachapoly_test = [ ] experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"] bench = [ - "dep:thiserror", - "dep:rand", - "dep:libcrux", - "dep:libcrux-blake2", - "dep:libcrux-ml-kem", - "dep:libcrux-chacha20poly1305", + "dep:thiserror", + "dep:rand", + "dep:libcrux", + "dep:libcrux-blake2", + "dep:libcrux-ml-kem", + "dep:libcrux-chacha20poly1305", ] [[bench]] diff --git a/util/src/lib.rs b/util/src/lib.rs index 43acbac1..7949a3b7 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -30,12 +30,12 @@ pub mod option; pub mod result; /// Time and duration utilities. pub mod time; +/// Trace benchmarking utilities +#[cfg(feature = "trace_bench")] +pub mod trace_bench; /// Type-level numbers and arithmetic. pub mod typenum; /// Zero-copy serialization utilities. pub mod zerocopy; /// Memory wiping utilities. pub mod zeroize; -/// Trace benchmarking utilities -#[cfg(feature = "trace_bench")] -pub mod trace_bench; From 77b50b70b1f91ffc43162b6d0346b40753c2dd62 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 27 May 2025 16:29:46 +0200 Subject: [PATCH 136/174] address feedback --- .github/workflows/bench-primitives.yml | 22 +++----- .github/workflows/bench-protocol.yml | 17 ++---- ciphers/benches/primitives.rs | 2 - flake.nix | 8 +++ readme.md | 73 ++++++++++++++------------ rosenpass/benches/trace_handshake.rs | 6 +-- 6 files changed, 62 insertions(+), 66 deletions(-) diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml index 395bdeaf..d35855c7 100644 --- a/.github/workflows/bench-primitives.yml +++ b/.github/workflows/bench-primitives.yml @@ -37,14 +37,6 @@ jobs: # Set up environment - - name: 🛠️ Config Linux x64 - run: echo "RUST_TARGET_FLAG=--target=x86_64-unknown-linux-gnu" > $GITHUB_ENV - if: ${{ matrix.system == 'x86_64-linux' }} - - - name: 🛠️ Config Linux x86 - run: echo "RUST_TARGET_FLAG=--target=i686-unknown-linux-gnu" > $GITHUB_ENV - if: ${{ matrix.system == 'i686-linux' }} - - name: 🛠️ Prepare Benchmark Path env: EVENT_NAME: ${{ github.event_name }} @@ -67,7 +59,7 @@ jobs: - name: 🏃🏻‍♀️ Benchmarks (using Nix as shell) working-directory: ciphers - run: nix develop ".#devShells.${{ matrix.system }}.default" --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt + run: nix develop ".#devShells.${{ matrix.system }}.benchmarks" --command cargo bench -F bench --bench primitives --verbose -- --output-format bencher | tee ../bench-primitives.txt - name: Extract benchmarks uses: cryspen/benchmark-data-extract-transform@v2 @@ -76,22 +68,24 @@ jobs: tool: "cargo" os: ${{ matrix.system }} output-file-path: bench-primitives.txt - data-out-path: bench-primitives.json + data-out-path: bench-primitives-os.json + + - name: Fix up 'os' label in benchmark data + run: jq 'map(with_entries(.key |= if . == "os" then "operating system" else . end))' bench-primitives.json - name: Upload benchmarks uses: cryspen/benchmark-upload-and-plot-action@v3 with: name: Crypto Primitives Benchmarks - group-by: "os,primitive,algorithm" - schema: "os,primitive,algorithm,implementation,operation,length" + group-by: "operating system,primitive,algorithm" + schema: "operating system,primitive,algorithm,implementation,operation,length" input-data-path: bench-primitives.json github-token: ${{ secrets.GITHUB_TOKEN }} # NOTE: pushes to current repository gh-repository: github.com/${{ github.repository }} - # use the default (gh-pages) for the demo - #gh-pages-branch: benchmarks auto-push: true fail-on-alert: true + base-path: benchmarks/ ciphers-primitives-bench-status: if: ${{ always() }} diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml index 752ed908..f531651b 100644 --- a/.github/workflows/bench-protocol.yml +++ b/.github/workflows/bench-protocol.yml @@ -37,14 +37,6 @@ jobs: # Set up environment - - name: 🛠️ Config Linux x64 - run: echo "RUST_TARGET_FLAG=--target=x86_64-unknown-linux-gnu" > $GITHUB_ENV - if: ${{ matrix.system == 'x86_64-linux' }} - - - name: 🛠️ Config Linux x86 - run: echo "RUST_TARGET_FLAG=--target=i686-unknown-linux-gnu" > $GITHUB_ENV - if: ${{ matrix.system == 'i686-linux' }} - - name: 🛠️ Prepare Benchmark Path env: EVENT_NAME: ${{ github.event_name }} @@ -66,22 +58,21 @@ jobs: # Benchmarks ... - name: 🏃🏻‍♀️ Benchmarks - run: nix develop ".#devShells.${{ matrix.system }}.default" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json + run: nix develop ".#devShells.${{ matrix.system }}.benchmarks" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose >bench-protocol.json - name: Upload benchmarks uses: cryspen/benchmark-upload-and-plot-action@v3 with: name: Protocol Benchmarks - group-by: "os,arch,protocol version,run time" - schema: "os,arch,protocol version,run time,name" + group-by: "operating system,architecture,protocol version,run time" + schema: "operating system,architecture,protocol version,run time,name" input-data-path: bench-protocol.json github-token: ${{ secrets.GITHUB_TOKEN }} # NOTE: pushes to current repository gh-repository: github.com/${{ github.repository }} - # use the default (gh-pages) for the demo - #gh-pages-branch: benchmarks auto-push: true fail-on-alert: true + base-path: benchmarks/ ciphers-protocol-bench-status: if: ${{ always() }} diff --git a/ciphers/benches/primitives.rs b/ciphers/benches/primitives.rs index eb81c66e..61a649b1 100644 --- a/ciphers/benches/primitives.rs +++ b/ciphers/benches/primitives.rs @@ -342,5 +342,3 @@ mod keyed_hash { }); } } - -mod templates {} diff --git a/flake.nix b/flake.nix index 8ae3fad9..9e2a9350 100644 --- a/flake.nix +++ b/flake.nix @@ -173,6 +173,14 @@ inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA; }; }; + devShells.benchmarks = pkgs.mkShell { + inputsFrom = [ pkgs.rosenpass ]; + nativeBuildInputs = with pkgs; [ + cargo-release + clippy + rustfmt + ]; + }; checks = { diff --git a/readme.md b/readme.md index c4ceb1af..c13782b7 100644 --- a/readme.md +++ b/readme.md @@ -1,37 +1,3 @@ -# Changes on This Branch - -This branch adds facilities for benchmarking both the Rosenpass protocol -code and the implementations of the primitives behind it. The primitives -are benchmarked using criterion. For the protocol code, we use a custom -library for instrumenting the code such that events are written to a -trace, which is then inspected after a run. - -## Protocol Benchmark - -The trace that is being written to lives in a new module -`trace_bench` in the util crate. A basic benchmark that -performs some minor statistical analysis of the trace can be run using - -``` -cargo bench -p rosenpass --bench trace_handshake -F trace_bench -``` - -## Primitive Benchmark - -Benchmarks for the functions of the traits `Kem`, `Aead` and `KeyedHash` -have been added and are run for all implementations in the `primitives` -benchmark of `rosenpass-ciphers`. Run the benchmarks using - -``` -cargo bench -p rosenpass-ciphers --bench primitives -F bench -``` - -Note that the `bench` feature enables the inclusion of the libcrux-backed -trait implementations in the module tree, but does not enable them -as default. - ---- - # Rosenpass README ![Nix](https://github.com/rosenpass/rosenpass/actions/workflows/nix.yaml/badge.svg) @@ -117,6 +83,45 @@ Rosenpass is also available as prebuilt Docker images: For details on how to use these images, refer to the [Docker usage guide](docker/USAGE.md). +## Benchmarks + +This repository contains facilities for benchmarking both the Rosenpass +protocol code and the implementations of the cryptographic primitives used +by it. The primitives are benchmarked using criterion. For the protocol code +benchmarks we use a library for instrumenting the code such that events are +written to a trace, which is then inspected after a run. + +Benchmarks are automatically run on CI. The measurements are visualized in the +[Benchmark Dashboard]. + +[Benchmark Dashboard]: https://rosenpass.github.io/benchmarks + +### Primitive Benchmarks + +There are benchmarks for the functions of the traits `Kem`, `Aead` and +`KeyedHash`. They are run for all implementations in the `primitives` +benchmark of `rosenpass-ciphers`. Run the benchmarks using + +``` +cargo bench -p rosenpass-ciphers --bench primitives -F bench +``` + +Note that the `bench` feature enables the inclusion of the libcrux-backed +trait implementations in the module tree, but does not enable them +as default. + +### Protocol Benchmarks + +The trace that is being written to lives in a new module +`trace_bench` in the util crate. A basic benchmark that +performs some minor statistical analysis of the trace can be run using + +``` +cargo bench -p rosenpass --bench trace_handshake -F trace_bench +``` + +--- + # Mirrors Don't want to use GitHub or only have an IPv6 connection? Rosenpass has set up two mirrors for this: diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs index 9adaafe8..a8f6d166 100644 --- a/rosenpass/benches/trace_handshake.rs +++ b/rosenpass/benches/trace_handshake.rs @@ -183,8 +183,8 @@ impl std::fmt::Display for RunTimeGroup { let txt = match self { RunTimeGroup::Long => "long", RunTimeGroup::Medium => "medium", - RunTimeGroup::BelowMillisec => "below_ms", - RunTimeGroup::BelowMicrosec => "below_us", + RunTimeGroup::BelowMillisec => "below 1ms", + RunTimeGroup::BelowMicrosec => "below 1us", }; write!(f, "{txt}") } @@ -357,7 +357,7 @@ impl AggregateStat { // Format the JSON string using measured values and environment constants writeln!( w, - r#"{{"name":"{name}", "unit":"ns/iter", "value":"{value}", "range":"± {range}", "protocol version":"{protocol_version}", "sample size":"{sample_size}", "os":"{os}", "arch":"{arch}", "run time":"{run_time}"}}"#, + r#"{{"name":"{name}", "unit":"ns/iter", "value":"{value}", "range":"± {range}", "protocol version":"{protocol_version}", "sample size":"{sample_size}", "operating system":"{os}", "architecture":"{arch}", "run time":"{run_time}"}}"#, name = label, // Benchmark name value = self.mean_duration.as_nanos(), // Mean duration in nanoseconds range = self.sd_duration.as_nanos(), // Standard deviation in nanoseconds From 7fc6fd2f52e05bf9e644d1d7463a654196b9db94 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 27 May 2025 18:20:45 +0200 Subject: [PATCH 137/174] format readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index c13782b7..655a563c 100644 --- a/readme.md +++ b/readme.md @@ -95,7 +95,7 @@ Benchmarks are automatically run on CI. The measurements are visualized in the [Benchmark Dashboard]. [Benchmark Dashboard]: https://rosenpass.github.io/benchmarks - + ### Primitive Benchmarks There are benchmarks for the functions of the traits `Kem`, `Aead` and From 5106ffd549186a404f815f1268021866d52f11b3 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 27 May 2025 18:30:24 +0200 Subject: [PATCH 138/174] strictly format attr macros --- rosenpass/src/protocol/protocol.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 5a5e7c54..bbc79208 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3565,7 +3565,10 @@ impl CryptoServer { /// on the initiator side, producing the InitHello message. #[cfg_attr( feature = "trace_bench", - rosenpass_util::trace_bench::trace_span("handle_initiation", rosenpass_util::trace_bench::TRACE) + rosenpass_util::trace_bench::trace_span( + "handle_initiation", + rosenpass_util::trace_bench::TRACE + ) )] pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { let mut hs = InitiatorHandshake::zero_with_timestamp( @@ -3632,7 +3635,10 @@ impl CryptoServer { /// [RespHello] message on the responder side. #[cfg_attr( feature = "trace_bench", - rosenpass_util::trace_bench::trace_span("handle_init_hello", rosenpass_util::trace_bench::TRACE) + rosenpass_util::trace_bench::trace_span( + "handle_init_hello", + rosenpass_util::trace_bench::TRACE + ) )] pub fn handle_init_hello( &mut self, @@ -3717,7 +3723,10 @@ impl CryptoServer { /// [InitConf] message on the initiator side. #[cfg_attr( feature = "trace_bench", - rosenpass_util::trace_bench::trace_span("handle_resp_hello", rosenpass_util::trace_bench::TRACE) + rosenpass_util::trace_bench::trace_span( + "handle_resp_hello", + rosenpass_util::trace_bench::TRACE + ) )] pub fn handle_resp_hello(&mut self, rh: &RespHello, ic: &mut InitConf) -> Result { // RHI2 @@ -3837,7 +3846,10 @@ impl CryptoServer { /// an acknowledgement message telling the initiator to stop performing retransmissions. #[cfg_attr( feature = "trace_bench", - rosenpass_util::trace_bench::trace_span("handle_init_conf", rosenpass_util::trace_bench::TRACE) + rosenpass_util::trace_bench::trace_span( + "handle_init_conf", + rosenpass_util::trace_bench::TRACE + ) )] pub fn handle_init_conf( &mut self, @@ -3946,7 +3958,10 @@ impl CryptoServer { /// The EmptyData message is just there to tell the initiator to abort retransmissions. #[cfg_attr( feature = "trace_bench", - rosenpass_util::trace_bench::trace_span("handle_resp_conf", rosenpass_util::trace_bench::TRACE) + rosenpass_util::trace_bench::trace_span( + "handle_resp_conf", + rosenpass_util::trace_bench::TRACE + ) )] pub fn handle_resp_conf( &mut self, @@ -4007,7 +4022,10 @@ impl CryptoServer { /// See more on DOS mitigation in Rosenpass in the [whitepaper](https://rosenpass.eu/whitepaper.pdf). #[cfg_attr( feature = "trace_bench", - rosenpass_util::trace_bench::trace_span("handle_cookie_reply", rosenpass_util::trace_bench::TRACE) + rosenpass_util::trace_bench::trace_span( + "handle_cookie_reply", + rosenpass_util::trace_bench::TRACE + ) )] pub fn handle_cookie_reply(&mut self, cr: &CookieReply) -> Result { let peer_ptr: Option = self From 9cc7a58ee7e12428caef54acd60bb8e4cea1ca0c Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 3 Jun 2025 10:05:47 +0200 Subject: [PATCH 139/174] Set adequate permissions to push benchmarks --- .github/workflows/bench-primitives.yml | 3 +++ .github/workflows/bench-protocol.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml index d35855c7..dc11cddd 100644 --- a/.github/workflows/bench-primitives.yml +++ b/.github/workflows/bench-primitives.yml @@ -1,5 +1,8 @@ name: rosenpass-ciphers - primitives - benchmark +permissions: + contents: write + on: pull_request: push: diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml index f531651b..139e6f6b 100644 --- a/.github/workflows/bench-protocol.yml +++ b/.github/workflows/bench-protocol.yml @@ -1,5 +1,8 @@ name: rosenpass - protocol - benchmark +permissions: + contents: write + on: pull_request: push: From 73df0ceca7818533631931199abddcb57d10a571 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Wed, 4 Jun 2025 10:55:43 +0200 Subject: [PATCH 140/174] Address feedback --- ciphers/benches/primitives.rs | 36 +++++++++++++++++++++++++++- rosenpass/benches/trace_handshake.rs | 23 ++++++++++-------- rosenpass/src/protocol/protocol.rs | 2 +- util/src/trace_bench.rs | 2 +- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/ciphers/benches/primitives.rs b/ciphers/benches/primitives.rs index 61a649b1..f1ef4d7e 100644 --- a/ciphers/benches/primitives.rs +++ b/ciphers/benches/primitives.rs @@ -194,6 +194,33 @@ mod aead { let nonce = [23; NONCE_LEN]; let ad = []; + c.bench_function(&aead_benchid("encrypt", "0byte"), |bench| { + const DATA_LEN: usize = 0; + + let ptxt = []; + let mut ctxt = [0; DATA_LEN + TAG_LEN]; + + bench.iter(|| { + scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap(); + }); + }); + + c.bench_function(&aead_benchid("decrypt", "0byte"), |bench| { + const DATA_LEN: usize = 0; + + let ptxt = []; + let mut ctxt = [0; DATA_LEN + TAG_LEN]; + let mut ptxt_out = [0u8; DATA_LEN]; + + scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap(); + + bench.iter(|| { + scheme + .decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt) + .unwrap() + }) + }); + c.bench_function(&aead_benchid("encrypt", "32byte"), |bench| { const DATA_LEN: usize = 32; @@ -312,7 +339,14 @@ mod keyed_hash { ]; let keyedhash_benchid = |len| benchid(KvPairs(&base), KvPairs(&[KvPair("length", len)])); - c.bench_function(&keyedhash_benchid("32byte"), |bench| { + c.bench_function(&keyedhash_benchid("0byte"), |bench| { + let bytes = []; + + bench.iter(|| { + H::keyed_hash(&key, &bytes, &mut out).unwrap(); + }) + }) + .bench_function(&keyedhash_benchid("32byte"), |bench| { let bytes = [34u8; 32]; bench.iter(|| { diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs index a8f6d166..7710495c 100644 --- a/rosenpass/benches/trace_handshake.rs +++ b/rosenpass/benches/trace_handshake.rs @@ -1,4 +1,3 @@ -// Standard library imports use std::{ collections::HashMap, hint::black_box, @@ -7,17 +6,18 @@ use std::{ time::{Duration, Instant}, }; -// External crate imports use anyhow::Result; use libcrux_test_utils::tracing::{EventType, Trace as _}; -use rosenpass::protocol::{ - CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, -}; + use rosenpass_cipher_traits::primitives::Kem; use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; use rosenpass_util::trace_bench::{RpEventType, TRACE}; +use rosenpass::protocol::{ + CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, +}; + const ITERATIONS: usize = 100; fn handle( @@ -116,8 +116,11 @@ fn main() { .expect("error writing json data"); } -/// Takes a vector of trace events, bins them by label, extracts durations, -/// filters empty bins, calculates aggregate statistics (mean, std dev), and returns them. +/// Performs a simple statistical analysis: +/// - bins trace events by label +/// - extracts durations of spamns +/// - filters out empty bins +/// - calculates aggregate statistics (mean, std dev) fn statistical_analysis(trace: Vec) -> Vec<(&'static str, AggregateStat)> { bin_events(trace) .into_iter() @@ -132,9 +135,9 @@ fn statistical_analysis(trace: Vec) -> Vec<(&'static str, Aggregate /// /// # Arguments /// * `w` - The writer to output JSON to (e.g., stdout, file). -/// * `item_groups` - An iterator producing tuples of (`&'static str`, `II`), where -/// `II` is itself an iterator producing (`&'static str`, `AggregateStat`). -/// Represents the protocol_version name and the statistics items within that protocol_version. +/// * `item_groups` - An iterator producing tuples `(version, stats): (&'static str, II)`. +/// Here `II` is itself an iterator producing `(label, agg_stat): (&'static str, AggregateStat)`, +/// where the label is the label of the span, e.g. "IHI2". /// /// # Type Parameters /// * `W` - A type that implements `std::io::Write`. diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index bbc79208..dd95a5c4 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3548,7 +3548,7 @@ impl CryptoServer { /// Marks a section of the protocol using the same identifiers as are used in the whitepaper. /// When building with the trace benchmarking feature enabled, this also emits span events into the -/// trace, which allows reconstructing the run times of the individual sections for performace +/// trace, which allows reconstructing the run times of the individual sections for performance /// measurement. macro_rules! protocol_section { ($label:expr, $body:tt) => {{ diff --git a/util/src/trace_bench.rs b/util/src/trace_bench.rs index d659dec2..5f1528a2 100644 --- a/util/src/trace_bench.rs +++ b/util/src/trace_bench.rs @@ -13,7 +13,7 @@ pub type RpTrace = tracing::MutexTrace<&'static str, Instant>; /// The trace event type used to trace Rosenpass for performance measurement. pub type RpEventType = tracing::TraceEvent<&'static str, Instant>; -// Re-export to make functionality availalable and callers don't need to also directly depend on +// Re-export to make functionality available and callers don't need to also directly depend on // [`libcrux_test_utils`]. pub use libcrux_test_utils::tracing::trace_span; pub use tracing::Trace; From 91707cc430710949f0e97356589b558a74a10bc1 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 23 Jun 2025 15:29:04 +0200 Subject: [PATCH 141/174] Address feedback --- Cargo.lock | 1 - ciphers/Cargo.toml | 43 ++++++++++-------- ciphers/src/subtle/libcrux/mod.rs | 6 +-- ciphers/src/subtle/mod.rs | 7 ++- readme.md | 6 ++- rosenpass/benches/trace_handshake.rs | 18 ++++++-- rosenpass/src/protocol/protocol.rs | 67 ++++++++++------------------ util/Cargo.toml | 3 +- util/src/trace_bench.rs | 12 +++-- 9 files changed, 82 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b356525c..cbe6f623 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2172,7 +2172,6 @@ version = "0.1.0" dependencies = [ "anyhow", "base64ct", - "lazy_static", "libcrux-test-utils", "mio", "rustix", diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index 5a1c375d..c20e338d 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -11,27 +11,34 @@ readme = "readme.md" rust-version = "1.77.0" [features] -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"] +# whether the types should be defined +experiment_libcrux_define_blake2 = ["dep:libcrux-blake2", "dep:thiserror"] +experiment_libcrux_define_kyber = ["dep:libcrux-ml-kem", "dep:rand"] +experiment_libcrux_define_chachapoly = ["dep:libcrux-chacha20poly1305"] + +# whether the types should be used by default +experiment_libcrux_blake2 = ["experiment_libcrux_define_blake2"] +experiment_libcrux_kyber = ["experiment_libcrux_define_kyber"] +experiment_libcrux_chachapoly = ["experiment_libcrux_define_chachapoly"] experiment_libcrux_chachapoly_test = [ - "experiment_libcrux_chachapoly", - "dep:libcrux", + "experiment_libcrux_define_chachapoly", + "dep:libcrux", ] -experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"] -bench = [ - "dep:thiserror", - "dep:rand", - "dep:libcrux", - "dep:libcrux-blake2", - "dep:libcrux-ml-kem", - "dep:libcrux-chacha20poly1305", + +# shorthands +experiment_libcrux_define_all = [ + "experiment_libcrux_define_blake2", + "experiment_libcrux_define_chachapoly", + "experiment_libcrux_define_kyber", ] +experiment_libcrux_all = [ + "experiment_libcrux_blake2", + "experiment_libcrux_chachapoly", + "experiment_libcrux_chachapoly_test", + "experiment_libcrux_kyber", +] + +bench = ["experiment_libcrux_define_all"] [[bench]] name = "primitives" diff --git a/ciphers/src/subtle/libcrux/mod.rs b/ciphers/src/subtle/libcrux/mod.rs index 210bc61a..f481e535 100644 --- a/ciphers/src/subtle/libcrux/mod.rs +++ b/ciphers/src/subtle/libcrux/mod.rs @@ -4,11 +4,11 @@ //! //! [Github](https://github.com/cryspen/libcrux) -#[cfg(any(feature = "experiment_libcrux_blake2", feature = "bench"))] +#[cfg(feature = "experiment_libcrux_define_blake2")] pub mod blake2b; -#[cfg(any(feature = "experiment_libcrux_chachapoly", feature = "bench"))] +#[cfg(feature = "experiment_libcrux_define_chachapoly")] pub mod chacha20poly1305_ietf; -#[cfg(any(feature = "experiment_libcrux_kyber", feature = "bench"))] +#[cfg(feature = "experiment_libcrux_define_kyber")] pub mod kyber512; diff --git a/ciphers/src/subtle/mod.rs b/ciphers/src/subtle/mod.rs index 6221324f..6d3083b2 100644 --- a/ciphers/src/subtle/mod.rs +++ b/ciphers/src/subtle/mod.rs @@ -9,9 +9,8 @@ pub mod custom; pub mod rust_crypto; #[cfg(any( - feature = "experiment_libcrux_blake2", - feature = "experiment_libcrux_chachapoly", - feature = "experiment_libcrux_kyber", - feature = "bench" + feature = "experiment_libcrux_define_blake2", + feature = "experiment_libcrux_define_chachapoly", + feature = "experiment_libcrux_define_kyber", ))] pub mod libcrux; diff --git a/readme.md b/readme.md index 655a563c..6021361c 100644 --- a/readme.md +++ b/readme.md @@ -94,13 +94,13 @@ written to a trace, which is then inspected after a run. Benchmarks are automatically run on CI. The measurements are visualized in the [Benchmark Dashboard]. -[Benchmark Dashboard]: https://rosenpass.github.io/benchmarks +[Benchmark Dashboard]: https://rosenpass.github.io/rosenpass/benchmarks ### Primitive Benchmarks There are benchmarks for the functions of the traits `Kem`, `Aead` and `KeyedHash`. They are run for all implementations in the `primitives` -benchmark of `rosenpass-ciphers`. Run the benchmarks using +benchmark of `rosenpass-ciphers`. Run the benchmarks and view their results using ``` cargo bench -p rosenpass-ciphers --bench primitives -F bench @@ -120,6 +120,8 @@ performs some minor statistical analysis of the trace can be run using cargo bench -p rosenpass --bench trace_handshake -F trace_bench ``` +This runs the benchmarks and prints the results in machine-readable JSON. + --- # Mirrors diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs index 7710495c..95a7bdb5 100644 --- a/rosenpass/benches/trace_handshake.rs +++ b/rosenpass/benches/trace_handshake.rs @@ -12,7 +12,7 @@ use libcrux_test_utils::tracing::{EventType, Trace as _}; use rosenpass_cipher_traits::primitives::Kem; use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; -use rosenpass_util::trace_bench::{RpEventType, TRACE}; +use rosenpass_util::trace_bench::RpEventType; use rosenpass::protocol::{ CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, @@ -20,6 +20,10 @@ use rosenpass::protocol::{ const ITERATIONS: usize = 100; +/// Performs a full protocol run by processing a message and recursing into handling that message, +/// until no further response is produced. Returns the keys produce by the two parties. +/// +/// Ensures that each party produces one of the two keys. fn handle( tx: &mut CryptoServer, msgb: &mut MsgBuf, @@ -46,6 +50,10 @@ fn handle( Ok((txk, rxk.or(xch))) } +/// Performs the full handshake by calling `handle` with the correct values, based on just two +/// `CryptoServer`s. +/// +/// Ensures that both parties compute the same keys. fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> { let (mut inib, mut resb) = (MsgBuf::zero(), MsgBuf::zero()); let sz = ini.initiate_handshake(PeerPtr(0), &mut *inib)?; @@ -54,12 +62,14 @@ fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> { Ok(()) } +/// Generates a new key pair. fn keygen() -> Result<(SSk, SPk)> { let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?; Ok((sk, pk)) } +/// Creates two instanves of `CryptoServer`, generating key pairs for each. fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> { let psk = SymKey::random(); let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?); @@ -73,6 +83,8 @@ fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, } fn main() { + let trace = rosenpass_util::trace_bench::trace(); + // Attempt to use memfd_secrets for storing sensitive key material secret_policy_try_use_memfd_secrets(); @@ -83,7 +95,7 @@ fn main() { } // Emit a marker event to separate V02 and V03 trace sections - TRACE.emit_on_the_fly("start-hs-v03"); + trace.emit_on_the_fly("start-hs-v03"); // Run protocol for V03 let (mut a_v03, mut b_v03) = make_server_pair(ProtocolVersion::V03).unwrap(); @@ -92,7 +104,7 @@ fn main() { } // Collect the trace events generated during the handshakes - let trace: Vec<_> = TRACE.clone().report(); + let trace: Vec<_> = trace.clone().report(); // Split the trace into V02 and V03 sections based on the marker let (trace_v02, trace_v03) = { diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index dd95a5c4..13df0037 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -17,6 +17,9 @@ use std::{ use anyhow::{bail, ensure, Context, Result}; +#[cfg(feature = "trace_bench")] +use rosenpass_util::trace_bench::Trace as _; + use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; use rosenpass_cipher_traits::primitives::{ @@ -3551,9 +3554,9 @@ impl CryptoServer { /// trace, which allows reconstructing the run times of the individual sections for performance /// measurement. macro_rules! protocol_section { - ($label:expr, $body:tt) => {{ + ($label:expr, $body:block) => {{ #[cfg(feature = "trace_bench")] - let _span_raii_handle = rosenpass_util::trace_bench::TRACE.emit_span($label); + let _span_guard = rosenpass_util::trace_bench::trace().emit_span($label); #[allow(unused_braces)] $body @@ -3563,14 +3566,10 @@ macro_rules! protocol_section { impl CryptoServer { /// Core cryptographic protocol implementation: Kicks of the handshake /// on the initiator side, producing the InitHello message. - #[cfg_attr( - feature = "trace_bench", - rosenpass_util::trace_bench::trace_span( - "handle_initiation", - rosenpass_util::trace_bench::TRACE - ) - )] pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result { + #[cfg(feature = "trace_bench")] + let _span_guard = rosenpass_util::trace_bench::trace().emit_span("handle_initiation"); + let mut hs = InitiatorHandshake::zero_with_timestamp( self, peer.get(self).protocol_version.keyed_hash(), @@ -3633,19 +3632,15 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a /// [RespHello] message on the responder side. - #[cfg_attr( - feature = "trace_bench", - rosenpass_util::trace_bench::trace_span( - "handle_init_hello", - rosenpass_util::trace_bench::TRACE - ) - )] pub fn handle_init_hello( &mut self, ih: &InitHello, rh: &mut RespHello, keyed_hash: KeyedHash, ) -> Result { + #[cfg(feature = "trace_bench")] + let _span_guard = rosenpass_util::trace_bench::trace().emit_span("handle_init_hello"); + let mut core = HandshakeState::zero(keyed_hash); core.sidi = SessionId::from_slice(&ih.sidi); @@ -3721,14 +3716,10 @@ impl CryptoServer { /// Core cryptographic protocol implementation: Parses an [RespHello] message and produces an /// [InitConf] message on the initiator side. - #[cfg_attr( - feature = "trace_bench", - rosenpass_util::trace_bench::trace_span( - "handle_resp_hello", - rosenpass_util::trace_bench::TRACE - ) - )] pub fn handle_resp_hello(&mut self, rh: &RespHello, ic: &mut InitConf) -> Result { + #[cfg(feature = "trace_bench")] + let _span_guard = rosenpass_util::trace_bench::trace().emit_span("handle_resp_hello"); + // RHI2 let peer = self .lookup_handshake(SessionId::from_slice(&rh.sidi)) @@ -3844,19 +3835,15 @@ impl CryptoServer { /// /// This concludes the handshake on the cryptographic level; the [EmptyData] message is just /// an acknowledgement message telling the initiator to stop performing retransmissions. - #[cfg_attr( - feature = "trace_bench", - rosenpass_util::trace_bench::trace_span( - "handle_init_conf", - rosenpass_util::trace_bench::TRACE - ) - )] pub fn handle_init_conf( &mut self, ic: &InitConf, rc: &mut EmptyData, keyed_hash: KeyedHash, ) -> Result { + #[cfg(feature = "trace_bench")] + let _span_guard = rosenpass_util::trace_bench::trace().emit_span("handle_init_conf"); + // (peer, bn) ← LoadBiscuit(InitConf.biscuit) // ICR1 let (peer, biscuit_no, mut core) = protocol_section!("ICR1", { @@ -3956,18 +3943,14 @@ impl CryptoServer { /// message then terminates the handshake. /// /// The EmptyData message is just there to tell the initiator to abort retransmissions. - #[cfg_attr( - feature = "trace_bench", - rosenpass_util::trace_bench::trace_span( - "handle_resp_conf", - rosenpass_util::trace_bench::TRACE - ) - )] pub fn handle_resp_conf( &mut self, msg_in: &Ref<&[u8], Envelope>, seal_broken: String, ) -> Result { + #[cfg(feature = "trace_bench")] + let _span_guard = rosenpass_util::trace_bench::trace().emit_span("handle_resp_conf"); + let rc: &EmptyData = &msg_in.payload; let sid = SessionId::from_slice(&rc.sid); let hs = self @@ -4020,14 +4003,10 @@ impl CryptoServer { /// DOS mitigation features. /// /// See more on DOS mitigation in Rosenpass in the [whitepaper](https://rosenpass.eu/whitepaper.pdf). - #[cfg_attr( - feature = "trace_bench", - rosenpass_util::trace_bench::trace_span( - "handle_cookie_reply", - rosenpass_util::trace_bench::TRACE - ) - )] pub fn handle_cookie_reply(&mut self, cr: &CookieReply) -> Result { + #[cfg(feature = "trace_bench")] + let _span_guard = rosenpass_util::trace_bench::trace().emit_span("handle_cookie_reply"); + let peer_ptr: Option = self .lookup_session(Public::new(cr.inner.sid)) .map(|v| PeerPtr(v.0)) diff --git a/util/Cargo.toml b/util/Cargo.toml index 99d5baf7..bdd1ddf6 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -25,8 +25,7 @@ mio = { workspace = true } tempfile = { workspace = true } uds = { workspace = true, optional = true, features = ["mio_1xx"] } libcrux-test-utils = { workspace = true, optional = true } -lazy_static = { workspace = true, optional = true } [features] experiment_file_descriptor_passing = ["uds"] -trace_bench = ["dep:libcrux-test-utils", "dep:lazy_static"] +trace_bench = ["dep:libcrux-test-utils"] diff --git a/util/src/trace_bench.rs b/util/src/trace_bench.rs index 5f1528a2..367efa61 100644 --- a/util/src/trace_bench.rs +++ b/util/src/trace_bench.rs @@ -1,11 +1,10 @@ +use std::sync::OnceLock; use std::time::Instant; use libcrux_test_utils::tracing; -lazy_static::lazy_static! { - /// The trace value used in all Rosepass crates. - pub static ref TRACE: RpTrace = RpTrace::default(); -} +/// The trace value used in all Rosepass crates. +static TRACE: OnceLock = OnceLock::new(); /// The trace type used to trace Rosenpass for performance measurement. pub type RpTrace = tracing::MutexTrace<&'static str, Instant>; @@ -17,3 +16,8 @@ pub type RpEventType = tracing::TraceEvent<&'static str, Instant>; // [`libcrux_test_utils`]. pub use libcrux_test_utils::tracing::trace_span; pub use tracing::Trace; + +/// Returns a reference to the trace and lazily initializes it. +pub fn trace() -> &'static tracing::MutexTrace<&'static str, Instant> { + TRACE.get_or_init(tracing::MutexTrace::default) +} From 811c1746c1195046f3337c7a6dab2b31b123ecb0 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Mon, 23 Jun 2025 15:36:11 +0200 Subject: [PATCH 142/174] format Cargo.toml --- ciphers/Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ciphers/Cargo.toml b/ciphers/Cargo.toml index c20e338d..7931f477 100644 --- a/ciphers/Cargo.toml +++ b/ciphers/Cargo.toml @@ -21,21 +21,21 @@ experiment_libcrux_blake2 = ["experiment_libcrux_define_blake2"] experiment_libcrux_kyber = ["experiment_libcrux_define_kyber"] experiment_libcrux_chachapoly = ["experiment_libcrux_define_chachapoly"] experiment_libcrux_chachapoly_test = [ - "experiment_libcrux_define_chachapoly", - "dep:libcrux", + "experiment_libcrux_define_chachapoly", + "dep:libcrux", ] # shorthands experiment_libcrux_define_all = [ - "experiment_libcrux_define_blake2", - "experiment_libcrux_define_chachapoly", - "experiment_libcrux_define_kyber", + "experiment_libcrux_define_blake2", + "experiment_libcrux_define_chachapoly", + "experiment_libcrux_define_kyber", ] experiment_libcrux_all = [ - "experiment_libcrux_blake2", - "experiment_libcrux_chachapoly", - "experiment_libcrux_chachapoly_test", - "experiment_libcrux_kyber", + "experiment_libcrux_blake2", + "experiment_libcrux_chachapoly", + "experiment_libcrux_chachapoly_test", + "experiment_libcrux_kyber", ] bench = ["experiment_libcrux_define_all"] From 6b618232551f56703878f36406bd7e916f3f15ca Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 24 Jun 2025 11:23:36 +0200 Subject: [PATCH 143/174] fix: Missing imports --- rosenpass/src/protocol/protocol.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 13df0037..4fce9271 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -2261,6 +2261,7 @@ impl CryptoServer { &cookie_value, )?; + use rand::Fill; msg_out .padding .try_fill(&mut rosenpass_secret_memory::rand::rng()) @@ -4439,6 +4440,8 @@ mod test { #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) { + use std::time::Duration; + setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); stacker::grow(8 * 1024 * 1024, || { @@ -4508,7 +4511,7 @@ mod test { break a.retransmit_handshake(peer, &mut *a_to_b_buf).unwrap(); } PollResult::Sleep(time) => { - sleep(Duration::from_secs_f64(time)); + std::thread::sleep(Duration::from_secs_f64(time)); } _ => {} } From 08ea0453250d9b72d35b4ac83dfb0a40ac414e8b Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 24 Jun 2025 11:45:31 +0200 Subject: [PATCH 144/174] fix: Prettier --- .github/workflows/bench-primitives.yml | 4 ++-- .github/workflows/bench-protocol.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml index dc11cddd..f7cb710d 100644 --- a/.github/workflows/bench-primitives.yml +++ b/.github/workflows/bench-primitives.yml @@ -42,9 +42,9 @@ jobs: - name: 🛠️ Prepare Benchmark Path env: - EVENT_NAME: ${{ github.event_name }} + EVENT_NAME: ${{ github.event_name }} BRANCH_NAME: ${{ github.ref_name }} - PR_NUMBER: ${{ github.event.pull_request.number }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | case "$EVENT_NAME" in "push") diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml index 139e6f6b..6f7a8091 100644 --- a/.github/workflows/bench-protocol.yml +++ b/.github/workflows/bench-protocol.yml @@ -42,9 +42,9 @@ jobs: - name: 🛠️ Prepare Benchmark Path env: - EVENT_NAME: ${{ github.event_name }} + EVENT_NAME: ${{ github.event_name }} BRANCH_NAME: ${{ github.ref_name }} - PR_NUMBER: ${{ github.event.pull_request.number }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | case "$EVENT_NAME" in "push") From a538dee0c315f53c98fb3a39920c332ea69f48c2 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 24 Jun 2025 11:45:52 +0200 Subject: [PATCH 145/174] fix: Broken QC workflow file Rust toolchain issues; need to set the nightly toolchain correctly --- .github/workflows/qc.yaml | 41 ++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 5a251114..3925d4fb 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -30,11 +30,27 @@ jobs: uses: ludeeus/action-shellcheck@master rustfmt: - name: Rust Format + name: Rust code formatting runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 - - name: Run Rust Formatting Script + - 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') }} + - name: Install nightly toolchain + run: | + rustup toolchain install nightly + rustup override set nightly + - run: rustup component add rustfmt + - name: Run Cargo Fmt + run: cargo fmt --all --check + - name: Run Rust Markdown code block Formatting Script run: bash format_rust_code.sh --mode check cargo-bench: @@ -159,7 +175,6 @@ jobs: cargo-fuzz: runs-on: ubicloud-standard-2-ubuntu-2204 - env: steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 @@ -174,7 +189,7 @@ jobs: - name: Install nightly toolchain run: | rustup toolchain install nightly - rustup override nightly + rustup override set nightly - name: Install cargo-fuzz run: cargo install cargo-fuzz - name: Run fuzzing @@ -193,9 +208,23 @@ jobs: codecov: runs-on: ubicloud-standard-2-ubuntu-2204 + env: + RUSTUP_TOOLCHAIN: nightly steps: - uses: actions/checkout@v4 - - run: rustup default nightly + - 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') }} + - name: Install nightly toolchain + run: | + rustup toolchain install nightly + rustup override set nightly - run: rustup component add llvm-tools-preview - run: | cargo install cargo-llvm-cov || true @@ -209,6 +238,4 @@ jobs: with: files: ./target/grcov/lcov verbose: true - env: - RUSTUP_TOOLCHAIN: 1.81 CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 240a1f923db30faec3a9dc15659ce10206ab90e2 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 24 Jun 2025 12:07:33 +0200 Subject: [PATCH 146/174] fix: Cargo test job from QC should not run on mac --- .github/workflows/qc.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/qc.yaml b/.github/workflows/qc.yaml index 3925d4fb..94d009e5 100644 --- a/.github/workflows/qc.yaml +++ b/.github/workflows/qc.yaml @@ -128,12 +128,7 @@ jobs: - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items cargo-test: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubicloud-standard-2-ubuntu-2204, warp-macos-13-arm64-6x] - # - ubuntu is x86-64 - # - macos-13 is also x86-64 architecture + runs-on: ubicloud-standard-2-ubuntu-2204 steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 From da642186f2356939852ae73b92b0e7d841327d4f Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 14:21:48 +0200 Subject: [PATCH 147/174] chore: Move timing related thing out of protocol.rs --- rosenpass/src/app_server.rs | 4 +- rosenpass/src/protocol/mod.rs | 6 ++- rosenpass/src/protocol/protocol.rs | 66 ++++++------------------------ rosenpass/src/protocol/timing.rs | 46 +++++++++++++++++++++ rosenpass/tests/poll_example.rs | 6 ++- 5 files changed, 68 insertions(+), 60 deletions(-) create mode 100644 rosenpass/src/protocol/timing.rs diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 58fc8ed1..55162adc 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -47,7 +47,7 @@ use crate::protocol::BuildCryptoServer; use crate::protocol::HostIdentification; use crate::{ config::Verbosity, - protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing}, + protocol::{timing::Timing, CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey}, }; use rosenpass_util::attempt; use rosenpass_util::b64::B64Display; @@ -1337,7 +1337,7 @@ impl AppServer { break A::SendRetransmission(AppPeerPtr(no)) } Some(C::Sleep(timeout)) => timeout, // No event from crypto-server, do IO - None => crate::protocol::UNENDING, // Crypto server is uninitialized, do IO + None => crate::protocol::timing::UNENDING, // Crypto server is uninitialized, do IO }; // Perform IO (look for a message) diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index f9ea0f3d..9d8608f5 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -76,8 +76,10 @@ //! ``` mod build_crypto_server; +pub use build_crypto_server::*; + +pub mod timing; + #[allow(clippy::module_inception)] mod protocol; - -pub use build_crypto_server::*; pub use protocol::*; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 4fce9271..f82e6d0a 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -36,26 +36,10 @@ use rosenpass_util::mem::DiscardResultExt; use rosenpass_util::{cat, mem::cpy_min, time::Timebase}; use zerocopy::{AsBytes, FromBytes, Ref}; +use super::timing::{has_happened, Timing, BCE, UNENDING}; + // CONSTANTS & SETTINGS ////////////////////////// -/// A type for time, e.g. for backoff before re-tries -pub type Timing = f64; - -/// Magic time stamp to indicate some object is ancient; "Before Common Era" -/// -/// This is for instance used as a magic time stamp indicating age when some -/// cryptographic object certainly needs to be refreshed. -/// -/// Using this instead of Timing::MIN or Timing::INFINITY to avoid floating -/// point math weirdness. -pub const BCE: Timing = -3600.0 * 24.0 * 356.0 * 10_000.0; - -/// Magic time stamp to indicate that some process is not time-limited -/// -/// Actually it's eight hours; This is intentional to avoid weirdness -/// regarding unexpectedly large numbers in system APIs as this is < i16::MAX -pub const UNENDING: Timing = 3600.0 * 8.0; - /// Time after which the responder attempts to rekey the session /// /// From the wireguard paper: rekey every two minutes, @@ -122,31 +106,6 @@ pub const EVENT_GRACE: Timing = 0.0025; // UTILITY FUNCTIONS ///////////////////////////// -/// An even `ev` has happened relative to a point in time `now` -/// if the `ev` does not lie in the future relative to now. -/// -/// An event lies in the future relative to `now` if -/// does not lie in the past or present. -/// -/// An event `ev` lies in the past if `ev < now`. It lies in the -/// present if the absolute difference between `ev` and `now` is -/// smaller than [EVENT_GRACE]. -/// -/// Think of this as `ev <= now` for with [EVENT_GRACE] applied. -/// -/// # Examples -/// -/// ``` -/// use rosenpass::protocol::{has_happened, EVENT_GRACE}; -/// assert!(has_happened(EVENT_GRACE * -1.0, 0.0)); -/// assert!(has_happened(0.0, 0.0)); -/// assert!(has_happened(EVENT_GRACE * 0.999, 0.0)); -/// assert!(!has_happened(EVENT_GRACE * 1.001, 0.0)); -/// ``` -pub fn has_happened(ev: Timing, now: Timing) -> bool { - (ev - now) < EVENT_GRACE -} - // DATA STRUCTURES & BASIC TRAITS & ACCESSORS //// /// Static public key @@ -274,7 +233,7 @@ pub struct CryptoServer { /// /// ``` /// use rosenpass_util::time::Timebase; -/// use rosenpass::protocol::{BCE, SymKey, CookieStore}; +/// use rosenpass::protocol::{timing::BCE, SymKey, CookieStore}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// @@ -1928,7 +1887,7 @@ impl Mortal for KnownInitConfResponsePtr { /// # Examples /// /// ``` -/// use rosenpass::protocol::{Timing, Mortal, MortalExt, Lifecycle, CryptoServer, ProtocolVersion}; +/// use rosenpass::protocol::{timing::Timing, Mortal, MortalExt, Lifecycle, CryptoServer, ProtocolVersion}; /// use rosenpass::protocol::testutils::{ServerForTesting, time_travel_forward}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); @@ -2536,7 +2495,7 @@ impl Wait { /// # Examples /// /// ``` - /// use rosenpass::protocol::{Wait, UNENDING}; + /// use rosenpass::protocol::{Wait, timing::UNENDING}; /// /// assert_eq!(Wait::hibernate().0, UNENDING); /// ``` @@ -2550,7 +2509,7 @@ impl Wait { /// # Examples /// /// ``` - /// use rosenpass::protocol::{Wait, UNENDING}; + /// use rosenpass::protocol::{Wait, timing::UNENDING}; /// /// assert_eq!(Wait::immediate_unless(false).0, 0.0); /// assert_eq!(Wait::immediate_unless(true).0, UNENDING); @@ -2568,7 +2527,7 @@ impl Wait { /// # Examples /// /// ``` - /// use rosenpass::protocol::{Wait, UNENDING}; + /// use rosenpass::protocol::{Wait, timing::UNENDING}; /// /// assert_eq!(Wait::or_hibernate(None).0, UNENDING); /// assert_eq!(Wait::or_hibernate(Some(20.0)).0, 20.0); @@ -2585,7 +2544,7 @@ impl Wait { /// # Examples /// /// ``` - /// use rosenpass::protocol::{Wait, UNENDING}; + /// use rosenpass::protocol::{Wait, timing::UNENDING}; /// /// assert_eq!(Wait::or_immediate(None).0, 0.0); /// assert_eq!(Wait::or_immediate(Some(20.0)).0, 20.0); @@ -2602,7 +2561,7 @@ impl Wait { /// # Examples /// /// ``` - /// use rosenpass::protocol::{Wait, UNENDING}; + /// use rosenpass::protocol::{Wait, timing::UNENDING}; /// /// /// assert_eq!(Wait(20.0).and(30.0).0, 30.0); @@ -2674,7 +2633,7 @@ impl PollResult { /// # Examples /// /// ``` - /// use rosenpass::protocol::{PollResult, UNENDING}; + /// use rosenpass::protocol::{PollResult, timing::UNENDING}; /// /// assert!(matches!(PollResult::hibernate(), PollResult::Sleep(UNENDING))); /// ``` @@ -2688,7 +2647,7 @@ impl PollResult { /// # Examples /// /// ``` - /// use rosenpass::protocol::{PollResult, PeerPtr, UNENDING}; + /// use rosenpass::protocol::{PollResult, PeerPtr, timing::UNENDING}; /// /// let p = PeerPtr(0); /// @@ -2943,7 +2902,7 @@ impl PollResult { /// # Examples /// /// ``` -/// use rosenpass::protocol::{begin_poll, PollResult, UNENDING}; +/// use rosenpass::protocol::{begin_poll, PollResult, timing::UNENDING}; /// /// assert!(matches!(begin_poll(), PollResult::Sleep(UNENDING))); /// ``` @@ -4763,7 +4722,6 @@ mod test { poll(&mut b)?; check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; } - // We can even validate that the data is coming out of the cache by changing the cache // to use our broken messages. It does not matter that these messages are cryptographically // broken since we insert them manually into the cache diff --git a/rosenpass/src/protocol/timing.rs b/rosenpass/src/protocol/timing.rs new file mode 100644 index 00000000..221d794a --- /dev/null +++ b/rosenpass/src/protocol/timing.rs @@ -0,0 +1,46 @@ +//! Time-keeping related utilities for the Rosenpass protocol + +use super::EVENT_GRACE; + +/// A type for time, e.g. for backoff before re-tries +pub type Timing = f64; + +/// Magic time stamp to indicate some object is ancient; "Before Common Era" +/// +/// This is for instance used as a magic time stamp indicating age when some +/// cryptographic object certainly needs to be refreshed. +/// +/// Using this instead of Timing::MIN or Timing::INFINITY to avoid floating +/// point math weirdness. +pub const BCE: Timing = -3600.0 * 24.0 * 356.0 * 10_000.0; + +/// Magic time stamp to indicate that some process is not time-limited +/// +/// Actually it's eight hours; This is intentional to avoid weirdness +/// regarding unexpectedly large numbers in system APIs as this is < i16::MAX +pub const UNENDING: Timing = 3600.0 * 8.0; + +/// An even `ev` has happened relative to a point in time `now` +/// if the `ev` does not lie in the future relative to now. +/// +/// An event lies in the future relative to `now` if +/// does not lie in the past or present. +/// +/// An event `ev` lies in the past if `ev < now`. It lies in the +/// present if the absolute difference between `ev` and `now` is +/// smaller than [EVENT_GRACE]. +/// +/// Think of this as `ev <= now` for with [EVENT_GRACE] applied. +/// +/// # Examples +/// +/// ``` +/// use rosenpass::protocol::{timing::has_happened, EVENT_GRACE}; +/// assert!(has_happened(EVENT_GRACE * -1.0, 0.0)); +/// assert!(has_happened(0.0, 0.0)); +/// assert!(has_happened(EVENT_GRACE * 0.999, 0.0)); +/// assert!(!has_happened(EVENT_GRACE * 1.001, 0.0)); +/// ``` +pub fn has_happened(ev: Timing, now: Timing) -> bool { + (ev - now) < EVENT_GRACE +} diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index 9fb8bbfc..cabded01 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -10,8 +10,10 @@ use rosenpass_ciphers::StaticKem; use rosenpass_util::result::OkExt; use rosenpass::protocol::{ - testutils::time_travel_forward, CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, - ProtocolVersion, SPk, SSk, SymKey, Timing, UNENDING, + testutils::time_travel_forward, + timing::{Timing, UNENDING}, + CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, ProtocolVersion, SPk, SSk, + SymKey, }; // TODO: Most of the utility functions in here should probably be moved to From d81649c1d18255881c32d794452f3a09bdf9cedb Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 14:27:42 +0200 Subject: [PATCH 148/174] chore: Restructure imports in protocol.rs --- rosenpass/src/protocol/protocol.rs | 54 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index f82e6d0a..a23bf763 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3,25 +3,21 @@ //! It is merged entirely into [crate::protocol] and should be split up into multiple //! files. -use std::borrow::Borrow; -use std::fmt::Debug; -use std::mem::size_of; -use std::ops::Deref; +use std::collections::hash_map::{ + Entry::{Occupied, Vacant}, + HashMap, +}; use std::{ - collections::hash_map::{ - Entry::{Occupied, Vacant}, - HashMap, - }, - fmt::Display, + borrow::Borrow, + fmt::{Debug, Display}, + mem::size_of, + ops::Deref, }; use anyhow::{bail, ensure, Context, Result}; - -#[cfg(feature = "trace_bench")] -use rosenpass_util::trace_bench::Trace as _; - -use crate::{hash_domains, msgs::*, RosenpassError}; use memoffset::span_of; +use zerocopy::{AsBytes, FromBytes, Ref}; + use rosenpass_cipher_traits::primitives::{ Aead as _, AeadWithNonceInCiphertext, Kem, KeyedHashInstance, }; @@ -29,15 +25,21 @@ use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace use rosenpass_ciphers::{Aead, EphemeralKem, KeyedHash, StaticKem, XAead, KEY_LEN}; use rosenpass_constant_time as constant_time; use rosenpass_secret_memory::{Public, PublicBox, Secret}; -use rosenpass_to::ops::copy_slice; -use rosenpass_to::To; -use rosenpass_util::functional::ApplyExt; -use rosenpass_util::mem::DiscardResultExt; -use rosenpass_util::{cat, mem::cpy_min, time::Timebase}; -use zerocopy::{AsBytes, FromBytes, Ref}; +use rosenpass_to::{ops::copy_slice, To}; +use rosenpass_util::{ + cat, + functional::ApplyExt, + mem::{cpy_min, DiscardResultExt}, + time::Timebase, +}; + +use crate::{hash_domains, msgs::*, RosenpassError}; use super::timing::{has_happened, Timing, BCE, UNENDING}; +#[cfg(feature = "trace_bench")] +use rosenpass_util::trace_bench::Trace as _; + // CONSTANTS & SETTINGS ////////////////////////// /// Time after which the responder attempts to rekey the session @@ -2220,11 +2222,13 @@ impl CryptoServer { &cookie_value, )?; - use rand::Fill; - msg_out - .padding - .try_fill(&mut rosenpass_secret_memory::rand::rng()) - .unwrap(); + { + use rand::Fill; + msg_out + .padding + .try_fill(&mut rosenpass_secret_memory::rand::rng()) + .unwrap(); + } // length of the response let _len = Some(size_of::()); From 7e8e502bca972a25f35c18f1f5b3ccce0a51254d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 14:40:31 +0200 Subject: [PATCH 149/174] chore: Split constants from protocol.rs into own file --- rosenpass/src/protocol/constants.rs | 64 ++++++++++++++++++++++++ rosenpass/src/protocol/mod.rs | 1 + rosenpass/src/protocol/protocol.rs | 77 +++-------------------------- rosenpass/src/protocol/timing.rs | 4 +- 4 files changed, 74 insertions(+), 72 deletions(-) create mode 100644 rosenpass/src/protocol/constants.rs diff --git a/rosenpass/src/protocol/constants.rs b/rosenpass/src/protocol/constants.rs new file mode 100644 index 00000000..c497e0e9 --- /dev/null +++ b/rosenpass/src/protocol/constants.rs @@ -0,0 +1,64 @@ +//! Constants and configuration values used in the rosenpass core protocol + +use crate::msgs::MAC_SIZE; + +use super::timing::Timing; + +/// Time after which the responder attempts to rekey the session +/// +/// From the wireguard paper: rekey every two minutes, +/// discard the key if no rekey is achieved within three +pub const REKEY_AFTER_TIME_RESPONDER: Timing = 120.0; +/// Time after which the initiator attempts to rekey the session. +/// +/// This happens ten seconds after [REKEY_AFTER_TIME_RESPONDER], so +/// parties would usually switch roles after every handshake. +/// +/// From the wireguard paper: rekey every two minutes, +/// discard the key if no rekey is achieved within three +pub const REKEY_AFTER_TIME_INITIATOR: Timing = 130.0; +/// Time after which either party rejects the current key. +/// +/// At this point a new key should have been negotiated. +/// Rejection happens 50-60 seconds after key renegotiation +/// to allow for a graceful handover. +/// +/// From the wireguard paper: rekey every two minutes, +/// discard the key if no rekey is achieved within three +pub const REJECT_AFTER_TIME: Timing = 180.0; + +/// The length of the `cookie_secret` in the [whitepaper](https://rosenpass.eu/whitepaper.pdf) +pub const COOKIE_SECRET_LEN: usize = MAC_SIZE; +/// The life time of the `cookie_secret` in the [whitepaper](https://rosenpass.eu/whitepaper.pdf) +pub const COOKIE_SECRET_EPOCH: Timing = 120.0; + +/// Length of a cookie value (see info about the cookie mechanism in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) +pub const COOKIE_VALUE_LEN: usize = MAC_SIZE; +/// Time after which to delete a cookie, as the initiator, for a certain peer (see info about the cookie mechanism in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) +pub const PEER_COOKIE_VALUE_EPOCH: Timing = 120.0; + +/// Seconds until the biscuit key is changed; we issue biscuits +/// using one biscuit key for one epoch and store the biscuit for +/// decryption for a second epoch +/// +/// The biscuit mechanism is used to make sure the responder is stateless in our protocol. +pub const BISCUIT_EPOCH: Timing = 300.0; + +/// The initiator opportunistically retransmits their messages; it applies an increasing delay +/// between each retreansmission. This is the factor by which the delay grows after each +/// retransmission. +pub const RETRANSMIT_DELAY_GROWTH: Timing = 2.0; +/// The initiator opportunistically retransmits their messages; it applies an increasing delay +/// between each retreansmission. This is the initial delay between retransmissions. +pub const RETRANSMIT_DELAY_BEGIN: Timing = 0.5; +/// The initiator opportunistically retransmits their messages; it applies an increasing delay +/// between each retreansmission. This is the maximum delay between retransmissions. +pub const RETRANSMIT_DELAY_END: Timing = 10.0; +/// The initiator opportunistically retransmits their messages; it applies an increasing delay +/// between each retreansmission. This is the jitter (randomness) applied to the retransmission +/// delay. +pub const RETRANSMIT_DELAY_JITTER: Timing = 0.5; + +/// This is the maximum delay that can separate two events for us to consider the events to have +/// happened at the same time. +pub const EVENT_GRACE: Timing = 0.0025; diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index 9d8608f5..b648903e 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -78,6 +78,7 @@ mod build_crypto_server; pub use build_crypto_server::*; +pub mod constants; pub mod timing; #[allow(clippy::module_inception)] diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index a23bf763..709efb25 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -35,79 +35,17 @@ use rosenpass_util::{ use crate::{hash_domains, msgs::*, RosenpassError}; +use super::constants::{ + BISCUIT_EPOCH, COOKIE_SECRET_EPOCH, COOKIE_SECRET_LEN, COOKIE_VALUE_LEN, + PEER_COOKIE_VALUE_EPOCH, REJECT_AFTER_TIME, REKEY_AFTER_TIME_INITIATOR, + REKEY_AFTER_TIME_RESPONDER, RETRANSMIT_DELAY_BEGIN, RETRANSMIT_DELAY_END, + RETRANSMIT_DELAY_GROWTH, RETRANSMIT_DELAY_JITTER, +}; use super::timing::{has_happened, Timing, BCE, UNENDING}; #[cfg(feature = "trace_bench")] use rosenpass_util::trace_bench::Trace as _; -// CONSTANTS & SETTINGS ////////////////////////// - -/// Time after which the responder attempts to rekey the session -/// -/// From the wireguard paper: rekey every two minutes, -/// discard the key if no rekey is achieved within three -pub const REKEY_AFTER_TIME_RESPONDER: Timing = 120.0; -/// Time after which the initiator attempts to rekey the session. -/// -/// This happens ten seconds after [REKEY_AFTER_TIME_RESPONDER], so -/// parties would usually switch roles after every handshake. -/// -/// From the wireguard paper: rekey every two minutes, -/// discard the key if no rekey is achieved within three -pub const REKEY_AFTER_TIME_INITIATOR: Timing = 130.0; -/// Time after which either party rejects the current key. -/// -/// At this point a new key should have been negotiated. -/// Rejection happens 50-60 seconds after key renegotiation -/// to allow for a graceful handover. -/// -/// From the wireguard paper: rekey every two minutes, -/// discard the key if no rekey is achieved within three -pub const REJECT_AFTER_TIME: Timing = 180.0; - -/// Maximum period between sending rekey initiation messages -/// -/// From the wireguard paper; "under no circumstances send an initiation message more than once every 5 seconds" -pub const REKEY_TIMEOUT: Timing = 5.0; - -/// The length of the `cookie_secret` in the [whitepaper](https://rosenpass.eu/whitepaper.pdf) -pub const COOKIE_SECRET_LEN: usize = MAC_SIZE; -/// The life time of the `cookie_secret` in the [whitepaper](https://rosenpass.eu/whitepaper.pdf) -pub const COOKIE_SECRET_EPOCH: Timing = 120.0; - -/// Length of a cookie value (see info about the cookie mechanism in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) -pub const COOKIE_VALUE_LEN: usize = MAC_SIZE; -/// Time after which to delete a cookie, as the initiator, for a certain peer (see info about the cookie mechanism in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) -pub const PEER_COOKIE_VALUE_EPOCH: Timing = 120.0; - -/// Seconds until the biscuit key is changed; we issue biscuits -/// using one biscuit key for one epoch and store the biscuit for -/// decryption for a second epoch -/// -/// The biscuit mechanism is used to make sure the responder is stateless in our protocol. -pub const BISCUIT_EPOCH: Timing = 300.0; - -/// The initiator opportunistically retransmits their messages; it applies an increasing delay -/// between each retreansmission. This is the factor by which the delay grows after each -/// retransmission. -pub const RETRANSMIT_DELAY_GROWTH: Timing = 2.0; -/// The initiator opportunistically retransmits their messages; it applies an increasing delay -/// between each retreansmission. This is the initial delay between retransmissions. -pub const RETRANSMIT_DELAY_BEGIN: Timing = 0.5; -/// The initiator opportunistically retransmits their messages; it applies an increasing delay -/// between each retreansmission. This is the maximum delay between retransmissions. -pub const RETRANSMIT_DELAY_END: Timing = 10.0; -/// The initiator opportunistically retransmits their messages; it applies an increasing delay -/// between each retreansmission. This is the jitter (randomness) applied to the retransmission -/// delay. -pub const RETRANSMIT_DELAY_JITTER: Timing = 0.5; - -/// This is the maximum delay that can separate two events for us to consider the events to have -/// happened at the same time. -pub const EVENT_GRACE: Timing = 0.0025; - -// UTILITY FUNCTIONS ///////////////////////////// - // DATA STRUCTURES & BASIC TRAITS & ACCESSORS //// /// Static public key @@ -118,7 +56,6 @@ pub type SPk = PublicBox<{ StaticKem::PK_LEN }>; pub type SSk = Secret<{ StaticKem::SK_LEN }>; /// Ephemeral public key pub type EPk = Public<{ EphemeralKem::PK_LEN }>; -/// Ephemeral secret key pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; /// Symmetric key @@ -2612,7 +2549,7 @@ pub enum PollResult { /// The caller should immediately erase any cryptographic keys exchanged with /// the peer previously and then immediately call poll again. /// - /// This is raised after [REKEY_TIMEOUT] if no successful rekey could be achieved. + /// This is raised after [super::constants::REKEY_TIMEOUT] if no successful rekey could be achieved. DeleteKey(PeerPtr), /// The caller should invoke [CryptoServer::handle_initiation] and transmit the /// initiation to the other party before invoking poll again. diff --git a/rosenpass/src/protocol/timing.rs b/rosenpass/src/protocol/timing.rs index 221d794a..3e34011a 100644 --- a/rosenpass/src/protocol/timing.rs +++ b/rosenpass/src/protocol/timing.rs @@ -1,6 +1,6 @@ //! Time-keeping related utilities for the Rosenpass protocol -use super::EVENT_GRACE; +use super::constants::EVENT_GRACE; /// A type for time, e.g. for backoff before re-tries pub type Timing = f64; @@ -35,7 +35,7 @@ pub const UNENDING: Timing = 3600.0 * 8.0; /// # Examples /// /// ``` -/// use rosenpass::protocol::{timing::has_happened, EVENT_GRACE}; +/// use rosenpass::protocol::{timing::has_happened, constants::EVENT_GRACE}; /// assert!(has_happened(EVENT_GRACE * -1.0, 0.0)); /// assert!(has_happened(0.0, 0.0)); /// assert!(has_happened(EVENT_GRACE * 0.999, 0.0)); From 53ddad30f1b3a65515eaf766720a4d246aee0c44 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 14:41:30 +0200 Subject: [PATCH 150/174] fix: Incorrect reference in protocol.rs REKEY_TIMEOUT is not used at all --- rosenpass/src/protocol/protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 709efb25..c95ff0cc 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -2549,7 +2549,7 @@ pub enum PollResult { /// The caller should immediately erase any cryptographic keys exchanged with /// the peer previously and then immediately call poll again. /// - /// This is raised after [super::constants::REKEY_TIMEOUT] if no successful rekey could be achieved. + /// This is raised after [REJECT_AFTER_TIME] if no successful rekey could be achieved. DeleteKey(PeerPtr), /// The caller should invoke [CryptoServer::handle_initiation] and transmit the /// initiation to the other party before invoking poll again. From 9656fa70252b4831994189d5e9c2bc15736171df Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 18:59:49 +0200 Subject: [PATCH 151/174] chore: Split basic types from protocol.rs into own file --- rosenpass/benches/handshake.rs | 5 +- rosenpass/benches/trace_handshake.rs | 5 +- rosenpass/src/api/api_handler.rs | 4 +- rosenpass/src/app_server.rs | 3 +- rosenpass/src/cli.rs | 6 +-- rosenpass/src/config.rs | 2 +- rosenpass/src/protocol/basic_types.rs | 38 +++++++++++++ rosenpass/src/protocol/build_crypto_server.rs | 32 ++++++----- rosenpass/src/protocol/mod.rs | 6 +-- rosenpass/src/protocol/protocol.rs | 54 ++++++------------- .../tests/api-integration-tests-api-setup.rs | 2 +- rosenpass/tests/api-integration-tests.rs | 2 +- rosenpass/tests/app_server_example.rs | 2 +- rosenpass/tests/poll_example.rs | 4 +- rp/src/exchange.rs | 2 +- rp/src/key.rs | 4 +- 16 files changed, 95 insertions(+), 76 deletions(-) create mode 100644 rosenpass/src/protocol/basic_types.rs diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index 6516a31f..a47e9a65 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -1,7 +1,6 @@ use anyhow::Result; -use rosenpass::protocol::{ - CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, -}; +use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion}; use std::ops::DerefMut; use rosenpass_cipher_traits::primitives::Kem; diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs index 95a7bdb5..b83c1356 100644 --- a/rosenpass/benches/trace_handshake.rs +++ b/rosenpass/benches/trace_handshake.rs @@ -14,9 +14,8 @@ use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; use rosenpass_util::trace_bench::RpEventType; -use rosenpass::protocol::{ - CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey, -}; +use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion}; const ITERATIONS: usize = 100; diff --git a/rosenpass/src/api/api_handler.rs b/rosenpass/src/api/api_handler.rs index c86d57c4..dcd85cca 100644 --- a/rosenpass/src/api/api_handler.rs +++ b/rosenpass/src/api/api_handler.rs @@ -158,10 +158,10 @@ where ); // Actually read the secrets - let mut sk = crate::protocol::SSk::zero(); + let mut sk = crate::protocol::basic_types::SSk::zero(); sk_io.read_exact_til_end(sk.secret_mut()).einvalid_req()?; - let mut pk = crate::protocol::SPk::zero(); + let mut pk = crate::protocol::basic_types::SPk::zero(); pk_io.read_exact_til_end(pk.borrow_mut()).einvalid_req()?; // Retrieve the construction site diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 55162adc..8d3fd0a6 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -47,7 +47,8 @@ use crate::protocol::BuildCryptoServer; use crate::protocol::HostIdentification; use crate::{ config::Verbosity, - protocol::{timing::Timing, CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey}, + protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}, + protocol::{timing::Timing, CryptoServer, PeerPtr}, }; use rosenpass_util::attempt; use rosenpass_util::b64::B64Display; diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 4d80761b..107ec9b6 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -17,7 +17,7 @@ use std::path::PathBuf; use crate::app_server::AppServerTest; use crate::app_server::{AppServer, BrokerPeer}; -use crate::protocol::{SPk, SSk, SymKey}; +use crate::protocol::basic_types::{SPk, SSk, SymKey}; use super::config; @@ -607,8 +607,8 @@ impl CliArgs { /// generate secret and public keys, store in files according to the paths passed as arguments 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(); + let mut ssk = crate::protocol::basic_types::SSk::random(); + let mut spk = crate::protocol::basic_types::SPk::random(); StaticKem.keygen(ssk.secret_mut(), spk.deref_mut())?; ssk.store_secret(secret_key)?; spk.store(public_key) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 5dc925ce..330ccea4 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -7,7 +7,7 @@ //! - TODO: support `~` in //! - TODO: provide tooling to create config file from shell -use crate::protocol::{SPk, SSk}; +use crate::protocol::basic_types::{SPk, SSk}; use rosenpass_util::file::LoadValue; use std::{ collections::HashSet, diff --git a/rosenpass/src/protocol/basic_types.rs b/rosenpass/src/protocol/basic_types.rs new file mode 100644 index 00000000..a810d6fc --- /dev/null +++ b/rosenpass/src/protocol/basic_types.rs @@ -0,0 +1,38 @@ +//! Key types and other fundamental types used in the Rosenpass protocol + +use rosenpass_cipher_traits::primitives::{Aead, Kem}; +use rosenpass_ciphers::{EphemeralKem, StaticKem, XAead, KEY_LEN}; +use rosenpass_secret_memory::{Public, PublicBox, Secret}; + +use crate::msgs::{BISCUIT_ID_LEN, MAX_MESSAGE_LEN, SESSION_ID_LEN}; + +/// Static public key +/// +/// Using [PublicBox] instead of [Public] because Classic McEliece keys are very large. +pub type SPk = PublicBox<{ StaticKem::PK_LEN }>; +/// Static secret key +pub type SSk = Secret<{ StaticKem::SK_LEN }>; +/// Ephemeral public key +pub type EPk = Public<{ EphemeralKem::PK_LEN }>; +pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; + +/// Symmetric key +pub type SymKey = Secret; +/// Symmetric hash +pub type SymHash = Public; + +/// Peer ID (derived from the public key, see the hash derivations in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) +pub type PeerId = Public; +/// Session ID +pub type SessionId = Public; +/// Biscuit ID +pub type BiscuitId = Public; + +/// Nonce for use with random-nonce AEAD +pub type XAEADNonce = Public<{ XAead::NONCE_LEN }>; + +/// Buffer capably of holding any Rosenpass protocol message +pub type MsgBuf = Public; + +/// Server-local peer number; this is just the index in [super::CryptoServer::peers] +pub type PeerNo = usize; diff --git a/rosenpass/src/protocol/build_crypto_server.rs b/rosenpass/src/protocol/build_crypto_server.rs index f372e4ea..f3ded3d7 100644 --- a/rosenpass/src/protocol/build_crypto_server.rs +++ b/rosenpass/src/protocol/build_crypto_server.rs @@ -1,4 +1,5 @@ -use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey}; +use super::basic_types::{SPk, SSk, SymKey}; +use super::{CryptoServer, PeerPtr}; use crate::config::ProtocolVersion; use rosenpass_util::{ build::Build, @@ -47,7 +48,8 @@ impl Keypair { /// # Example /// /// ```rust - /// use rosenpass::protocol::{Keypair, SSk, SPk}; + /// use rosenpass::protocol::basic_types::{SSk, SPk}; + /// use rosenpass::protocol::Keypair; /// /// // We have to define the security policy before using Secrets. /// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets; @@ -66,12 +68,13 @@ impl Keypair { /// 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. + /// See [SSk:zero()][SSk::zero] and [SPk:zero()][SPk::zero], respectively. /// /// # Example /// /// ```rust - /// use rosenpass::protocol::{Keypair, SSk, SPk}; + /// use rosenpass::protocol::basic_types::{SSk, SPk}; + /// use rosenpass::protocol::Keypair; /// /// // We have to define the security policy before using Secrets. /// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets; @@ -90,7 +93,7 @@ impl Keypair { /// 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. + /// See [SSk:random()][SSk::random] and [SPk:random()][SPk::random], respectively. pub fn random() -> Self { Self::new(SSk::random(), SPk::random()) } @@ -127,7 +130,7 @@ pub struct MissingKeypair; /// /// There are multiple ways of creating a crypto server: /// -/// 1. Provide the key pair at initialization time (using [CryptoServer::new][crate::protocol::CryptoServer::new]) +/// 1. Provide the key pair at initialization time (using [CryptoServer::new][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. @@ -145,7 +148,8 @@ pub struct MissingKeypair; /// /// ```rust /// use rosenpass_util::build::Build; -/// use rosenpass::protocol::{BuildCryptoServer, Keypair, PeerParams, SPk, SymKey}; +/// use rosenpass::protocol::basic_types::{SPk, SymKey}; +/// use rosenpass::protocol::{BuildCryptoServer, Keypair, PeerParams}; /// use rosenpass::config::ProtocolVersion; /// /// // We have to define the security policy before using Secrets. @@ -205,13 +209,13 @@ impl Build for BuildCryptoServer { } #[derive(Debug)] -/// Cryptographic key(s) identifying the connected [peer][crate::protocol::Peer] ("client") +/// Cryptographic key(s) identifying the connected [peer][super::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] +/// Each peer must be identified by a [public key (SPk)][SPk]. +/// Optionally, a [symmetric key (SymKey)][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]. +/// For more information on the intended usage and security considerations, see [Peer::psk][super::Peer::psk] and [Peer::spkt][super::Peer::spkt]. pub struct PeerParams { /// Pre-shared (symmetric) encryption keys that should be used with this peer. pub psk: Option, @@ -322,7 +326,8 @@ impl BuildCryptoServer { /// secret_policy_use_only_malloc_secrets(); /// /// use rosenpass_util::build::Build; - /// use rosenpass::protocol::{BuildCryptoServer, Keypair, SymKey, SPk}; + /// use rosenpass::protocol::basic_types::{SymKey, SPk}; + /// use rosenpass::protocol::{BuildCryptoServer, Keypair}; /// /// // Deferred initialization: Create builder first, add some peers later /// let keypair_option = Some(Keypair::random()); @@ -388,7 +393,8 @@ impl BuildCryptoServer { /// secret_policy_use_only_malloc_secrets(); /// /// use rosenpass_util::build::Build; - /// use rosenpass::protocol::{BuildCryptoServer, Keypair, SymKey, SPk}; + /// use rosenpass::protocol::basic_types::{SymKey, SPk}; + /// use rosenpass::protocol::{BuildCryptoServer, Keypair}; /// /// let keypair = Keypair::random(); /// let peer_pk = SPk::random(); diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index b648903e..de8f435c 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -27,9 +27,8 @@ //! use rosenpass_secret_memory::policy::*; //! use rosenpass_cipher_traits::primitives::Kem; //! use rosenpass_ciphers::StaticKem; -//! use rosenpass::{ -//! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey}, -//! }; +//! use rosenpass::protocol::basic_types::{SSk, SPk, MsgBuf, SymKey}; +//! use rosenpass::protocol::{PeerPtr, CryptoServer}; //! # fn main() -> anyhow::Result<()> { //! // Set security policy for storing secrets //! @@ -78,6 +77,7 @@ mod build_crypto_server; pub use build_crypto_server::*; +pub mod basic_types; pub mod constants; pub mod timing; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index c95ff0cc..9c367f0b 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -24,7 +24,7 @@ use rosenpass_cipher_traits::primitives::{ use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; use rosenpass_ciphers::{Aead, EphemeralKem, KeyedHash, StaticKem, XAead, KEY_LEN}; use rosenpass_constant_time as constant_time; -use rosenpass_secret_memory::{Public, PublicBox, Secret}; +use rosenpass_secret_memory::{Public, Secret}; use rosenpass_to::{ops::copy_slice, To}; use rosenpass_util::{ cat, @@ -35,6 +35,9 @@ use rosenpass_util::{ use crate::{hash_domains, msgs::*, RosenpassError}; +use super::basic_types::{ + BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, SPk, SSk, SessionId, SymKey, XAEADNonce, +}; use super::constants::{ BISCUIT_EPOCH, COOKIE_SECRET_EPOCH, COOKIE_SECRET_LEN, COOKIE_VALUE_LEN, PEER_COOKIE_VALUE_EPOCH, REJECT_AFTER_TIME, REKEY_AFTER_TIME_INITIATOR, @@ -47,38 +50,6 @@ use super::timing::{has_happened, Timing, BCE, UNENDING}; use rosenpass_util::trace_bench::Trace as _; // DATA STRUCTURES & BASIC TRAITS & ACCESSORS //// - -/// Static public key -/// -/// Using [PublicBox] instead of [Public] because Classic McEliece keys are very large. -pub type SPk = PublicBox<{ StaticKem::PK_LEN }>; -/// Static secret key -pub type SSk = Secret<{ StaticKem::SK_LEN }>; -/// Ephemeral public key -pub type EPk = Public<{ EphemeralKem::PK_LEN }>; -pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; - -/// Symmetric key -pub type SymKey = Secret; -/// Symmetric hash -pub type SymHash = Public; - -/// Peer ID (derived from the public key, see the hash derivations in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) -pub type PeerId = Public; -/// Session ID -pub type SessionId = Public; -/// Biscuit ID -pub type BiscuitId = Public; - -/// Nonce for use with random-nonce AEAD -pub type XAEADNonce = Public<{ XAead::NONCE_LEN }>; - -/// Buffer capably of holding any Rosenpass protocol message -pub type MsgBuf = Public; - -/// Server-local peer number; this is just the index in [CryptoServer::peers] -pub type PeerNo = usize; - /// This is the implementation of our cryptographic protocol. /// /// The scope of this is: @@ -172,7 +143,7 @@ pub struct CryptoServer { /// /// ``` /// use rosenpass_util::time::Timebase; -/// use rosenpass::protocol::{timing::BCE, SymKey, CookieStore}; +/// use rosenpass::protocol::{timing::BCE, basic_types::SymKey, CookieStore}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// @@ -299,7 +270,8 @@ impl From for ProtocolVersion { /// /// ``` /// use std::ops::DerefMut; -/// use rosenpass::protocol::{SSk, SPk, SymKey, Peer, ProtocolVersion}; +/// use rosenpass::protocol::basic_types::{SSk, SPk, SymKey}; +/// use rosenpass::protocol::{Peer, ProtocolVersion}; /// use rosenpass_ciphers::StaticKem; /// use rosenpass_cipher_traits::primitives::Kem; /// @@ -387,7 +359,8 @@ impl Peer { /// This is dirty but allows us to perform easy incremental construction of [Self]. /// /// ``` - /// use rosenpass::protocol::{Peer, SymKey, SPk, ProtocolVersion}; + /// use rosenpass::protocol::basic_types::{SymKey, SPk}; + /// use rosenpass::protocol::{Peer, ProtocolVersion}; /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// let p = Peer::zero(ProtocolVersion::V03); /// assert_eq!(p.psk.secret(), SymKey::zero().secret()); @@ -735,7 +708,8 @@ pub trait Mortal { /// ``` /// use std::ops::DerefMut; /// use rosenpass_ciphers::StaticKem; -/// use rosenpass::protocol::{SSk, SPk, testutils::ServerForTesting, ProtocolVersion}; +/// use rosenpass::protocol::basic_types::{SSk, SPk}; +/// use rosenpass::protocol::{testutils::ServerForTesting, ProtocolVersion}; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// @@ -1275,7 +1249,8 @@ impl CryptoServer { /// /// ``` /// use std::ops::DerefMut; - /// use rosenpass::protocol::{SSk, SPk, CryptoServer, ProtocolVersion}; + /// use rosenpass::protocol::basic_types::{SSk, SPk}; + /// use rosenpass::protocol::{CryptoServer, ProtocolVersion}; /// use rosenpass_ciphers::StaticKem; /// use rosenpass_cipher_traits::primitives::Kem; /// @@ -1339,7 +1314,8 @@ impl CryptoServer { /// /// ``` /// use std::ops::DerefMut; - /// use rosenpass::protocol::{SSk, SPk, SymKey, CryptoServer, ProtocolVersion}; + /// use rosenpass::protocol::basic_types::{SSk, SPk, SymKey}; + /// use rosenpass::protocol::{CryptoServer, ProtocolVersion}; /// use rosenpass_ciphers::StaticKem; /// use rosenpass_cipher_traits::primitives::Kem; /// diff --git a/rosenpass/tests/api-integration-tests-api-setup.rs b/rosenpass/tests/api-integration-tests-api-setup.rs index c4f2d22a..8313bb87 100644 --- a/rosenpass/tests/api-integration-tests-api-setup.rs +++ b/rosenpass/tests/api-integration-tests-api-setup.rs @@ -15,7 +15,7 @@ use rosenpass::api::{ supply_keypair_response_status, }; use rosenpass::config::ProtocolVersion; -use rosenpass::protocol::SymKey; +use rosenpass::protocol::basic_types::SymKey; use rosenpass_util::{ b64::B64Display, file::LoadValueB64, diff --git a/rosenpass/tests/api-integration-tests.rs b/rosenpass/tests/api-integration-tests.rs index de02832b..b3b638d7 100644 --- a/rosenpass/tests/api-integration-tests.rs +++ b/rosenpass/tests/api-integration-tests.rs @@ -17,7 +17,7 @@ use tempfile::TempDir; use zerocopy::AsBytes; use rosenpass::config::ProtocolVersion; -use rosenpass::protocol::SymKey; +use rosenpass::protocol::basic_types::SymKey; struct KillChild(std::process::Child); diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index 555b96ca..d384b79c 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -10,7 +10,7 @@ use std::{ use rosenpass::config::ProtocolVersion; use rosenpass::{ app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}, - protocol::{SPk, SSk, SymKey}, + protocol::basic_types::{SPk, SSk, SymKey}, }; use rosenpass_cipher_traits::primitives::Kem; use rosenpass_ciphers::StaticKem; diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index cabded01..43a4d863 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -10,10 +10,10 @@ use rosenpass_ciphers::StaticKem; use rosenpass_util::result::OkExt; use rosenpass::protocol::{ + basic_types::{MsgBuf, SPk, SSk, SymKey}, testutils::time_travel_forward, timing::{Timing, UNENDING}, - CryptoServer, HostIdentification, MsgBuf, PeerPtr, PollResult, ProtocolVersion, SPk, SSk, - SymKey, + CryptoServer, HostIdentification, PeerPtr, PollResult, ProtocolVersion, }; // TODO: Most of the utility functions in here should probably be moved to diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index bbbac0dd..e1e75447 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -206,7 +206,7 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> { use rosenpass::{ app_server::{AppServer, BrokerPeer}, config::Verbosity, - protocol::{SPk, SSk, SymKey}, + protocol::basic_types::{SPk, SSk, SymKey}, }; use rosenpass_secret_memory::Secret; use rosenpass_util::file::{LoadValue as _, LoadValueB64}; diff --git a/rp/src/key.rs b/rp/src/key.rs index f8f7f3d1..f6089e7e 100644 --- a/rp/src/key.rs +++ b/rp/src/key.rs @@ -9,7 +9,7 @@ use anyhow::{anyhow, Result}; use rosenpass_util::file::{LoadValueB64, StoreValue, StoreValueB64}; use zeroize::Zeroize; -use rosenpass::protocol::{SPk, SSk}; +use rosenpass::protocol::basic_types::{SPk, SSk}; use rosenpass_cipher_traits::primitives::Kem; use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::{file::StoreSecret as _, Public, Secret}; @@ -118,7 +118,7 @@ pub fn pubkey(private_keys_dir: &Path, public_keys_dir: &Path) -> Result<()> { mod tests { use std::fs; - use rosenpass::protocol::{SPk, SSk}; + use rosenpass::protocol::basic_types::{SPk, SSk}; use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; use rosenpass_secret_memory::Secret; use rosenpass_util::file::LoadValue; From d9a6430472cc79fe92401d8be5068729ebb6ffad Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 19:00:44 +0200 Subject: [PATCH 152/174] chore: Remove unused type SymHash --- rosenpass/src/protocol/basic_types.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rosenpass/src/protocol/basic_types.rs b/rosenpass/src/protocol/basic_types.rs index a810d6fc..f3acfaba 100644 --- a/rosenpass/src/protocol/basic_types.rs +++ b/rosenpass/src/protocol/basic_types.rs @@ -18,8 +18,6 @@ pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; /// Symmetric key pub type SymKey = Secret; -/// Symmetric hash -pub type SymHash = Public; /// Peer ID (derived from the public key, see the hash derivations in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) pub type PeerId = Public; From c318cf7bac74b18031b8c78ac0a3a62646fd86ce Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 19:40:57 +0200 Subject: [PATCH 153/174] chore: Split protocol tests into own file --- rosenpass/src/protocol/mod.rs | 3 + rosenpass/src/protocol/protocol.rs | 674 +--------------------------- rosenpass/src/protocol/test.rs | 680 +++++++++++++++++++++++++++++ 3 files changed, 686 insertions(+), 671 deletions(-) create mode 100644 rosenpass/src/protocol/test.rs diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index de8f435c..da9ddc50 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -84,3 +84,6 @@ pub mod timing; #[allow(clippy::module_inception)] mod protocol; pub use protocol::*; + +#[cfg(test)] +mod test; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 9c367f0b..a4a7798b 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3951,7 +3951,9 @@ impl CryptoServer { } /// Used to parse a network message using [zerocopy] -fn truncating_cast_into(buf: &mut [u8]) -> Result, RosenpassError> { +pub fn truncating_cast_into( + buf: &mut [u8], +) -> Result, RosenpassError> { Ref::new(&mut buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch) } @@ -4004,673 +4006,3 @@ pub mod testutils { srv.timebase.0 = srv.timebase.0.checked_sub(dur).unwrap(); } } - -#[cfg(test)] -mod test { - use std::{borrow::BorrowMut, net::SocketAddrV4, ops::DerefMut}; - - use super::*; - use serial_test::serial; - use zerocopy::FromZeroes; - - struct VecHostIdentifier(Vec); - - impl HostIdentification for VecHostIdentifier { - fn encode(&self) -> &[u8] { - &self.0 - } - } - - impl Display for VecHostIdentifier { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - - impl From> for VecHostIdentifier { - fn from(v: Vec) -> Self { - VecHostIdentifier(v) - } - } - - fn setup_logging() { - use std::io::Write; - let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults) - log_builder.filter_level(log::LevelFilter::Info); - log_builder.format_timestamp_nanos(); - log_builder.format(|buf, record| { - let ts_format = buf.timestamp_nanos().to_string(); - writeln!(buf, "{}: {}", &ts_format[14..], record.args()) - }); - - let _ = log_builder.try_init(); - } - - #[test] - #[serial] - fn handles_incorrect_size_messages_v02() { - handles_incorrect_size_messages(ProtocolVersion::V02) - } - - #[test] - #[serial] - fn handles_incorrect_size_messages_v03() { - handles_incorrect_size_messages(ProtocolVersion::V03) - } - - /// 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(protocol_version: ProtocolVersion) { - setup_logging(); - rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - stacker::grow(8 * 1024 * 1024, || { - const OVERSIZED_MESSAGE: usize = ((MAX_MESSAGE_LEN as f32) * 1.2) as usize; - type MsgBufPlus = Public; - - const PEER0: PeerPtr = PeerPtr(0); - - let (mut me, mut they) = make_server_pair(protocol_version).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()); - while 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); - } - - assert_eq!( - me.osk(PEER0).unwrap().secret(), - they.osk(PEER0).unwrap().secret() - ); - }); - } - - /// 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 { - resbuf.fill(0); - - for l in 0..(((msglen as f32) * 1.2) as usize) { - if l == msglen { - continue; - } - - let res = srv.handle_msg(&msgbuf[..l], resbuf); - assert!(res.is_err()); // handle_msg should raise an error - assert!(!resbuf.iter().any(|x| *x != 0)); // 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.deref_mut())?; - Ok((sk, pk)) - } - - fn make_server_pair(protocol_version: ProtocolVersion) -> 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, protocol_version.clone())?; - b.add_peer(Some(psk), pka, protocol_version)?; - Ok((a, b)) - } - - #[test] - #[serial] - fn test_regular_exchange_v02() { - test_regular_exchange(ProtocolVersion::V02) - } - - #[test] - #[serial] - fn test_regular_exchange_v03() { - test_regular_exchange(ProtocolVersion::V03) - } - - fn test_regular_exchange(protocol_version: ProtocolVersion) { - setup_logging(); - rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - stacker::grow(8 * 1024 * 1024, || { - type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); - - let mut a_to_b_buf = MsgBufPlus::zero(); - let mut b_to_a_buf = MsgBufPlus::zero(); - - let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); - let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); - ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); - - let _ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); - - let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); - - let init_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); - assert_eq!(init_msg_type, MsgType::InitHello); - - //B handles InitHello, sends RespHello - let HandleMsgResult { resp, .. } = b - .handle_msg(&a_to_b_buf.as_slice()[..init_hello_len], &mut *b_to_a_buf) - .unwrap(); - - let resp_hello_len = resp.unwrap(); - - let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - assert_eq!(resp_msg_type, MsgType::RespHello); - - let HandleMsgResult { - resp, - exchanged_with, - } = a - .handle_msg(&b_to_a_buf[..resp_hello_len], &mut *a_to_b_buf) - .unwrap(); - - let init_conf_len = resp.unwrap(); - let init_conf_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); - - assert_eq!(exchanged_with, Some(PeerPtr(0))); - assert_eq!(init_conf_msg_type, MsgType::InitConf); - - //B handles InitConf, sends EmptyData - let HandleMsgResult { - resp: _, - exchanged_with, - } = b - .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) - .unwrap(); - - let empty_data_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - - assert_eq!(exchanged_with, Some(PeerPtr(0))); - assert_eq!(empty_data_msg_type, MsgType::EmptyData); - }); - } - - #[test] - #[serial] - fn test_regular_init_conf_retransmit_v02() { - test_regular_init_conf_retransmit(ProtocolVersion::V02) - } - - #[test] - #[serial] - fn test_regular_init_conf_retransmit_v03() { - test_regular_init_conf_retransmit(ProtocolVersion::V03) - } - - fn test_regular_init_conf_retransmit(protocol_version: ProtocolVersion) { - setup_logging(); - rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - stacker::grow(8 * 1024 * 1024, || { - type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); - - let mut a_to_b_buf = MsgBufPlus::zero(); - let mut b_to_a_buf = MsgBufPlus::zero(); - - let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); - let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); - ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); - - let _ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); - - let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); - - let init_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); - assert_eq!(init_msg_type, MsgType::InitHello); - - //B handles InitHello, sends RespHello - let HandleMsgResult { resp, .. } = b - .handle_msg(&a_to_b_buf.as_slice()[..init_hello_len], &mut *b_to_a_buf) - .unwrap(); - - let resp_hello_len = resp.unwrap(); - - let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - assert_eq!(resp_msg_type, MsgType::RespHello); - - //A handles RespHello, sends InitConf, exchanges keys - let HandleMsgResult { - resp, - exchanged_with, - } = a - .handle_msg(&b_to_a_buf[..resp_hello_len], &mut *a_to_b_buf) - .unwrap(); - - let init_conf_len = resp.unwrap(); - let init_conf_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); - - assert_eq!(exchanged_with, Some(PeerPtr(0))); - assert_eq!(init_conf_msg_type, MsgType::InitConf); - - //B handles InitConf, sends EmptyData - let HandleMsgResult { - resp: _, - exchanged_with, - } = b - .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) - .unwrap(); - - let empty_data_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - - assert_eq!(exchanged_with, Some(PeerPtr(0))); - assert_eq!(empty_data_msg_type, MsgType::EmptyData); - - //B handles InitConf again, sends EmptyData - let HandleMsgResult { - resp: _, - exchanged_with, - } = b - .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) - .unwrap(); - - let empty_data_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - - assert!(exchanged_with.is_none()); - assert_eq!(empty_data_msg_type, MsgType::EmptyData); - }); - } - - #[test] - #[serial] - #[cfg(feature = "experiment_cookie_dos_mitigation")] - fn cookie_reply_mechanism_responder_under_load_v02() { - cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) - } - - #[test] - #[serial] - #[cfg(feature = "experiment_cookie_dos_mitigation")] - fn cookie_reply_mechanism_responder_under_load_v03() { - cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) - } - - #[cfg(feature = "experiment_cookie_dos_mitigation")] - fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) { - use std::time::Duration; - - setup_logging(); - rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - stacker::grow(8 * 1024 * 1024, || { - type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair(protocol_version.clone()).unwrap(); - - let mut a_to_b_buf = MsgBufPlus::zero(); - let mut b_to_a_buf = MsgBufPlus::zero(); - - let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); - let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); - ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); - - let _ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); - - let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); - let socket_addr_a = std::net::SocketAddr::V4(ip_a); - let mut ip_addr_port_a = match socket_addr_a.ip() { - std::net::IpAddr::V4(ipv4) => ipv4.octets().to_vec(), - std::net::IpAddr::V6(ipv6) => ipv6.octets().to_vec(), - }; - - ip_addr_port_a.extend_from_slice(&socket_addr_a.port().to_be_bytes()); - - let ip_addr_port_a: VecHostIdentifier = ip_addr_port_a.into(); - - //B handles handshake under load, should send cookie reply message with invalid cookie - let HandleMsgResult { resp, .. } = b - .handle_msg_under_load( - &a_to_b_buf.as_slice()[..init_hello_len], - &mut *b_to_a_buf, - &ip_addr_port_a, - ) - .unwrap(); - - let cookie_reply_len = resp.unwrap(); - - //A handles cookie reply message - a.handle_msg(&b_to_a_buf[..cookie_reply_len], &mut *a_to_b_buf) - .unwrap(); - - assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young); - - let expected_cookie_value = hash_domains::cookie_value(protocol_version.keyed_hash()) - .unwrap() - .mix( - b.active_or_retired_cookie_secrets()[0] - .unwrap() - .get(&b) - .value - .secret(), - ) - .unwrap() - .mix(ip_addr_port_a.encode()) - .unwrap() - .into_value()[..16] - .to_vec(); - - assert_eq!( - PeerPtr(0).cv().get(&a).map(|x| &x.value.secret()[..]), - Some(&expected_cookie_value[..]) - ); - - let retx_init_hello_len = loop { - match a.poll().unwrap() { - PollResult::SendRetransmission(peer) => { - break a.retransmit_handshake(peer, &mut *a_to_b_buf).unwrap(); - } - PollResult::Sleep(time) => { - std::thread::sleep(Duration::from_secs_f64(time)); - } - _ => {} - } - }; - - let retx_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); - assert_eq!(retx_msg_type, MsgType::InitHello); - - //B handles retransmitted message - let HandleMsgResult { resp, .. } = b - .handle_msg_under_load( - &a_to_b_buf.as_slice()[..retx_init_hello_len], - &mut *b_to_a_buf, - &ip_addr_port_a, - ) - .unwrap(); - - let _resp_hello_len = resp.unwrap(); - - let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - assert_eq!(resp_msg_type, MsgType::RespHello); - }); - } - - #[test] - #[serial] - #[cfg(feature = "experiment_cookie_dos_mitigation")] - fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v02() { - cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) - } - - #[test] - #[serial] - #[cfg(feature = "experiment_cookie_dos_mitigation")] - fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v03() { - cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) - } - - #[cfg(feature = "experiment_cookie_dos_mitigation")] - fn cookie_reply_mechanism_initiator_bails_on_message_under_load( - protocol_version: ProtocolVersion, - ) { - setup_logging(); - rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - stacker::grow(8 * 1024 * 1024, || { - type MsgBufPlus = Public; - let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); - - let mut a_to_b_buf = MsgBufPlus::zero(); - let mut b_to_a_buf = MsgBufPlus::zero(); - - let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); - let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); - ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); - let ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); - - //A initiates handshake - let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); - - //B handles InitHello message, should respond with RespHello - let HandleMsgResult { resp, .. } = b - .handle_msg(&a_to_b_buf.as_slice()[..init_hello_len], &mut *b_to_a_buf) - .unwrap(); - - let resp_hello_len = resp.unwrap(); - let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); - assert_eq!(resp_msg_type, MsgType::RespHello); - - let socket_addr_b = std::net::SocketAddr::V4(ip_b); - let mut ip_addr_port_b = [0u8; 18]; - let mut ip_addr_port_b_len = 0; - match socket_addr_b.ip() { - std::net::IpAddr::V4(ipv4) => { - ip_addr_port_b[0..4].copy_from_slice(&ipv4.octets()); - ip_addr_port_b_len += 4; - } - std::net::IpAddr::V6(ipv6) => { - ip_addr_port_b[0..16].copy_from_slice(&ipv6.octets()); - ip_addr_port_b_len += 16; - } - }; - - ip_addr_port_b[ip_addr_port_b_len..ip_addr_port_b_len + 2] - .copy_from_slice(&socket_addr_b.port().to_be_bytes()); - ip_addr_port_b_len += 2; - - let ip_addr_port_b: VecHostIdentifier = - ip_addr_port_b[..ip_addr_port_b_len].to_vec().into(); - - //A handles RespHello message under load, should not send cookie reply - assert!(a - .handle_msg_under_load( - &b_to_a_buf[..resp_hello_len], - &mut *a_to_b_buf, - &ip_addr_port_b - ) - .is_err()); - }); - } - - #[test] - fn init_conf_retransmission_v02() -> Result<()> { - init_conf_retransmission(ProtocolVersion::V02) - } - - #[test] - fn init_conf_retransmission_v03() -> Result<()> { - init_conf_retransmission(ProtocolVersion::V03) - } - - fn init_conf_retransmission(protocol_version: ProtocolVersion) -> anyhow::Result<()> { - rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); - - fn keypair() -> Result<(SSk, SPk)> { - let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); - StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?; - Ok((sk, pk)) - } - - fn proc_initiation(srv: &mut CryptoServer, peer: PeerPtr) -> Result> { - let mut buf = MsgBuf::zero(); - srv.initiate_handshake(peer, buf.as_mut_slice())? - .discard_result(); - let msg = truncating_cast_into::>(buf.borrow_mut())?; - Ok(msg.read()) - } - - fn proc_msg( - srv: &mut CryptoServer, - rx: &Envelope, - ) -> anyhow::Result> { - let mut buf = MsgBuf::zero(); - srv.handle_msg(rx.as_bytes(), buf.as_mut_slice())? - .resp - .context("Failed to produce RespHello message")? - .discard_result(); - let msg = truncating_cast_into::>(buf.borrow_mut())?; - Ok(msg.read()) - } - - fn proc_init_hello( - srv: &mut CryptoServer, - ih: &Envelope, - ) -> anyhow::Result> { - proc_msg::(srv, ih) - } - - fn proc_resp_hello( - srv: &mut CryptoServer, - rh: &Envelope, - ) -> anyhow::Result> { - proc_msg::(srv, rh) - } - - fn proc_init_conf( - srv: &mut CryptoServer, - rh: &Envelope, - ) -> anyhow::Result> { - proc_msg::(srv, rh) - } - - fn poll(srv: &mut CryptoServer) -> anyhow::Result<()> { - // Discard all events; just apply the side effects - while !matches!(srv.poll()?, PollResult::Sleep(_)) {} - Ok(()) - } - - // TODO: Implement Clone on our message types - fn clone_msg(msg: &Msg) -> anyhow::Result { - Ok(truncating_cast_into_nomut::(msg.as_bytes())?.read()) - } - - fn break_payload( - srv: &mut CryptoServer, - peer: PeerPtr, - msg: &Envelope, - ) -> anyhow::Result> { - let mut msg = clone_msg(msg)?; - msg.as_bytes_mut()[memoffset::offset_of!(Envelope, payload)] ^= 0x01; - msg.seal(peer, srv)?; // Recalculate seal; we do not want to focus on "seal broken" errs - Ok(msg) - } - - fn check_faulty_proc_init_conf(srv: &mut CryptoServer, ic_broken: &Envelope) { - let mut buf = MsgBuf::zero(); - let res = srv.handle_msg(ic_broken.as_bytes(), buf.as_mut_slice()); - assert!(res.is_err()); - } - - // we this as a closure in orer to use the protocol_version variable in it. - let check_retransmission = |srv: &mut CryptoServer, - ic: &Envelope, - ic_broken: &Envelope, - rc: &Envelope| - -> Result<()> { - // Processing the same RespHello package again leads to retransmission (i.e. exactly the - // same output) - let rc_dup = proc_init_conf(srv, ic)?; - assert_eq!(rc.as_bytes(), rc_dup.as_bytes()); - - // Though if we directly call handle_resp_hello() we get an error since - // retransmission is not being handled by the cryptographic code - let mut discard_resp_conf = EmptyData::new_zeroed(); - let res = srv.handle_init_conf( - &ic.payload, - &mut discard_resp_conf, - protocol_version.clone().keyed_hash(), - ); - assert!(res.is_err()); - - // Obviously, a broken InitConf message should still be rejected - check_faulty_proc_init_conf(srv, ic_broken); - - Ok(()) - }; - - let (ska, pka) = keypair()?; - let (skb, pkb) = keypair()?; - - // initialize server and a pre-shared key - let mut a = CryptoServer::new(ska, pka.clone()); - let mut b = CryptoServer::new(skb, pkb.clone()); - - // introduce peers to each other - let b_peer = a.add_peer(None, pkb, protocol_version.clone())?; - let a_peer = b.add_peer(None, pka, protocol_version.clone())?; - - // Execute protocol up till the responder confirmation (EmptyData) - let ih1 = proc_initiation(&mut a, b_peer)?; - let rh1 = proc_init_hello(&mut b, &ih1)?; - let ic1 = proc_resp_hello(&mut a, &rh1)?; - let rc1 = proc_init_conf(&mut b, &ic1)?; - - // Modified version of ic1 and rc1, for tests that require it - let ic1_broken = break_payload(&mut a, b_peer, &ic1)?; - assert_ne!(ic1.as_bytes(), ic1_broken.as_bytes()); - - // Modified version of rc1, for tests that require it - let rc1_broken = break_payload(&mut b, a_peer, &rc1)?; - assert_ne!(rc1.as_bytes(), rc1_broken.as_bytes()); - - // Retransmission works as designed - check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; - - // Even with a couple of poll operations in between (which clears the cache - // after a time out of two minutes…we should never hit this time out in this - // cache) - for _ in 0..4 { - poll(&mut b)?; - check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; - } - // We can even validate that the data is coming out of the cache by changing the cache - // to use our broken messages. It does not matter that these messages are cryptographically - // broken since we insert them manually into the cache - // a_peer.known_init_conf_response() - KnownInitConfResponsePtr::insert_for_request_msg( - &mut b, - a_peer, - &ic1_broken, - rc1_broken.clone(), - ); - check_retransmission(&mut b, &ic1_broken, &ic1, &rc1_broken)?; - - // Lets reset to the correct message though - KnownInitConfResponsePtr::insert_for_request_msg(&mut b, a_peer, &ic1, rc1.clone()); - - // Again, nothing changes after calling poll - poll(&mut b)?; - check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; - - // Except if we jump forward into the future past the point where the responder - // starts to initiate rekeying; in this case, the automatic time out is triggered and the cache is cleared - super::testutils::time_travel_forward(&mut b, REKEY_AFTER_TIME_RESPONDER); - - // As long as we do not call poll, everything is fine - check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; - - // But after we do, the response is gone and can not be recreated - // since the biscuit is stale - poll(&mut b)?; - check_faulty_proc_init_conf(&mut b, &ic1); // ic1 is now effectively broken - assert!(b.peers[0].known_init_conf_response.is_none()); // The cache is gone - - Ok(()) - } -} diff --git a/rosenpass/src/protocol/test.rs b/rosenpass/src/protocol/test.rs new file mode 100644 index 00000000..4869e670 --- /dev/null +++ b/rosenpass/src/protocol/test.rs @@ -0,0 +1,680 @@ +use std::{borrow::BorrowMut, fmt::Display, net::SocketAddrV4, ops::DerefMut}; + +use anyhow::{Context, Result}; +use serial_test::serial; +use zerocopy::{AsBytes, FromBytes, FromZeroes}; + +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::StaticKem; +use rosenpass_secret_memory::Public; +use rosenpass_util::mem::DiscardResultExt; + +use crate::{ + msgs::{EmptyData, Envelope, InitConf, InitHello, MsgType, RespHello, MAX_MESSAGE_LEN}, + protocol::{basic_types::MsgBuf, constants::REKEY_AFTER_TIME_RESPONDER}, +}; + +use super::{ + basic_types::{SPk, SSk, SymKey}, + *, +}; + +struct VecHostIdentifier(Vec); + +impl HostIdentification for VecHostIdentifier { + fn encode(&self) -> &[u8] { + &self.0 + } +} + +impl Display for VecHostIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl From> for VecHostIdentifier { + fn from(v: Vec) -> Self { + VecHostIdentifier(v) + } +} + +fn setup_logging() { + use std::io::Write; + let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults) + log_builder.filter_level(log::LevelFilter::Info); + log_builder.format_timestamp_nanos(); + log_builder.format(|buf, record| { + let ts_format = buf.timestamp_nanos().to_string(); + writeln!(buf, "{}: {}", &ts_format[14..], record.args()) + }); + + let _ = log_builder.try_init(); +} + +#[test] +#[serial] +fn handles_incorrect_size_messages_v02() { + handles_incorrect_size_messages(ProtocolVersion::V02) +} + +#[test] +#[serial] +fn handles_incorrect_size_messages_v03() { + handles_incorrect_size_messages(ProtocolVersion::V03) +} + +/// 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(protocol_version: ProtocolVersion) { + setup_logging(); + rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); + stacker::grow(8 * 1024 * 1024, || { + const OVERSIZED_MESSAGE: usize = ((MAX_MESSAGE_LEN as f32) * 1.2) as usize; + type MsgBufPlus = Public; + + const PEER0: PeerPtr = PeerPtr(0); + + let (mut me, mut they) = make_server_pair(protocol_version).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()); + while 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); + } + + assert_eq!( + me.osk(PEER0).unwrap().secret(), + they.osk(PEER0).unwrap().secret() + ); + }); +} + +/// 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 { + resbuf.fill(0); + + for l in 0..(((msglen as f32) * 1.2) as usize) { + if l == msglen { + continue; + } + + let res = srv.handle_msg(&msgbuf[..l], resbuf); + assert!(res.is_err()); // handle_msg should raise an error + assert!(!resbuf.iter().any(|x| *x != 0)); // 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.deref_mut())?; + Ok((sk, pk)) +} + +fn make_server_pair(protocol_version: ProtocolVersion) -> 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, protocol_version.clone())?; + b.add_peer(Some(psk), pka, protocol_version)?; + Ok((a, b)) +} + +#[test] +#[serial] +fn test_regular_exchange_v02() { + test_regular_exchange(ProtocolVersion::V02) +} + +#[test] +#[serial] +fn test_regular_exchange_v03() { + test_regular_exchange(ProtocolVersion::V03) +} + +fn test_regular_exchange(protocol_version: ProtocolVersion) { + setup_logging(); + rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); + stacker::grow(8 * 1024 * 1024, || { + type MsgBufPlus = Public; + let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); + + let mut a_to_b_buf = MsgBufPlus::zero(); + let mut b_to_a_buf = MsgBufPlus::zero(); + + let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); + let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); + ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); + + let _ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); + + let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); + + let init_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); + assert_eq!(init_msg_type, MsgType::InitHello); + + //B handles InitHello, sends RespHello + let HandleMsgResult { resp, .. } = b + .handle_msg(&a_to_b_buf.as_slice()[..init_hello_len], &mut *b_to_a_buf) + .unwrap(); + + let resp_hello_len = resp.unwrap(); + + let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + assert_eq!(resp_msg_type, MsgType::RespHello); + + let HandleMsgResult { + resp, + exchanged_with, + } = a + .handle_msg(&b_to_a_buf[..resp_hello_len], &mut *a_to_b_buf) + .unwrap(); + + let init_conf_len = resp.unwrap(); + let init_conf_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); + + assert_eq!(exchanged_with, Some(PeerPtr(0))); + assert_eq!(init_conf_msg_type, MsgType::InitConf); + + //B handles InitConf, sends EmptyData + let HandleMsgResult { + resp: _, + exchanged_with, + } = b + .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) + .unwrap(); + + let empty_data_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + + assert_eq!(exchanged_with, Some(PeerPtr(0))); + assert_eq!(empty_data_msg_type, MsgType::EmptyData); + }); +} + +#[test] +#[serial] +fn test_regular_init_conf_retransmit_v02() { + test_regular_init_conf_retransmit(ProtocolVersion::V02) +} + +#[test] +#[serial] +fn test_regular_init_conf_retransmit_v03() { + test_regular_init_conf_retransmit(ProtocolVersion::V03) +} + +fn test_regular_init_conf_retransmit(protocol_version: ProtocolVersion) { + setup_logging(); + rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); + stacker::grow(8 * 1024 * 1024, || { + type MsgBufPlus = Public; + let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); + + let mut a_to_b_buf = MsgBufPlus::zero(); + let mut b_to_a_buf = MsgBufPlus::zero(); + + let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); + let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); + ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); + + let _ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); + + let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); + + let init_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); + assert_eq!(init_msg_type, MsgType::InitHello); + + //B handles InitHello, sends RespHello + let HandleMsgResult { resp, .. } = b + .handle_msg(&a_to_b_buf.as_slice()[..init_hello_len], &mut *b_to_a_buf) + .unwrap(); + + let resp_hello_len = resp.unwrap(); + + let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + assert_eq!(resp_msg_type, MsgType::RespHello); + + //A handles RespHello, sends InitConf, exchanges keys + let HandleMsgResult { + resp, + exchanged_with, + } = a + .handle_msg(&b_to_a_buf[..resp_hello_len], &mut *a_to_b_buf) + .unwrap(); + + let init_conf_len = resp.unwrap(); + let init_conf_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); + + assert_eq!(exchanged_with, Some(PeerPtr(0))); + assert_eq!(init_conf_msg_type, MsgType::InitConf); + + //B handles InitConf, sends EmptyData + let HandleMsgResult { + resp: _, + exchanged_with, + } = b + .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) + .unwrap(); + + let empty_data_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + + assert_eq!(exchanged_with, Some(PeerPtr(0))); + assert_eq!(empty_data_msg_type, MsgType::EmptyData); + + //B handles InitConf again, sends EmptyData + let HandleMsgResult { + resp: _, + exchanged_with, + } = b + .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) + .unwrap(); + + let empty_data_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + + assert!(exchanged_with.is_none()); + assert_eq!(empty_data_msg_type, MsgType::EmptyData); + }); +} + +#[test] +#[serial] +#[cfg(feature = "experiment_cookie_dos_mitigation")] +fn cookie_reply_mechanism_responder_under_load_v02() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) +} + +#[test] +#[serial] +#[cfg(feature = "experiment_cookie_dos_mitigation")] +fn cookie_reply_mechanism_responder_under_load_v03() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) +} + +#[cfg(feature = "experiment_cookie_dos_mitigation")] +fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) { + use std::time::Duration; + + setup_logging(); + rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); + stacker::grow(8 * 1024 * 1024, || { + type MsgBufPlus = Public; + let (mut a, mut b) = make_server_pair(protocol_version.clone()).unwrap(); + + let mut a_to_b_buf = MsgBufPlus::zero(); + let mut b_to_a_buf = MsgBufPlus::zero(); + + let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); + let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); + ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); + + let _ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); + + let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); + let socket_addr_a = std::net::SocketAddr::V4(ip_a); + let mut ip_addr_port_a = match socket_addr_a.ip() { + std::net::IpAddr::V4(ipv4) => ipv4.octets().to_vec(), + std::net::IpAddr::V6(ipv6) => ipv6.octets().to_vec(), + }; + + ip_addr_port_a.extend_from_slice(&socket_addr_a.port().to_be_bytes()); + + let ip_addr_port_a: VecHostIdentifier = ip_addr_port_a.into(); + + //B handles handshake under load, should send cookie reply message with invalid cookie + let HandleMsgResult { resp, .. } = b + .handle_msg_under_load( + &a_to_b_buf.as_slice()[..init_hello_len], + &mut *b_to_a_buf, + &ip_addr_port_a, + ) + .unwrap(); + + let cookie_reply_len = resp.unwrap(); + + //A handles cookie reply message + a.handle_msg(&b_to_a_buf[..cookie_reply_len], &mut *a_to_b_buf) + .unwrap(); + + assert_eq!(PeerPtr(0).cv().lifecycle(&a), Lifecycle::Young); + + let expected_cookie_value = + crate::hash_domains::cookie_value(protocol_version.keyed_hash()) + .unwrap() + .mix( + b.active_or_retired_cookie_secrets()[0] + .unwrap() + .get(&b) + .value + .secret(), + ) + .unwrap() + .mix(ip_addr_port_a.encode()) + .unwrap() + .into_value()[..16] + .to_vec(); + + assert_eq!( + PeerPtr(0).cv().get(&a).map(|x| &x.value.secret()[..]), + Some(&expected_cookie_value[..]) + ); + + let retx_init_hello_len = loop { + match a.poll().unwrap() { + PollResult::SendRetransmission(peer) => { + break a.retransmit_handshake(peer, &mut *a_to_b_buf).unwrap(); + } + PollResult::Sleep(time) => { + std::thread::sleep(Duration::from_secs_f64(time)); + } + _ => {} + } + }; + + let retx_msg_type: MsgType = a_to_b_buf.value[0].try_into().unwrap(); + assert_eq!(retx_msg_type, MsgType::InitHello); + + //B handles retransmitted message + let HandleMsgResult { resp, .. } = b + .handle_msg_under_load( + &a_to_b_buf.as_slice()[..retx_init_hello_len], + &mut *b_to_a_buf, + &ip_addr_port_a, + ) + .unwrap(); + + let _resp_hello_len = resp.unwrap(); + + let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + assert_eq!(resp_msg_type, MsgType::RespHello); + }); +} + +#[test] +#[serial] +#[cfg(feature = "experiment_cookie_dos_mitigation")] +fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v02() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V02) +} + +#[test] +#[serial] +#[cfg(feature = "experiment_cookie_dos_mitigation")] +fn cookie_reply_mechanism_initiator_bails_on_message_under_load_v03() { + cookie_reply_mechanism_initiator_bails_on_message_under_load(ProtocolVersion::V03) +} + +#[cfg(feature = "experiment_cookie_dos_mitigation")] +fn cookie_reply_mechanism_initiator_bails_on_message_under_load(protocol_version: ProtocolVersion) { + setup_logging(); + rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); + stacker::grow(8 * 1024 * 1024, || { + type MsgBufPlus = Public; + let (mut a, mut b) = make_server_pair(protocol_version).unwrap(); + + let mut a_to_b_buf = MsgBufPlus::zero(); + let mut b_to_a_buf = MsgBufPlus::zero(); + + let ip_a: SocketAddrV4 = "127.0.0.1:8080".parse().unwrap(); + let mut ip_addr_port_a = ip_a.ip().octets().to_vec(); + ip_addr_port_a.extend_from_slice(&ip_a.port().to_be_bytes()); + let ip_b: SocketAddrV4 = "127.0.0.1:8081".parse().unwrap(); + + //A initiates handshake + let init_hello_len = a.initiate_handshake(PeerPtr(0), &mut *a_to_b_buf).unwrap(); + + //B handles InitHello message, should respond with RespHello + let HandleMsgResult { resp, .. } = b + .handle_msg(&a_to_b_buf.as_slice()[..init_hello_len], &mut *b_to_a_buf) + .unwrap(); + + let resp_hello_len = resp.unwrap(); + let resp_msg_type: MsgType = b_to_a_buf.value[0].try_into().unwrap(); + assert_eq!(resp_msg_type, MsgType::RespHello); + + let socket_addr_b = std::net::SocketAddr::V4(ip_b); + let mut ip_addr_port_b = [0u8; 18]; + let mut ip_addr_port_b_len = 0; + match socket_addr_b.ip() { + std::net::IpAddr::V4(ipv4) => { + ip_addr_port_b[0..4].copy_from_slice(&ipv4.octets()); + ip_addr_port_b_len += 4; + } + std::net::IpAddr::V6(ipv6) => { + ip_addr_port_b[0..16].copy_from_slice(&ipv6.octets()); + ip_addr_port_b_len += 16; + } + }; + + ip_addr_port_b[ip_addr_port_b_len..ip_addr_port_b_len + 2] + .copy_from_slice(&socket_addr_b.port().to_be_bytes()); + ip_addr_port_b_len += 2; + + let ip_addr_port_b: VecHostIdentifier = + ip_addr_port_b[..ip_addr_port_b_len].to_vec().into(); + + //A handles RespHello message under load, should not send cookie reply + assert!(a + .handle_msg_under_load( + &b_to_a_buf[..resp_hello_len], + &mut *a_to_b_buf, + &ip_addr_port_b + ) + .is_err()); + }); +} + +#[test] +fn init_conf_retransmission_v02() -> Result<()> { + init_conf_retransmission(ProtocolVersion::V02) +} + +#[test] +fn init_conf_retransmission_v03() -> Result<()> { + init_conf_retransmission(ProtocolVersion::V03) +} + +fn init_conf_retransmission(protocol_version: ProtocolVersion) -> anyhow::Result<()> { + rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); + + fn keypair() -> Result<(SSk, SPk)> { + let (mut sk, mut pk) = (SSk::zero(), SPk::zero()); + StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?; + Ok((sk, pk)) + } + + fn proc_initiation(srv: &mut CryptoServer, peer: PeerPtr) -> Result> { + let mut buf = MsgBuf::zero(); + srv.initiate_handshake(peer, buf.as_mut_slice())? + .discard_result(); + let msg = truncating_cast_into::>(buf.borrow_mut())?; + Ok(msg.read()) + } + + fn proc_msg( + srv: &mut CryptoServer, + rx: &Envelope, + ) -> anyhow::Result> { + let mut buf = MsgBuf::zero(); + srv.handle_msg(rx.as_bytes(), buf.as_mut_slice())? + .resp + .context("Failed to produce RespHello message")? + .discard_result(); + let msg = truncating_cast_into::>(buf.borrow_mut())?; + Ok(msg.read()) + } + + fn proc_init_hello( + srv: &mut CryptoServer, + ih: &Envelope, + ) -> anyhow::Result> { + proc_msg::(srv, ih) + } + + fn proc_resp_hello( + srv: &mut CryptoServer, + rh: &Envelope, + ) -> anyhow::Result> { + proc_msg::(srv, rh) + } + + fn proc_init_conf( + srv: &mut CryptoServer, + rh: &Envelope, + ) -> anyhow::Result> { + proc_msg::(srv, rh) + } + + fn poll(srv: &mut CryptoServer) -> anyhow::Result<()> { + // Discard all events; just apply the side effects + while !matches!(srv.poll()?, PollResult::Sleep(_)) {} + Ok(()) + } + + // TODO: Implement Clone on our message types + fn clone_msg(msg: &Msg) -> anyhow::Result { + Ok(truncating_cast_into_nomut::(msg.as_bytes())?.read()) + } + + fn break_payload( + srv: &mut CryptoServer, + peer: PeerPtr, + msg: &Envelope, + ) -> anyhow::Result> { + let mut msg = clone_msg(msg)?; + msg.as_bytes_mut()[memoffset::offset_of!(Envelope, payload)] ^= 0x01; + msg.seal(peer, srv)?; // Recalculate seal; we do not want to focus on "seal broken" errs + Ok(msg) + } + + fn check_faulty_proc_init_conf(srv: &mut CryptoServer, ic_broken: &Envelope) { + let mut buf = MsgBuf::zero(); + let res = srv.handle_msg(ic_broken.as_bytes(), buf.as_mut_slice()); + assert!(res.is_err()); + } + + // we this as a closure in orer to use the protocol_version variable in it. + let check_retransmission = |srv: &mut CryptoServer, + ic: &Envelope, + ic_broken: &Envelope, + rc: &Envelope| + -> Result<()> { + // Processing the same RespHello package again leads to retransmission (i.e. exactly the + // same output) + let rc_dup = proc_init_conf(srv, ic)?; + assert_eq!(rc.as_bytes(), rc_dup.as_bytes()); + + // Though if we directly call handle_resp_hello() we get an error since + // retransmission is not being handled by the cryptographic code + let mut discard_resp_conf = EmptyData::new_zeroed(); + let res = srv.handle_init_conf( + &ic.payload, + &mut discard_resp_conf, + protocol_version.clone().keyed_hash(), + ); + assert!(res.is_err()); + + // Obviously, a broken InitConf message should still be rejected + check_faulty_proc_init_conf(srv, ic_broken); + + Ok(()) + }; + + let (ska, pka) = keypair()?; + let (skb, pkb) = keypair()?; + + // initialize server and a pre-shared key + let mut a = CryptoServer::new(ska, pka.clone()); + let mut b = CryptoServer::new(skb, pkb.clone()); + + // introduce peers to each other + let b_peer = a.add_peer(None, pkb, protocol_version.clone())?; + let a_peer = b.add_peer(None, pka, protocol_version.clone())?; + + // Execute protocol up till the responder confirmation (EmptyData) + let ih1 = proc_initiation(&mut a, b_peer)?; + let rh1 = proc_init_hello(&mut b, &ih1)?; + let ic1 = proc_resp_hello(&mut a, &rh1)?; + let rc1 = proc_init_conf(&mut b, &ic1)?; + + // Modified version of ic1 and rc1, for tests that require it + let ic1_broken = break_payload(&mut a, b_peer, &ic1)?; + assert_ne!(ic1.as_bytes(), ic1_broken.as_bytes()); + + // Modified version of rc1, for tests that require it + let rc1_broken = break_payload(&mut b, a_peer, &rc1)?; + assert_ne!(rc1.as_bytes(), rc1_broken.as_bytes()); + + // Retransmission works as designed + check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; + + // Even with a couple of poll operations in between (which clears the cache + // after a time out of two minutes…we should never hit this time out in this + // cache) + for _ in 0..4 { + poll(&mut b)?; + check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; + } + // We can even validate that the data is coming out of the cache by changing the cache + // to use our broken messages. It does not matter that these messages are cryptographically + // broken since we insert them manually into the cache + // a_peer.known_init_conf_response() + KnownInitConfResponsePtr::insert_for_request_msg( + &mut b, + a_peer, + &ic1_broken, + rc1_broken.clone(), + ); + check_retransmission(&mut b, &ic1_broken, &ic1, &rc1_broken)?; + + // Lets reset to the correct message though + KnownInitConfResponsePtr::insert_for_request_msg(&mut b, a_peer, &ic1, rc1.clone()); + + // Again, nothing changes after calling poll + poll(&mut b)?; + check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; + + // Except if we jump forward into the future past the point where the responder + // starts to initiate rekeying; in this case, the automatic time out is triggered and the cache is cleared + super::testutils::time_travel_forward(&mut b, REKEY_AFTER_TIME_RESPONDER); + + // As long as we do not call poll, everything is fine + check_retransmission(&mut b, &ic1, &ic1_broken, &rc1)?; + + // But after we do, the response is gone and can not be recreated + // since the biscuit is stale + poll(&mut b)?; + check_faulty_proc_init_conf(&mut b, &ic1); // ic1 is now effectively broken + assert!(b.peers[0].known_init_conf_response.is_none()); // The cache is gone + + Ok(()) +} From 348650d50732518e6c2c50d0b512d9009d022cda Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 20:16:28 +0200 Subject: [PATCH 154/174] chore: protocol::test should not import super::* --- rosenpass/src/protocol/test.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rosenpass/src/protocol/test.rs b/rosenpass/src/protocol/test.rs index 4869e670..1873a57c 100644 --- a/rosenpass/src/protocol/test.rs +++ b/rosenpass/src/protocol/test.rs @@ -9,14 +9,13 @@ use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::Public; use rosenpass_util::mem::DiscardResultExt; -use crate::{ - msgs::{EmptyData, Envelope, InitConf, InitHello, MsgType, RespHello, MAX_MESSAGE_LEN}, - protocol::{basic_types::MsgBuf, constants::REKEY_AFTER_TIME_RESPONDER}, -}; +use crate::msgs::{EmptyData, Envelope, InitConf, InitHello, MsgType, RespHello, MAX_MESSAGE_LEN}; use super::{ - basic_types::{SPk, SSk, SymKey}, - *, + basic_types::{MsgBuf, SPk, SSk, SymKey}, + constants::REKEY_AFTER_TIME_RESPONDER, + truncating_cast_into, truncating_cast_into_nomut, CryptoServer, HandleMsgResult, + HostIdentification, KnownInitConfResponsePtr, PeerPtr, PollResult, ProtocolVersion, }; struct VecHostIdentifier(Vec); @@ -322,7 +321,9 @@ fn cookie_reply_mechanism_responder_under_load_v03() { #[cfg(feature = "experiment_cookie_dos_mitigation")] fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion) { - use std::time::Duration; + use std::{thread::sleep, time::Duration}; + + use super::{Lifecycle, MortalExt}; setup_logging(); rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); @@ -394,7 +395,7 @@ fn cookie_reply_mechanism_responder_under_load(protocol_version: ProtocolVersion break a.retransmit_handshake(peer, &mut *a_to_b_buf).unwrap(); } PollResult::Sleep(time) => { - std::thread::sleep(Duration::from_secs_f64(time)); + sleep(Duration::from_secs_f64(time)); } _ => {} } From f33c3a6928c57cde4acbc88adfebd283133f239a Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 20:07:03 +0200 Subject: [PATCH 155/174] chore: Split protocol testutils into own file --- rosenpass/src/protocol/mod.rs | 1 + rosenpass/src/protocol/protocol.rs | 43 -------------------------- rosenpass/src/protocol/testutils.rs | 48 +++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 43 deletions(-) create mode 100644 rosenpass/src/protocol/testutils.rs diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index da9ddc50..eb209478 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -79,6 +79,7 @@ pub use build_crypto_server::*; pub mod basic_types; pub mod constants; +pub mod testutils; pub mod timing; #[allow(clippy::module_inception)] diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index a4a7798b..b36ee6d2 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -3963,46 +3963,3 @@ pub fn truncating_cast_into_nomut( ) -> Result, RosenpassError> { Ref::new(&buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch) } - -pub mod testutils { - use std::ops::DerefMut; - - use super::*; - - /// Helper for tests and examples - pub struct ServerForTesting { - pub peer: PeerPtr, - pub peer_keys: (SSk, SPk), - pub srv: CryptoServer, - } - - /// TODO: Document that the protocol version is only used for creating the peer for testing - impl ServerForTesting { - pub fn new(protocol_version: ProtocolVersion) -> anyhow::Result { - let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); - StaticKem.keygen(sskm.secret_mut(), spkm.deref_mut())?; - let mut srv = CryptoServer::new(sskm, spkm); - - let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); - StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; - let peer = srv.add_peer(None, spkt.clone(), protocol_version)?; - - let peer_keys = (sskt, spkt); - Ok(ServerForTesting { - peer, - peer_keys, - srv, - }) - } - - pub fn tuple(self) -> (PeerPtr, (SSk, SPk), CryptoServer) { - (self.peer, self.peer_keys, self.srv) - } - } - - /// Time travel forward in time - pub fn time_travel_forward(srv: &mut CryptoServer, secs: f64) { - let dur = std::time::Duration::from_secs_f64(secs); - srv.timebase.0 = srv.timebase.0.checked_sub(dur).unwrap(); - } -} diff --git a/rosenpass/src/protocol/testutils.rs b/rosenpass/src/protocol/testutils.rs new file mode 100644 index 00000000..2cc85a6f --- /dev/null +++ b/rosenpass/src/protocol/testutils.rs @@ -0,0 +1,48 @@ +//! Helpers used in tests + +use std::ops::DerefMut; + +use rosenpass_cipher_traits::primitives::Kem; +use rosenpass_ciphers::StaticKem; + +use super::{ + basic_types::{SPk, SSk}, + CryptoServer, PeerPtr, ProtocolVersion, +}; + +/// Helper for tests and examples +pub struct ServerForTesting { + pub peer: PeerPtr, + pub peer_keys: (SSk, SPk), + pub srv: CryptoServer, +} + +/// TODO: Document that the protocol version is only used for creating the peer for testing +impl ServerForTesting { + pub fn new(protocol_version: ProtocolVersion) -> anyhow::Result { + let (mut sskm, mut spkm) = (SSk::zero(), SPk::zero()); + StaticKem.keygen(sskm.secret_mut(), spkm.deref_mut())?; + let mut srv = CryptoServer::new(sskm, spkm); + + let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); + StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; + let peer = srv.add_peer(None, spkt.clone(), protocol_version)?; + + let peer_keys = (sskt, spkt); + Ok(ServerForTesting { + peer, + peer_keys, + srv, + }) + } + + pub fn tuple(self) -> (PeerPtr, (SSk, SPk), CryptoServer) { + (self.peer, self.peer_keys, self.srv) + } +} + +/// Time travel forward in time +pub fn time_travel_forward(srv: &mut CryptoServer, secs: f64) { + let dur = std::time::Duration::from_secs_f64(secs); + srv.timebase.0 = srv.timebase.0.checked_sub(dur).unwrap(); +} From 4e77e67f1085cbf3802f930dc1aeb4f0185130ea Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 20:10:03 +0200 Subject: [PATCH 156/174] chore: Split utils for zerocopy in protocol into own file --- rosenpass/src/protocol/mod.rs | 1 + rosenpass/src/protocol/protocol.rs | 15 +-------------- rosenpass/src/protocol/test.rs | 5 +++-- rosenpass/src/protocol/zerocopy.rs | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 rosenpass/src/protocol/zerocopy.rs diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index eb209478..93f9e861 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -81,6 +81,7 @@ pub mod basic_types; pub mod constants; pub mod testutils; pub mod timing; +pub mod zerocopy; #[allow(clippy::module_inception)] mod protocol; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index b36ee6d2..2464b062 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -45,6 +45,7 @@ use super::constants::{ RETRANSMIT_DELAY_GROWTH, RETRANSMIT_DELAY_JITTER, }; use super::timing::{has_happened, Timing, BCE, UNENDING}; +use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; #[cfg(feature = "trace_bench")] use rosenpass_util::trace_bench::Trace as _; @@ -3949,17 +3950,3 @@ impl CryptoServer { } } } - -/// Used to parse a network message using [zerocopy] -pub fn truncating_cast_into( - buf: &mut [u8], -) -> Result, RosenpassError> { - Ref::new(&mut buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch) -} - -/// Used to parse a network message using [zerocopy], mutably -pub fn truncating_cast_into_nomut( - buf: &[u8], -) -> Result, RosenpassError> { - Ref::new(&buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch) -} diff --git a/rosenpass/src/protocol/test.rs b/rosenpass/src/protocol/test.rs index 1873a57c..f62775ba 100644 --- a/rosenpass/src/protocol/test.rs +++ b/rosenpass/src/protocol/test.rs @@ -14,8 +14,9 @@ use crate::msgs::{EmptyData, Envelope, InitConf, InitHello, MsgType, RespHello, use super::{ basic_types::{MsgBuf, SPk, SSk, SymKey}, constants::REKEY_AFTER_TIME_RESPONDER, - truncating_cast_into, truncating_cast_into_nomut, CryptoServer, HandleMsgResult, - HostIdentification, KnownInitConfResponsePtr, PeerPtr, PollResult, ProtocolVersion, + zerocopy::{truncating_cast_into, truncating_cast_into_nomut}, + CryptoServer, HandleMsgResult, HostIdentification, KnownInitConfResponsePtr, PeerPtr, + PollResult, ProtocolVersion, }; struct VecHostIdentifier(Vec); diff --git a/rosenpass/src/protocol/zerocopy.rs b/rosenpass/src/protocol/zerocopy.rs new file mode 100644 index 00000000..3e8639b6 --- /dev/null +++ b/rosenpass/src/protocol/zerocopy.rs @@ -0,0 +1,21 @@ +//! Helpers for working with the zerocopy crate + +use std::mem::size_of; + +use zerocopy::{FromBytes, Ref}; + +use crate::RosenpassError; + +/// Used to parse a network message using [zerocopy] +pub fn truncating_cast_into( + buf: &mut [u8], +) -> Result, RosenpassError> { + Ref::new(&mut buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch) +} + +/// Used to parse a network message using [zerocopy], mutably +pub fn truncating_cast_into_nomut( + buf: &[u8], +) -> Result, RosenpassError> { + Ref::new(&buf[..size_of::()]).ok_or(RosenpassError::BufferSizeMismatch) +} From bdaedc4e2ad4d9d5ba0fd2d2ff45d53501e7ddff Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 20:33:39 +0200 Subject: [PATCH 157/174] chore: CookieStore split from protocol.rs --- rosenpass/src/protocol/cookies.rs | 98 ++++++++++++++++++++++++++++++ rosenpass/src/protocol/mod.rs | 1 + rosenpass/src/protocol/protocol.rs | 86 ++++---------------------- 3 files changed, 110 insertions(+), 75 deletions(-) create mode 100644 rosenpass/src/protocol/cookies.rs diff --git a/rosenpass/src/protocol/cookies.rs b/rosenpass/src/protocol/cookies.rs new file mode 100644 index 00000000..b3c54139 --- /dev/null +++ b/rosenpass/src/protocol/cookies.rs @@ -0,0 +1,98 @@ +//! Cryptographic key management for cookies and biscuits used in the protocol +//! +//! Cookies in general are conceptually similar to browser cookies; +//! i.e. mechanisms to store information in the party connected to via network. +//! +//! In our case specifically we refer to any mechanisms in the Rosenpass protocol +//! where a peer stores some information in the other party that is cryptographically +//! protected using a temporary, randomly generated key. This file contains the mechanisms +//! used to store the secret keys. +//! +//! We have two cookie-mechanisms in particular: +//! +//! - Rosenpass "biscuits" — the mechanism used to make sure the Rosenpass protocol is stateless +//! with respect to the responder +//! - WireGuard's cookie mechanism to enable proof of IP ownership; Rosenpass has experimental +//! support for this mechanism +//! +//! The CookieStore type is also used to store cookie secrets sent from the responder to the +//! initiator. This is a bad design and we should separate out this functionality. +//! +//! TODO: CookieStore should not be used for cookie secrets sent from responder to initiator. +//! TODO: Move cookie lifetime management functionality into here + +use rosenpass_ciphers::KEY_LEN; +use rosenpass_secret_memory::Secret; + +use super::{constants::COOKIE_SECRET_LEN, timing::Timing}; + +/// Container for storing cookie secrets like [BiscuitKey] or [CookieSecret]. +/// +/// This is really just a secret key and a time stamp of creation. Concrete +/// usages (such as for the biscuit key) impose a time limit about how long +/// a key can be used and the time of creation is used to impose that time limit. +/// +/// # Examples +/// +/// ``` +/// use rosenpass_util::time::Timebase; +/// use rosenpass::protocol::{timing::BCE, basic_types::SymKey, cookies::CookieStore}; +/// +/// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); +/// +/// let fixed_secret = SymKey::random(); +/// let timebase = Timebase::default(); +/// +/// let mut store = CookieStore::<32>::new(); +/// assert_ne!(store.value.secret(), SymKey::zero().secret()); +/// assert_eq!(store.created_at, BCE); +/// +/// let time_before_call = timebase.now(); +/// store.update(&timebase, fixed_secret.secret()); +/// assert_eq!(store.value.secret(), fixed_secret.secret()); +/// assert!(store.created_at < timebase.now()); +/// assert!(store.created_at > time_before_call); +/// +/// // Same as new() +/// store.erase(); +/// assert_ne!(store.value.secret(), SymKey::zero().secret()); +/// assert_eq!(store.created_at, BCE); +/// +/// let secret_before_call = store.value.clone(); +/// let time_before_call = timebase.now(); +/// store.randomize(&timebase); +/// assert_ne!(store.value.secret(), secret_before_call.secret()); +/// assert!(store.created_at < timebase.now()); +/// assert!(store.created_at > time_before_call); +/// ``` +#[derive(Debug)] +pub struct CookieStore { + /// Time of creation of the secret key + pub created_at: Timing, + /// The secret key + pub value: Secret, +} + +/// Stores cookie secret, which is used to create a rotating the cookie value +/// +/// Concrete value is in [super::CryptoServer::cookie_secrets]. +/// +/// The pointer type is [super::ServerCookieSecretPtr]. +pub type CookieSecret = CookieStore; + +/// Storage for our biscuit keys. +/// +/// The biscuit keys encrypt what we call "biscuits". +/// These biscuits contain the responder state for a particular handshake. By moving +/// state into these biscuits, we make sure the responder is stateless. +/// +/// A Biscuit is like a fancy cookie. To avoid state disruption attacks, +/// the responder doesn't store state. Instead the state is stored in a +/// Biscuit, that is encrypted using the [BiscuitKey] which is only known to +/// the Responder. Thus secrecy of the Responder state is not violated, still +/// the responder can avoid storing this state. +/// +/// Concrete value is in [super::CryptoServer::biscuit_keys]. +/// +/// The pointer type is [super::BiscuitKeyPtr]. +pub type BiscuitKey = CookieStore; diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index 93f9e861..f7865f1a 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -79,6 +79,7 @@ pub use build_crypto_server::*; pub mod basic_types; pub mod constants; +pub mod cookies; pub mod testutils; pub mod timing; pub mod zerocopy; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 2464b062..2155b31d 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -22,7 +22,7 @@ use rosenpass_cipher_traits::primitives::{ Aead as _, AeadWithNonceInCiphertext, Kem, KeyedHashInstance, }; use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace}; -use rosenpass_ciphers::{Aead, EphemeralKem, KeyedHash, StaticKem, XAead, KEY_LEN}; +use rosenpass_ciphers::{Aead, EphemeralKem, KeyedHash, StaticKem, XAead}; use rosenpass_constant_time as constant_time; use rosenpass_secret_memory::{Public, Secret}; use rosenpass_to::{ops::copy_slice, To}; @@ -35,9 +35,6 @@ use rosenpass_util::{ use crate::{hash_domains, msgs::*, RosenpassError}; -use super::basic_types::{ - BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, SPk, SSk, SessionId, SymKey, XAEADNonce, -}; use super::constants::{ BISCUIT_EPOCH, COOKIE_SECRET_EPOCH, COOKIE_SECRET_LEN, COOKIE_VALUE_LEN, PEER_COOKIE_VALUE_EPOCH, REJECT_AFTER_TIME, REKEY_AFTER_TIME_INITIATOR, @@ -46,6 +43,14 @@ use super::constants::{ }; use super::timing::{has_happened, Timing, BCE, UNENDING}; use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; +use super::{ + basic_types::{ + BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, SPk, SSk, SessionId, SymKey, XAEADNonce, + }, + cookies::BiscuitKey, +}; + +use super::cookies::{CookieSecret, CookieStore}; #[cfg(feature = "trace_bench")] use rosenpass_util::trace_bench::Trace as _; @@ -134,77 +139,6 @@ pub struct CryptoServer { pub cookie_secrets: [CookieSecret; 2], } -/// Container for storing cookie secrets like [BiscuitKey] or [CookieSecret]. -/// -/// This is really just a secret key and a time stamp of creation. Concrete -/// usages (such as for the biscuit key) impose a time limit about how long -/// a key can be used and the time of creation is used to impose that time limit. -/// -/// # Examples -/// -/// ``` -/// use rosenpass_util::time::Timebase; -/// use rosenpass::protocol::{timing::BCE, basic_types::SymKey, CookieStore}; -/// -/// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); -/// -/// let fixed_secret = SymKey::random(); -/// let timebase = Timebase::default(); -/// -/// let mut store = CookieStore::<32>::new(); -/// assert_ne!(store.value.secret(), SymKey::zero().secret()); -/// assert_eq!(store.created_at, BCE); -/// -/// let time_before_call = timebase.now(); -/// store.update(&timebase, fixed_secret.secret()); -/// assert_eq!(store.value.secret(), fixed_secret.secret()); -/// assert!(store.created_at < timebase.now()); -/// assert!(store.created_at > time_before_call); -/// -/// // Same as new() -/// store.erase(); -/// assert_ne!(store.value.secret(), SymKey::zero().secret()); -/// assert_eq!(store.created_at, BCE); -/// -/// let secret_before_call = store.value.clone(); -/// let time_before_call = timebase.now(); -/// store.randomize(&timebase); -/// assert_ne!(store.value.secret(), secret_before_call.secret()); -/// assert!(store.created_at < timebase.now()); -/// assert!(store.created_at > time_before_call); -/// ``` -#[derive(Debug)] -pub struct CookieStore { - /// Time of creation of the secret key - pub created_at: Timing, - /// The secret key - pub value: Secret, -} - -/// Stores cookie secret, which is used to create a rotating the cookie value -/// -/// Concrete value is in [CryptoServer::cookie_secrets]. -/// -/// The pointer type is [ServerCookieSecretPtr]. -pub type CookieSecret = CookieStore; - -/// Storage for our biscuit keys. -/// -/// The biscuit keys encrypt what we call "biscuits". -/// These biscuits contain the responder state for a particular handshake. By moving -/// state into these biscuits, we make sure the responder is stateless. -/// -/// A Biscuit is like a fancy cookie. To avoid state disruption attacks, -/// the responder doesn't store state. Instead the state is stored in a -/// Biscuit, that is encrypted using the [BiscuitKey] which is only known to -/// the Responder. Thus secrecy of the Responder state is not violated, still -/// the responder can avoid storing this state. -/// -/// Concrete value is in [CryptoServer::biscuit_keys]. -/// -/// The pointer type is [BiscuitKeyPtr]. -pub type BiscuitKey = CookieStore; - /// We maintain various indices in [CryptoServer::index], mapping some key to a particular /// [PeerNo], i.e. to an index in [CryptoServer::peers]. These are the possible index key. #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] @@ -480,6 +414,8 @@ pub struct InitiatorHandshake { /// mechanism. /// /// TODO: cookie_value should be an Option<_> + /// TODO: This should not use CookieStore, which exists to store randomly generated cookie + /// secrets /// /// This value seems to default-initialized with a random value according to /// [Self::zero_with_timestamp], which does not really make sense since this From 5ced547a075188de5d53fe4b5b0b3260cd3d8bc8 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Sun, 1 Jun 2025 20:52:21 +0200 Subject: [PATCH 158/174] chore: PeerIndex split from protocol.rs --- rosenpass/src/protocol/constants.rs | 2 +- rosenpass/src/protocol/index.rs | 45 ++++++++++++++++++ rosenpass/src/protocol/mod.rs | 1 + rosenpass/src/protocol/protocol.rs | 72 +++++++++-------------------- 4 files changed, 70 insertions(+), 50 deletions(-) create mode 100644 rosenpass/src/protocol/index.rs diff --git a/rosenpass/src/protocol/constants.rs b/rosenpass/src/protocol/constants.rs index c497e0e9..8478cdd9 100644 --- a/rosenpass/src/protocol/constants.rs +++ b/rosenpass/src/protocol/constants.rs @@ -20,9 +20,9 @@ pub const REKEY_AFTER_TIME_INITIATOR: Timing = 130.0; /// Time after which either party rejects the current key. /// /// At this point a new key should have been negotiated. +/// /// Rejection happens 50-60 seconds after key renegotiation /// to allow for a graceful handover. -/// /// From the wireguard paper: rekey every two minutes, /// discard the key if no rekey is achieved within three pub const REJECT_AFTER_TIME: Timing = 180.0; diff --git a/rosenpass/src/protocol/index.rs b/rosenpass/src/protocol/index.rs new file mode 100644 index 00000000..9f3556be --- /dev/null +++ b/rosenpass/src/protocol/index.rs @@ -0,0 +1,45 @@ +//! Quick lookup of values in [super::CryptoServer] + +use std::collections::HashMap; + +use super::basic_types::{PeerId, PeerNo, SessionId}; +use super::KnownResponseHash; + +/// Maps various keys to peer (numbers). +/// +/// See: +/// - [super::CryptoServer::index] +/// - [super::CryptoServer::peers] +/// - [PeerNo] +/// - [super::PeerPtr] +/// - [super::Peer] +pub type PeerIndex = HashMap; + +/// We maintain various indices in [super::CryptoServer::index], mapping some key to a particular +/// [PeerNo], i.e. to an index in [super::CryptoServer::peers]. These are the possible index key. +#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub enum PeerIndexKey { + /// Lookup of a particular peer given the [PeerId], i.e. a value derived from the peers public + /// key as created by [super::CryptoServer::pidm] or [super::Peer::pidt]. + /// + /// The peer id is used by the initiator to tell the responder about its identity in + /// [crate::msgs::InitHello]. + /// + /// See also the pointer types [super::PeerPtr]. + Peer(PeerId), + /// Lookup of a particular session id. + /// + /// This is used to look up both established sessions (see + /// [super::CryptoServer::lookup_session]) and ongoing handshakes (see [super::CryptoServer::lookup_handshake]). + /// + /// Lookup of a peer to get an established session or a handshake is sufficient, because a peer + /// contains a limited number of sessions and handshakes ([super::Peer::session] and [super::Peer::handshake] respectively). + /// + /// See also the pointer types [super::IniHsPtr] and [super::SessionPtr]. + Sid(SessionId), + /// Lookup of a cached response ([crate::msgs::Envelope]<[crate::msgs::EmptyData]>) to an [crate::msgs::InitConf] (i.e. + /// [crate::msgs::Envelope]<[crate::msgs::InitConf]>) message. + /// + /// See [super::KnownInitConfResponsePtr] on how this value is maintained. + KnownInitConfResponse(KnownResponseHash), +} diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index f7865f1a..cb14c3b1 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -80,6 +80,7 @@ pub use build_crypto_server::*; pub mod basic_types; pub mod constants; pub mod cookies; +pub mod index; pub mod testutils; pub mod timing; pub mod zerocopy; diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 2155b31d..8300be69 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -41,6 +41,7 @@ use super::constants::{ REKEY_AFTER_TIME_RESPONDER, RETRANSMIT_DELAY_BEGIN, RETRANSMIT_DELAY_END, RETRANSMIT_DELAY_GROWTH, RETRANSMIT_DELAY_JITTER, }; +use super::index::{PeerIndex, PeerIndexKey}; use super::timing::{has_happened, Timing, BCE, UNENDING}; use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; use super::{ @@ -105,12 +106,12 @@ pub struct CryptoServer { /// List of peers and their session and handshake states pub peers: Vec, - /// Index into the list of peers. See [IndexKey] for details. - pub index: HashMap, + /// Index into the list of peers. See [PeerIndexKey] for details. + pub index: PeerIndex, /// Hash key for known responder confirmation responses. /// /// These hashes are then used for lookups in [Self::index] using - /// the [IndexKey::KnownInitConfResponse] enum case. + /// the [PeerIndexKey::KnownInitConfResponse] enum case. /// /// This is used to allow for retransmission of responder confirmation (i.e. /// [Envelope]<[EmptyData]>) messages in response to [Envelope]<[InitConf]> @@ -139,35 +140,6 @@ pub struct CryptoServer { pub cookie_secrets: [CookieSecret; 2], } -/// We maintain various indices in [CryptoServer::index], mapping some key to a particular -/// [PeerNo], i.e. to an index in [CryptoServer::peers]. These are the possible index key. -#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub enum IndexKey { - /// Lookup of a particular peer given the [PeerId], i.e. a value derived from the peers public - /// key as created by [CryptoServer::pidm] or [Peer::pidt]. - /// - /// The peer id is used by the initiator to tell the responder about its identity in - /// [crate::msgs::InitHello]. - /// - /// See also the pointer types [PeerPtr]. - Peer(PeerId), - /// Lookup of a particular session id. - /// - /// This is used to look up both established sessions (see - /// [CryptoServer::lookup_session]) and ongoing handshakes (see [CryptoServer::lookup_handshake]). - /// - /// Lookup of a peer to get an established session or a handshake is sufficient, because a peer - /// contains a limited number of sessions and handshakes ([Peer::session] and [Peer::handshake] respectively). - /// - /// See also the pointer types [IniHsPtr] and [SessionPtr]. - Sid(SessionId), - /// Lookup of a cached response ([Envelope]<[EmptyData]>) to an [InitConf] (i.e. - /// [Envelope]<[InitConf]>) message. - /// - /// See [KnownInitConfResponsePtr] on how this value is maintained. - KnownInitConfResponse(KnownResponseHash), -} - /// Specifies the protocol version used by a peer. #[derive(Debug, Clone)] pub enum ProtocolVersion { @@ -199,7 +171,7 @@ impl From for ProtocolVersion { /// Peers generally live in [CryptoServer::peers]. [PeerNo] captures an array /// into this field and [PeerPtr] is a wrapper around a [PeerNo] imbued with /// peer specific functionality. [CryptoServer::index] contains a list of lookup-keys -/// for retrieving peers using various keys (see [IndexKey]). +/// for retrieving peers using various keys (see [PeerIndexKey]). /// /// # Examples /// @@ -257,7 +229,7 @@ pub struct Peer { pub biscuit_used: BiscuitId, /// The last established session /// - /// This is indexed though [IndexKey::Sid]. + /// This is indexed though [PeerIndexKey::Sid]. pub session: Option, /// Ongoing handshake, in initiator mode. /// @@ -265,7 +237,7 @@ pub struct Peer { /// because the state is stored inside a [Biscuit] to make sure the responder /// is stateless. /// - /// This is indexed though [IndexKey::Sid]. + /// This is indexed though [PeerIndexKey::Sid]. pub handshake: Option, /// Used to make sure that the same [PollResult::SendInitiation] event is never issued twice. /// @@ -277,7 +249,7 @@ pub struct Peer { /// [Envelope]<[EmptyData]>. /// /// Upon reception of an InitConf message, [CryptoServer::handle_msg] first checks - /// if a cached response exists through [IndexKey::KnownInitConfResponse]. If one exists, + /// if a cached response exists through [PeerIndexKey::KnownInitConfResponse]. If one exists, /// then this field must be set to [Option::Some] and the cached response is returned. /// /// This allows us to perform retransmission for the purpose of dealing with packet loss @@ -472,14 +444,14 @@ fn known_response_format() { pub type KnownInitConfResponse = KnownResponse; /// The type used to represent the hash of a known response -/// in the context of [KnownResponse]/[IndexKey::KnownInitConfResponse] +/// in the context of [KnownResponse]/[PeerIndexKey::KnownInitConfResponse] pub type KnownResponseHash = Public<16>; /// Object that produces [KnownResponseHash]. /// /// Merely a key plus some utility functions. /// -/// See [IndexKey::KnownInitConfResponse] and [KnownResponse::request_mac] +/// See [PeerIndexKey::KnownInitConfResponse] and [KnownResponse::request_mac] /// /// # Examples /// @@ -1051,7 +1023,7 @@ impl KnownInitConfResponsePtr { pub fn remove(&self, srv: &mut CryptoServer) -> Option { let peer = self.peer(); let val = peer.get_mut(srv).known_init_conf_response.take()?; - let lookup_key = IndexKey::KnownInitConfResponse(val.request_mac); + let lookup_key = PeerIndexKey::KnownInitConfResponse(val.request_mac); srv.index.remove(&lookup_key).unwrap(); Some(val) } @@ -1070,7 +1042,7 @@ impl KnownInitConfResponsePtr { pub fn insert(&self, srv: &mut CryptoServer, known_response: KnownInitConfResponse) { self.remove(srv).discard_result(); - let index_key = IndexKey::KnownInitConfResponse(known_response.request_mac); + let index_key = PeerIndexKey::KnownInitConfResponse(known_response.request_mac); self.peer().get_mut(srv).known_init_conf_response = Some(known_response); // There is a question here whether we should just discard the result…or panic if the @@ -1171,9 +1143,9 @@ impl KnownInitConfResponsePtr { /// Calculate an appropriate index key for `req` /// - /// Merely forwards [Self::index_key_for_msg] and wraps the result in [IndexKey::KnownInitConfResponse] - pub fn index_key_for_msg(srv: &CryptoServer, req: &Envelope) -> IndexKey { - Self::index_key_hash_for_msg(srv, req).apply(IndexKey::KnownInitConfResponse) + /// Merely forwards [Self::index_key_for_msg] and wraps the result in [PeerIndexKey::KnownInitConfResponse] + pub fn index_key_for_msg(srv: &CryptoServer, req: &Envelope) -> PeerIndexKey { + Self::index_key_hash_for_msg(srv, req).apply(PeerIndexKey::KnownInitConfResponse) } } @@ -1291,7 +1263,7 @@ impl CryptoServer { }; let peerid = peer.pidt()?; let peerno = self.peers.len(); - match self.index.entry(IndexKey::Peer(peerid)) { + match self.index.entry(PeerIndexKey::Peer(peerid)) { Occupied(_) => bail!( "Cannot insert peer with id {:?}; peer with this id already registered.", peerid @@ -1315,7 +1287,7 @@ impl CryptoServer { /// To rgister a session, you should generally use [SessionPtr::insert] or [IniHsPtr::insert] /// instead of this, more lower level function. pub fn register_session(&mut self, id: SessionId, peer: PeerPtr) -> Result<()> { - match self.index.entry(IndexKey::Sid(id)) { + match self.index.entry(PeerIndexKey::Sid(id)) { Occupied(p) if PeerPtr(*p.get()) == peer => {} // Already registered Occupied(_) => bail!("Cannot insert session with id {:?}; id is in use.", id), Vacant(e) => { @@ -1334,7 +1306,7 @@ impl CryptoServer { /// To unregister a session, you should generally use [SessionPtr::take] or [IniHsPtr::take] /// instead of this, more lower level function. pub fn unregister_session(&mut self, id: SessionId) { - self.index.remove(&IndexKey::Sid(id)); + self.index.remove(&PeerIndexKey::Sid(id)); } /// Unregister a session previously registered using [Self::register_session], @@ -1363,7 +1335,9 @@ impl CryptoServer { /// This function is used in cryptographic message processing /// [CryptoServer::handle_init_hello], and [HandshakeState::load_biscuit] pub fn find_peer(&self, id: PeerId) -> Option { - self.index.get(&IndexKey::Peer(id)).map(|no| PeerPtr(*no)) + self.index + .get(&PeerIndexKey::Peer(id)) + .map(|no| PeerPtr(*no)) } /// Look up a handshake given its session id [HandshakeState::sidi] @@ -1371,7 +1345,7 @@ impl CryptoServer { /// This is called `lookup_session` in [whitepaper](https://rosenpass.eu/whitepaper.pdf). pub fn lookup_handshake(&self, id: SessionId) -> Option { self.index - .get(&IndexKey::Sid(id)) // lookup the session in the index + .get(&PeerIndexKey::Sid(id)) // lookup the session in the index .map(|no| IniHsPtr(*no)) // convert to peer pointer .filter(|hsptr| { hsptr @@ -1387,7 +1361,7 @@ impl CryptoServer { /// This is called `lookup_session` in [whitepaper](https://rosenpass.eu/whitepaper.pdf). pub fn lookup_session(&self, id: SessionId) -> Option { self.index - .get(&IndexKey::Sid(id)) + .get(&PeerIndexKey::Sid(id)) .map(|no| SessionPtr(*no)) .filter(|sptr| { sptr.get(self) From 4deee59e9046b0962f3822e22c0dfe3079ece76d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 25 Jun 2025 19:11:15 +0200 Subject: [PATCH 159/174] chore: Restructure imports in various places --- rosenpass/benches/handshake.rs | 11 +-- rosenpass/benches/trace_handshake.rs | 11 ++- rosenpass/src/app_server.rs | 67 ++++++------------- rosenpass/src/config.rs | 18 +++-- rosenpass/src/protocol/build_crypto_server.rs | 24 ++++--- rosenpass/src/protocol/mod.rs | 3 + rosenpass/src/protocol/protocol.rs | 18 +++-- rosenpass/src/protocol/test.rs | 6 +- rosenpass/tests/app_server_example.rs | 19 ++---- rosenpass/tests/poll_example.rs | 10 ++- rp/src/exchange.rs | 16 ++--- 11 files changed, 84 insertions(+), 119 deletions(-) diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index a47e9a65..e04c71c4 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -1,14 +1,15 @@ -use anyhow::Result; -use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; -use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion}; use std::ops::DerefMut; +use anyhow::Result; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + 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; +use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion}; + fn handle( tx: &mut CryptoServer, msgb: &mut MsgBuf, diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs index b83c1356..37ad1a68 100644 --- a/rosenpass/benches/trace_handshake.rs +++ b/rosenpass/benches/trace_handshake.rs @@ -1,12 +1,9 @@ -use std::{ - collections::HashMap, - hint::black_box, - io::{self, Write}, - ops::DerefMut, - time::{Duration, Instant}, -}; +use std::io::{self, Write}; +use std::time::{Duration, Instant}; +use std::{collections::HashMap, hint::black_box, ops::DerefMut}; use anyhow::Result; + use libcrux_test_utils::tracing::{EventType, Trace as _}; use rosenpass_cipher_traits::primitives::Kem; diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 8d3fd0a6..02d4a5b8 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -1,57 +1,32 @@ /// 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 std::collections::{HashMap, VecDeque}; +use std::io::{stdout, ErrorKind, Write}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +use std::time::{Duration, Instant}; +use std::{cell::Cell, fmt::Debug, io, path::PathBuf, slice}; -use anyhow::Context; -use anyhow::Result; +use anyhow::{bail, Context, Result}; use derive_builder::Builder; use log::{error, info, warn}; -use mio::Interest; -use mio::Token; -use rosenpass_secret_memory::Public; -use rosenpass_secret_memory::Secret; -use rosenpass_util::build::ConstructionSite; -use rosenpass_util::file::StoreValueB64; -use rosenpass_util::functional::run; -use rosenpass_util::functional::ApplyExt; -use rosenpass_util::io::IoResultKindHintExt; -use rosenpass_util::io::SubstituteForIoErrorKindExt; -use rosenpass_util::option::SomeExt; -use rosenpass_util::result::OkExt; -use rosenpass_wireguard_broker::WireguardBrokerMio; -use rosenpass_wireguard_broker::{WireguardBrokerCfg, WG_KEY_LEN}; +use mio::{Interest, Token}; use zerocopy::AsBytes; -use std::cell::Cell; - -use std::collections::HashMap; -use std::collections::VecDeque; -use std::fmt::Debug; -use std::io; -use std::io::stdout; -use std::io::ErrorKind; -use std::io::Write; -use std::net::Ipv4Addr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV4; -use std::net::SocketAddrV6; -use std::net::ToSocketAddrs; -use std::path::PathBuf; -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::{ - config::Verbosity, - protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}, - protocol::{timing::Timing, CryptoServer, PeerPtr}, -}; use rosenpass_util::attempt; -use rosenpass_util::b64::B64Display; +use rosenpass_util::functional::{run, ApplyExt}; +use rosenpass_util::io::{IoResultKindHintExt, SubstituteForIoErrorKindExt}; +use rosenpass_util::{ + b64::B64Display, build::ConstructionSite, file::StoreValueB64, option::SomeExt, result::OkExt, +}; + +use rosenpass_secret_memory::{Public, Secret}; +use rosenpass_wireguard_broker::{WireguardBrokerCfg, WireguardBrokerMio, WG_KEY_LEN}; + +use crate::config::{ProtocolVersion, Verbosity}; + +use crate::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use crate::protocol::timing::Timing; +use crate::protocol::{BuildCryptoServer, CryptoServer, HostIdentification, PeerPtr}; /// The maximum size of a base64 encoded symmetric key (estimate) pub const MAX_B64_KEY_SIZE: usize = 32 * 5 / 3; diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 330ccea4..9f79cce6 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -7,20 +7,18 @@ //! - TODO: support `~` in //! - TODO: provide tooling to create config file from shell -use crate::protocol::basic_types::{SPk, SSk}; -use rosenpass_util::file::LoadValue; -use std::{ - collections::HashSet, - fs, - io::Write, - net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, - path::{Path, PathBuf}, -}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; +use std::path::{Path, PathBuf}; +use std::{collections::HashSet, fs, io::Write}; use anyhow::{bail, ensure}; -use rosenpass_util::file::{fopen_w, Visibility}; + use serde::{Deserialize, Serialize}; +use rosenpass_util::file::{fopen_w, LoadValue, Visibility}; + +use crate::protocol::basic_types::{SPk, SSk}; + use crate::app_server::AppServer; #[cfg(feature = "experiment_api")] diff --git a/rosenpass/src/protocol/build_crypto_server.rs b/rosenpass/src/protocol/build_crypto_server.rs index f3ded3d7..73243b3d 100644 --- a/rosenpass/src/protocol/build_crypto_server.rs +++ b/rosenpass/src/protocol/build_crypto_server.rs @@ -1,12 +1,12 @@ +use thiserror::Error; + +use rosenpass_util::mem::{DiscardResultExt, SwapWithDefaultExt}; +use rosenpass_util::{build::Build, result::ensure_or}; + +use crate::config::ProtocolVersion; + use super::basic_types::{SPk, SSk, SymKey}; use super::{CryptoServer, PeerPtr}; -use crate::config::ProtocolVersion; -use rosenpass_util::{ - build::Build, - mem::{DiscardResultExt, SwapWithDefaultExt}, - result::ensure_or, -}; -use thiserror::Error; #[derive(Debug, Clone)] /// A pair of matching public/secret keys used to launch the crypto server. @@ -386,16 +386,18 @@ impl BuildCryptoServer { /// Extracting the server configuration from a builder: /// /// ```rust - /// // We have to define the security policy before using Secrets. + /// use rosenpass_util::build::Build; + /// use rosenpass_secret_memory::secret_policy_use_only_malloc_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::basic_types::{SymKey, SPk}; /// use rosenpass::protocol::{BuildCryptoServer, Keypair}; /// + /// // We have to define the security policy before using Secrets. + /// secret_policy_use_only_malloc_secrets(); + /// /// let keypair = Keypair::random(); /// let peer_pk = SPk::random(); /// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![]); diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index cb14c3b1..72bead00 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -24,11 +24,14 @@ //! //! ``` //! use std::ops::DerefMut; +//! //! use rosenpass_secret_memory::policy::*; //! use rosenpass_cipher_traits::primitives::Kem; //! use rosenpass_ciphers::StaticKem; +//! //! use rosenpass::protocol::basic_types::{SSk, SPk, MsgBuf, SymKey}; //! use rosenpass::protocol::{PeerPtr, CryptoServer}; +//! //! # fn main() -> anyhow::Result<()> { //! // Set security policy for storing secrets //! diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index 8300be69..a40f83db 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -35,23 +35,19 @@ use rosenpass_util::{ use crate::{hash_domains, msgs::*, RosenpassError}; +use super::basic_types::{ + BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, SPk, SSk, SessionId, SymKey, XAEADNonce, +}; use super::constants::{ BISCUIT_EPOCH, COOKIE_SECRET_EPOCH, COOKIE_SECRET_LEN, COOKIE_VALUE_LEN, PEER_COOKIE_VALUE_EPOCH, REJECT_AFTER_TIME, REKEY_AFTER_TIME_INITIATOR, REKEY_AFTER_TIME_RESPONDER, RETRANSMIT_DELAY_BEGIN, RETRANSMIT_DELAY_END, RETRANSMIT_DELAY_GROWTH, RETRANSMIT_DELAY_JITTER, }; +use super::cookies::{BiscuitKey, CookieSecret, CookieStore}; use super::index::{PeerIndex, PeerIndexKey}; use super::timing::{has_happened, Timing, BCE, UNENDING}; use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; -use super::{ - basic_types::{ - BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, SPk, SSk, SessionId, SymKey, XAEADNonce, - }, - cookies::BiscuitKey, -}; - -use super::cookies::{CookieSecret, CookieStore}; #[cfg(feature = "trace_bench")] use rosenpass_util::trace_bench::Trace as _; @@ -177,11 +173,13 @@ impl From for ProtocolVersion { /// /// ``` /// use std::ops::DerefMut; -/// use rosenpass::protocol::basic_types::{SSk, SPk, SymKey}; -/// use rosenpass::protocol::{Peer, ProtocolVersion}; +/// /// use rosenpass_ciphers::StaticKem; /// use rosenpass_cipher_traits::primitives::Kem; /// +/// use rosenpass::protocol::basic_types::{SSk, SPk, SymKey}; +/// use rosenpass::protocol::{Peer, ProtocolVersion}; +/// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// /// let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); diff --git a/rosenpass/src/protocol/test.rs b/rosenpass/src/protocol/test.rs index f62775ba..c525c7e9 100644 --- a/rosenpass/src/protocol/test.rs +++ b/rosenpass/src/protocol/test.rs @@ -11,10 +11,10 @@ use rosenpass_util::mem::DiscardResultExt; use crate::msgs::{EmptyData, Envelope, InitConf, InitHello, MsgType, RespHello, MAX_MESSAGE_LEN}; +use super::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use super::constants::REKEY_AFTER_TIME_RESPONDER; +use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; use super::{ - basic_types::{MsgBuf, SPk, SSk, SymKey}, - constants::REKEY_AFTER_TIME_RESPONDER, - zerocopy::{truncating_cast_into, truncating_cast_into_nomut}, CryptoServer, HandleMsgResult, HostIdentification, KnownInitConfResponsePtr, PeerPtr, PollResult, ProtocolVersion, }; diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index d384b79c..bd5a77e5 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -1,21 +1,14 @@ -use std::{ - net::SocketAddr, - ops::DerefMut, - str::FromStr, - sync::mpsc, - thread::{self, sleep}, - time::Duration, -}; +use std::thread::{self, sleep}; +use std::{net::SocketAddr, ops::DerefMut, str::FromStr, sync::mpsc, time::Duration}; -use rosenpass::config::ProtocolVersion; -use rosenpass::{ - app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}, - protocol::basic_types::{SPk, SSk, SymKey}, -}; use rosenpass_cipher_traits::primitives::Kem; use rosenpass_ciphers::StaticKem; use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt}; +use rosenpass::app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}; +use rosenpass::config::ProtocolVersion; +use rosenpass::protocol::basic_types::{SPk, SSk, SymKey}; + #[test] fn key_exchange_with_app_server_v02() -> anyhow::Result<()> { key_exchange_with_app_server(ProtocolVersion::V02) diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index 43a4d863..62fe4f8f 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -9,12 +9,10 @@ use rosenpass_cipher_traits::primitives::Kem; use rosenpass_ciphers::StaticKem; use rosenpass_util::result::OkExt; -use rosenpass::protocol::{ - basic_types::{MsgBuf, SPk, SSk, SymKey}, - testutils::time_travel_forward, - timing::{Timing, UNENDING}, - CryptoServer, HostIdentification, PeerPtr, PollResult, ProtocolVersion, -}; +use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::testutils::time_travel_forward; +use rosenpass::protocol::timing::{Timing, UNENDING}; +use rosenpass::protocol::{CryptoServer, HostIdentification, PeerPtr, PollResult, ProtocolVersion}; // TODO: Most of the utility functions in here should probably be moved to // rosenpass::protocol::testutils; diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index e1e75447..e099b58e 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -1,15 +1,15 @@ -use anyhow::Error; +use std::{ + future::Future, net::SocketAddr, ops::DerefMut, path::PathBuf, pin::Pin, process::Command, + sync::Arc, +}; + +use anyhow::{Error, Result}; use serde::Deserialize; -use std::future::Future; -use std::ops::DerefMut; -use std::pin::Pin; -use std::sync::Arc; -use std::{net::SocketAddr, path::PathBuf, process::Command}; + +use rosenpass::config::ProtocolVersion; #[cfg(any(target_os = "linux", target_os = "freebsd"))] use crate::key::WG_B64_LEN; -use anyhow::Result; -use rosenpass::config::ProtocolVersion; /// Used to define a peer for the rosenpass connection that consists of /// a directory for storing public keys and optionally an IP address and port of the endpoint, From 864407f90bc7b3e685151a22bf36734ab3595cfe Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 25 Jun 2025 19:12:30 +0200 Subject: [PATCH 160/174] chore: Fix module documentation for app_server --- rosenpass/src/app_server.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index 02d4a5b8..ba984bb3 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -1,5 +1,6 @@ -/// This contains the bulk of the rosenpass server IO handling code whereas -/// the actual cryptographic code lives in the [crate::protocol] module +//! This contains the bulk of the rosenpass server IO handling code whereas +//! the actual cryptographic code lives in the [crate::protocol] module + use std::collections::{HashMap, VecDeque}; use std::io::{stdout, ErrorKind, Write}; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; From 8bad02bcda6afc1f0cf075688e030c01f6b90bf1 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 25 Jun 2025 19:15:13 +0200 Subject: [PATCH 161/174] feat: Disallow unknown fields in rosenpass and rp configuration --- .ci/boot_race/a.toml | 5 ----- .ci/boot_race/b.toml | 5 ----- rosenpass/src/api/config.rs | 1 + rosenpass/src/config.rs | 6 ++++++ rp/src/exchange.rs | 2 ++ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.ci/boot_race/a.toml b/.ci/boot_race/a.toml index d8e69fc1..439966a5 100644 --- a/.ci/boot_race/a.toml +++ b/.ci/boot_race/a.toml @@ -3,11 +3,6 @@ secret_key = "rp-a-secret-key" listen = ["127.0.0.1:9999"] verbosity = "Verbose" -[api] -listen_path = [] -listen_fd = [] -stream_fd = [] - [[peers]] public_key = "rp-b-public-key" endpoint = "127.0.0.1:9998" diff --git a/.ci/boot_race/b.toml b/.ci/boot_race/b.toml index b7609759..52f43f14 100644 --- a/.ci/boot_race/b.toml +++ b/.ci/boot_race/b.toml @@ -3,11 +3,6 @@ secret_key = "rp-b-secret-key" listen = ["127.0.0.1:9998"] verbosity = "Verbose" -[api] -listen_path = [] -listen_fd = [] -stream_fd = [] - [[peers]] public_key = "rp-a-public-key" endpoint = "127.0.0.1:9999" diff --git a/rosenpass/src/api/config.rs b/rosenpass/src/api/config.rs index 122bfdbe..0fa11b8b 100644 --- a/rosenpass/src/api/config.rs +++ b/rosenpass/src/api/config.rs @@ -8,6 +8,7 @@ use crate::app_server::AppServer; /// Configuration options for the Rosenpass API #[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq)] +#[serde(deny_unknown_fields)] pub struct ApiConfig { /// Where in the file-system to create the unix socket the rosenpass API will be listening for /// connections on diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 9f79cce6..403c2c86 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -34,6 +34,7 @@ fn empty_api_config() -> crate::api::config::ApiConfig { /// /// i.e. configuration for the `rosenpass exchange` and `rosenpass exchange-config` commands #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(deny_unknown_fields)] 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 @@ -75,6 +76,7 @@ pub struct Rosenpass { /// Public key and secret key locations. #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] +#[serde(deny_unknown_fields)] pub struct Keypair { /// path to the public key file pub public_key: PathBuf, @@ -102,6 +104,7 @@ impl Keypair { /// /// - TODO: replace this type with [`log::LevelFilter`], also see #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone)] +#[serde(deny_unknown_fields)] pub enum Verbosity { Quiet, Verbose, @@ -109,6 +112,7 @@ pub enum Verbosity { /// The protocol version to be used by a peer. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone, Default)] +#[serde(deny_unknown_fields)] pub enum ProtocolVersion { #[default] V02, @@ -117,6 +121,7 @@ pub enum ProtocolVersion { /// Configuration data for a single Rosenpass peer #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct RosenpassPeer { /// path to the public key of the peer pub public_key: PathBuf, @@ -152,6 +157,7 @@ pub struct RosenpassPeer { /// Information for supplying exchanged keys directly to WireGuard #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct WireGuard { /// Name of the WireGuard interface to supply with pre-shared keys generated by the Rosenpass /// key exchange diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index e099b58e..e4df77d1 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -15,6 +15,7 @@ use crate::key::WG_B64_LEN; /// a directory for storing public keys and optionally an IP address and port of the endpoint, /// for how long the connection should be kept alive and a list of allowed IPs for the peer. #[derive(Default, Deserialize)] +#[serde(deny_unknown_fields)] pub struct ExchangePeer { /// Directory where public keys are stored pub public_keys_dir: PathBuf, @@ -31,6 +32,7 @@ pub struct ExchangePeer { /// Options for the exchange operation of the `rp` binary. #[derive(Default, Deserialize)] +#[serde(deny_unknown_fields)] pub struct ExchangeOptions { /// Whether the cli output should be verbose. pub verbose: bool, From 77e3682820167504ab5bc9ddcdf21ddacc2674ed Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 25 Jun 2025 19:24:14 +0200 Subject: [PATCH 162/174] chore: Whitespace issues in the whitepaper --- papers/whitepaper.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/papers/whitepaper.md b/papers/whitepaper.md index 6397e367..9471d4f3 100644 --- a/papers/whitepaper.md +++ b/papers/whitepaper.md @@ -395,7 +395,7 @@ fn load_biscuit(nct) { // 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 @@ -536,7 +536,8 @@ When the responder is under load and it recieves an InitConf message, the messag \vspace{0.5em} Author: David Niehues -PR: [#653](https://github.com/rosenpass/rosenpass/pull/653) + +PR: [#653](https://github.com/rosenpass/rosenpass/pull/653) \vspace{0.5em} @@ -554,9 +555,11 @@ In order to maintain compatablity without introducing an explcit version number \vspace{0.5em} -Author: Karolin Varner -Issue: [#331](https://github.com/rosenpass/rosenpass/issues/331) -PR: [#513](https://github.com/rosenpass/rosenpass/pull/513) +Author: Karolin Varner + +Issue: [#331](https://github.com/rosenpass/rosenpass/issues/331) + +PR: [#513](https://github.com/rosenpass/rosenpass/pull/513) \vspace{0.5em} @@ -574,7 +577,7 @@ By removing all retransmission handling code from the cryptographic protocol, we 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 + 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. @@ -597,7 +600,7 @@ By removing all retransmission handling code from the cryptographic protocol, we \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 @@ -611,9 +614,11 @@ By removing all retransmission handling code from the cryptographic protocol, we \vspace{0.5em} -Author: Prabhpreet Dua -Issue: [#137](https://github.com/rosenpass/rosenpass/issues/137) -PR: [#142](https://github.com/rosenpass/rosenpass/pull/142) +Author: Prabhpreet Dua + +Issue: [#137](https://github.com/rosenpass/rosenpass/issues/137) + +PR: [#142](https://github.com/rosenpass/rosenpass/pull/142) \vspace{0.5em} From 48b7bb2f14f6b74eaa48951eb0bcbfdde05f6e19 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 25 Jun 2025 19:26:04 +0200 Subject: [PATCH 163/174] feat(whitepaper): Introduce protocol extensions & specify WG integration as one --- .../rosenpass-wireguard-hybrid-security.pdf | Bin 0 -> 16089 bytes papers/tex/template-rosenpass.tex | 1 + papers/whitepaper.md | 167 ++++++++++++++++-- pkgs/whitepaper.nix | 1 + 4 files changed, 150 insertions(+), 19 deletions(-) create mode 100644 papers/graphics/rosenpass-wireguard-hybrid-security.pdf diff --git a/papers/graphics/rosenpass-wireguard-hybrid-security.pdf b/papers/graphics/rosenpass-wireguard-hybrid-security.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b7a8884b69c25a734a099c6d8c777fa08f6bc579 GIT binary patch literal 16089 zcma*O1y~)+(l&|&cXwF0+rkO%?jGDBxI=JvcXxtIaCdhP5ZooW+a>$#eeZd`@7(|V zv(~d#O;2@KclAt7cU8UQ3L;|k%nWR>B`gmQETfExjhT}1jQ?i;p#F$r{x^{-?sg^sMh$sGOA{j}SVm=ML#Mw= z#B3d`Kcs*B{>8xz%c!6ts=@Hl9TPbNYZCw!1EZOPft|U9kt3snt)q#Joq?kxy{mud*L`xl#%iKDHvgOQ0N;E(piY;BxA zh!0&c0l5A!Fp4n)nEy2LK>-2Ge}=8}p*u!VHz#prrw@JpRZU!(3GgA|FS+8%A2nG1 zp+D;UnVpaRh%2)InEw_kuFMKxX8)(04ZzIt7tQx^{J%5E0b~LG_qjXMk&ZEuv}b^f zLBKL5^tL{)TSZMtI3KSh#~be=X3tBoz`@$CXeBCjvHt*u;flc zN=gbOCEdaIek^w#dUW;SdA(9UNA->w<5Xg}_0)dS>R@~_DpXVAd*5;`NoS<5w3qA9 zjn1CBx!)VUhId%Bf9vzf@_wEwQ94Z1oTMg5N&J3WmI6HC3ZT>ZvRB7Y>95{0e*H`- z&-+QJYcDl$nT2agy3G8VUL)>8GjQrmBSB=+86TnU;ODh@Y{#t=HCN6Teuf{c$IQr2 zyfp7)Wno8rcyrFiCFk<5N~?#6Jeeyd6KP*~9FB&TIdQ3V)^p&gZsYe2Az>5Yydw$X ziehye_{opl(In$S65uLBk)*h|sd?Ai2CY^!9p4?L4g%gX%(<^jDjaMYVlk)g&yVyj z^kz4^Je{BRx)d`!-;SFo>)(z!N~$;3yw0shZGU$*KfnFb`{m_+zvSv8?Bn(R8|vnH z?-$b1ea1%Dt0>p{+Uw!(A=@E5%bN+J-*3rpNz|+lFt6t&uKaD!kH2C{QDVCa)O_Tl z1m62r^%UB2I$m$p-@Tufs^#9d>L{1Zbw~|*=T4LAFLJuLldj+MB?ZxzVIY-H+Q$)T zS;$gcEZ|r!CTr&*e|}ljitC7RaP>Ukf)&CS8namHa-fw8Z(SzvocLv`>jtKJ`mMN5;^P%uwP->Ne~AtqwEe z#$5@^8pbE`$5jcuUknNGPjB_4P(Q!eeNRbW;d(+&&jtix=s*?@)37Kkvs zC!vMq@o)-1Yg}^AkT{6ON+HwH&lLZm9 z3E?YVj^*M|kAHLu-8Mvb{~dlkjfG0P{VhPcp$&wM4sf(7GBFAY}_BWz@sL_x&F&3;cegkC0o z4Kba2f4H@Oo3L=t3NgmI8cKD9`Ym|6=15@3XdaePzN;FLy`zjUq+F;C*Gc569y6&GONq zgvC=c!}G8*u9NWow&a|LT1Q$Bs?v>B=2-uBRJ>Z)cd}c!o(i^I#wpuDSLMkubK_Ss zL|FXbV7~k&4`{bZ52&2)(uiTCjh(kxve4GSs8_-p6?~ws`4H-6l82%g2PZ%2SZ|)M ztBsc-0a!DgBlZQiX50kMq*t|6 zkaxI*^6rZk26nD5dUrsRgsfjmSgK3WoD5i6)p5iur*ddd2O z`NbU->w2O`{7V)+fQKPlwo874?(1}k!(-gko?8D<5q>}?_Z1uL?#TkqG73i5%oew` z*=BIx`T`EN&c1gRI#n}Bc)fsVWF`r{zEdFtd|16NX)>llb~1L2t`0dv8ts6IdjC){ zPe7*Adst^;b;j8I~(Q#WuyYRF5eAqY==D z2aTv*Y&?L@mf@~yGQ$Bd>@x!za_A&e{G}8hyW2C8B1h0ep|bUoI8)n{V92hYK`Y8^ zdiJD4Z$OP(T~^NNRANG67FvE9Dl4Tzy}*K6X$0P#Gwn2Zc{%bxu3!nvO|I!$?oe&AoK2)aWC$+bX?uq5k|{4(pvNEk>PA6duFG(VvGBpha_XOQ^(P%(ba^^ zXVi&951e}>y`SF0j>mBpwz)?oqBR%Wnn!DCw2awKG%~}vGV|#A@2()_JoWN$2g4%D zFUe(M`ePPW**7ArxsQcAjuWJW77~>=`bVtylKAks1ZSz*D*6((WNaE#()iyM8*{yA zU<#CK(fw3QH^U#g6N~0nO806J2r|-ZhA_Xr$g|`lf-5_>N0Tr3qM;^f5E$uLe_MLr0?(#A>pi(s0=5?_Omm~#^7+w zZBQ?=CoPDl7J_RYsS1se{lnEds=MuU^c_F7a|0QcZq5O(U?9?vnuD4AS8pOB_>VhF z+Q~_aBc{`@2DWjYjU;F>&Caq6w&tcfV!_ThsEa0{QIgoWCIp>i-1TNa(M<6b^%9vm z<_GN(Oj#5O=?M&z`mdwD3Q7F=J1)s`(j|GcWJRM3SBcl#`OpVx_HCytK!~O6FF+Qkgh8%*3-7!E29>|CY+jA>k=fFA$~<4pf4#t;LZqX;?ak_o6B; zwnGJvZL9v$^KwbWDoA#fz|Z1#kdGeEBF#JV41&oewhc^j|5Z+S?)JWaE z3q>6NvwqbQjmvEw;53DG-t?|q`h860w4$dm+wi48hX68=`vrv{{fCp*6TGvXmZYjB z^f7T;Irve2Tx)6HutErN9-@y3lqr5xB7>eyNg@ALH~)}a4p4YRW#H{Dg?X&5Ui~W9 zL%Gg<;-tjsCX2J}{nVPh4SRz{V3wms-86h4(_ay^%@qLvUw(q!3; zjWuLo!y~CB*W|3l5laOE2ULP`uAhR6YVa4lvS_U9M7bpRuPkI06GSt9IO3#5(vE!L zpmyScRx0cV!pW&<-Vs0L5;vv5kj112haMqTQWe)ko%ks-1Sau^3$_c1fr7cCev5vy z7F<}$g%eY`4yXrf*+sv95a&R6)G+3~HJL@*P^3^%;%mjGl2s|=qqktu(*llsZ>S%^ zs^m)}3#IvRH&oI2JSf++ZhEc|J2|}-_lS-ug2~q0M#Q#Dgb-i^;VAL(;umy~$p}|- zf9{LJV)Dj9Cz|K0??*)i(uehBX<<47j7g43e2bkbu$*%nOmN4y}6M_~alS-*Qlh2Tr~~TqSX5GZ!{8tqM4)78Qitlj6X@ z0p0~!n9ZGi2yS7OtCgk1@Ou?{<5k{77Hge@^B8#y37jvw0IEFd zBwfh*Sz+*%_`EU32>30-%{E*H362k$cr3R6jDsW*DQ5#bc5`NeWn63U09y(fq!k3K z`r0)*o&6g302k&`!F85_*0B<2Yi%2T`*#5Y2_m^;C@1oJ3)F(iap)ErDQjZu{hL>5 z{P2Dvo5pi4hQdDN4EAjdk?>)+h+6r;YpUh%r+Af=#9M=+gOWWIVn61MTSPtyi*q8V z<}yK+NuB0&NWvrO0O?1@126^cuUHo%J%Yv%S!}XHD6&az(WrjQoJu831>X9?;<*z=QK!##~CaLQ%h_Z21xbv6>4P8hTqn8Wwf4^v(3&>zY(nSi=7Ni zhsjHrw19LZ(HTq1ODfpIdZN06GRQNu_)QrIL(QbKG6P@%6=c^?f}#=*8ad%&~dmICc+ z4&PJy`b0J=mWmJVmx+M>@solp1~d%4i1R5$l{ad)ImyJu`l~p1q;M6yHZa3j`JT_D zka9VJxBZAYe1j@{T+7&PYxcD5oGBzNi!xuF_MGf?X7AVH9Q!L}QUY`5h7lzY6;Z+q z;bzPG4g3nRQQPdqHNB?qwfUzHu{Y^!z7Z>%!be6k;P=pSr;#QZIbovO>gEf9LSYb! zWoZ5AhIVNk#|pv&E^_P5Xg5s55D6_^g$_xa9fx&ZvHe;tpmd1G46Tp-lB()dYwDnd zTH`Jj0hR5mIeP~$E5V32m0PJU3OTFydWz5Lam|<5xl#OUyG>vi;Sm`kkR+J)Q+0!} zxIl_lDk1c6W--WnO*x^k2R~Fmexr#FbO#&-bmKb{09SX!ssQ6MkCbPV`H>*p5LTbt zYCg7yai2THk9Hbgtc*1~R{-%|W8ILX5EfEO3<)+B`l&g>jTibGLhqn-t&&dLGx4sl z&JQjuh;(<#qg~d;-(D71lKt(BJ*H+i*qmoNoc`vOA@^~Sq3pko!)&CRoEYP%+H&-~f@ zjEV?H+b(}33lyztk3nR2y#4izZoeUfYiVPg`klDG=;GKKzIoi|hhiSb!8PW#_N2hb zp&)2dcQ!j{b;!l=JyjJR+u1iFCApv`=*vncz}HxI+>w@gcRlVKCc5>M=)pALna0Qk z@3X5jozIHJ!8x=fQ%D=3Wa`5wM$8!E$hvO`+iM;T=b)1G?HF7=Kij?$nm-(4#3)9) z+nR{PD7^%Hc(ac~2(>d}1UaIupvNJ=&!Q@;&2l z#AQEQx!zhxyKlB27DD);XK1|EF03;QS2H|~x>hq1j9fEQAYvMA zgruDDN@%i&3?Z~qnX0a5(D|jf0L^Ft#WP{DEP8}X+7D5muvDYrY1)GuXW|Pd5SO&xe zku6(yKnZJW#nKCJ*ht+=ju!65G%0aEBRMV>n5nW^KZT4FPA{nusNpXOM)Yw~Go3hX zV`k%CJ7at+*Y?2KPgR!X<9^9h4g+~aE5gxH*_`1b3gF5*K?xfBLWro6Wz^*xG$iN; zz189dV>~iN=BA!>Y!rnraYTU@qjdNLE%x6K(os(D1?s&+3rMFm7n6<1i?6SQ7%P$m zTGj9c!E;Zh%b>hSR%zn67^#Vv46;QJ<3*1MhHUZ^puUMG1R8ABXK?zrHz(B0)|0yg zU`ii?|I}X#m#miUqywwMAr+1wXJsH1Y+6gpwmgssEO%)&htm5M7fD0uGVs1jBN(4W zr?F>didJPI(F2)5?J=RmHp^Ks>>w-arDktGL&VrdlENwW!-%pTO#i~fmw}E*3VY_U zrh%GzCNoZU5HeyFLRjCKd<=tyj|ZiGSNmK(dXtw^Db~?E%B%lb;pr_YN7Vfa~r6lvw*Gj+XxAAJ%X;AJR7Mvzz4N|Ld z96W*WXmtMwFy2rpELep)(qW@zeN3s`%)2ezS&b&M17DfTB&nccNrA*~PU76KoNh{i z0dvgO(f^21J2G>8Keya-jkgPPQ|r9U!cyl$E^->gwO!g6HGho;TN~x?s!oV zgtPb0o8$X8{^JdE!_UZnJWkX5#@v958ZPF6`%UNjEhNx_0DS?^2;An$q&OBBA+=Br z&s;4`#F@lN(C1uu)zISDJ(j0V!&{#_-9CzZ&Ao$MFP}k76Xd8sO%!8vKQ^_0g_S<$ z`l~|;ySHhgx%n($V|Xkq08}`ZU5g5*=Pr_b@MzFt@mU$*mVubGIhrTuy|?nLke)pZ zEhtu>t59g473E;J@@ywk#Ps2OE3g-sF%OW3vZI!ZuO*Ik+9dWJ5aa~Wx>rDV0ngK8 z_zm3|to>*bDg;HMNZpAU3d<=9J0v09S(cNwU(<>s&e=(yOC9^UgBmf zAs;_8+CsU%@L%nRO7Ulmi8H61SA+??Vj{{HX7<=pamAAj+|&?rYD_rlpyO|z22UBP zl7!$N2DS6gxW53A-tR+{`zVW>ZNmJ5Te0!|67{B)l^dgTv;FkWpvfG$7c^&UK`bx$ zk~;ZYG{W`qX_`p*81``LZ#=pzpnken@o0pZ7TfVYDi+n`+ZP=r$~!vbsePs?sm)id z7ZgPIdArILCXFt~N#iFZHRDrQ@}v3eO>a2kWTdiQybU3o*~7sn3ES9SZO(G*nBN0+ z>$K#h8KpIBWL>B>m7Eb?k)<_C?5q(%2O|!QcLm09+mlm>>JHEN+Ei51R2Op6*RAKc z6cWq|ak4t-j)&d)Wn%MhIZXG&eC3C?GlD6+cttI)J*0NL?9Z@(?ntX3v9xR96f%?E zX3I32Hl>Sh-uWct5~GeCWkw=iZSX_LhJx1DjS!B(H%9sq!aDl;%q-;XbehQJFy#zt z`D&NNEP8VcRcZ7nwB65U&Nnu9v%rm8Q7i-$2NlyeG%er~UdsakZF{zBmIF;F3@eND zS0J*)F6qnIArrB1Zy@|-E%j7x(bBW5aD=j#fzWc-p5tc&p@+elL=K;Yzz>y;XtSUw z2kVy+dS$4gOD?80md3D~5`HFP&O4-C3FW~fD(qTx)(b$3%-~tyzsm;Ev`a$$2DW=P z-lyhNrC9W4H4tLu_Ujwz?GM=K>G+K~E^PgUlMjdOwa)Y_1*F#R&jQ%e0M&KN*&>Xh zsGU&Jsly3#j4<9FQVGFHGMhz3sk|FrJovJ22ysVK zyeq8p?^VBf0+Mxmgi&^Nn@5Rb`*Thmv#9{1X6Gsr%z$KRklLIwj4|FEFYQrA^P@}e zIYh3!c1%B|Cvv@jMD-#AzBK9OyuR`CmEKh8gtbF%y83PcGT0^u%>L}rr*!VlRQ>9o zO{Rmu%U)g&+NMQI?&Fiwy$WgPvt+xZ@7vv9`XV>S9yQ!4k#cJqKIz=jpAsCooiA_= zE3!XzFM&M3U>O~|!sB0#z>$q{gsRUWVa>y_G_)8xun>bf-4#GAu64Fv!>v$Ep>XU9 z*mYz~PNLe<)+na`gzZR+CE13j`}@JbxoXPEI!)mGseIseEYOsBT1v|2vZ!8>;dZ^> zoUY{kMHb6P6#2A%S27VW;u zk+l-WPg7Nt9M(M?rqE=dQ6^x~Nva}9!dmd*zrwiCQb;55`{Dh4q>77hg z%2PoPmDXP=?=h>Bqe_xSPkD!cAPhd>WwYMjzkEFts2N-?fAy!|6Ehkuh(7*CmA-Oe3u^rqR4$h(4HF`bUBF&Ed5>RXa;?^A8M zojYVA6E=Kh?Jp737_3O;^?Cle#FtjhdmC5c?KHNO{j_70<28U9?rJrp*l0np`#HpO zWC?kGbnj@6`(n$^Q&=p?%t@x4n7J}|8T~I;K&smK_4Hf>2`5_0@mzR$TUi>zjpaWq z%aMQl2{I=lKOHkx>$iLB$DPdcZKkb^+W<(s1&`*vXtdCPeLBH7}R} z%G!8S5LHBc@h>Zwmj+8s(%K0DS zx__dl|09+R{5yjA-{QzR@nbdtOvs}5o*L7e(ntC#Qq06Xxpl))~t!TbJW68q2x$^-S_etKe-tI!6*@GG*Sx`9Fw0qg@`i zurU6DexWPDJH>_ZP#kN0uF#?|umhF-Rdo$!R1i{1_0Tq!h1F)mWH6>z`KkQIYX{ia z?uC7gP(a(eT~`QJ)i-46ORk|1_&Xu+rxWk8+y*g?VbkdLu^NtF^7}4XQN`-Ck27Y0 z=+Pt{H3}+H2(92Tu{_M(cjHeuy+ND42F!xd;Q&q#34aG66M4JAVy+%E8cKiI@}QIX z?}0UNG+U5MbrObx1|)Z*ZxX)V4rLU>^3cNkIFBg1lps$dM>b%ug~TB*^AE{eGcV;& zaqn`Ow@6=qZrG&Hx8gsu(Z#g0vU?j(YWOf)f5s>i)LYU?9 zV5YwunL0cKlnQ?*itfmz`J%q}o?>g?`}y2LE>t+#defi)k)FT$(hEiI5+b_$5{PN| z@(Q7C`qt5P&-5|$!kDyym?c_+Ya`EsV2m1O;iB~qV-S1bzT86i6&=DjW8eLVL0OVd z-V?_cJ^hI^V0-Fn5Xc{8?>N}79sBB5U``|96tX zf7O2MzdsTV05StVx`x6ejhnn#Pv2T7%hKg!X1KS`yHuB%IIAP`{dM>0;*JI__ov^H zhusN+7DxK*5oiJ)*h!OD{u1P#`8N?^tC8%CYn@jxhXJg-_*yV4v|WV1WyQ(h^Kv_! zPfiUiZ}pGUy+56u{%RHO$?oKWkvp=-LjSfxzv$hEE;AfgYmCh8S%oR#7yP*ROCpsX zYqObjEk1z(B(txlHza5J;~ zoiFj9)cJQt3p0S_UuNjvGgq9|zR7%9!r?!7u=ir_yz|Qbsyn!xnu5@88);ob3O#FudaI3AlhboaTBGDD zk#hX3wQM>$__vLY&QJIzSg*|opYI>MH}0Qxu?zxZ5qKkHpb7MJIb$-Pq4W{7Oo>GvE=5x=S}_0&U?}xNUWHocaeFZP#&>Ta$2!FOyAIr`G>_* zCJFQ|@*z=nmjn?R8MS$0R;EJ$J}|q3*X5K`j9Pkbh`Z+j;~-4SiV*jf4|DxCDxE4S z?rgX<7UyiaAi)UDATc=8JZrWfR)TYoth{iLtiOifp?!BC$+}^{={5^NSvFz75xXtG zUApT)Vcljy?1Cq8riZz0yJ0oha^2faLGt~wfdFs@>`jmq_zM9)1xSZu-E`fm_ldb2 zCw%+jb3noo5w7CW5v|YsZy&@iR9anzb>Ig3XXlQ7eN=C=r}_tLrWx=l#thpN+ZFfH z2X@$m^L*;rDE|jrDiL&aG7DBAF;HN*;RWKN;5C`07*PSMZt$x;Cz`OZG30Fm!b2rQ z>C+?2cf5DPb;q)L(5WYxYjc&Q?_l@v7e?+9T{v6i%{Oa!`_&{_5f(Q*PskB&NN>Q- z;A)gOd@?<<>V$Y|tCg=$?MG~LvW|lFl3n;Q^kml7 zvbc$)jX*N>Uf1E0(0Uo zsfOE=VsAojDC!H-0p67B?hBfgOG9k>)N>te(EL`rZL?RMt7#R&+r-yP#H8QbIljR) zze(NV$yF3bqs^lS@-LhB%kzDEeT(6p{0?vt-eD&iS2WQm)te$8&_?!?AFVd3(y}*2 zjM$8}(8axqiL)=B4jdnURA~1N*Nyd436(cZ8lkmlld*1=pu2FPkzSt3vdPwDgdxiL zs%~=VLX~qjwl7AS19ImMqTJKWb5Ge)!ICMq5&B0pDx19w$uqdxzAcH=>7s{MZ}F|q z;hhrR5Unk7ZrZo%?9)ts9y3*gH7b8yKg}zQkhh#WJB`x~) zfOvRsElvo=*mS5(1!T0a53$Zd(tOISD65k>yNZ)J*8mw->fidvFFaM zk$vEihkk3nv$0`46#&GHqrnul${59ZFHmHe$FCL_TSU9kO-ng@pq)2Et~UX1&?2G% zQ-1HoB+$ZE5mh^zB<(pf9In6^R0pmJVzd$o@=l1e8zx*s#rOvXMtfyEZ~@ty@pcMG@P=-oiNW0{7xk??nWDJO_YwcsTs z2o}{Z?vz$mS2xWoyf`nMsYTG%2ahk6vo$oZS@Hs}o><>F%F4$%^zeJ(*lbL&JIX4s zO0%a`tI|MI0&B_O9*Lg-IdDf6IM?wKFs8xJHVUdSKS62_F;!1^8n*sp`WwOXjrou=Tla@-}B`~-u(q%>Sl$--S#yKn`-Rnn{4 z9&onebuD`AqD+TnE5s6TUtaX<0qvr-@zj}wSXfA%tS)=Rw#68YrV2|aqC0BQKzCktR;uPAk>}u+P3BJ zs#N#6cPNeFz@O2jdT+n=z=25pXOS)Y_z=V&#SIl2yBxD;L zEp6f*Y&^ggw`=`sy0%mE#VzTCCoSz~4k5Rr8(+==PGdq>lhn-S)KYl78XyX)3r**=JN08NBtZbD~&1$QqoW|KRcK0ZBw5?cAwcrne zwWv(pUB$HD&9hf)a-}YcqTXm;vT5Kf6^<&M;H9744aIf-j9!vf%Mo4c^>wGTMLQvX z!9QX7Qwr{=vkW~aM8*g;Z6qvFZ*x6pYs$zT)GO;g?SVYG4Skyy8J|jtPwTWutLC+NoM?e!Wd0uRVN`LB0?ipI#-gJRdi!f_SVi!tH{v# zMa?JraH$2$+ll3!tWp_k9gHmtgw;bNS(BA}0%)D;BoW0$pijNi6dCBji1>v1@iYHU zyHcw;ysLc~$LIacz(*^?;p(EVOe-w8t`N=qhf&HxB)>A`Zd_%oC%(<*WG{Oe4b{QD zB0bEgB8(?go28B*>EwJl2n z7JH=gsjK=W78OtI3JYCGM(PZyRduN2O>kV`<|k%u!UNb}Q`jZ^a^GmtEs#G6f{=wrjPhkVL7ggnP`xRYTH zFC5b<;=q{$wa$kJ{?6w^IOw?QUTJ=9T`i5sUzQfWW*7JvDF(f@LWE$?CHf`F>KxUD zT_&#nuY}7+v|kS}n~@%zAG|S7_ZNa_PpE>tkz5F^+`Wt}ywY0k=$30B9RSDk!P-n7 zo8r~h#=4+R7q|@q1^fm*P@EP$aH(}5m$?>jQN9pxu}~i4U~n4s0=$fxoL~>_5c<$w zJz!97y;xd4)xTv#lFpUN0gYDJ#M#euB31=#& zZ(WZb4o+`{&W=`|@2uO9)=l82hK+mf6wjby<3CO1G}_|Po^^q_;hET@MHBh7jD_+(){!{BU$STVRgVgonz7sXc_w+&<;(S1&4QyVRQ>}vG1FShxk}W z`T|+S|4jOPJrA4j;)Lmi_KN>}38Fhxy^SOpq)pB-++}65O1^s1=(Ab^h_Uj5e-CU& zzz0_@;SBn!#7Q2}rTh)T%+kkg!sY03qiGyzqjmq9tL01JgjzS>KLqGv{`lv zx@bK5&r&I4Z8?g)Z~VzBPofQPF!nn!u^yWd`mGY;&09Da#c=MjvOs+=(ZoFwAs0Bu zy{lA;i2PZX`nF>gvKMm6%?6_-$_Dt>ikS_ zd4!jOAxtD?OBND~xs-sq$||a*9e|Qy;WCjO_WY`i(v3kq(y3f{p`ira{*v^Q-HHMK z{qQo}-5~A8Wu3z00D~GO-i34|@q(>(lJF~c57*9yfQVL}ij?cdRv^-S2Of&h7Ebzv z3@4e0jf)Eq+fh;1@Dy=W1MlIDl1Yu#A22h~Jhtqv+4uq}d`W7D?g916IXx zpSL4JVZ?nWcz`!VQjR6*5#U2wuk2w$D){kqa#!5UJl9cVvIRi@d}c#;D;;&BRC-Ne zJY4ESAN|(w-O7bvu;BOZ?o7L7KyjmP_({83$<)4=np_`;b+oSc`~EGgPYZ^O^UnAIC|20UH+pSfChMx5`m+&xjsj zR-UA}Sm5cS{?&E_^(far71^=X+rVKvU-#%KgUraL>sUr7B)8y9q((Zhbx9;L3d1lp z0N5q|%5bA@s?ju~CW1R*b?Uo9$#~eIvn;pM+$8Ec7nr(qfFvfc;0`)}BwpKh+}?#* zZPQ6J#)yQ7fY510ZR_a%I4g_1S|ru7CVj62Wqhl7MLTa@cILgu?sDGgaK6wMxA``H z1T(U+41zkwqn3$3awI=K#TLV$>Y=`(!9hyZQ(P7gi1)&RxE2M` z^PNxaUJl!zTx}mM4zlWwGP2E|3Mq5oLGLQ<6H)ik z3OlPyS<@<6o3gb&#V_mIi7dnNZ>^u_=S^?8ax$c%F<1L0)ymBBUJmY(J`>>-F~?JD z{a*Q1rK{}nJZA0E@-FmgB!E(N@^q)YPjUH7w9Fmf|Fc6DR>4fF&q?qGMesIkmuO~N zbZ5r^95ele*qbSU03~oq@2I4#ELSzYyGs}rSKzzlsoT!DuW-RPvl6_lVk&5+lZSbZ zi-@@V#rfkMpLP7RVeeU~#Wm&p!b0chZ;JSC5wtI^+o#aBt-Qme+3b0+E?|={ zBi`Q+eWnPnRR6btD|IerKFF(@1zn8s~WUcT-v9oYi+t&Ma-%n3% z5*lsrKevS$eI8wDi~h(pOh{F-LroVD!UeEd0|aI{izF&l!pe~SawP!ve$t|%0~mQy zc85jZLv(3kL5LfaP$VQI(4~mKhv(vT+BeG5Ba`w@b>d9juiu*@i$S~3NbU+9ZW=ZO zgS8{6$${(oxNJSJEWpt>A@}N$>^?brS9zG9EHEJ1!9FkY&|Sdo5^rK`UOo;K3>C7F zA+!%Z^v?E_eLg$_@doA8sUR%RQ@g>qaP@0ih#p_qZ<(X*o&rAr#r9;SQ@%sEFZ--eEs3vgH+dW41u1pv`>OnV!K3Io#8*8|aB&(PhBV?P z5B6uk?8CSDzD|Q`Au6dQd6+{ByXM{IpZGR`{Cy|1L~R>}GGZn-h^>LV4=69NZ|ED4 zjDsE!K5`5~^3z)?RJg~X!TFHht9%g&cS{F4jIXc7Td>Sc$|qlU=~^OPy5CpWSP~n6 z!6bb!2LTSujrc(^2`b6dNtfcMDLvW6?NTH5nu^BHki}M=*SM;x;iPv+Dkis;x2q4p z&aes(uZeLjH44(96)w4Aslgh$xp7MYcLv#t%mI<}(sZ1-7xTy>M@Aw!@_XK&apz}r z{gIxhpgM(ov!~K>$KX3zN*ek89BfpFeK6(AmXO;sRTb>ZUWKV}%Ij!})FsnZVewuO zG_V8<70}nJntFX4J?a&Cwwkt1$YtLGOkW*G@q9(&qlTL1U$M2 z#q~&SBgH!D?7jr^>=MzEsZ`Mcgb6L9WPZdd3HkPFKG^N~WTBLO1BBXZV2Kj2lm2&7 zzmSXFjm=Z6w9!-E@_V5?`M`Vi%?Qk5o7Y*_?1sW4rpgMQDiCifa>rd+^;oKvu#Hlv zD%61!d$6;-D^v+k@u@mNHm^w9n|YqZJMKl!3IPgCoq=Nci(%6Xi}9M*JApVf8Foav zB44?+_VHlAlqL#TbynZgu#&-&2O!+6aMuI7?}TUYgqq%51`;+YwT?Ffr6v@ z0LQ7AXncSIS(zAsY#%r|;SY45lkLZqkK%t$Eo^N>{$SRqM7Wu`IGLE3KR$mQS|1#; zw#NVe7gauhdUhtpAHX;RD@T)mkbKToR)z*PHh<)+m|HjkKED6z2OU7o#KG}H9Ds#^ znU$H13&_sKLJwqM|EGh0bqgS4;bH;+G67i_Z7keu9T{vL%%}lh?F^jE9Zd`XE`K+{ z&cMR(F`^G6`8W9R&t~Qyhy1_5hw_G&%1$3(LpcBo(;v{BgQF7w2>c-b-Xt+I0h#}W zhy3r5x&bv!+f`0v@1^S0CT?i_OJpSaaIomCLODmb9b@I(+W^BEU2AK}SmWHs`)9@Q zriflSb_o9|-wJ01m_1PCi4I_NK&fC1bL zt{7}dH5`<%%k0@o{ydn3Vvi8s6VL}NF*n_U9Gg4$xXZ(3!%|+i`$F!Lils(pi{`(O zm9G$NmX4CkDk|C@mstr043Lcd*pCydnoMpfZ|@gBmhiu2|Ed=4v9Z>gW458qZ!g)} zK}>t4IIHh^lc5h5yp=wLn$BswO<;81%~2Ze#4;4sF>>gX#EUN{W~6ZmofCi0^0d-6 zJ;t9vkl_?inh*ksGt9GUHlHJjHZqJP%UlfdSTwJiAJFT~Lis!w&$ z{%2^QlaC%KlOY^^&ogR)cui{+@yGlU#CKA}t`hcF9b6ggOwP!<^51Xs0d%8hrydTH zp66liLZ_j9*FoOVX73PK!eILm*9V$zV_<`0aGc9ynJ=26NJFIZm*2Kcy1?&mYHij$ zG8tj7QhHG}f2qXG;}s4B#I&5yh}QIwbyP|szI-!jarjEqSvg#Zy-ua7_5^~VgZ~U= z6Y=Bn5J>HlioV|_jtjE;?Y!sV(ds*^L*YOuSy3dw1Yuu7JH5$qGHps1Qc^lDJWhhi zl1x0Hzc`kWH$kjWBv+b@|8%1L%jQ?MoY(jj7tzsG>WOwKi(U_MVpZBr(JM6+Cw!WfLJsoVaWl;I(&MEyOIPf!tnh91(qXraxOG)Jm7rFmmYgYt z48hoZ2zt>MvRbd^WA{19Nh8qpaq29MdZ^0V9ph z%D`6=+&l=5rzqqz*tB16_3JSqwwKViK3Zl?-_8S#&b(Q-iCBAc@ge|BO;VqFX$Sz- zwII&LCl$q1$*S>7MYhrV7e`y@D6i*Lhu61#dN%8Tip{65PQl&@o;A4^Y4SSY`oyi6 zPt=88O={0B);qW8fjM>PL5}J_@hLzw=mLHZD+fSEcY(T_-BtYuYy0mjyT5_XN+zbT zjFL9SCV%0JSy?_H#mW{QCID6zSVncg7iPc*==Y;c$=22h!1@<<_z%0OE#NQH{Q(7* z6ajqU5n>kuG6PvTSy`EZOe`Wy%&b5Wb`BOG(}$^I6%!HU1N?WE4=Mi`xDN>N|E+<; z82yY2YJe`X^}ELn@0>%K3@p#LUyM#LNmj!T;%9JhbD6 zTy%*EsG=Z>z;$e%8dB73=6^;Naxu@S#d(HVz;VmYiHvUJUmC0Sx%}!2kdN literal 0 HcmV?d00001 diff --git a/papers/tex/template-rosenpass.tex b/papers/tex/template-rosenpass.tex index d7535944..30178927 100644 --- a/papers/tex/template-rosenpass.tex +++ b/papers/tex/template-rosenpass.tex @@ -2,6 +2,7 @@ \usepackage{amssymb} \usepackage{mathtools} \usepackage{fontspec} +\usepackage{dirtytalk} %font fallback \directlua{luaotfload.add_fallback diff --git a/papers/whitepaper.md b/papers/whitepaper.md index 9471d4f3..b41d9a07 100644 --- a/papers/whitepaper.md +++ b/papers/whitepaper.md @@ -8,13 +8,15 @@ author: - Lisa Schmidt = {Scientific Illustrator – \\url{mullana.de}} - Prabhpreet Dua abstract: | - Rosenpass is used to create post-quantum-secure VPNs. Rosenpass computes a shared key, WireGuard (WG) [@wg] uses the shared key to establish a secure connection. Rosenpass can also be used without WireGuard, deriving post-quantum-secure symmetric keys for another application. The Rosenpass protocol builds on “Post-quantum WireGuard” (PQWG) [@pqwg] and improves it by using a cookie mechanism to provide security against state disruption attacks. + Rosenpass is a post-quantum-secure authenticated key exchange protocol. Its main practical use case is creating post-quantum-secure VPNs by combining WireGuard and Rosenpass. - The WireGuard implementation enjoys great trust from the cryptography community and has excellent performance characteristics. To preserve these features, the Rosenpass application runs side-by-side with WireGuard and supplies a new post-quantum-secure pre-shared key (PSK) every two minutes. WireGuard itself still performs the pre-quantum-secure key exchange and transfers any transport data with no involvement from Rosenpass at all. + In this combination, Rosenpass generates a post-quantum-secure shared key every two minutes that is then used by WireGuard (WG) [@wg] to establish a secure connection. Rosenpass can also be used without WireGuard, providing post-quantum-secure symmetric keys for other applications, as long as the other application accepts a pre-shared key and provides cryptographic security based on the pre-shared key alone. + + The Rosenpass protocol builds on “Post-quantum WireGuard” (PQWG) [@pqwg] and improves it by using a cookie mechanism to provide security against state disruption attacks. From a cryptographic perspective, Rosenpass can be thought of as a post-quantum secure variant of the Noise IK[@noise] key exchange. \say{Noise IK} means that the protocol makes both parties authenticate themselves, but that the initiator knows before the protocol starts which other party they are communicating with. There is no negotiation step where the responder communicates their identity to the initiator. The Rosenpass project consists of a protocol description, an implementation written in Rust, and a symbolic analysis of the protocol’s security using ProVerif [@proverif]. We are working on a cryptographic security proof using CryptoVerif [@cryptoverif]. - This document is a guide for engineers and researchers implementing the protocol; a scientific paper discussing the security properties of Rosenpass is work in progress. + This document is a guide for engineers and researchers implementing the protocol. --- \enlargethispage{5mm} @@ -31,7 +33,7 @@ abstract: | # Security -Rosenpass inherits most security properties from Post-Quantum WireGuard (PQWG). The security properties mentioned here are covered by the symbolic analysis in the Rosenpass repository. +Rosenpass inherits most security properties from Post-Quantum WireGuard (PQWG). The security properties mentioned here are covered by the symbolic analysis in the Rosenpass repository. ## Secrecy Three key encapsulations using the keypairs `sski`/`spki`, `sskr`/`spkr`, and `eski`/`epki` provide secrecy (see Section \ref{variables} for an introduction of the variables). Their respective ciphertexts are called `scti`, `sctr`, and `ectr` and the resulting keys are called `spti`, `sptr`, `epti`. A single secure encapsulation is sufficient to provide secrecy. We use two different KEMs (Key Encapsulation Mechanisms; see Section \ref{skem}): Kyber and Classic McEliece. @@ -154,16 +156,18 @@ Rosenpass uses two types of ID variables. See Figure \ref{img:HashingTree} for h The first lower-case character indicates whether the variable is a session ID (`sid`) or a peer ID (`pid`). The final character indicates the role using the characters `i`, `r`, `m`, or `t`, for `initiator`, `responder`, `mine`, or `theirs` respectively. -### Symmetric Keys - -Rosenpass uses two symmetric key variables `psk` and `osk` in its interface, and maintains the entire handshake state in a variable called the chaining key. +### Symmetric Keys {#symmetric-keys} +Rosenpass uses two main symmetric key variables `psk` and `osk` in its interface, and maintains the entire handshake state in a variable called the chaining key. * `psk`: A pre-shared key that can be optionally supplied as input to Rosenpass. -* `osk`: The output shared key, generated by Rosenpass and supplied to WireGuard for use as its pre-shared key. -* `ck`: The chaining key. +* `osk`: The output shared key, generated by Rosenpass. The main use case is to supply the key to WireGuard for use as its pre-shared key. +* `ck`: The chaining key. This refers to various intermediate keys produced during the execution of the protocol, before the final `osk` is produced. + +We mix all key material (e.g. `psk`) into the chaining key and derive symmetric keys such as `osk` from it. We authenticate public values by mixing them into the chaining key; in particular, we include the entire protocol transcript in the chaining key, i.e., all values transmitted over the network. + +The protocol allows for multiple `osk`s to be generated; each of these keys is labeled with a domain separator to make sure different key usages are always given separate keys. The domain separator for using Rosenpass and WireGuard together is a token generated using the domain separator sequence `["rosenpass.eu", "wireguard psk"]` (see Fig. \ref{img:HashingTree}), as described in \ref{protocol-extension-wireguard-psk}. Third-parties using Rosenpass-keys for other purposes are asked to define their own protocol-extensions. Standard protocol extensions are described in \ref{protocol-extensions}. -We mix all key material (e.g. `psk`) into the chaining key, and derive symmetric keys such as `osk` from it. We authenticate public values by mixing them into the chaining key; in particular, we include the entire protocol transcript in the chaining key, i.e., all values transmitted over the network. ## Hashes @@ -182,7 +186,7 @@ Using one hash function for multiple purposes can cause real-world security issu \setupimage{landscape,fullpage,label=img:HashingTree} ![Rosenpass Hashing Tree](graphics/rosenpass-wp-hashing-tree-rgb.svg) -Each tree node $\circ{}$ in Figure 3 represents the application of the keyed hash function, using the previous chaining key value as first parameter. The root of the tree is the zero key. In level one, the `PROTOCOL` identifier is applied to the zero key to generate a label unique across cryptographic protocols (unless the same label is deliberately used elsewhere). In level two, purpose identifiers are applied to the protocol label to generate labels to use with each separate hash function application within the Rosenpass protocol. The following layers contain the inputs used in each separate usage of the hash function: Beneath the identifiers `"mac"`, `"cookie"`, `"peer id"`, and `"biscuit additional data"` are hash functions or message authentication codes with a small number of inputs. The second, third, and fourth column in Figure 3 cover the long sequential branch beneath the identifier `"chaining key init"` representing the entire protocol execution, one column for each message processed during the handshake. The leaves beneath `"chaining key extract"` in the left column represent pseudo-random labels for use when extracting values from the chaining key during the protocol execution. These values such as `mix >` appear as outputs in the left column, and then as inputs `< mix` in the other three columns. +Each tree node $\circ{}$ in Figure \ref{img:HashingTree} represents the application of the keyed hash function, using the previous chaining key value as first parameter. The root of the tree is the zero key. In level one, the `PROTOCOL` identifier is applied to the zero key to generate a label unique across cryptographic protocols (unless the same label is deliberately used elsewhere). In level two, purpose identifiers are applied to the protocol label to generate labels to use with each separate hash function application within the Rosenpass protocol. The following layers contain the inputs used in each separate usage of the hash function: Beneath the identifiers `"mac"`, `"cookie"`, `"peer id"`, and `"biscuit additional data"` are hash functions or message authentication codes with a small number of inputs. The second, third, and fourth column in Figure \ref{img:HashingTree} cover the long sequential branch beneath the identifier `"chaining key init"` representing the entire protocol execution, one column for each message processed during the handshake. The leaves beneath `"chaining key extract"` in the left column represent pseudo-random labels for use when extracting values from the chaining key during the protocol execution. These values such as `mix >` appear as outputs in the left column, and then as inputs `< mix` in the other three columns. The protocol identifier depends on the hash function used with the respective peer is defined as follows if BLAKE2s [@rfc_blake2] is used: @@ -289,7 +293,7 @@ fn lookup_session(sid); The protocol framework used by Rosenpass allows arbitrarily many different keys to be extracted using labels for each key. The `extract_key` function is used to derive protocol-internal keys, its labels are under the “chaining key extract” node in Figure \ref{img:HashingTree}. The export key function is used to export application keys. -Third-party applications using the protocol are supposed to choose a unique label (e.g., their domain name) and use that as their own namespace for custom labels. The Rosenpass project itself uses the “rosenpass.eu” namespace. +Third-party applications using the protocol are supposed to define a protocol extension (see \ref{protocol-extensions}) and choose a globally unique label, such as their domain name for custom labels of their own. The Rosenpass project itself uses the `["rosenpass.eu"]` namespace in the WireGuard PSK protocol extension (see \ref{protocol-extension-wireguard-psk}). Applications can cache or statically compile the pseudo-random label values into their binary to improve performance. @@ -421,6 +425,18 @@ fn enter_live() { txkr ← extract_key("responder payload encryption"); txnm ← 0; txnt ← 0; + + // Setup output keys for protocol extensions such as the + // WireGuard PSK protocol extension. + setup_osks(); +} +``` + +The final step `setup_osks()` can be defined by protocol extensions (see \ref{protocol-extensions}) to set up `osk`s for custom use cases. By default, the WireGuard PSK (see \ref{protocol-extension-wireguard-psk}) is active. + +```pseudorust +fn setup_osks() { + ... // Defined by protocol extensions } ``` @@ -448,11 +464,11 @@ ICR5 and ICR6 perform biscuit replay protection using the biscuit number. This i ### Denial of Service Mitigation and Cookies -Rosenpass derives its cookie-based DoS mitigation technique for a responder when receiving InitHello messages from Wireguard [@wg]. +Rosenpass derives its cookie-based DoS mitigation technique for a responder when receiving InitHello messages from Wireguard [@wg]. When the responder is under load, it may choose to not process further InitHello handshake messages, but instead to respond with a cookie reply message (see Figure \ref{img:MessageTypes}). -The sender of the exchange then uses this cookie in order to resend the message and have it accepted the following time by the reciever. +The sender of the exchange then uses this cookie in order to resend the message and have it accepted the following time by the reciever. For an initiator, Rosenpass ignores all messages when under load. @@ -465,7 +481,7 @@ cookie_value = lhash("cookie-value", cookie_secret, initiator_host_info)[0..16] cookie_encrypted = XAEAD(lhash("cookie-key", spkm), nonce, cookie_value, mac_peer) ``` -where `cookie_secret` is a secret variable that changes every two minutes to a random value. Moreover, `lhash` is always instantiated with SHAKE256 when computing `cookie_value` for compatability reasons. `initiator_host_info` is used to identify the initiator host, and is implementation-specific for the client. This paramaters used to identify the host must be carefully chosen to ensure there is a unique mapping, especially when using IPv4 and IPv6 addresses to identify the host (such as taking care of IPv6 link-local addresses). `cookie_value` is a truncated 16 byte value from the above hash operation. `mac_peer` is the `mac` field of the peer's handshake message to which message is the reply. +where `cookie_secret` is a secret variable that changes every two minutes to a random value. Moreover, `lhash` is always instantiated with SHAKE256 when computing `cookie_value` for compatability reasons. `initiator_host_info` is used to identify the initiator host, and is implementation-specific for the client. This paramaters used to identify the host must be carefully chosen to ensure there is a unique mapping, especially when using IPv4 and IPv6 addresses to identify the host (such as taking care of IPv6 link-local addresses). `cookie_value` is a truncated 16 byte value from the above hash operation. `mac_peer` is the `mac` field of the peer's handshake message to which message is the reply. #### Envelope `mac` Field @@ -495,13 +511,13 @@ else { Here, `seconds_since_update(peer.cookie_value)` is the amount of time in seconds ellapsed since last cookie was received, and `COOKIE_WIRE_DATA` are the message contents of all bytes of the retransmitted message prior to the `cookie` field. The inititator can use an invalid value for the `cookie` value, when the responder is not under load, and the responder must ignore this value. -However, when the responder is under load, it may reject InitHello messages with the invalid `cookie` value, and issue a cookie reply message. +However, when the responder is under load, it may reject InitHello messages with the invalid `cookie` value, and issue a cookie reply message. ### Conditions to trigger DoS Mechanism This whitepaper does not mandate any specific mechanism to detect responder contention (also mentioned as the under load condition) that would trigger use of the cookie mechanism. -For the reference implemenation, Rosenpass has derived inspiration from the Linux implementation of Wireguard. This implementation suggests that the reciever keep track of the number of messages it is processing at a given time. +For the reference implemenation, Rosenpass has derived inspiration from the Linux implementation of Wireguard. This implementation suggests that the reciever keep track of the number of messages it is processing at a given time. On receiving an incoming message, if the length of the message queue to be processed exceeds a threshold `MAX_QUEUED_INCOMING_HANDSHAKES_THRESHOLD`, the client is considered under load and its state is stored as under load. In addition, the timestamp of this instant when the client was last under load is stored. When recieving subsequent messages, if the client is still in an under load state, the client will check if the time ellpased since the client was last under load has exceeded `LAST_UNDER_LOAD_WINDOW` seconds. If this is the case, the client will update its state to normal operation, and process the message in a normal fashion. @@ -520,18 +536,131 @@ The responder uses less complex form of the same mechanism: The responder never ### Interaction with cookie reply system -The cookie reply system does not interfere with the retransmission logic discussed above. +The cookie reply system does not interfere with the retransmission logic discussed above. When the initator is under load, it will ignore processing any incoming messages. -When a responder is under load and it receives an InitHello handshake message, the InitHello message will be discarded and a cookie reply message is sent. The initiator, then on the reciept of the cookie reply message, will store a decrypted `cookie_value` to set the `cookie` field to subsequently sent messages. As per the retransmission mechanism above, the initiator will send a retransmitted InitHello message with a valid `cookie` value appended. On receiving the retransmitted handshake message, the responder will validate the `cookie` value and resume with the handshake process. +When a responder is under load and it receives an InitHello handshake message, the InitHello message will be discarded and a cookie reply message is sent. The initiator, then on the reciept of the cookie reply message, will store a decrypted `cookie_value` to set the `cookie` field to subsequently sent messages. As per the retransmission mechanism above, the initiator will send a retransmitted InitHello message with a valid `cookie` value appended. On receiving the retransmitted handshake message, the responder will validate the `cookie` value and resume with the handshake process. When the responder is under load and it recieves an InitConf message, the message will be directly processed without checking the validity of the cookie field. +# Protocol extensions {#protocol-extensions} + +The main extension point for the Rosenpass protocol is to generate `osk`s (speak output shared keys, see Sec. \ref{symmetric-keys}) for purposes other than using them to secure WireGuard. By default, the Rosenpass application generates keys for the WireGuard PSK (see \ref{protocol-extension-wireguard-psk}). It would not be impossible to use the keys generated for WireGuard in other use cases, but this might lead to attacks[@oraclecloning]. Specifying a custom protocol extension in practice just means settling on alternative domain separators (see Sec. \ref{symmetric-keys}, Fig. \ref{img:HashingTree}). + +## Extension: WireGuard PSK {#protocol-extension-wireguard-psk} + +The WireGuard PSK protocol extension is active by default; this is the mode where Rosenpass is used to provide post-quantum security for WireGuard. Hybrid security (i.e. redundant pre-quantum and post-quantum security) is achieved because WireGuard provides pre-quantum security, with or without Rosenpass. + +This extension uses the `"rosenpass.eu"` namespace for user-labels and specifies a single additional user-label: + +* `["rosenpass.eu", "wireguard psk"]` + +The label's full domain separator is + +* `[PROTOCOL, "user", "rosenpass.eu", "wireguard psk"]` + +and can be seen in Figure \ref{img:HashingTree}. + +We require two extra per-peer configuration variables: + +* `wireguard_interface` — Name of a local network interface. Identifies local WireGuard interface we are supplying a PSK to. +* `wireguard_peer` — A WireGuard public key. Identifies the particular WireGuard peer whose connection we are supplying PSKs for. + +When creating the WireGuard interface for use with Rosenpass, the PSK used by WireGuard must be initialized to a random value; otherwise, WireGuard can establish an insecure key before Rosenpass had a change to exchange its own key. + +```pseudorust +fn on_wireguard_setup() { + // We use a random PSK to make sure the other side will never + // have a matching PSK when the WireGuard interface is created. + // + // Never use a fixed value here as this would lead to an attack! + let fake_wireguard_psk = random_key(); + + // How the interface is create + let wg_peer = WireGuard::setup_peer() + .public_key(wireguard_peer) + ... // Supply any custom peerconfiguration + .psk(fake_wireguard_psk); + + // The random PSK must be supplied before the + // WireGuard interface comes up + WireGuard::setup_interface() + .name(wireguard_interface) + ... // Supply any custom configuration + .add_peer(wg_peer) + .create(); +} +``` + +Every time a key is successfully negotiated, we upload the key to WireGuard. +For this protocol extension, the `setup_osks()` function is thus defined as: + +```pseudorust +fn setup_osks() { + // Generate WireGuard OSK (output shared key) from Rosenpass' + // perspective, respectively the PSK (preshared key) from + // WireGuard's perspective + let wireguard_psk = export_key("rosenpass.eu", "wireguard psk"); + + /// Supply the PSK to WireGuard + WireGuard::get_interface(wireguard_interface) + .get_peer(wireguard_peer) + .set_psk(wireguard_psk); +} +``` + +The Rosenpass protocol uses key renegotiation, just like WireGuard. +If no new `osk` is produced within a set amount of time, the OSK generated by Rosenpass times out. +In this case, the WireGuard PSK must be overwritten with a random key. +This interaction is visualized in Figure \ref{img:ExtWireguardPSKHybridSecurity}. + +```pseudorust +fn on_key_timeout() { + // Generate a random – deliberately invalid – WireGuard PSK. + // Never use a fixed value here as this would lead to an attack! + let fake_wireguard_psk = random_key(); + + // Securely erase the PSK currently used by WireGuard by + // overwriting it with the fake key we just generated. + WireGuard::get_interface(wireguard_interface) + .get_peer(wireguard_peer) + .set_psk(fake_wireguard_psk); +} +``` + +\setupimage{label=img:ExtWireguardPSKHybridSecurity,fullpage} +![Rosenpass + WireGuard: Hybrid Security](graphics/rosenpass-wireguard-hybrid-security.pdf) + # Changelog ### 0.3.x +#### 2025-06-24 – Specifying the `osk` used for WireGuard as a protocol extension + +\vspace{0.5em} + +Author: Karolin varner + +PR: [#664](https://github.com/rosenpass/rosenpass/pull/664) + +\vspace{0.5em} + +We introduce the concept of protocol extensions to make the option of using Rosenpass for purposes other than encrypting WireGuard more explicit. This captures the status-quo in a better way and does not constitute a functional change of the protocol. + +When we designed the Rosenpass protocol, we built it with support for alternative `osk`-labels in mind. +This is why we specified the domain separator for the `osk` to be `[PROTOCOL, "user", "rosenpass.eu", "wireguard psk"]`. +By choosing alternative values for the namespace (e.g. `"myorg.eu"` instead of `"rosenpass.eu`) and the label (e.g. `"MyApp Symmetric Encryption"`), the protocol could easily accommodate alternative usage scenarios. + +By introducing the concept of protocol extensions, we make this possibility explicit. + +1. Reworded the abstract to make it clearer that Rosenpass can be used for other purposes than to secure WireGuard +2. Reworded Section Symmetric Keys, adding references to the new section on protocol extension +3. Added a `setup_osks()` function in section Hashes, to make the reference to protocol extensions explicit +4. Added a new section on protocol extensions and the standard extension for using Rosenpass with WireGuard +5. Added a new graphic to showcase how Rosenpass and WireGuard interact +5. Minor formatting and intra-document references fixes + #### 2025-05-22 - SHAKE256 keyed hash \vspace{0.5em} diff --git a/pkgs/whitepaper.nix b/pkgs/whitepaper.nix index b01f8023..52312a20 100644 --- a/pkgs/whitepaper.nix +++ b/pkgs/whitepaper.nix @@ -44,6 +44,7 @@ let xifthen xkeyval xurl + dirtytalk ; } ); From b1a7d942954ce6a41decc5a9f93c95cffafde801 Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 25 Jun 2025 19:27:19 +0200 Subject: [PATCH 164/174] feat: Support for custom osk (output key) domain separators in Rosenpass app This allows for custom protocol extensions with custom domain separators to be used without modifying the Rosenpass source code --- ciphers/src/hash_domain.rs | 67 ++++++++ papers/whitepaper.md | 22 +++ rosenpass/benches/handshake.rs | 15 +- rosenpass/benches/trace_handshake.rs | 15 +- rosenpass/src/app_server.rs | 11 +- rosenpass/src/cli.rs | 1 + rosenpass/src/config.rs | 72 +++++++++ rosenpass/src/hash_domains.rs | 10 +- rosenpass/src/protocol/basic_types.rs | 2 + rosenpass/src/protocol/build_crypto_server.rs | 50 +++--- rosenpass/src/protocol/mod.rs | 6 +- .../src/protocol/osk_domain_separator.rs | 91 +++++++++++ rosenpass/src/protocol/protocol.rs | 81 ++++++++-- rosenpass/src/protocol/test.rs | 29 +++- rosenpass/src/protocol/testutils.rs | 8 +- .../tests/api-integration-tests-api-setup.rs | 2 + rosenpass/tests/api-integration-tests.rs | 2 + rosenpass/tests/app_server_example.rs | 5 +- rosenpass/tests/poll_example.rs | 150 ++++++++++++++++-- rp/src/exchange.rs | 6 +- util/src/fd.rs | 2 +- util/src/length_prefix_encoding/encoder.rs | 2 +- util/src/mem.rs | 2 +- 23 files changed, 579 insertions(+), 72 deletions(-) create mode 100644 rosenpass/src/protocol/osk_domain_separator.rs diff --git a/ciphers/src/hash_domain.rs b/ciphers/src/hash_domain.rs index 4a56cfbc..ad11a4bc 100644 --- a/ciphers/src/hash_domain.rs +++ b/ciphers/src/hash_domain.rs @@ -83,6 +83,33 @@ impl HashDomain { Ok(Self(new_key, self.1)) } + /// Version of [Self::mix] that accepts an iterator and mixes all values from the iterator into + /// this hash domain. + /// + /// # Examples + /// + /// ```rust + /// use rosenpass_ciphers::{hash_domain::HashDomain, KeyedHash}; + /// + /// let hasher = HashDomain::zero(KeyedHash::keyed_shake256()); + /// assert_eq!( + /// hasher.clone().mix(b"Hello")?.mix(b"World")?.into_value(), + /// hasher.clone().mix_many([b"Hello", b"World"])?.into_value() + /// ); + /// + /// Ok::<(), anyhow::Error>(()) + /// ``` + pub fn mix_many(mut self, it: I) -> Result + where + I: IntoIterator, + T: AsRef<[u8]>, + { + for e in it { + self = self.mix(e.as_ref())?; + } + Ok(self) + } + /// 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`. @@ -161,6 +188,46 @@ impl SecretHashDomain { Self::invoke_primitive(self.0.secret(), v, self.1) } + /// Version of [Self::mix] that accepts an iterator and mixes all values from the iterator into + /// this hash domain. + /// + /// # Examples + /// + /// ```rust + /// use rosenpass_ciphers::{hash_domain::HashDomain, KeyedHash}; + /// + /// rosenpass_secret_memory::secret_policy_use_only_malloc_secrets(); + /// + /// let hasher = HashDomain::zero(KeyedHash::keyed_shake256()); + /// assert_eq!( + /// hasher + /// .clone() + /// .turn_secret() + /// .mix(b"Hello")? + /// .mix(b"World")? + /// .into_secret() + /// .secret(), + /// hasher + /// .clone() + /// .turn_secret() + /// .mix_many([b"Hello", b"World"])? + /// .into_secret() + /// .secret(), + /// ); + + /// Ok::<(), anyhow::Error>(()) + /// ``` + pub fn mix_many(mut self, it: I) -> Result + where + I: IntoIterator, + T: AsRef<[u8]>, + { + for e in it { + self = self.mix(e.as_ref())?; + } + Ok(self) + } + /// 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`. diff --git a/papers/whitepaper.md b/papers/whitepaper.md index b41d9a07..0f81f20d 100644 --- a/papers/whitepaper.md +++ b/papers/whitepaper.md @@ -548,6 +548,28 @@ When the responder is under load and it recieves an InitConf message, the messag The main extension point for the Rosenpass protocol is to generate `osk`s (speak output shared keys, see Sec. \ref{symmetric-keys}) for purposes other than using them to secure WireGuard. By default, the Rosenpass application generates keys for the WireGuard PSK (see \ref{protocol-extension-wireguard-psk}). It would not be impossible to use the keys generated for WireGuard in other use cases, but this might lead to attacks[@oraclecloning]. Specifying a custom protocol extension in practice just means settling on alternative domain separators (see Sec. \ref{symmetric-keys}, Fig. \ref{img:HashingTree}). +## Using custom domain separators in the Rosenpass application + +The Rosenpass application supports protocol extensions to change the OSK domain separator without modification of the source code. + +The following example configuration file can be used to execute Rosenpass in outfile mode with custom domain separators. +In this mode, the Rosenpass application will write keys to the file specified with `key_out` and send notifications when new keys are exchanged via standard out. +This can be used to embed Rosenpass into third-party application. + +```toml +# peer-a.toml +public_key = "peer-a.pk" +secret_key = "peer-a.sk" +listen = ["[::1]:6789"] +verbosity = "Verbose" + +[[peers]] +public_key = "peer-b.pk" +key_out = "peer-a.osk" # path to store the key +osk_organization = "myorg.com" +osk_label = ["My Custom Messenger app", "Backend VPN Example Subusecase"] +``` + ## Extension: WireGuard PSK {#protocol-extension-wireguard-psk} The WireGuard PSK protocol extension is active by default; this is the mode where Rosenpass is used to provide post-quantum security for WireGuard. Hybrid security (i.e. redundant pre-quantum and post-quantum security) is achieved because WireGuard provides pre-quantum security, with or without Rosenpass. diff --git a/rosenpass/benches/handshake.rs b/rosenpass/benches/handshake.rs index e04c71c4..cceafcba 100644 --- a/rosenpass/benches/handshake.rs +++ b/rosenpass/benches/handshake.rs @@ -8,6 +8,7 @@ use rosenpass_ciphers::StaticKem; use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion}; fn handle( @@ -54,8 +55,18 @@ fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer::new(ska, pka.clone()), CryptoServer::new(skb, pkb.clone()), ); - a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?; - b.add_peer(Some(psk), pka, protocol_version)?; + a.add_peer( + Some(psk.clone()), + pkb, + protocol_version.clone(), + OskDomainSeparator::default(), + )?; + b.add_peer( + Some(psk), + pka, + protocol_version, + OskDomainSeparator::default(), + )?; Ok((a, b)) } diff --git a/rosenpass/benches/trace_handshake.rs b/rosenpass/benches/trace_handshake.rs index 37ad1a68..8f55ed57 100644 --- a/rosenpass/benches/trace_handshake.rs +++ b/rosenpass/benches/trace_handshake.rs @@ -12,6 +12,7 @@ use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets; use rosenpass_util::trace_bench::RpEventType; use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion}; const ITERATIONS: usize = 100; @@ -73,8 +74,18 @@ fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer::new(ska, pka.clone()), CryptoServer::new(skb, pkb.clone()), ); - a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?; - b.add_peer(Some(psk), pka, protocol_version)?; + a.add_peer( + Some(psk.clone()), + pkb, + protocol_version.clone(), + OskDomainSeparator::default(), + )?; + b.add_peer( + Some(psk), + pka, + protocol_version, + OskDomainSeparator::default(), + )?; Ok((a, b)) } diff --git a/rosenpass/src/app_server.rs b/rosenpass/src/app_server.rs index ba984bb3..cd406f92 100644 --- a/rosenpass/src/app_server.rs +++ b/rosenpass/src/app_server.rs @@ -26,6 +26,7 @@ use rosenpass_wireguard_broker::{WireguardBrokerCfg, WireguardBrokerMio, WG_KEY_ use crate::config::{ProtocolVersion, Verbosity}; use crate::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use crate::protocol::osk_domain_separator::OskDomainSeparator; use crate::protocol::timing::Timing; use crate::protocol::{BuildCryptoServer, CryptoServer, HostIdentification, PeerPtr}; @@ -1013,6 +1014,7 @@ impl AppServer { /// # Examples /// /// See [Self::new]. + #[allow(clippy::too_many_arguments)] pub fn add_peer( &mut self, psk: Option, @@ -1021,11 +1023,16 @@ impl AppServer { broker_peer: Option, hostname: Option, protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, ) -> anyhow::Result { 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, protocol_version), - ConstructionSite::Product(srv) => srv.add_peer(psk, pk, protocol_version.into())?, + ConstructionSite::Builder(builder) => { + builder.add_peer(psk, pk, protocol_version, osk_domain_separator) + } + ConstructionSite::Product(srv) => { + srv.add_peer(psk, pk, protocol_version.into(), osk_domain_separator)? + } }; assert!(pn == self.peers.len()); diff --git a/rosenpass/src/cli.rs b/rosenpass/src/cli.rs index 107ec9b6..186f617a 100644 --- a/rosenpass/src/cli.rs +++ b/rosenpass/src/cli.rs @@ -491,6 +491,7 @@ impl CliArgs { broker_peer, cfg_peer.endpoint.clone(), cfg_peer.protocol_version.into(), + cfg_peer.osk_domain_separator.try_into()?, )?; } diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index 403c2c86..d74c7760 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize}; use rosenpass_util::file::{fopen_w, LoadValue, Visibility}; use crate::protocol::basic_types::{SPk, SSk}; +use crate::protocol::osk_domain_separator::OskDomainSeparator; use crate::app_server::AppServer; @@ -153,6 +154,73 @@ pub struct RosenpassPeer { #[serde(default)] /// The protocol version to use for the exchange pub protocol_version: ProtocolVersion, + + /// Allows using a custom domain separator + #[serde(flatten)] + pub osk_domain_separator: RosenpassPeerOskDomainSeparator, +} + +/// Configuration for [crate::protocol::osk_domain_separator::OskDomainSeparator] +/// +/// Refer to its documentation for more information and examples of how to use this. +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RosenpassPeerOskDomainSeparator { + /// If Rosenpass is used for purposes other then securing WireGuard, + /// a custom domain separator and domain separator must be specified. + /// + /// Use `osk_organization` to indicate the organization who specifies the use case + /// and `osk_label` for a specific purpose within that organization. + /// + /// ```toml + /// [[peer]] + /// public_key = "my_public_key" + /// ... + /// osk_organization = "myorg.com" + /// osk_label = ["My Custom Messenger app"] + /// ``` + pub osk_organization: Option, + // If Rosenpass is used for purposes other then securing WireGuard, + /// a custom domain separator and domain separator must be specified. + /// + /// Use `osk_organization` to indicate the organization who specifies the use case + /// and `osk_label` for a specific purpose within that organization. + /// + /// ```toml + /// [[peer]] + /// public_key = "my_public_key" + /// ... + /// osk_namespace = "myorg.com" + /// osk_label = ["My Custom Messenger app"] + /// ``` + pub osk_label: Option>, +} + +impl RosenpassPeerOskDomainSeparator { + pub fn org_and_label(&self) -> anyhow::Result)>> { + match (&self.osk_organization, &self.osk_label) { + (None, None) => Ok(None), + (Some(org), Some(label)) => Ok(Some((&org, &label))), + (Some(_), None) => bail!("Specified osk_organization but not osk_label in config file. You need to specify both, or none."), + (None, Some(_)) => bail!("Specified osk_label but not osk_organization in config file. You need to specify both, or none."), + } + } + + pub fn validate(&self) -> anyhow::Result<()> { + let _org_and_label: Option<(_, _)> = self.org_and_label()?; + Ok(()) + } +} + +impl TryFrom for OskDomainSeparator { + type Error = anyhow::Error; + + fn try_from(val: RosenpassPeerOskDomainSeparator) -> anyhow::Result { + match val.org_and_label()? { + None => Ok(OskDomainSeparator::default()), + Some((org, label)) => Ok(OskDomainSeparator::custom_utf8(org, label)), + } + } } /// Information for supplying exchanged keys directly to WireGuard @@ -341,6 +409,10 @@ impl Rosenpass { ); } } + + if let Err(e) = peer.osk_domain_separator.validate() { + bail!("Invalid OSK domain separation configuration for peer {i}: {e}"); + } } Ok(()) diff --git a/rosenpass/src/hash_domains.rs b/rosenpass/src/hash_domains.rs index a1ac2b22..cc0333b1 100644 --- a/rosenpass/src/hash_domains.rs +++ b/rosenpass/src/hash_domains.rs @@ -295,25 +295,21 @@ hash_domain_ns!( /// 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"); + _ckextract, cke_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"); + cke_user, cke_user_rosenpass, "rosenpass.eu"); hash_domain!( /// Chaining key domain separator for deriving the key sent to WireGuard. /// @@ -325,4 +321,4 @@ hash_domain!( /// Check out its source code! /// /// See the [module](self) documentation on how to use the hash domains in general. - _rp, osk, "wireguard psk"); + cke_user_rosenpass, ext_wireguard_psk_osk, "wireguard psk"); diff --git a/rosenpass/src/protocol/basic_types.rs b/rosenpass/src/protocol/basic_types.rs index f3acfaba..00078ac6 100644 --- a/rosenpass/src/protocol/basic_types.rs +++ b/rosenpass/src/protocol/basic_types.rs @@ -18,6 +18,8 @@ pub type ESk = Secret<{ EphemeralKem::SK_LEN }>; /// Symmetric key pub type SymKey = Secret; +/// Variant of [SymKey] for use cases where the value is public +pub type PublicSymKey = [u8; 32]; /// Peer ID (derived from the public key, see the hash derivations in the [whitepaper](https://rosenpass.eu/whitepaper.pdf)) pub type PeerId = Public; diff --git a/rosenpass/src/protocol/build_crypto_server.rs b/rosenpass/src/protocol/build_crypto_server.rs index 73243b3d..7929a47d 100644 --- a/rosenpass/src/protocol/build_crypto_server.rs +++ b/rosenpass/src/protocol/build_crypto_server.rs @@ -6,6 +6,7 @@ use rosenpass_util::{build::Build, result::ensure_or}; use crate::config::ProtocolVersion; use super::basic_types::{SPk, SSk, SymKey}; +use super::osk_domain_separator::OskDomainSeparator; use super::{CryptoServer, PeerPtr}; #[derive(Debug, Clone)] @@ -148,20 +149,23 @@ pub struct MissingKeypair; /// /// ```rust /// use rosenpass_util::build::Build; -/// use rosenpass::protocol::basic_types::{SPk, SymKey}; -/// use rosenpass::protocol::{BuildCryptoServer, Keypair, PeerParams}; +/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets; +/// /// use rosenpass::config::ProtocolVersion; /// +/// use rosenpass::protocol::basic_types::{SPk, SymKey}; +/// use rosenpass::protocol::{BuildCryptoServer, Keypair, PeerParams}; +/// use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; +/// /// // 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 peer1 = PeerParams { psk: Some(SymKey::random()), pk: SPk::random(), protocol_version: ProtocolVersion::V02, osk_domain_separator: OskDomainSeparator::default() }; +/// let peer2 = PeerParams { psk: None, pk: SPk::random(), protocol_version: ProtocolVersion::V02, osk_domain_separator: OskDomainSeparator::default() }; /// /// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![peer1]); -/// builder.add_peer(peer2.psk.clone(), peer2.pk, ProtocolVersion::V02); +/// builder.add_peer(peer2.psk.clone(), peer2.pk, ProtocolVersion::V02, OskDomainSeparator::default()); /// /// let server = builder.build().expect("build failed"); /// assert_eq!(server.peers.len(), 2); @@ -191,16 +195,17 @@ impl Build for BuildCryptoServer { let mut srv = CryptoServer::new(sk, pk); - for ( - idx, - PeerParams { + for (idx, params) in self.peers.into_iter().enumerate() { + let PeerParams { psk, pk, protocol_version, - }, - ) in self.peers.into_iter().enumerate() - { - let PeerPtr(idx2) = srv.add_peer(psk, pk, protocol_version.into())?; + osk_domain_separator, + } = params; + + let PeerPtr(idx2) = + srv.add_peer(psk, pk, protocol_version.into(), osk_domain_separator)?; + assert!(idx == idx2, "Peer id changed during CryptoServer construction from {idx} to {idx2}. This is a developer error.") } @@ -223,6 +228,7 @@ pub struct PeerParams { pub pk: SPk, /// The used protocol version. pub protocol_version: ProtocolVersion, + pub osk_domain_separator: OskDomainSeparator, } impl BuildCryptoServer { @@ -321,13 +327,15 @@ impl BuildCryptoServer { /// /// ```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::basic_types::{SymKey, SPk}; /// use rosenpass::protocol::{BuildCryptoServer, Keypair}; + /// use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; + /// + /// // 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(); /// /// // Deferred initialization: Create builder first, add some peers later /// let keypair_option = Some(Keypair::random()); @@ -340,7 +348,7 @@ impl BuildCryptoServer { /// // 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); + /// builder.with_added_peer(Some(pre_shared_key.clone()), public_key.clone(), ProtocolVersion::V02, OskDomainSeparator::default()); /// /// // New server instances will then start with the peer being registered already /// let server = builder.build().expect("build failed"); @@ -355,12 +363,14 @@ impl BuildCryptoServer { psk: Option, pk: SPk, protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, ) -> &mut Self { // TODO: Check here already whether peer was already added self.peers.push(PeerParams { psk, pk, protocol_version, + osk_domain_separator, }); self } @@ -371,9 +381,10 @@ impl BuildCryptoServer { psk: Option, pk: SPk, protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, ) -> PeerPtr { let id = PeerPtr(self.peers.len()); - self.with_added_peer(psk, pk, protocol_version); + self.with_added_peer(psk, pk, protocol_version, osk_domain_separator); id } @@ -394,6 +405,7 @@ impl BuildCryptoServer { /// /// use rosenpass::protocol::basic_types::{SymKey, SPk}; /// use rosenpass::protocol::{BuildCryptoServer, Keypair}; + /// use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; /// /// // We have to define the security policy before using Secrets. /// secret_policy_use_only_malloc_secrets(); @@ -401,7 +413,7 @@ impl BuildCryptoServer { /// 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); + /// builder.add_peer(None, peer_pk, ProtocolVersion::V02, OskDomainSeparator::default()); /// /// // Extract configuration parameters from the decomissioned builder /// let (keypair_option, peers) = builder.take_parts(); diff --git a/rosenpass/src/protocol/mod.rs b/rosenpass/src/protocol/mod.rs index 72bead00..7de618fe 100644 --- a/rosenpass/src/protocol/mod.rs +++ b/rosenpass/src/protocol/mod.rs @@ -31,6 +31,7 @@ //! //! use rosenpass::protocol::basic_types::{SSk, SPk, MsgBuf, SymKey}; //! use rosenpass::protocol::{PeerPtr, CryptoServer}; +//! use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; //! //! # fn main() -> anyhow::Result<()> { //! // Set security policy for storing secrets @@ -52,8 +53,8 @@ //! 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)?; +//! a.add_peer(Some(psk.clone()), peer_b_pk, ProtocolVersion::V03, OskDomainSeparator::default())?; +//! b.add_peer(Some(psk), peer_a_pk, ProtocolVersion::V03, OskDomainSeparator::default())?; //! //! // declare buffers for message exchange //! let (mut a_buf, mut b_buf) = (MsgBuf::zero(), MsgBuf::zero()); @@ -84,6 +85,7 @@ pub mod basic_types; pub mod constants; pub mod cookies; pub mod index; +pub mod osk_domain_separator; pub mod testutils; pub mod timing; pub mod zerocopy; diff --git a/rosenpass/src/protocol/osk_domain_separator.rs b/rosenpass/src/protocol/osk_domain_separator.rs new file mode 100644 index 00000000..e8f2b9ec --- /dev/null +++ b/rosenpass/src/protocol/osk_domain_separator.rs @@ -0,0 +1,91 @@ +//! Management of domain separators for the OSK (output key) in the rosenpass protocol +//! +//! The domain separator is there to ensure that keys are bound to the purpose they are used for. +//! +//! See the whitepaper section on protocol extensions for more details on how this is used. +//! +//! # See also +//! +//! - [crate::protocol::Peer] +//! - [crate::protocol::CryptoServer::add_peer] +//! - [crate::protocol::CryptoServer::osk] +//! +//! # Examples +//! +//! There are some basic examples of using custom domain separators in the examples of +//! [super::CryptoServer::poll]. Look for the test function `test_osk_label_mismatch()` +//! in particular. + +use rosenpass_ciphers::subtle::keyed_hash::KeyedHash; +use rosenpass_util::result::OkExt; + +use crate::hash_domains; + +use super::basic_types::PublicSymKey; + +/// The OSK (output shared key) domain separator to use for a specific peer +/// +#[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Default)] +pub enum OskDomainSeparator { + /// By default we use the domain separator that indicates that the resulting keys + /// are used by WireGuard to establish a connection + #[default] + ExtensionWireguardPsk, + /// Used for user-defined domain separators + Custom { + /// A globally unique string identifying the vendor or group who defines this domain + /// separator (we use our domain ourselves – "rosenpass.eu") + namespace: Vec, + /// Any custom labels within that namespace. Could be descriptive prose. + labels: Vec>, + }, +} + +impl OskDomainSeparator { + /// Construct [OskDomainSeparator::ExtensionWireguardPsk] + pub fn for_wireguard_psk() -> Self { + Self::ExtensionWireguardPsk + } + + /// Construct [OskDomainSeparator::Custom] from strings + pub fn custom_utf8(namespace: &str, label: I) -> Self + where + I: IntoIterator, + T: AsRef, + { + let namespace = namespace.as_bytes().to_owned(); + let labels = label + .into_iter() + .map(|e| e.as_ref().as_bytes().to_owned()) + .collect::>(); + Self::Custom { namespace, labels } + } + + /// Variant of [Self::custom_utf8] that takes just one label (instead of a sequence) + pub fn custom_utf8_single_label(namespace: &str, label: &str) -> Self { + Self::custom_utf8(namespace, std::iter::once(label)) + } + + /// The domain separator is not just an encoded string, it instead uses + /// [rosenpass_ciphers::hash_domain::HashDomain], starting from [hash_domains::cke_user]. + /// + /// This means, that the domain separator is really a sequence of multiple different domain + /// separators, each of which is allowed to be quite long. This is very useful as it allows + /// users to avoid specifying complex, prosaic domain separators. To ensure that this does not + /// force us create extra overhead when the protocol is executed, this sequence of strings is + /// compressed into a single, fixed-length hash of all the inputs. This hash could be created + /// at program startup and cached. + /// + /// This function generates this fixed-length hash. + pub fn compress_with(&self, hash_choice: KeyedHash) -> anyhow::Result { + use OskDomainSeparator as O; + match &self { + O::ExtensionWireguardPsk => hash_domains::ext_wireguard_psk_osk(hash_choice), + O::Custom { namespace, labels } => hash_domains::cke_user(hash_choice)? + .mix(namespace)? + .mix_many(labels)? + .into_value() + .ok(), + } + } +} diff --git a/rosenpass/src/protocol/protocol.rs b/rosenpass/src/protocol/protocol.rs index a40f83db..778a1bed 100644 --- a/rosenpass/src/protocol/protocol.rs +++ b/rosenpass/src/protocol/protocol.rs @@ -36,7 +36,8 @@ use rosenpass_util::{ use crate::{hash_domains, msgs::*, RosenpassError}; use super::basic_types::{ - BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, SPk, SSk, SessionId, SymKey, XAEADNonce, + BiscuitId, EPk, ESk, MsgBuf, PeerId, PeerNo, PublicSymKey, SPk, SSk, SessionId, SymKey, + XAEADNonce, }; use super::constants::{ BISCUIT_EPOCH, COOKIE_SECRET_EPOCH, COOKIE_SECRET_LEN, COOKIE_VALUE_LEN, @@ -46,6 +47,7 @@ use super::constants::{ }; use super::cookies::{BiscuitKey, CookieSecret, CookieStore}; use super::index::{PeerIndex, PeerIndexKey}; +use super::osk_domain_separator::OskDomainSeparator; use super::timing::{has_happened, Timing, BCE, UNENDING}; use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; @@ -179,6 +181,7 @@ impl From for ProtocolVersion { /// /// use rosenpass::protocol::basic_types::{SSk, SPk, SymKey}; /// use rosenpass::protocol::{Peer, ProtocolVersion}; +/// use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; /// /// rosenpass_secret_memory::secret_policy_try_use_memfd_secrets(); /// @@ -191,13 +194,13 @@ impl From for ProtocolVersion { /// let psk = SymKey::random(); /// /// // Creation with a PSK -/// let peer_psk = Peer::new(psk, spkt.clone(), ProtocolVersion::V03); +/// let peer_psk = Peer::new(psk, spkt.clone(), ProtocolVersion::V03, OskDomainSeparator::default()); /// /// // Creation without a PSK -/// let peer_nopsk = Peer::new(SymKey::zero(), spkt, ProtocolVersion::V03); +/// let peer_nopsk = Peer::new(SymKey::zero(), spkt, ProtocolVersion::V03, OskDomainSeparator::default()); /// /// // Create a second peer -/// let peer_psk_2 = Peer::new(SymKey::zero(), spkt2, ProtocolVersion::V03); +/// let peer_psk_2 = Peer::new(SymKey::zero(), spkt2, ProtocolVersion::V03, OskDomainSeparator::default()); /// /// // Peer ID does not depend on PSK, but it does depend on the public key /// assert_eq!(peer_psk.pidt()?, peer_nopsk.pidt()?); @@ -253,9 +256,10 @@ pub struct Peer { /// This allows us to perform retransmission for the purpose of dealing with packet loss /// on the network without having to account for it in the cryptographic code itself. pub known_init_conf_response: Option, - /// The protocol version used by with this peer. pub protocol_version: ProtocolVersion, + /// Domain separator for generated OSKs + pub osk_domain_separator: OskDomainSeparator, } impl Peer { @@ -282,6 +286,7 @@ impl Peer { handshake: None, known_init_conf_response: None, protocol_version, + osk_domain_separator: OskDomainSeparator::default(), } } } @@ -1222,6 +1227,7 @@ impl CryptoServer { /// ``` /// use std::ops::DerefMut; /// use rosenpass::protocol::basic_types::{SSk, SPk, SymKey}; + /// use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; /// use rosenpass::protocol::{CryptoServer, ProtocolVersion}; /// use rosenpass_ciphers::StaticKem; /// use rosenpass_cipher_traits::primitives::Kem; @@ -1237,7 +1243,7 @@ impl CryptoServer { /// /// let psk = SymKey::random(); /// // We use the latest protocol version for the example. - /// let peer = srv.add_peer(Some(psk), spkt.clone(), ProtocolVersion::V03)?; + /// let peer = srv.add_peer(Some(psk), spkt.clone(), ProtocolVersion::V03, OskDomainSeparator::for_wireguard_psk())?; /// /// assert_eq!(peer.get(&srv).spkt, spkt); /// @@ -1248,6 +1254,7 @@ impl CryptoServer { psk: Option, pk: SPk, protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, ) -> Result { let peer = Peer { psk: psk.unwrap_or_else(SymKey::zero), @@ -1258,6 +1265,7 @@ impl CryptoServer { known_init_conf_response: None, initiation_requested: false, protocol_version, + osk_domain_separator, }; let peerid = peer.pidt()?; let peerno = self.peers.len(); @@ -1447,7 +1455,12 @@ impl Peer { /// # Examples /// /// See example in [Self]. - pub fn new(psk: SymKey, pk: SPk, protocol_version: ProtocolVersion) -> Peer { + pub fn new( + psk: SymKey, + pk: SPk, + protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, + ) -> Peer { Peer { psk, spkt: pk, @@ -1457,6 +1470,7 @@ impl Peer { known_init_conf_response: None, initiation_requested: false, protocol_version, + osk_domain_separator, } } @@ -3310,28 +3324,61 @@ impl HandshakeState { } impl CryptoServer { + /// Variant of [Self::osk] that allows a custom, already compressed domain separator to be specified + /// + /// Refer to the documentation of [Self::osk] for more information. + pub fn osk_with_compressed_domain_separator( + &self, + peer: PeerPtr, + compressed_domain_separator: &PublicSymKey, + ) -> Result { + let session = peer + .session() + .get(self) + .as_ref() + .with_context(|| format!("No current session for peer {:?}", peer))?; + + Ok(session.ck.mix(compressed_domain_separator)?.into_secret()) + } + + /// Variant of [Self::osk] that allows a custom domain separator to be specified + /// + /// Refer to the documentation of [Self::osk] for more information. + pub fn osk_with_domain_separator( + &self, + peer: PeerPtr, + domain_separator: &OskDomainSeparator, + ) -> Result { + let hash_choice = peer.get(self).protocol_version.keyed_hash(); + let compressed_domain_separator = domain_separator.compress_with(hash_choice)?; + self.osk_with_compressed_domain_separator(peer, &compressed_domain_separator) + } + /// Get the shared key that was established with given peer /// /// Fail if no session is available with the peer /// + /// # See also + /// + /// - [Self::osk_with_domain_separator] + /// - [Self::osk_with_compressed_domain_separator] + /// /// # Examples /// /// See the example in [crate::protocol] for an incomplete but working example /// of how to perform a key exchange using Rosenpass. /// /// See the example in [CryptoServer::poll] for a complete example. + /// + /// See the documentation and examples in [super::osk_domain_separator] for more information + /// about using custom domain separators. pub fn osk(&self, peer: PeerPtr) -> Result { - let session = peer - .session() + let hash_choice = peer.get(self).protocol_version.keyed_hash(); + let compressed_domain_separator = peer .get(self) - .as_ref() - .with_context(|| format!("No current session for peer {:?}", peer))?; - Ok(session - .ck - .mix(&hash_domains::osk( - peer.get(self).protocol_version.keyed_hash(), - )?)? - .into_secret()) + .osk_domain_separator + .compress_with(hash_choice)?; + self.osk_with_compressed_domain_separator(peer, &compressed_domain_separator) } } diff --git a/rosenpass/src/protocol/test.rs b/rosenpass/src/protocol/test.rs index c525c7e9..8bd17f91 100644 --- a/rosenpass/src/protocol/test.rs +++ b/rosenpass/src/protocol/test.rs @@ -13,6 +13,7 @@ use crate::msgs::{EmptyData, Envelope, InitConf, InitHello, MsgType, RespHello, use super::basic_types::{MsgBuf, SPk, SSk, SymKey}; use super::constants::REKEY_AFTER_TIME_RESPONDER; +use super::osk_domain_separator::OskDomainSeparator; use super::zerocopy::{truncating_cast_into, truncating_cast_into_nomut}; use super::{ CryptoServer, HandleMsgResult, HostIdentification, KnownInitConfResponsePtr, PeerPtr, @@ -145,8 +146,18 @@ fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer::new(ska, pka.clone()), CryptoServer::new(skb, pkb.clone()), ); - a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?; - b.add_peer(Some(psk), pka, protocol_version)?; + a.add_peer( + Some(psk.clone()), + pkb, + protocol_version.clone(), + OskDomainSeparator::default(), + )?; + b.add_peer( + Some(psk), + pka, + protocol_version, + OskDomainSeparator::default(), + )?; Ok((a, b)) } @@ -619,8 +630,18 @@ fn init_conf_retransmission(protocol_version: ProtocolVersion) -> anyhow::Result let mut b = CryptoServer::new(skb, pkb.clone()); // introduce peers to each other - let b_peer = a.add_peer(None, pkb, protocol_version.clone())?; - let a_peer = b.add_peer(None, pka, protocol_version.clone())?; + let b_peer = a.add_peer( + None, + pkb, + protocol_version.clone(), + OskDomainSeparator::default(), + )?; + let a_peer = b.add_peer( + None, + pka, + protocol_version.clone(), + OskDomainSeparator::default(), + )?; // Execute protocol up till the responder confirmation (EmptyData) let ih1 = proc_initiation(&mut a, b_peer)?; diff --git a/rosenpass/src/protocol/testutils.rs b/rosenpass/src/protocol/testutils.rs index 2cc85a6f..8ba51832 100644 --- a/rosenpass/src/protocol/testutils.rs +++ b/rosenpass/src/protocol/testutils.rs @@ -7,6 +7,7 @@ use rosenpass_ciphers::StaticKem; use super::{ basic_types::{SPk, SSk}, + osk_domain_separator::OskDomainSeparator, CryptoServer, PeerPtr, ProtocolVersion, }; @@ -26,7 +27,12 @@ impl ServerForTesting { let (mut sskt, mut spkt) = (SSk::zero(), SPk::zero()); StaticKem.keygen(sskt.secret_mut(), spkt.deref_mut())?; - let peer = srv.add_peer(None, spkt.clone(), protocol_version)?; + let peer = srv.add_peer( + None, + spkt.clone(), + protocol_version, + OskDomainSeparator::default(), + )?; let peer_keys = (sskt, spkt); Ok(ServerForTesting { diff --git a/rosenpass/tests/api-integration-tests-api-setup.rs b/rosenpass/tests/api-integration-tests-api-setup.rs index 8313bb87..ee9341a2 100644 --- a/rosenpass/tests/api-integration-tests-api-setup.rs +++ b/rosenpass/tests/api-integration-tests-api-setup.rs @@ -106,6 +106,7 @@ fn api_integration_api_setup(protocol_version: ProtocolVersion) -> anyhow::Resul extra_params: vec![], }), protocol_version: protocol_version.clone(), + osk_domain_separator: Default::default(), }], }; @@ -127,6 +128,7 @@ fn api_integration_api_setup(protocol_version: ProtocolVersion) -> anyhow::Resul pre_shared_key: None, wg: None, protocol_version: protocol_version.clone(), + osk_domain_separator: Default::default(), }], }; diff --git a/rosenpass/tests/api-integration-tests.rs b/rosenpass/tests/api-integration-tests.rs index b3b638d7..18380c27 100644 --- a/rosenpass/tests/api-integration-tests.rs +++ b/rosenpass/tests/api-integration-tests.rs @@ -83,6 +83,7 @@ fn api_integration_test(protocol_version: ProtocolVersion) -> anyhow::Result<()> pre_shared_key: None, wg: None, protocol_version: protocol_version.clone(), + osk_domain_separator: Default::default(), }], }; @@ -104,6 +105,7 @@ fn api_integration_test(protocol_version: ProtocolVersion) -> anyhow::Result<()> pre_shared_key: None, wg: None, protocol_version: protocol_version.clone(), + osk_domain_separator: Default::default(), }], }; diff --git a/rosenpass/tests/app_server_example.rs b/rosenpass/tests/app_server_example.rs index bd5a77e5..4b8169a4 100644 --- a/rosenpass/tests/app_server_example.rs +++ b/rosenpass/tests/app_server_example.rs @@ -6,8 +6,8 @@ use rosenpass_ciphers::StaticKem; use rosenpass_util::{file::LoadValueB64, functional::run, mem::DiscardResultExt, result::OkExt}; use rosenpass::app_server::{AppServer, AppServerTest, MAX_B64_KEY_SIZE}; -use rosenpass::config::ProtocolVersion; use rosenpass::protocol::basic_types::{SPk, SSk, SymKey}; +use rosenpass::{config::ProtocolVersion, protocol::osk_domain_separator::OskDomainSeparator}; #[test] fn key_exchange_with_app_server_v02() -> anyhow::Result<()> { @@ -62,7 +62,8 @@ fn key_exchange_with_app_server(protocol_version: ProtocolVersion) -> anyhow::Re outfile, broker_peer, hostname, - protocol_version.clone(), + protocol_version, + OskDomainSeparator::default(), )?; srv.app_srv.event_loop() diff --git a/rosenpass/tests/poll_example.rs b/rosenpass/tests/poll_example.rs index 62fe4f8f..10cc8757 100644 --- a/rosenpass/tests/poll_example.rs +++ b/rosenpass/tests/poll_example.rs @@ -10,6 +10,7 @@ use rosenpass_ciphers::StaticKem; use rosenpass_util::result::OkExt; use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey}; +use rosenpass::protocol::osk_domain_separator::OskDomainSeparator; use rosenpass::protocol::testutils::time_travel_forward; use rosenpass::protocol::timing::{Timing, UNENDING}; use rosenpass::protocol::{CryptoServer, HostIdentification, PeerPtr, PollResult, ProtocolVersion}; @@ -19,19 +20,38 @@ use rosenpass::protocol::{CryptoServer, HostIdentification, PeerPtr, PollResult, #[test] fn test_successful_exchange_with_poll_v02() -> anyhow::Result<()> { - test_successful_exchange_with_poll(ProtocolVersion::V02) + test_successful_exchange_with_poll(ProtocolVersion::V02, OskDomainSeparator::default()) } #[test] fn test_successful_exchange_with_poll_v03() -> anyhow::Result<()> { - test_successful_exchange_with_poll(ProtocolVersion::V03) + test_successful_exchange_with_poll(ProtocolVersion::V03, OskDomainSeparator::default()) } -fn test_successful_exchange_with_poll(protocol_version: ProtocolVersion) -> anyhow::Result<()> { +#[test] +fn test_successful_exchange_with_poll_v02_custom_domain_separator() -> anyhow::Result<()> { + test_successful_exchange_with_poll( + ProtocolVersion::V02, + OskDomainSeparator::custom_utf8_single_label("example.org", "Example Label"), + ) +} + +#[test] +fn test_successful_exchange_with_poll_v03_custom_domain_separator() -> anyhow::Result<()> { + test_successful_exchange_with_poll( + ProtocolVersion::V03, + OskDomainSeparator::custom_utf8_single_label("example.org", "Example Label"), + ) +} + +fn test_successful_exchange_with_poll( + protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, +) -> anyhow::Result<()> { // Set security policy for storing secrets; choose the one that is faster for testing rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); - let mut sim = RosenpassSimulator::new(protocol_version)?; + let mut sim = RosenpassSimulator::new(protocol_version, osk_domain_separator)?; sim.poll_loop(150)?; // Poll 75 times let transcript = sim.transcript; @@ -104,7 +124,7 @@ fn test_successful_exchange_under_packet_loss( rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); // Create the simulator - let mut sim = RosenpassSimulator::new(protocol_version)?; + let mut sim = RosenpassSimulator::new(protocol_version, OskDomainSeparator::default())?; // Make sure the servers are set to under load condition sim.srv_a.under_load = true; @@ -181,6 +201,94 @@ fn test_successful_exchange_under_packet_loss( Ok(()) } +#[test] +fn test_osk_label_mismatch() -> anyhow::Result<()> { + // Set security policy for storing secrets; choose the one that is faster for testing + rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets(); + + let ds_wg = OskDomainSeparator::for_wireguard_psk(); + let ds_custom1 = OskDomainSeparator::custom_utf8("example.com", ["Example Label"]); + let ds_custom2 = + OskDomainSeparator::custom_utf8("example.com", ["Example Label", "Second Token"]); + + // Create the simulator + let mut sim = RosenpassSimulator::new(ProtocolVersion::V03, ds_custom1.clone())?; + assert_eq!(sim.srv_a.srv.peers[0].osk_domain_separator, ds_custom1); + assert_eq!(sim.srv_b.srv.peers[0].osk_domain_separator, ds_custom1); + + // Deliberately produce a label mismatch + sim.srv_b.srv.peers[0].osk_domain_separator = ds_custom2.clone(); + assert_eq!(sim.srv_a.srv.peers[0].osk_domain_separator, ds_custom1); + assert_eq!(sim.srv_b.srv.peers[0].osk_domain_separator, ds_custom2); + + // Perform the key exchanges + for _ in 0..300 { + let ev = sim.poll()?; + + assert!(!matches!(ev, TranscriptEvent::CompletedExchange(_)), + "We deliberately provoked a mismatch in OSK domain separator, but still saw a successfully completed key exchange"); + + // Wait for a key exchange that failed with a KeyMismatch event + let (osk_a_custom1, osk_b_custom2) = match ev { + TranscriptEvent::FailedExchangeWithKeyMismatch(osk_a, osk_b) => { + (osk_a.clone(), osk_b.clone()) + } + _ => continue, + }; + + // The OSKs have been produced through the call to the function CryptoServer::osk(…) + assert_eq!( + sim.srv_a.srv.osk(PeerPtr(0))?.secret(), + osk_a_custom1.secret() + ); + assert_eq!( + sim.srv_b.srv.osk(PeerPtr(0))?.secret(), + osk_b_custom2.secret() + ); + + // They are not matching (obviously) + assert_ne!(osk_a_custom1.secret(), osk_b_custom2.secret()); + + // We can manually generate OSKs with matching labels + let osk_a_custom2 = sim + .srv_a + .srv + .osk_with_domain_separator(PeerPtr(0), &ds_custom2)?; + let osk_b_custom1 = sim + .srv_b + .srv + .osk_with_domain_separator(PeerPtr(0), &ds_custom1)?; + let osk_a_wg = sim + .srv_a + .srv + .osk_with_domain_separator(PeerPtr(0), &ds_wg)?; + let osk_b_wg = sim + .srv_b + .srv + .osk_with_domain_separator(PeerPtr(0), &ds_wg)?; + + // The key exchange may have failed for some other reason, in this case we expect a + // successful-but-label-mismatch exchange later in the protocol + if osk_a_custom1.secret() != osk_b_custom1.secret() { + continue; + } + + // But if one of the labeled keys match, all should match + assert_eq!(osk_a_custom2.secret(), osk_b_custom2.secret()); + assert_eq!(osk_a_wg.secret(), osk_b_wg.secret()); + + // But the three keys do not match each other + assert_ne!(osk_a_custom1.secret(), osk_a_custom2.secret()); + assert_ne!(osk_a_custom1.secret(), osk_a_wg.secret()); + assert_ne!(osk_a_custom2.secret(), osk_a_wg.secret()); + + // The test succeeded + return Ok(()); + } + + panic!("Test did not succeed even after allowing for a large number of communication rounds"); +} + type MessageType = u8; /// Lets record the events that are produced by Rosenpass @@ -193,6 +301,7 @@ enum TranscriptEvent { event: ServerEvent, }, CompletedExchange(SymKey), + FailedExchangeWithKeyMismatch(SymKey, SymKey), } #[derive(Debug)] @@ -292,7 +401,10 @@ struct SimulatorServer { impl RosenpassSimulator { /// Set up the simulator - fn new(protocol_version: ProtocolVersion) -> anyhow::Result { + fn new( + protocol_version: ProtocolVersion, + osk_domain_separator: OskDomainSeparator, + ) -> anyhow::Result { // Set up the first server 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())?; @@ -305,8 +417,18 @@ impl RosenpassSimulator { // Generate a PSK and introduce the Peers to each other. let psk = SymKey::random(); - let peer_a = srv_a.add_peer(Some(psk.clone()), peer_b_pk, protocol_version.clone())?; - let peer_b = srv_b.add_peer(Some(psk), peer_a_pk, protocol_version.clone())?; + let peer_a = srv_a.add_peer( + Some(psk.clone()), + peer_b_pk, + protocol_version.clone(), + osk_domain_separator.clone(), + )?; + let peer_b = srv_b.add_peer( + Some(psk), + peer_a_pk, + protocol_version.clone(), + osk_domain_separator.clone(), + )?; // Set up the individual server data structures let srv_a = SimulatorServer::new(srv_a, peer_b); @@ -566,10 +688,18 @@ impl ServerPtr { None => return Ok(()), }; + // Make sure the OSK of server A always comes first + let (osk_a, osk_b) = match self == ServerPtr::A { + true => (osk, other_osk), + false => (other_osk, osk), + }; + // Issue the successful exchange event if the OSKs are equal; // be careful to use constant time comparison for things like this! - if rosenpass_constant_time::memcmp(osk.secret(), other_osk.secret()) { - self.enqueue_upcoming_poll_event(sim, TE::CompletedExchange(osk)); + if rosenpass_constant_time::memcmp(osk_a.secret(), osk_b.secret()) { + self.enqueue_upcoming_poll_event(sim, TE::CompletedExchange(osk_a)); + } else { + self.enqueue_upcoming_poll_event(sim, TE::FailedExchangeWithKeyMismatch(osk_a, osk_b)); } Ok(()) diff --git a/rp/src/exchange.rs b/rp/src/exchange.rs index e4df77d1..d3f729cd 100644 --- a/rp/src/exchange.rs +++ b/rp/src/exchange.rs @@ -208,7 +208,10 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> { use rosenpass::{ app_server::{AppServer, BrokerPeer}, config::Verbosity, - protocol::basic_types::{SPk, SSk, SymKey}, + protocol::{ + basic_types::{SPk, SSk, SymKey}, + osk_domain_separator::OskDomainSeparator, + }, }; use rosenpass_secret_memory::Secret; use rosenpass_util::file::{LoadValue as _, LoadValueB64}; @@ -362,6 +365,7 @@ pub async fn exchange(options: ExchangeOptions) -> Result<()> { broker_peer, peer.endpoint.map(|x| x.to_string()), peer.protocol_version, + OskDomainSeparator::for_wireguard_psk(), )?; // Configure routes, equivalent to `ip route replace dev ` and set up diff --git a/util/src/fd.rs b/util/src/fd.rs index 658246ff..351d011c 100644 --- a/util/src/fd.rs +++ b/util/src/fd.rs @@ -561,7 +561,7 @@ mod tests { let mut file = FdIo(open_nullfd()?); let mut buf = [0; 10]; assert!(matches!(file.read(&mut buf), Ok(0) | Err(_))); - assert!(matches!(file.write(&buf), Err(_))); + assert!(file.write(&buf).is_err()); Ok(()) } diff --git a/util/src/length_prefix_encoding/encoder.rs b/util/src/length_prefix_encoding/encoder.rs index 1d3071e1..1e0c6d1f 100644 --- a/util/src/length_prefix_encoding/encoder.rs +++ b/util/src/length_prefix_encoding/encoder.rs @@ -618,7 +618,7 @@ mod tests { #[test] fn test_lpe_error_conversion_downcast_invalid() { let pos_error = PositionOutOfBufferBounds; - let sanity_error = SanityError::PositionOutOfBufferBounds(pos_error.into()); + let sanity_error = SanityError::PositionOutOfBufferBounds(pos_error); match MessageLenSanityError::try_from(sanity_error) { Ok(_) => panic!("Conversion should always fail (incompatible enum variant)"), Err(err) => assert!(matches!(err, PositionOutOfBufferBounds)), diff --git a/util/src/mem.rs b/util/src/mem.rs index f3f45f57..5d43f079 100644 --- a/util/src/mem.rs +++ b/util/src/mem.rs @@ -302,6 +302,6 @@ mod test_forgetting { drop_was_called.store(false, SeqCst); let forgetting = Forgetting::new(SetFlagOnDrop(drop_was_called.clone())); drop(forgetting); - assert_eq!(drop_was_called.load(SeqCst), false); + assert!(!drop_was_called.load(SeqCst)); } } From 7003671cde86d2a7cf8332de818b93e71e1740de Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Wed, 9 Jul 2025 10:08:05 +0200 Subject: [PATCH 165/174] fix: Regression caused by benchmarks CI keeps failing for external pull requests as GH's permission model was not fully accounted for --- .github/workflows/bench-primitives.yml | 2 +- .github/workflows/bench-protocol.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bench-primitives.yml b/.github/workflows/bench-primitives.yml index f7cb710d..b4d9da42 100644 --- a/.github/workflows/bench-primitives.yml +++ b/.github/workflows/bench-primitives.yml @@ -4,7 +4,7 @@ permissions: contents: write on: - pull_request: + #pull_request: push: env: diff --git a/.github/workflows/bench-protocol.yml b/.github/workflows/bench-protocol.yml index 6f7a8091..93c98646 100644 --- a/.github/workflows/bench-protocol.yml +++ b/.github/workflows/bench-protocol.yml @@ -4,7 +4,7 @@ permissions: contents: write on: - pull_request: + #pull_request: push: env: From 4cd2cdfcff8cc0a84c090d665d84d389af83f0aa Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:32:28 +0200 Subject: [PATCH 166/174] fix(rosenpass): Fix the error message if the secret key is invalid --- rosenpass/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosenpass/src/config.rs b/rosenpass/src/config.rs index d74c7760..a4f494b8 100644 --- a/rosenpass/src/config.rs +++ b/rosenpass/src/config.rs @@ -364,7 +364,7 @@ impl Rosenpass { // check the secret-key file is a valid key ensure!( SSk::load(&keypair.secret_key).is_ok(), - "could not load public-key file {:?}: invalid key", + "could not load secret-key file {:?}: invalid key", keypair.secret_key ); } From 3c744c253b104e8d61a97e9c6cd34e25263a2707 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:45:22 +0200 Subject: [PATCH 167/174] fix(CI+dependabot): add instructions on how to set up a repository to work with the supply-chain+dependabot accomodations --- supply-chain-CI.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 supply-chain-CI.md diff --git a/supply-chain-CI.md b/supply-chain-CI.md new file mode 100644 index 00000000..8e883596 --- /dev/null +++ b/supply-chain-CI.md @@ -0,0 +1,25 @@ +# Continuous Integration for supply chain protection + +This repository's CI uses non-standard mechanisms to harmonize the usage of `dependabot` together with [`cargo vet`](https://mozilla.github.io/cargo-vet/). Since cargo-vet audits for new versions of crates are rarely immediately available once dependabots bumps the version, +the exemptions for `cargo vet` have to be regenerated for each push request opened by dependabot. To make this work, some setup is neccessary to setup the CI. The required steps are as follows: + +1. Create a mew user on github. For the purpose of these instructions, we will assume that its mail address is `ci@example.com` and that its username is `ci-bot`. Protect this user account as you would any other user account that you intend to gve write permissions to. For example, setup MFA or protect the email address of the user. Make sure to verify your e-mail. +2. Add `ci-bot` as a member of your organizaton with write access to the repository. +3. In your organization, go to "Settings" -> "Personal Access tokens" -> "Settings". There select "Allow access via fine-grained personal access tokens" and save. Depending on your preferences either choose "Require administrator approval" or "Do not require administrator approval". +4. Create a new personal access token as `ci-bot` for the rosenpass repository. That is, in the settings for `ci-bot`, select "Developer settings" -> "Personal Access tokens" -> "Fine-grained tokens". Then click on "Generate new token". Enter a name of your choosing and choose an expiration date that you feel comfortable with. A shorter expiration period will requrie more manual management by you but is more secure than a longer one. Select your organization as the resource owner and select the rosenpass repository as the repository. Under "Repository permissions", grant "Read and write"-access to the "Contens" premission for the token. Grant no other permissions to the token, except for the read-only access to the "Metadata" permission, which is mandatory. Then generate the token and copy it for the next steps. +5. If you chose "Require administrator approval" in step 3, approve the fine grained access token by, as a organization administrator, going to "Settings" -> "Personal Access tokens" -> "Pending requests" and grant the request. +6. Now, with your account that has administrative permissions for the repository, open the settings page for the repository and select "Secrets and variables" -> "Actions" and click "New repository secret". In the name field enter "CI_BOT_PAT". This name is mandatory, since it is explicitly referenced in the supply-chain workflow. Below, enter the token that was generated in step 4. +7. Analogously to step 6, open the settings page for the repository and select "Secrets and variables" -> "Dependabot" and click "New repository secret". In the name field enter "CI_BOT_PAT". This name is mandatory, since it is explicitly referenced in the supply-chain workflow. Below, enter the token that was generated in step 4. + +## What this does + +For the `cargo vet` check in the CI for dependabot, the `cargo vet`-exemptions have to automatically be regenerated, because otherwise this CI job will always fail for dependabot PRs. After the exemptions have been regenerated, they need to be commited and pushed to the PR. This invalidates the CI run that pushed the commit so that it does not show up in the PR anymore but does not trigger a new CI run. This is a [protection by Github](https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow) to prevent infinite loops. However, in this case it prevents us from having a proper CI run for dependabot PRs. The solution to this is to execute `push` operation with a personal access token. + +## Preventing infinite loops + +The CI is configured to avoid infinite loops by only regenerating and pushing the `cargo vet` exemptions if the CI run happens with respect to a PR opened by dependabot and not for any other pushed or pull requests. In addition one of the following conditions has to be met: + +- The last commit was performed by dependabot +- The last commit message ends in `--regenerate-exemptions` + +Summarizing, the exemptions are only regenerated in the context of pull requests opened by dependabot and, the last commit was was performed by dependabot or the last commit message ends in `--regenerate-exemptions`. From afa62122641bea58d7bd0a21e4348602f9b16c34 Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:45:59 +0200 Subject: [PATCH 168/174] fix(CI+dependabot): adapt the supply-chain workflow for cargo-vet to work with dependabot, i.e. regenerating exemptions for dependabot and restart the CI afterwards --- .github/workflows/supply-chain.yml | 119 +++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 23 deletions(-) diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index de26200f..0c0db6af 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -28,10 +28,10 @@ jobs: ~/.cargo/registry/cache/ ~/.cache/cargo-supply-chain/ key: cargo-supply-chain-cache - - name: Install stable toolchain # Cargo-supply-chain is incompatible with older versions + - name: Install nightly toolchain run: | - rustup toolchain install stable - rustup default stable + rustup toolchain install nightly + rustup override set nightly - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-supply-chain @@ -39,7 +39,7 @@ jobs: - 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 +stable install --root ${{ runner.tool_cache }}/cargo-supply-chain cargo-supply-chain + 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 @@ -54,6 +54,8 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + with: + token: ${{ secrets.CI_BOT_PAT }} - uses: actions/cache@v4 with: path: | @@ -61,10 +63,10 @@ jobs: ~/.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. + - name: Install nightly toolchain run: | - rustup toolchain install stable - rustup default stable + rustup toolchain install nightly + rustup override set nightly - uses: actions/cache@v4 with: path: ${{ runner.tool_cache }}/cargo-vet @@ -72,24 +74,95 @@ jobs: - 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 +stable 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: cargo install --root ${{ runner.tool_cache }}/cargo-vet cargo-vet + - name: Check which event triggered this CI run, a push or a pull request. 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 }} + EVENT_NAME="${{ github.event_name }}" + IS_PR="false" + IS_PUSH="false" + if [[ "$EVENT_NAME" == "pull_request" ]]; then + echo "This CI run was triggered in the context of a pull request." + IS_PR="true" + elif [[ "$EVENT_NAME" == "push" ]]; then + echo "This CI run was triggered in the context of a push." + IS_PUSH="true" + else + echo "ERROR: This CI run was not triggered in the context of a pull request or a push. Exiting with error." + exit 1 + fi + echo "IS_PR=$IS_PR" >> $GITHUB_ENV + echo "IS_PUSH=$IS_PUSH" >> $GITHUB_ENV + - name: Check if last commit was by Dependabot + run: | + # Depending on the trigger for, the relevant commit has to be deduced differently. + if [[ "$IS_PR" == true ]]; then + # This is the commit ID for the last commit to the head branch of the pull request. + # If we used github.sha here instead, it would point to a merge commit between the PR and the main branch, which is only created for the CI run. + SHA="${{ github.event.pull_request.head.sha }}" + REF="${{ github.head_ref }}" + elif [[ "$IS_PUSH" == "true" ]]; then + SHA="${{ github.sha }}" # This is the last commit to the branch. + REF=${GITHUB_REF#refs/heads/} + else + echo "ERROR: This action only supports pull requests and push events as triggers. Exiting with error." + exit 1 + fi + echo "Commit SHA is $SHA" + echo "Branch is $REF" + + git fetch origin $REF # ensure that we are up to date. + git switch $REF # ensure that we are NOT in a detached HEAD state. This is important for the commit action in the end. + + COMMIT_AUTHOR=$(gh api repos/${{ github.repository }}/commits/$SHA --jq .author.login) # .author.login might be null, but for dependabot it will always be there and cannot be spoofed in contrast to .commit.author.name + echo "The author of the last commit is $COMMIT_AUTHOR" + if [[ "$COMMIT_AUTHOR" == "dependabot[bot]" ]]; then + echo "The last commit was made by dependabot" + LAST_COMMIT_IS_BY_DEPENDABOT=true + else + echo "The last commit was made by $COMMIT_AUTHOR not by dependabot" + LAST_COMMIT_IS_BY_DEPENDABOT=false + fi + echo "LAST_COMMIT_IS_BY_DEPENDABOT=$LAST_COMMIT_IS_BY_DEPENDABOT" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check if the last commit's message ends in "--regenerate-exemptions" + run: | + # Get commit message + COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s") + if [[ "$COMMIT_MESSAGE" == *"--regenerate-exemptions" ]]; then + echo "The last commit message ends in --regenerate-exemptions" + REGEN_EXEMP=true + else + echo "The last commit message does not end in --regenerate-exemptions" + REGEN_EXEMP=false + fi + echo "REGEN_EXEMP=$REGEN_EXEMP" >> $GITHUB_ENV + - name: Check if the CI run happens in the context of a dependabot PR # Even if a PR is created by dependabot, the last commit can, and often should be, the regeneration of the cargo vet exemptions. It could also be from an individual making manual changes. + run: | + IN_DEPENDABOT_PR_CONTEXT="false" + if [[ $IS_PR == "true" && "${{ github.event.pull_request.user.login }}" == "dependabot[bot]" ]]; then + IN_DEPENDABOT_PR_CONTEXT="true" + echo "This CI run is in the context of PR by dependabot." + else + echo "This CI run is NOT in the context of PR by dependabot." + IN_DEPENDABOT_PR_CONTEXT="false" + fi + echo "IN_DEPENDABOT_PR_CONTEXT=$IN_DEPENDABOT_PR_CONTEXT" >> $GITHUB_ENV + - name: Regenerate cargo vet exemptions if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested. + if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested + run: cargo vet regenerate exemptions + - name: Check for changes if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested. + if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested + run: git diff --exit-code || echo "Changes detected, committing..." + - name: Commit and push changes if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested. + if: success() && env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') + uses: stefanzweifel/git-auto-commit-action@v6 + with: + commit_message: Regenerate cargo vet exemptions + commit_user_name: rosenpass-ci-bot[bot] + commit_user_email: noreply@rosenpass.eu + commit_author: Rosenpass CI Bot + env: + GITHUB_TOKEN: ${{ secrets.CI_BOT_PAT }} - name: Invoke cargo-vet run: cargo vet --locked From ae060f7cfb32a6284ad1d7b9327051b7e320dc9a Mon Sep 17 00:00:00 2001 From: David Niehues <7667041+DavidNiehues@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:55:16 +0200 Subject: [PATCH 169/174] fixes to PR --- .github/workflows/supply-chain.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index 0c0db6af..458b9b38 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -55,7 +55,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - token: ${{ secrets.CI_BOT_PAT }} + token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/cache@v4 with: path: | @@ -92,6 +92,7 @@ jobs: fi echo "IS_PR=$IS_PR" >> $GITHUB_ENV echo "IS_PUSH=$IS_PUSH" >> $GITHUB_ENV + shell: bash - name: Check if last commit was by Dependabot run: | # Depending on the trigger for, the relevant commit has to be deduced differently. @@ -109,9 +110,7 @@ jobs: fi echo "Commit SHA is $SHA" echo "Branch is $REF" - - git fetch origin $REF # ensure that we are up to date. - git switch $REF # ensure that we are NOT in a detached HEAD state. This is important for the commit action in the end. + echo "REF=$REF" >> $GITHUB_ENV COMMIT_AUTHOR=$(gh api repos/${{ github.repository }}/commits/$SHA --jq .author.login) # .author.login might be null, but for dependabot it will always be there and cannot be spoofed in contrast to .commit.author.name echo "The author of the last commit is $COMMIT_AUTHOR" @@ -125,6 +124,7 @@ jobs: echo "LAST_COMMIT_IS_BY_DEPENDABOT=$LAST_COMMIT_IS_BY_DEPENDABOT" >> $GITHUB_ENV env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash - name: Check if the last commit's message ends in "--regenerate-exemptions" run: | # Get commit message @@ -137,6 +137,7 @@ jobs: REGEN_EXEMP=false fi echo "REGEN_EXEMP=$REGEN_EXEMP" >> $GITHUB_ENV + shell: bash - name: Check if the CI run happens in the context of a dependabot PR # Even if a PR is created by dependabot, the last commit can, and often should be, the regeneration of the cargo vet exemptions. It could also be from an individual making manual changes. run: | IN_DEPENDABOT_PR_CONTEXT="false" @@ -148,14 +149,18 @@ jobs: IN_DEPENDABOT_PR_CONTEXT="false" fi echo "IN_DEPENDABOT_PR_CONTEXT=$IN_DEPENDABOT_PR_CONTEXT" >> $GITHUB_ENV + shell: bash + - name: In case of a dependabot PR, ensure that we are not in a detached HEAD state + if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' + run: | + git fetch origin $REF # ensure that we are up to date. + git switch $REF # ensure that we are NOT in a detached HEAD state. This is important for the commit action in the end + shell: bash - name: Regenerate cargo vet exemptions if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested. if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested run: cargo vet regenerate exemptions - - name: Check for changes if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested. - if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested - run: git diff --exit-code || echo "Changes detected, committing..." - name: Commit and push changes if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested. - if: success() && env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') + if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') uses: stefanzweifel/git-auto-commit-action@v6 with: commit_message: Regenerate cargo vet exemptions From 8d81be56f31963c67e296b77bad54700d9d9423d Mon Sep 17 00:00:00 2001 From: Karolin Varner Date: Tue, 29 Jul 2025 17:16:11 +0200 Subject: [PATCH 170/174] fix: Re-trigger CI when cargo vet exemptions are regenerated for Dependabot PRs Co-authored-by: David Niehues --- .github/workflows/supply-chain.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml index 458b9b38..998b2d9b 100644 --- a/.github/workflows/supply-chain.yml +++ b/.github/workflows/supply-chain.yml @@ -150,6 +150,10 @@ jobs: fi echo "IN_DEPENDABOT_PR_CONTEXT=$IN_DEPENDABOT_PR_CONTEXT" >> $GITHUB_ENV shell: bash + - uses: actions/checkout@v4 + if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' + with: + token: ${{ secrets.CI_BOT_PAT }} - name: In case of a dependabot PR, ensure that we are not in a detached HEAD state if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' run: | From e76e5b253fb381c82bb54a6345f61e88a931a8e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:19:47 +0000 Subject: [PATCH 171/174] chore(deps): bump clap_mangen from 0.2.24 to 0.2.27 Dependabot couldn't find the original pull request head commit, 518c533e040c5dd92156f84f8c20cffb9c7eacf6. --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbe6f623..98c33f6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,9 +408,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clap_mangen" -version = "0.2.24" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbae9cbfdc5d4fa8711c09bd7b83f644cb48281ac35bf97af3e47b0675864bdf" +checksum = "27b4c3c54b30f0d9adcb47f25f61fcce35c4dd8916638c6b82fbd5f4fb4179e2" dependencies = [ "clap", "roff", @@ -1408,7 +1408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 368b8917..e1d86e15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ rand = "0.8.5" typenum = "1.17.0" log = { version = "0.4.22" } clap = { version = "4.5.23", features = ["derive"] } -clap_mangen = "0.2.24" +clap_mangen = "0.2.29" clap_complete = "4.5.40" serde = { version = "1.0.217", features = ["derive"] } arbitrary = { version = "1.4.1", features = ["derive"] } From 3af479a27e3756f7873a686a445bf601a572a93c Mon Sep 17 00:00:00 2001 From: Rosenpass CI Bot Date: Tue, 29 Jul 2025 15:20:29 +0000 Subject: [PATCH 172/174] Regenerate cargo vet exemptions --- supply-chain/config.toml | 42 +++-------- supply-chain/imports.lock | 145 +++++++++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 34 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index e344e32e..1b5f913f 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -142,7 +142,7 @@ version = "0.7.4" criteria = "safe-to-deploy" [[exemptions.clap_mangen]] -version = "0.2.24" +version = "0.2.29" criteria = "safe-to-deploy" [[exemptions.cmake]] @@ -257,10 +257,6 @@ criteria = "safe-to-deploy" version = "0.10.2" criteria = "safe-to-deploy" -[[exemptions.fastrand]] -version = "2.3.0" -criteria = "safe-to-deploy" - [[exemptions.findshlibs]] version = "0.10.2" criteria = "safe-to-run" @@ -285,10 +281,6 @@ criteria = "safe-to-deploy" version = "0.2.15" criteria = "safe-to-deploy" -[[exemptions.gimli]] -version = "0.31.1" -criteria = "safe-to-deploy" - [[exemptions.hash32]] version = "0.2.1" criteria = "safe-to-deploy" @@ -529,10 +521,6 @@ criteria = "safe-to-deploy" version = "1.0.15" criteria = "safe-to-deploy" -[[exemptions.pin-project-lite]] -version = "0.2.16" -criteria = "safe-to-deploy" - [[exemptions.pkg-config]] version = "0.3.31" criteria = "safe-to-deploy" @@ -581,14 +569,6 @@ criteria = "safe-to-deploy" version = "0.9.0" criteria = "safe-to-deploy" -[[exemptions.rand_chacha]] -version = "0.9.0" -criteria = "safe-to-deploy" - -[[exemptions.rand_core]] -version = "0.9.3" -criteria = "safe-to-deploy" - [[exemptions.redox_syscall]] version = "0.5.9" criteria = "safe-to-deploy" @@ -733,10 +713,6 @@ criteria = "safe-to-deploy" version = "1.0.17" criteria = "safe-to-deploy" -[[exemptions.utf8parse]] -version = "0.2.2" -criteria = "safe-to-deploy" - [[exemptions.uuid]] version = "1.14.0" criteria = "safe-to-deploy" @@ -847,7 +823,7 @@ criteria = "safe-to-deploy" [[exemptions.windows-targets]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows-targets]] version = "0.52.6" @@ -859,7 +835,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_aarch64_gnullvm]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_aarch64_gnullvm]] version = "0.52.6" @@ -871,7 +847,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_aarch64_msvc]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_aarch64_msvc]] version = "0.52.6" @@ -883,7 +859,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_i686_gnu]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_i686_gnu]] version = "0.52.6" @@ -899,7 +875,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_i686_msvc]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_i686_msvc]] version = "0.52.6" @@ -911,7 +887,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnu]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_x86_64_gnu]] version = "0.52.6" @@ -923,7 +899,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnullvm]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_x86_64_gnullvm]] version = "0.52.6" @@ -935,7 +911,7 @@ criteria = "safe-to-deploy" [[exemptions.windows_x86_64_msvc]] version = "0.48.5" -criteria = "safe-to-deploy" +criteria = "safe-to-run" [[exemptions.windows_x86_64_msvc]] version = "0.52.6" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index babb9a4f..445f975b 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -35,7 +35,7 @@ who = "Alex Crichton " criteria = "safe-to-deploy" user-id = 73222 # wasmtime-publish start = "2023-01-01" -end = "2025-05-08" +end = "2026-06-03" notes = """ The Bytecode Alliance uses the `wasmtime-publish` crates.io account to automate publication of this crate from CI. This repository requires all PRs are reviewed @@ -144,6 +144,21 @@ who = "Dan Gohman " criteria = "safe-to-deploy" delta = "0.3.9 -> 0.3.10" +[[audits.bytecode-alliance.audits.fastrand]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "2.0.0 -> 2.0.1" +notes = """ +This update had a few doc updates but no otherwise-substantial source code +updates. +""" + +[[audits.bytecode-alliance.audits.fastrand]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "2.1.1 -> 2.3.0" +notes = "Minor refactoring, nothing new." + [[audits.bytecode-alliance.audits.futures]] who = "Joel Dice " criteria = "safe-to-deploy" @@ -190,6 +205,18 @@ who = "Pat Hickey " criteria = "safe-to-deploy" delta = "0.3.28 -> 0.3.31" +[[audits.bytecode-alliance.audits.gimli]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.29.0 -> 0.31.0" +notes = "Various updates here and there, nothing too major, what you'd expect from a DWARF parsing crate." + +[[audits.bytecode-alliance.audits.gimli]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.31.0 -> 0.31.1" +notes = "No fundmanetally new `unsafe` code, some small refactoring of existing code. Lots of changes in tests, not as many changes in the rest of the crate. More dwarf!" + [[audits.bytecode-alliance.audits.heck]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -249,6 +276,12 @@ criteria = "safe-to-deploy" version = "1.0.0" notes = "I am the author of this crate." +[[audits.bytecode-alliance.audits.pin-project-lite]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.2.13 -> 0.2.14" +notes = "No substantive changes in this update" + [[audits.bytecode-alliance.audits.pin-utils]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -301,6 +334,12 @@ criteria = "safe-to-deploy" version = "1.0.40" notes = "Found no unsafe or ambient capabilities used" +[[audits.embark-studios.audits.utf8parse]] +who = "Johan Andersson " +criteria = "safe-to-deploy" +version = "0.2.1" +notes = "Single unsafe usage that looks sound, no ambient capabilities" + [[audits.fermyon.audits.oorandom]] who = "Radu Matei " criteria = "safe-to-run" @@ -411,6 +450,16 @@ delta = "1.0.1 -> 1.0.2" notes = "No changes to any .rs files or Rust code." aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.fastrand]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "1.9.0" +notes = """ +`does-not-implement-crypto` is certified because this crate explicitly says +that the RNG here is not cryptographically secure. +""" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.glob]] who = "George Burgess IV " criteria = "safe-to-deploy" @@ -554,6 +603,20 @@ version = "0.1.46" notes = "Contains no unsafe" aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +version = "0.2.9" +notes = "Reviewed on https://fxrev.dev/824504" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.2.9 -> 0.2.13" +notes = "Audited at https://fxrev.dev/946396" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.proc-macro-error-attr]] who = "George Burgess IV " criteria = "safe-to-deploy" @@ -708,6 +771,24 @@ For more detailed unsafe review notes please see https://crrev.com/c/6362797 """ aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" +[[audits.google.audits.rand_chacha]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "0.3.1" +notes = """ +For more detailed unsafe review notes please see https://crrev.com/c/6362797 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.rand_core]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "0.6.4" +notes = """ +For more detailed unsafe review notes please see https://crrev.com/c/6362797 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.regex-syntax]] who = "Manish Goregaokar " criteria = "safe-to-deploy" @@ -1160,11 +1241,21 @@ who = "David Cook " criteria = "safe-to-deploy" version = "0.3.1" +[[audits.isrg.audits.rand_chacha]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.3.1 -> 0.9.0" + [[audits.isrg.audits.rand_core]] who = "David Cook " criteria = "safe-to-deploy" version = "0.6.3" +[[audits.isrg.audits.rand_core]] +who = "David Cook " +criteria = "safe-to-deploy" +delta = "0.6.4 -> 0.9.3" + [[audits.isrg.audits.rayon]] who = "Brandon Pitman " criteria = "safe-to-deploy" @@ -1379,6 +1470,25 @@ criteria = "safe-to-deploy" delta = "0.3.1 -> 0.3.3" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.fastrand]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "1.9.0 -> 2.0.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.fastrand]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "2.0.1 -> 2.1.0" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.fastrand]] +who = "Chris Martin " +criteria = "safe-to-deploy" +delta = "2.1.0 -> 2.1.1" +notes = "Fairly trivial changes, no chance of security regression." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.fnv]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -1409,6 +1519,23 @@ documentation. """ aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.gimli]] +who = "Alex Franchuk " +criteria = "safe-to-deploy" +version = "0.30.0" +notes = """ +Unsafe code blocks are sound. Minimal dependencies used. No use of +side-effectful std functions. +""" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.gimli]] +who = "Chris Martin " +criteria = "safe-to-deploy" +delta = "0.30.0 -> 0.29.0" +notes = "No unsafe code, mostly algorithms and parsing. Very unlikely to cause security issues." +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.hex]] who = "Simon Friedberger " criteria = "safe-to-deploy" @@ -1428,6 +1555,16 @@ delta = "1.0.0 -> 0.1.2" notes = "Small refactor of some simple iterator logic, no unsafe code or capabilities." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.pin-project-lite]] +who = "Nika Layzell " +criteria = "safe-to-deploy" +delta = "0.2.14 -> 0.2.16" +notes = """ +Only functional change is to work around a bug in the negative_impls feature +(https://github.com/taiki-e/pin-project/issues/340#issuecomment-2432146009) +""" +aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" + [[audits.mozilla.audits.rand_core]] who = "Mike Hommey " criteria = "safe-to-deploy" @@ -1491,6 +1628,12 @@ criteria = "safe-to-deploy" delta = "1.0.43 -> 1.0.69" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" +[[audits.mozilla.audits.utf8parse]] +who = "Nika Layzell " +criteria = "safe-to-deploy" +delta = "0.2.1 -> 0.2.2" +aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" + [[audits.mozilla.audits.zeroize]] who = "Benjamin Beurdouche " criteria = "safe-to-deploy" From f31d635df82f75ee161f7e1f9949cd2c48d93b85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:44:49 +0000 Subject: [PATCH 173/174] chore(deps): bump tokio from 1.44.2 to 1.46.1 Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.44.2 to 1.46.1. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.44.2...tokio-1.46.1) --- updated-dependencies: - dependency-name: tokio dependency-version: 1.46.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 29 +++++++++++++++++++++-------- Cargo.toml | 2 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98c33f6a..7efbdde6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1153,6 +1153,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "libc", +] + [[package]] name = "ipc-channel" version = "0.18.3" @@ -1246,9 +1257,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libcrux" @@ -2461,12 +2472,12 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.5.8" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2630,20 +2641,22 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e1d86e15..9b94447d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ zerocopy = { version = "0.7.35", features = ["derive"] } home = "=0.5.9" # 5.11 requires rustc 1.81 derive_builder = "0.20.1" -tokio = { version = "1.42", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.46", 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" } From 8dfa67a2ddac518cf16049eac26c39e8e4e689da Mon Sep 17 00:00:00 2001 From: Rosenpass CI Bot Date: Wed, 30 Jul 2025 23:45:24 +0000 Subject: [PATCH 174/174] Regenerate cargo vet exemptions --- supply-chain/config.toml | 10 +++++++--- supply-chain/imports.lock | 16 ---------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 1b5f913f..a261f9a3 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -333,6 +333,10 @@ criteria = "safe-to-deploy" version = "2.1.0" criteria = "safe-to-deploy" +[[exemptions.io-uring]] +version = "0.7.9" +criteria = "safe-to-deploy" + [[exemptions.ipc-channel]] version = "0.18.3" criteria = "safe-to-run" @@ -362,7 +366,7 @@ version = "1.3.0" criteria = "safe-to-deploy" [[exemptions.libc]] -version = "0.2.169" +version = "0.2.174" criteria = "safe-to-deploy" [[exemptions.libcrux]] @@ -638,7 +642,7 @@ version = "0.4.9" criteria = "safe-to-deploy" [[exemptions.socket2]] -version = "0.5.8" +version = "0.6.0" criteria = "safe-to-deploy" [[exemptions.spin]] @@ -682,7 +686,7 @@ version = "2.0.11" criteria = "safe-to-deploy" [[exemptions.tokio]] -version = "1.44.2" +version = "1.47.0" criteria = "safe-to-deploy" [[exemptions.tokio-macros]] diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 445f975b..2b5306ea 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1236,21 +1236,11 @@ who = "David Cook " criteria = "safe-to-deploy" version = "0.3.0" -[[audits.isrg.audits.rand_chacha]] -who = "David Cook " -criteria = "safe-to-deploy" -version = "0.3.1" - [[audits.isrg.audits.rand_chacha]] who = "David Cook " criteria = "safe-to-deploy" delta = "0.3.1 -> 0.9.0" -[[audits.isrg.audits.rand_core]] -who = "David Cook " -criteria = "safe-to-deploy" -version = "0.6.3" - [[audits.isrg.audits.rand_core]] who = "David Cook " criteria = "safe-to-deploy" @@ -1565,12 +1555,6 @@ Only functional change is to work around a bug in the negative_impls feature """ aggregated-from = "https://raw.githubusercontent.com/mozilla/cargo-vet/main/supply-chain/audits.toml" -[[audits.mozilla.audits.rand_core]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -delta = "0.6.3 -> 0.6.4" -aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" - [[audits.mozilla.audits.rayon]] who = "Josh Stone " criteria = "safe-to-deploy"