Compare commits

..

6 Commits

Author SHA1 Message Date
Karolin Varner
6b3bebb9b9 fix: CI issues under Darwin 2024-12-08 13:57:43 +01:00
Jacek Galowicz
9948169127 rp systemd unit file: introduce and test 2024-12-08 09:43:35 +01:00
Jacek Galowicz
eced56bd70 rp: Add exchange-config command
This is similar to `rosenpass exchange`/`rosenpass exchange-config`.
It's however slightly different to the configuration file models the `rp
exchange` command line.
2024-12-08 09:43:35 +01:00
Jacek Galowicz
df1e195b5d rp: set allowed-ips as routes
Prepare the rp app for a systemd unit file that sets up wireguard
connections.
2024-12-08 09:43:35 +01:00
Jacek Galowicz
e1e280c4c5 rp: Add ip parameter to exchange command
Prepare the `rp` app for a systemd unit that sets up a wireguard connection.
2024-12-08 09:43:35 +01:00
Jacek Galowicz
06cd178977 rosenpass systemd unit file: introduce and test 2024-12-08 09:43:35 +01:00
305 changed files with 8374 additions and 29657 deletions

View File

@@ -3,6 +3,11 @@ secret_key = "rp-a-secret-key"
listen = ["127.0.0.1:9999"] listen = ["127.0.0.1:9999"]
verbosity = "Verbose" verbosity = "Verbose"
[api]
listen_path = []
listen_fd = []
stream_fd = []
[[peers]] [[peers]]
public_key = "rp-b-public-key" public_key = "rp-b-public-key"
endpoint = "127.0.0.1:9998" endpoint = "127.0.0.1:9998"

View File

@@ -3,6 +3,11 @@ secret_key = "rp-b-secret-key"
listen = ["127.0.0.1:9998"] listen = ["127.0.0.1:9998"]
verbosity = "Verbose" verbosity = "Verbose"
[api]
listen_path = []
listen_fd = []
stream_fd = []
[[peers]] [[peers]]
public_key = "rp-a-public-key" public_key = "rp-a-public-key"
endpoint = "127.0.0.1:9999" endpoint = "127.0.0.1:9999"

View File

@@ -32,9 +32,9 @@ let systems_map = {
# aarch64-darwin # aarch64-darwin
# aarch64-linux # aarch64-linux
i686-linux: ubicloud-standard-2-ubuntu-2204, i686-linux: ubuntu-latest,
x86_64-darwin: macos-13, x86_64-darwin: macos-13,
x86_64-linux: ubicloud-standard-2-ubuntu-2204 x86_64-linux: ubuntu-latest
} }
let targets = (get-attr-names ".#packages" let targets = (get-attr-names ".#packages"
@@ -61,13 +61,14 @@ mut release_workflow = {
let runner_setup = [ let runner_setup = [
{ {
uses: "actions/checkout@v4" uses: "actions/checkout@v3"
} }
{ {
uses: "cachix/install-nix-action@v30", uses: "cachix/install-nix-action@v22",
with: { nix_path: "nixpkgs=channel:nixos-unstable" }
} }
{ {
uses: "cachix/cachix-action@v15", uses: "cachix/cachix-action@v12",
with: { with: {
name: rosenpass, name: rosenpass,
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@@ -153,7 +154,7 @@ for system in ($targets | columns) {
} }
{ {
name: Release, name: Release,
uses: "softprops/action-gh-release@v2", uses: "softprops/action-gh-release@v1",
with: { with: {
draft: "${{ contains(github.ref_name, 'rc') }}", draft: "${{ contains(github.ref_name, 'rc') }}",
prerelease: "${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}", prerelease: "${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}",
@@ -181,7 +182,7 @@ $cachix_workflow.jobs = ($cachix_workflow.jobs | insert $"($system)---whitepaper
} }
{ {
name: "Deploy PDF artifacts", name: "Deploy PDF artifacts",
uses: "peaceiris/actions-gh-pages@v4", uses: "peaceiris/actions-gh-pages@v3",
with: { with: {
github_token: "${{ secrets.GITHUB_TOKEN }}", github_token: "${{ secrets.GITHUB_TOKEN }}",
publish_dir: result/, publish_dir: result/,

View File

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

View File

@@ -1,103 +0,0 @@
name: rosenpass-ciphers - primitives - benchmark
permissions:
contents: write
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: ubicloud-standard-2
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v5
# 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: 🛠️ 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 }}.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
with:
name: rosenpass-ciphers primitives benchmarks
tool: "cargo"
os: ${{ matrix.system }}
output-file-path: bench-primitives.txt
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-os.json >bench-primitives.json
- name: Upload benchmarks
uses: cryspen/benchmark-upload-and-plot-action@v3
with:
name: Crypto Primitives Benchmarks
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 }}
auto-push: true
fail-on-alert: true
base-path: benchmarks/
ciphers-primitives-bench-status:
if: ${{ always() }}
needs: [prim-benchmark]
runs-on: ubicloud-standard-2
steps:
- name: Successful
if: ${{ !(contains(needs.*.result, 'failure')) }}
run: exit 0
- name: Failing
if: ${{ (contains(needs.*.result, 'failure')) }}
run: exit 1

View File

@@ -1,90 +0,0 @@
name: rosenpass - protocol - benchmark
permissions:
contents: write
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: ubicloud-standard-2
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v5
# 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: 🛠️ 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 }}.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: "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 }}
auto-push: true
fail-on-alert: true
base-path: benchmarks/
ciphers-protocol-bench-status:
if: ${{ always() }}
needs: [proto-benchmark]
runs-on: ubicloud-standard-2
steps:
- name: Successful
if: ${{ !(contains(needs.*.result, 'failure')) }}
run: exit 0
- name: Failing
if: ${{ (contains(needs.*.result, 'failure')) }}
run: exit 1

View File

@@ -13,10 +13,10 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Clone rosenpass-website repository - name: Clone rosenpass-website repository
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
repository: rosenpass/rosenpass-website repository: rosenpass/rosenpass-website
ref: main ref: main

View File

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

View File

@@ -1,166 +0,0 @@
name: Integration Tests
on:
pull_request:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
integration-tests-x86_64-linux:
name: Integration tests x86_64-linux
runs-on:
- ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v5
- 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: Extract the reference of before and after for the integration tests.
run: |
EVENT_NAME="${{ github.event_name }}"
REF_BEFORE=""
REF_AFTER="path:../../"
if [[ "$EVENT_NAME" == "pull_request" ]]; then
echo "This CI run was triggered in the context of a pull request."
REF_BEFORE="github:rosenpass/rosenpass/main"
git checkout -B pr-${{ github.event.pull_request.number }}
REF_AFTER="git+file://../../../?ref=pr-${{ github.event.pull_request.number }}"
elif [[ "$EVENT_NAME" == "push" ]]; then
echo "This CI run was triggered in the context of a push."
REF_BEFORE="github:rosenpass/rosenpass/${{ github.event.before }}"
REF_AFTER="github:rosenpass/rosenpass/${{ github.event.after }}"
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 "REF_BEFORE=$REF_BEFORE" >> $GITHUB_ENV
echo "REF_AFTER=$REF_AFTER" >> $GITHUB_ENV
- name: Check
run: |
cd ./tests/integration
nix flake check --print-build-logs --system x86_64-linux --override-input rosenpass-old $REF_BEFORE --override-input rosenpass-new $REF_AFTER
# THE FOLLOWING TEST IS DISABLED FOR THE TIME BENG UNTIL WE GET AN ARM64 RUNNER THAT SUPPORTS KVM
#integration-tests-aarch64-linux:
# name: Integration tests aarch64-linux
# runs-on:
# - ubicloud-standard-2-arm-ubuntu-2204
# steps:
# - uses: actions/checkout@v5
# - 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: Extract the reference of before and after for the integration tests.
# run: |
# EVENT_NAME="${{ github.event_name }}"
# REF_BEFORE=""
# REF_AFTER="path:../../"
# if [[ "$EVENT_NAME" == "pull_request" ]]; then
# echo "This CI run was triggered in the context of a pull request."
# REF_BEFORE="github:rosenpass/rosenpass/main"
# #git checkout -B pr-${{ github.event.pull_request.number }}
# REF_AFTER="git+file://../../../?ref=pr-${{ github.event.pull_request.number }}"
# elif [[ "$EVENT_NAME" == "push" ]]; then
# echo "This CI run was triggered in the context of a push."
# REF_BEFORE="github:rosenpass/rosenpass/${{ github.event.before }}"
# REF_AFTER="github:rosenpass/rosenpass/${{ github.event.after }}"
# #git checkout -B ${{ github.ref_name }}
# 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 "REF_BEFORE=$REF_BEFORE" >> $GITHUB_ENV
# echo "REF_AFTER=$REF_AFTER" >> $GITHUB_ENV
# - name: Check
# run: |
# cd ./tests/integration
# # export QEMU_OPTS="-machine virt -cpu cortex-a57"
# nix flake check --print-build-logs --system aarch64-linux --override-input rosenpass-old $REF_BEFORE --override-input rosenpass-new $REF_AFTER
#integration-tests-i686-linux:
# name: Integration tests i686-linux
# timeout-minutes: 144000
# runs-on:
# - ubicloud-standard-8-ubuntu-2204
# steps:
# - uses: actions/checkout@v5
# - 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: Extract the reference of before and after for the integration tests.
# run: |
# EVENT_NAME="${{ github.event_name }}"
# REF_BEFORE=""
# REF_AFTER="path:../../"
# if [[ "$EVENT_NAME" == "pull_request" ]]; then
# echo "This CI run was triggered in the context of a pull request."
# REF_BEFORE="github:rosenpass/rosenpass/main"
# git checkout -B pr-${{ github.event.pull_request.number }}
# REF_AFTER="git+file://../../../?ref=pr-${{ github.event.pull_request.number }}"
# elif [[ "$EVENT_NAME" == "push" ]]; then
# echo "This CI run was triggered in the context of a push."
# REF_BEFORE="github:rosenpass/rosenpass/${{ github.event.before }}"
# REF_AFTER="github:rosenpass/rosenpass/${{ github.event.after }}"
# 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 "REF_BEFORE=$REF_BEFORE" >> $GITHUB_ENV
# echo "REF_AFTER=$REF_AFTER" >> $GITHUB_ENV
# - name: Check
# run: |
# cd ./tests/integration
# nix flake check --print-build-logs --system i686-linux --override-input rosenpass-old $REF_BEFORE --override-input rosenpass-new $REF_AFTER
# THE FOLLOWING TEST IS DISABLED FOR THE TIME BENG UNTIL THIS ISSUE WITH NIXOS TESTS ON DARWIN GETS RESOLVED: https://github.com/NixOS/nixpkgs/issues/294725
#integration-tests-aarch64-darwin:
# name: Integration tests aarch64-darwin
# runs-on:
# - warp-macos-13-arm64-6x
# steps:
# - uses: actions/checkout@v5
# - 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: Extract the reference of before and after for the integration tests.
# run: |
# EVENT_NAME="${{ github.event_name }}"
# REF_BEFORE=""
# REF_AFTER="path:../../"
# if [[ "$EVENT_NAME" == "pull_request" ]]; then
# echo "This CI run was triggered in the context of a pull request."
# REF_BEFORE="github:rosenpass/rosenpass/main"
# git checkout -B pr-${{ github.event.pull_request.number }}
# REF_AFTER="git+file://../../../?ref=pr-${{ github.event.pull_request.number }}"
# elif [[ "$EVENT_NAME" == "push" ]]; then
# echo "This CI run was triggered in the context of a push."
# REF_BEFORE="github:rosenpass/rosenpass/${{ github.event.before }}"
# REF_AFTER="github:rosenpass/rosenpass/${{ github.event.after }}"
# 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 "REF_BEFORE=$REF_BEFORE" >> $GITHUB_ENV
# echo "REF_AFTER=$REF_AFTER" >> $GITHUB_ENV
# - name: Check
# run: |
# cd ./tests/integration
# nix flake check --print-build-logs --system aarch64-darwin --override-input rosenpass-old $REF_BEFORE --override-input rosenpass-new $REF_AFTER

View File

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

View File

@@ -1,114 +0,0 @@
name: Nix on Mac
permissions:
contents: write
on:
push:
branches:
- main
workflow_call:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
aarch64-darwin---default:
name: Build aarch64-darwin.default
runs-on:
- warp-macos-13-arm64-6x
needs:
- aarch64-darwin---rosenpass
steps:
- uses: actions/checkout@v5
- 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@v5
- 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@v5
- 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@v5
- 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@v5
- 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@v5
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Check
run: nix flake check . --print-build-logs

View File

@@ -15,11 +15,11 @@ jobs:
i686-linux---default: i686-linux---default:
name: Build i686-linux.default name: Build i686-linux.default
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- i686-linux---rosenpass - i686-linux---rosenpass
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -32,10 +32,10 @@ jobs:
i686-linux---rosenpass: i686-linux---rosenpass:
name: Build i686-linux.rosenpass name: Build i686-linux.rosenpass
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -48,11 +48,11 @@ jobs:
i686-linux---rosenpass-oci-image: i686-linux---rosenpass-oci-image:
name: Build i686-linux.rosenpass-oci-image name: Build i686-linux.rosenpass-oci-image
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- i686-linux---rosenpass - i686-linux---rosenpass
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -65,9 +65,9 @@ jobs:
i686-linux---check: i686-linux---check:
name: Run Nix checks on i686-linux name: Run Nix checks on i686-linux
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -76,16 +76,115 @@ jobs:
name: rosenpass name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Check - name: Check
run: | run: nix flake check . --print-build-logs
nix flake check . --print-build-logs x86_64-darwin---default:
name: Build x86_64-darwin.default
runs-on:
- macos-13
needs:
- x86_64-darwin---rosenpass
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.default --print-build-logs
x86_64-darwin---release-package:
name: Build x86_64-darwin.release-package
runs-on:
- macos-13
needs:
- x86_64-darwin---rosenpass
- x86_64-darwin---rp
- x86_64-darwin---rosenpass-oci-image
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.release-package --print-build-logs
x86_64-darwin---rosenpass:
name: Build x86_64-darwin.rosenpass
runs-on:
- macos-13
needs: []
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.rosenpass --print-build-logs
x86_64-darwin---rp:
name: Build x86_64-darwin.rp
runs-on:
- macos-13
needs: []
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.rp --print-build-logs
x86_64-darwin---rosenpass-oci-image:
name: Build x86_64-darwin.rosenpass-oci-image
runs-on:
- macos-13
needs:
- x86_64-darwin---rosenpass
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.rosenpass-oci-image --print-build-logs
x86_64-darwin---check:
name: Run Nix checks on x86_64-darwin
runs-on:
- macos-13
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: x86_64-linux---default:
name: Build x86_64-linux.default name: Build x86_64-linux.default
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- x86_64-linux---rosenpass - x86_64-linux---rosenpass
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -98,11 +197,11 @@ jobs:
x86_64-linux---proof-proverif: x86_64-linux---proof-proverif:
name: Build x86_64-linux.proof-proverif name: Build x86_64-linux.proof-proverif
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- x86_64-linux---proverif-patched - x86_64-linux---proverif-patched
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -115,10 +214,10 @@ jobs:
x86_64-linux---proverif-patched: x86_64-linux---proverif-patched:
name: Build x86_64-linux.proverif-patched name: Build x86_64-linux.proverif-patched
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -131,13 +230,13 @@ jobs:
x86_64-linux---release-package: x86_64-linux---release-package:
name: Build x86_64-linux.release-package name: Build x86_64-linux.release-package
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- x86_64-linux---rosenpass-static - x86_64-linux---rosenpass-static
- x86_64-linux---rosenpass-static-oci-image - x86_64-linux---rosenpass-static-oci-image
- x86_64-linux---rp-static - x86_64-linux---rp-static
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -150,7 +249,7 @@ jobs:
# aarch64-linux---release-package: # aarch64-linux---release-package:
# name: Build aarch64-linux.release-package # name: Build aarch64-linux.release-package
# runs-on: # runs-on:
# - ubicloud-standard-2-arm-ubuntu-2204 # - ubuntu-latest
# needs: # needs:
# - aarch64-linux---rosenpass-oci-image # - aarch64-linux---rosenpass-oci-image
# - aarch64-linux---rosenpass # - aarch64-linux---rosenpass
@@ -159,7 +258,7 @@ jobs:
# - run: | # - run: |
# DEBIAN_FRONTEND=noninteractive # DEBIAN_FRONTEND=noninteractive
# sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static # sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
# - uses: actions/checkout@v5 # - uses: actions/checkout@v4
# - uses: cachix/install-nix-action@v30 # - uses: cachix/install-nix-action@v30
# with: # with:
# nix_path: nixpkgs=channel:nixos-unstable # nix_path: nixpkgs=channel:nixos-unstable
@@ -174,10 +273,10 @@ jobs:
x86_64-linux---rosenpass: x86_64-linux---rosenpass:
name: Build x86_64-linux.rosenpass name: Build x86_64-linux.rosenpass
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -190,13 +289,13 @@ jobs:
aarch64-linux---rosenpass: aarch64-linux---rosenpass:
name: Build aarch64-linux.rosenpass name: Build aarch64-linux.rosenpass
runs-on: runs-on:
- ubicloud-standard-2-arm-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- run: | - run: |
DEBIAN_FRONTEND=noninteractive DEBIAN_FRONTEND=noninteractive
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -211,13 +310,13 @@ jobs:
aarch64-linux---rp: aarch64-linux---rp:
name: Build aarch64-linux.rp name: Build aarch64-linux.rp
runs-on: runs-on:
- ubicloud-standard-2-arm-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- run: | - run: |
DEBIAN_FRONTEND=noninteractive DEBIAN_FRONTEND=noninteractive
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -232,11 +331,11 @@ jobs:
x86_64-linux---rosenpass-oci-image: x86_64-linux---rosenpass-oci-image:
name: Build x86_64-linux.rosenpass-oci-image name: Build x86_64-linux.rosenpass-oci-image
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- x86_64-linux---rosenpass - x86_64-linux---rosenpass
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -249,14 +348,14 @@ jobs:
aarch64-linux---rosenpass-oci-image: aarch64-linux---rosenpass-oci-image:
name: Build aarch64-linux.rosenpass-oci-image name: Build aarch64-linux.rosenpass-oci-image
runs-on: runs-on:
- ubicloud-standard-2-arm-ubuntu-2204 - ubuntu-latest
needs: needs:
- aarch64-linux---rosenpass - aarch64-linux---rosenpass
steps: steps:
- run: | - run: |
DEBIAN_FRONTEND=noninteractive DEBIAN_FRONTEND=noninteractive
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -271,10 +370,10 @@ jobs:
x86_64-linux---rosenpass-static: x86_64-linux---rosenpass-static:
name: Build x86_64-linux.rosenpass-static name: Build x86_64-linux.rosenpass-static
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -287,10 +386,10 @@ jobs:
x86_64-linux---rp-static: x86_64-linux---rp-static:
name: Build x86_64-linux.rp-static name: Build x86_64-linux.rp-static
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -303,11 +402,11 @@ jobs:
x86_64-linux---rosenpass-static-oci-image: x86_64-linux---rosenpass-static-oci-image:
name: Build x86_64-linux.rosenpass-static-oci-image name: Build x86_64-linux.rosenpass-static-oci-image
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: needs:
- x86_64-linux---rosenpass-static - x86_64-linux---rosenpass-static
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -320,10 +419,10 @@ jobs:
x86_64-linux---whitepaper: x86_64-linux---whitepaper:
name: Build x86_64-linux.whitepaper name: Build x86_64-linux.whitepaper
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
needs: [] needs: []
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -336,9 +435,9 @@ jobs:
x86_64-linux---check: x86_64-linux---check:
name: Run Nix checks on x86_64-linux name: Run Nix checks on x86_64-linux
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable
@@ -350,10 +449,10 @@ jobs:
run: nix flake check . --print-build-logs run: nix flake check . --print-build-logs
x86_64-linux---whitepaper-upload: x86_64-linux---whitepaper-upload:
name: Upload whitepaper x86_64-linux name: Upload whitepaper x86_64-linux
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
if: ${{ github.ref == 'refs/heads/main' }} if: ${{ github.ref == 'refs/heads/main' }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
nix_path: nixpkgs=channel:nixos-unstable nix_path: nixpkgs=channel:nixos-unstable

View File

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

View File

@@ -14,49 +14,33 @@ permissions:
jobs: jobs:
prettier: prettier:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actionsx/prettier@v3 - uses: actionsx/prettier@v3
with: with:
args: --check . args: --check .
shellcheck: shellcheck:
name: Shellcheck name: Shellcheck
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Run ShellCheck - name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
rustfmt: rustfmt:
name: Rust code formatting name: Rust Format
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - name: Run Rust Formatting Script
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 run: bash format_rust_code.sh --mode check
cargo-bench: cargo-bench:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -73,26 +57,26 @@ jobs:
mandoc: mandoc:
name: mandoc name: mandoc
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- name: Install mandoc - name: Install mandoc
run: sudo apt-get install -y mandoc run: sudo apt-get install -y mandoc
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Check rp.1 - name: Check rp.1
run: doc/check.sh doc/rp.1 run: doc/check.sh doc/rp.1
cargo-audit: cargo-audit:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions-rs/audit-check@v1 - uses: actions-rs/audit-check@v1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
cargo-clippy: cargo-clippy:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -109,9 +93,9 @@ jobs:
args: --all-features args: --all-features
cargo-doc: cargo-doc:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -128,9 +112,14 @@ jobs:
- run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items
cargo-test: cargo-test:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-13]
# - ubuntu is x86-64
# - macos-13 is also x86-64 architecture
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -147,9 +136,9 @@ jobs:
cargo-test-nix-devshell-x86_64-linux: cargo-test-nix-devshell-x86_64-linux:
runs-on: runs-on:
- ubicloud-standard-2-ubuntu-2204 - ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -169,9 +158,9 @@ jobs:
- run: nix develop --command cargo test --workspace --all-features - run: nix develop --command cargo test --workspace --all-features
cargo-fuzz: cargo-fuzz:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -184,7 +173,7 @@ jobs:
- name: Install nightly toolchain - name: Install nightly toolchain
run: | run: |
rustup toolchain install nightly rustup toolchain install nightly
rustup override set nightly rustup default nightly
- name: Install cargo-fuzz - name: Install cargo-fuzz
run: cargo install cargo-fuzz run: cargo install cargo-fuzz
- name: Run fuzzing - name: Run fuzzing
@@ -202,35 +191,22 @@ jobs:
cargo fuzz run fuzz_vec_secret_alloc_memfdsec_mallocfb -- -max_total_time=5 cargo fuzz run fuzz_vec_secret_alloc_memfdsec_mallocfb -- -max_total_time=5
codecov: codecov:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
env:
RUSTUP_TOOLCHAIN: nightly
steps: steps:
- uses: actions/checkout@v5 - 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') }}
- name: Install nightly toolchain
run: |
rustup toolchain install nightly
rustup override set nightly
- run: rustup component add llvm-tools-preview - run: rustup component add llvm-tools-preview
- run: | - run: |
cargo install cargo-llvm-cov || true cargo install cargo-llvm-cov || true
cargo install grcov || true cargo llvm-cov \
./coverage_report.sh --workspace\
--all-features \
--lcov \
--output-path coverage.lcov
# If using tarapulin # If using tarapulin
#- run: cargo install cargo-tarpaulin #- run: cargo install cargo-tarpaulin
#- run: cargo tarpaulin --out Xml #- run: cargo tarpaulin --out Xml
- name: Upload coverage reports to Codecov - name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:
files: ./target/grcov/lcov files: ./coverage.lcov
verbose: true verbose: true
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -14,9 +14,9 @@ permissions:
jobs: jobs:
multi-peer: multi-peer:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- run: cargo build --bin rosenpass --release - run: cargo build --bin rosenpass --release
- run: python misc/generate_configs.py - run: python misc/generate_configs.py
- run: chmod +x .ci/run-regression.sh - run: chmod +x .ci/run-regression.sh
@@ -25,9 +25,9 @@ jobs:
[ $(ls -1 output/ate/out | wc -l) -eq 100 ] [ $(ls -1 output/ate/out | wc -l) -eq 100 ]
boot-race: boot-race:
runs-on: ubicloud-standard-2-ubuntu-2204 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- run: cargo build --bin rosenpass --release - run: cargo build --bin rosenpass --release
- run: chmod +x .ci/boot_race/run.sh - run: chmod +x .ci/boot_race/run.sh
- run: cargo run --release --bin rosenpass gen-keys .ci/boot_race/a.toml - run: cargo run --release --bin rosenpass gen-keys .ci/boot_race/a.toml

View File

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

View File

@@ -1,177 +0,0 @@
name: Supply-Chain
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
cargo-deny:
name: Deny dependencies with vulnerabilities or incompatible licenses
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: EmbarkStudios/cargo-deny-action@v2
cargo-supply-chain:
name: Supply Chain Report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cache/cargo-supply-chain/
key: cargo-supply-chain-cache
- name: Install nightly toolchain
run: |
rustup toolchain install nightly
rustup override set nightly
- uses: actions/cache@v4
with:
path: ${{ runner.tool_cache }}/cargo-supply-chain
key: cargo-supply-chain-bin
- name: Add the tool cache directory to the search path
run: echo "${{ runner.tool_cache }}/cargo-supply-chain/bin" >> $GITHUB_PATH
- name: Ensure that the tool cache is populated with the cargo-supply-chain binary
run: cargo install --root ${{ runner.tool_cache }}/cargo-supply-chain cargo-supply-chain
- name: Update data for cargo-supply-chain
run: cargo supply-chain update
- name: Generate cargo-supply-chain report about publishers
run: cargo supply-chain publishers
- name: Generate cargo-supply-chain report about crates
run: cargo supply-chain crates
# The setup for cargo-vet follows the recommendations in the cargo-vet documentation: https://mozilla.github.io/cargo-vet/configuring-ci.html
cargo-vet:
name: Vet Dependencies
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
key: cargo-vet-cache
- name: Install nightly toolchain
run: |
rustup toolchain install nightly
rustup override set nightly
- 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: Check which event triggered this CI run, a push or a pull request.
run: |
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
shell: bash
- 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"
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"
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 }}
shell: bash
- 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
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"
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
shell: bash
- uses: actions/checkout@v5
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: |
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: 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: 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 <noreply@rosenpass.eu>
env:
GITHUB_TOKEN: ${{ secrets.CI_BOT_PAT }}
- name: Invoke cargo-vet
run: cargo vet --locked

1
.gitignore vendored
View File

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

View File

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

1151
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,17 +2,17 @@
resolver = "2" resolver = "2"
members = [ members = [
"rosenpass", "rosenpass",
"cipher-traits", "cipher-traits",
"ciphers", "ciphers",
"util", "util",
"constant-time", "constant-time",
"oqs", "oqs",
"to", "to",
"fuzz", "fuzz",
"secret-memory", "secret-memory",
"rp", "rp",
"wireguard-broker", "wireguard-broker",
] ]
default-members = ["rosenpass", "rp", "wireguard-broker"] default-members = ["rosenpass", "rp", "wireguard-broker"]
@@ -42,66 +42,51 @@ toml = "0.7.8"
static_assertions = "1.1.0" static_assertions = "1.1.0"
allocator-api2 = "0.2.14" allocator-api2 = "0.2.14"
memsec = { git = "https://github.com/rosenpass/memsec.git", rev = "aceb9baee8aec6844125bd6612f92e9a281373df", features = [ memsec = { git = "https://github.com/rosenpass/memsec.git", rev = "aceb9baee8aec6844125bd6612f92e9a281373df", features = [
"alloc_ext", "alloc_ext",
] } ] }
rand = "0.8.5" rand = "0.8.5"
typenum = "1.17.0" typenum = "1.17.0"
log = { version = "0.4.27" } log = { version = "0.4.22" }
clap = { version = "4.5.23", features = ["derive"] } clap = { version = "4.5.22", features = ["derive"] }
clap_mangen = "0.2.29" clap_mangen = "0.2.24"
clap_complete = "4.5.40" clap_complete = "4.5.38"
serde = { version = "1.0.217", features = ["derive"] } serde = { version = "1.0.215", features = ["derive"] }
arbitrary = { version = "1.4.1", features = ["derive"] } arbitrary = { version = "1.4.1", features = ["derive"] }
anyhow = { version = "1.0.98", features = ["backtrace", "std"] } anyhow = { version = "1.0.94", features = ["backtrace", "std"] }
mio = { version = "1.0.3", features = ["net", "os-poll"] } mio = { version = "1.0.3", features = ["net", "os-poll"] }
signal-hook-mio = { version = "0.2.4", features = ["support-v1_0"] }
signal-hook = "0.3.17"
oqs-sys = { version = "0.9.1", default-features = false, features = [ oqs-sys = { version = "0.9.1", default-features = false, features = [
'classic_mceliece', 'classic_mceliece',
'kyber', 'kyber',
] } ] }
blake2 = "0.10.6" blake2 = "0.10.6"
sha3 = "0.10.8"
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
"std", "std",
"heapless", "heapless",
] } ] }
zerocopy = { version = "0.8.27", features = ["derive"] } zerocopy = { version = "0.7.35", features = ["derive"] }
home = "=0.5.9" # 5.11 requires rustc 1.81 home = "0.5.9"
derive_builder = "0.20.1" derive_builder = "0.20.1"
tokio = { version = "1.46", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.42", features = ["macros", "rt-multi-thread"] }
postcard = { version = "1.1.1", features = ["alloc"] } postcard = { version = "1.1.1", features = ["alloc"] }
libcrux = { version = "0.0.2-pre.2" } 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-literal = { version = "0.4.1" }
hex = { version = "0.4.3" } hex = { version = "0.4.3" }
heck = { version = "0.5.0" } heck = { version = "0.5.0" }
libc = { version = "0.2" } libc = { version = "0.2" }
errno = { version = "0.3.13" }
uds = { git = "https://github.com/rosenpass/uds" } uds = { git = "https://github.com/rosenpass/uds" }
lazy_static = "1.5"
#Dev dependencies #Dev dependencies
assert_tv = { version = "0.6.4" }
base64 = { version = "0.22.1" }
serial_test = "3.2.0" serial_test = "3.2.0"
tempfile = "3" tempfile = "3"
stacker = "0.1.21" stacker = "0.1.17"
libfuzzer-sys = "0.4" libfuzzer-sys = "0.4"
test_bin = "0.4.0" test_bin = "0.4.0"
criterion = "0.5.1" criterion = "0.4.0"
allocator-api2-tests = "0.2.15" allocator-api2-tests = "0.2.15"
procspawn = { version = "1.0.1", features = ["test-support"] } procspawn = { version = "1.0.1", features = ["test-support"] }
serde_json = { version = "1.0.140" }
bitflags = "2.9.3"
#Broker dependencies (might need cleanup or changes) #Broker dependencies (might need cleanup or changes)
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] } wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
command-fds = "0.2.3" command-fds = "0.2.3"
rustix = { version = "0.38.42", features = ["net", "fs", "process"] } rustix = { version = "0.38.41", features = ["net", "fs"] }
futures = "0.3"
futures-util = "0.3"
x25519-dalek = "2"

View File

@@ -0,0 +1,25 @@
#define INITIATOR_TEST 1
#include "rosenpass/03_identity_hiding.mpv"
// nounif a:Atom, s:seed, a2:Atom;
// ConsumeSeed(a, s, a2) / 6300[conclusion].
nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis].
nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis].
nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis].
nounif v:seed; attacker(rng_key( v ))/6214[hypothesis].
nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis].
nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis].
nounif v:key; attacker(prepare_key( v ))/6211[hypothesis].
nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis].
nounif Spk:kem_sk_tmpl;
attacker(Creveal_kem_pk(Spk))/6110[conclusion].
nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl;
attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion].
nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t;
attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion].
nounif rh:RespHello_t;
attacker(Cresp_hello( *rh ))/6107[conclusion].
nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t;
attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion].

View File

@@ -0,0 +1,96 @@
#define RESPONDER_TEST 1
#include "rosenpass/03_identity_hiding.mpv"
// select k:kem_pk,ih: InitHello_t; attacker(prf(prf(prf(prf(key0, PROTOCOL), MAC), kem_pk2b(k) ), IH2b(ih))) phase 1/6300[hypothesis].
// select epki:kem_pk, sctr:bits, pidiC:bits, auth:bits, epki2:kem_pk, sctr2:bits, pidiC2:bits, auth2:bits;
// mess(D, prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(kem_pub(trusted_kem_sk(responder1)))),
// IH2b(InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth)))
// ) [hypothesis, conclusion].
// select epki:kem_pk, sctr:bits, pidiC:bits, auth:bits, epki2:kem_pk, sctr2:bits, pidiC2:bits, auth2:bits;
// attacker(choice[prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(kem_pub(trusted_kem_sk(responder1)))),
// IH2b(InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth))),
// prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(kem_pub(trusted_kem_sk(responder2)))),
// IH2b(InitHello(secure_sidi, *epki2, *sctr2, *pidiC2, *auth2)))]
// ) [hypothesis, conclusion].
// select
// attacker(prf(prf(key0,PROTOCOL),MAC)) [hypothesis, conclusion].
// select
// attacker(prf(key0,PROTOCOL)) [conclusion].
// select
// attacker(key0) [conclusion].
// select
// attacker(PROTOCOL) [conclusion].
// select
// attacker(kem_pub(trusted_kem_sk(responder1))) /9999 [hypothesis, conclusion].
// select
// attacker(kem_pub(trusted_kem_sk(responder2))) /9999 [hypothesis, conclusion].
// nounif ih:InitHello_t;
// attacker(ih) / 9999 [hypothesis].
// nounif rh:RespHello_t;
// attacker(rh) / 9999 [hypothesis].
// nounif ic:InitConf_t;
// attacker(ic) / 9999 [hypothesis].
// nounif k:key;
// attacker(ck_hs_enc( *k )) [hypothesis, conclusion].
// nounif k:key;
// attacker(ck_hs_enc( *k )) phase 1 [hypothesis, conclusion].
// nounif k:key, b:bits;
// attacker(ck_mix( *k , *b )) [hypothesis, conclusion].
// nounif k:key, b:bits;
// attacker(ck_mix( *k , *b ))phase 1 [hypothesis, conclusion].
// // select k:kem_pk, epki2:kem_pk, sctr2:bits, pidiC2:bits, auth2:bits, epki:kem_pk, sctr:bits, pidiC:bits, auth:bits;
// // attacker(choice[Envelope(prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pub(trusted_kem_sk(responder1))),
// // InitHello(secure_sidi, *epki2, *sctr2, *pidiC2, *auth2)
// // ), InitHello(secure_sidi, *epki2, *sctr2, *pidiC2, *auth2))
// // Envelope(prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pub(trusted_kem_sk(responder2))),
// // InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth)),
// // InitHello(secure_sidi, *epki, *sctr, *pidiC, *auth))
// // ]) / 9999[hypothesis, conclusion].
// nounif k:key, b1:bits, b2:bits;
// attacker(xaead_enc( *k, *b1, *b2)) / 9999[hypothesis,conclusion].
// nounif pk:kem_pk, k:key;
// attacker(kem_enc( *pk , *k )) / 9999[hypothesis,conclusion].
// nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t;
// attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/9999[hypothesis, conclusion].
// nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t;
// attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/9999[hypothesis, conclusion].
// nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl;
// attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr )) /9999 [hypothesis, conclusion].
// nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t;
// mess(C, Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/9999[hypothesis, conclusion].
// nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t;
// mess(C, Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/9999[hypothesis, conclusion].
// nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl;
// mess(C, Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr )) /9999 [hypothesis, conclusion].
// nounif rh:RespHello_t;
// attacker(Cresp_hello( *rh ))[conclusion].
// nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis].
// nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis].
// nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis].
// nounif v:seed; attacker(rng_key( v ))/6214[hypothesis].
// nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis].
// nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis].
// nounif v:key; attacker(prepare_key( v ))/6211[hypothesis].
// nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis].

View File

@@ -0,0 +1,29 @@
#define INITIATOR_TEST 1
#define CUSTOM_MAIN 1
#include "rosenpass/03_identity_hiding.mpv"
let Oinitiator_bad_actor_inner(sk_tmp:kem_sk_prec) =
in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr));
#if RANDOMIZED_CALL_IDS
new call:Atom;
#else
call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr);
#endif
in(C, last_cookie:key);
tmpl <- make_trusted_kem_sk(sk_tmp);
out(C, setup_kem_sk(tmpl));
Oinitiator_inner(sidi, Ssskm, Spsk, tmpl, Seski, Ssptr, last_cookie, C, call).
let Oinitiator_bad_actor() =
Oinitiator_bad_actor_inner(responder1) | Oinitiator_bad_actor_inner(responder2) | Oinitiator_bad_actor_inner(initiator1) | Oinitiator_bad_actor_inner(initiator2).
let identity_hiding_main2() =
0 | Oinitiator_bad_actor() | rosenpass_main2() | participants_communication() | phase 1; secretCommunication().
let main = identity_hiding_main2.

View File

@@ -0,0 +1,136 @@
#define CHAINING_KEY_EVENTS 1
#define MESSAGE_TRANSMISSION_EVENTS 0
#define SESSION_START_EVENTS 0
#define RANDOMIZED_CALL_IDS 0
#define COOKIE_EVENTS 1
#define KEM_EVENTS 1
#include "config.mpv"
#include "prelude/basic.mpv"
#include "crypto/key.mpv"
#include "crypto/kem.mpv"
#include "rosenpass/handshake_state.mpv"
/* The cookie data structure is implemented based on the WireGuard protocol.
* The ip and port is based purely on the public key and the implementation of the private cookie key is intended to mirror the biscuit key.
* The code tests the response to a possible DOS attack by setting up alternative branches for the protocol
* processes: Oinit_conf, Oinit_hello and resp_hello to simulate what happens when the responder or initiator is overloaded.
* When under heavy load a valid cookie is required. When such a cookie is not present a cookie message is sent as a response.
* Queries then test to make sure that expensive KEM operations are only conducted after a cookie has been successfully validated.
*/
type CookieMsg_t.
fun CookieMsg(
SessionId, // sender
bits, // nonce
bits // cookie
) : CookieMsg_t [data].
#define COOKIE_EVENTS(eventLbl) \
COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (SessionId, SessionId, Atom).) \
COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (SessionId, SessionId, Atom).) \
COOKIE_EV(event MCAT(eventLbl, _CookieSent) (SessionId, SessionId, Atom, CookieMsg_t).)
fun cookie_key(kem_sk) : key [private].
fun ip_and_port(kem_pk):bits.
letfun create_mac2_key(sskm:kem_sk, spkt:kem_pk) = prf(cookie_key(sskm), ip_and_port(spkt)).
letfun create_cookie(sskm:kem_sk, spkm:kem_pk, spkt:kem_pk, nonce:bits, msg:bits) = xaead_enc(lprf2(COOKIE, kem_pk2b(spkm), nonce),
k2b(create_mac2_key(sskm, spkm)), msg).
#define COOKIE_PROCESS(eventLbl, innerFunc) \
new nonce:bits; \
in(C, Ccookie(mac1, mac2)); \
COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (sidi, sidr, call);) \
msgB <- Envelope(mac1, msg); \
mac2_key <- create_mac2_key(sskm, spkt); \
if k2b(create_mac2(mac2_key, msgB)) = mac2 then \
COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (sidi, sidr, call);) \
innerFunc \
else \
cookie <- create_cookie(sskm, spkm, spkt, nonce, msg); \
cookie_msg <- CookieMsg(sidi, nonce, cookie); \
COOKIE_EV(event MCAT(eventLbl, _CookieSent) (sidi, sidr, call, cookie_msg);) \
out(C, cookie_msg). \
#include "rosenpass/oracles.mpv"
#include "rosenpass/responder.macro"
COOKIE_EVENTS(Oinit_conf)
let Oinit_conf_underLoad() =
in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic));
in(C, last_cookie:bits);
msg <- IC2b(ic);
let InitConf(sidi, sidr, biscuit, auth) = ic in
new call:Atom;
SETUP_HANDSHAKE_STATE()
COOKIE_PROCESS(Oinit_conf, Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic, call))
#include "rosenpass/responder.macro"
COOKIE_EVENTS(Oinit_hello)
let Oinit_hello_underLoad() =
in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih));
in(C, Oinit_hello_last_cookie:key);
new call:Atom;
msg <- IH2b(ih);
let InitHello(sidi, epki, sctr, pidic, auth) = ih in
SETUP_HANDSHAKE_STATE()
COOKIE_PROCESS(Oinit_hello, Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, Oinit_hello_last_cookie, C, call))
let rosenpass_dos_main() = 0
| !Oreveal_kem_pk
| REP(INITIATOR_BOUND, Oinitiator)
| REP(RESPONDER_BOUND, Oinit_hello)
| REP(RESPONDER_BOUND, Oinit_conf)
| REP(RESPONDER_BOUND, Oinit_hello_underLoad)
| REP(RESPONDER_BOUND, Oinit_conf_underLoad).
let main = rosenpass_dos_main.
select cookie:CookieMsg_t; attacker(cookie)/6220[hypothesis].
nounif v:key; attacker(prepare_key( v ))/6217[hypothesis].
nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis].
nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis].
nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis].
nounif v:seed; attacker(rng_key( v ))/6214[hypothesis].
nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis].
// nounif Spk:kem_sk_tmpl;
// attacker(Creveal_kem_pk(Spk))/6110[conclusion].
// nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl;
// attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion].
// nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t;
// attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion].
nounif rh:RespHello_t;
attacker(Cresp_hello( *rh ))/6107[conclusion].
nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t;
attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion].
@reachable "DOS protection: cookie sent"
query sidi:SessionId, sidr:SessionId, call:Atom, cookieMsg:CookieMsg_t;
event (Oinit_hello_CookieSent(sidi, sidr, call, cookieMsg)).
@lemma "DOS protection: Oinit_hello kem use when under load implies validated cookie"
lemma sidi:SessionId, sidr:SessionId, call:Atom;
event(Oinit_hello_UnderLoadEV(sidi, sidr, call))
&& event(Oinit_hello_KemUse(sidi, sidr, call))
==> event(Oinit_hello_CookieValidated(sidi, sidr, call)).
@lemma "DOS protection: Oinit_conf kem use when under load implies validated cookie"
lemma sidi:SessionId, sidr:SessionId, call:Atom;
event(Oinit_conf_UnderLoadEV(sidi, sidr, call))
&& event(Oinit_conf_KemUse(sidi, sidr, call))
==> event(Oinit_conf_CookieValidated(sidi, sidr, call)).
@lemma "DOS protection: Oresp_hello kem use when under load implies validated cookie"
lemma sidi:SessionId, sidr:SessionId, call:Atom;
event(Oresp_hello_UnderLoadEV(sidi, sidr, call))
&& event(Oresp_hello_KemUse(sidi, sidr, call))
==> event(Oresp_hello_CookieValidated(sidi, sidr, call)).

View File

@@ -58,7 +58,7 @@ let secure_init_hello(initiator: kem_sk_tmpl, sidi : SessionId, psk: key_tmpl, r
new epkit:kem_pk; // epki new epkit:kem_pk; // epki
new sctrt:bits; // sctr new sctrt:bits; // sctr
new pidi_ct:bits; // pidi_ct new pidiCt:bits; // pidiC
new autht:bits; // auth new autht:bits; // auth
NEW_TRUSTED_SEED(seski_trusted_seed) NEW_TRUSTED_SEED(seski_trusted_seed)
@@ -70,9 +70,9 @@ let secure_init_hello(initiator: kem_sk_tmpl, sidi : SessionId, psk: key_tmpl, r
let secure_resp_hello(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, sidi:SessionId, sidr:SessionId, biscuit_no:Atom, psk:key_tmpl) = let secure_resp_hello(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, sidi:SessionId, sidr:SessionId, biscuit_no:Atom, psk:key_tmpl) =
in(D, InitHello(=secure_sidi, epki, sctr, pidi_ct, auth)); in(D, InitHello(=secure_sidi, epki, sctr, pidiC, auth));
ih <- InitHello(sidi, epki, sctr, pidi_ct, auth); ih <- InitHello(sidi, epki, sctr, pidiC, auth);
NEW_TRUSTED_SEED(septi_trusted_seed) NEW_TRUSTED_SEED(septi_trusted_seed)
NEW_TRUSTED_SEED(sspti_trusted_seed) NEW_TRUSTED_SEED(sspti_trusted_seed)
new last_cookie:key; new last_cookie:key;

View File

@@ -19,7 +19,7 @@ fun CookieMsg(
COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (spkm, spkt, last_cookie);) \ COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (spkm, spkt, last_cookie);) \
msgB <- Envelope(mac1, RH2b(rh)); \ msgB <- Envelope(mac1, RH2b(rh)); \
mac2_key <- create_mac2_key(sskm, spkt) \ mac2_key <- create_mac2_key(sskm, spkt) \
let RespHello(sidi, sidr, ecti, scti, biscuit_ct, auth) = rh in \ let RespHello(sidi, sidr, ecti, scti, biscuit, auth) = rh in \
if Envelope(mac2_key, msgB) = mac2 then \ if Envelope(mac2_key, msgB) = mac2 then \
COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (spkm, last_cookie);) \ COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (spkm, last_cookie);) \
innerFunc \ innerFunc \

View File

@@ -143,10 +143,10 @@ letfun ENCRYPT_AND_MIX(ct, pt) \
// TODO: Migrate kems to use binary ciphertexts directly // TODO: Migrate kems to use binary ciphertexts directly
#define ENCAPS_AND_MIX(ct, pk, shk) \ #define ENCAPS_AND_MIX(ct, pk, shk) \
ct <- kem_enc(pk, shk); \ ct <- kem_enc(pk, shk); \
MIX3(kem_pk2b(pk), k2b(shk), ct) MIX3(kem_pk2b(pk), ct, k2b(shk))
#define DECAPS_AND_MIX(sk, pk, ct) \ #define DECAPS_AND_MIX(sk, pk, ct) \
DUMMY(shk) <- kem_dec(sk, ct); \ DUMMY(shk) <- kem_dec(sk, ct); \
MIX3(kem_pk2b(pk), k2b(DUMMY(shk)), ct) MIX3(kem_pk2b(pk), ct, k2b(DUMMY(shk)))
// biscuits // biscuits

View File

@@ -86,8 +86,8 @@ MTX_EV( event RHRjct(RespHello_t, key, kem_sk, kem_pk). )
MTX_EV( event ICSent(RespHello_t, InitConf_t, key, kem_sk, kem_pk). ) MTX_EV( event ICSent(RespHello_t, InitConf_t, key, kem_sk, kem_pk). )
SES_EV( event InitiatorSession(RespHello_t, key). ) SES_EV( event InitiatorSession(RespHello_t, key). )
let Oresp_hello(HS_DECL_ARGS) = let Oresp_hello(HS_DECL_ARGS) =
in(C, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit_ct, auth))); in(C, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
rh <- RespHello(sidr, sidi, ecti, scti, biscuit_ct, auth); rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
/* try */ let ic = ( /* try */ let ic = (
ck_ini <- ck; ck_ini <- ck;
RESPHELLO_CONSUME() RESPHELLO_CONSUME()
@@ -124,7 +124,7 @@ let Oinit_hello() =
call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih); call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih);
#endif #endif
// TODO: This is ugly // TODO: This is ugly
let InitHello(sidi, epki, sctr, pidi_ct, auth) = ih in let InitHello(sidi, epki, sctr, pidiC, auth) = ih in
SETUP_HANDSHAKE_STATE() SETUP_HANDSHAKE_STATE()
eski <- kem_sk0; eski <- kem_sk0;
epti <- rng_key(setup_seed(Septi)); // RHR4 epti <- rng_key(setup_seed(Septi)); // RHR4

View File

@@ -7,7 +7,7 @@ fun InitHello(
SessionId, // sidi SessionId, // sidi
kem_pk, // epki kem_pk, // epki
bits, // sctr bits, // sctr
bits, // pidi_ct bits, // pidiC
bits // auth bits // auth
) : InitHello_t [data]. ) : InitHello_t [data].
@@ -17,16 +17,16 @@ fun InitHello(
/* not handled here */ /* IHI3 */ \ /* not handled here */ /* IHI3 */ \
MIX2(sid2b(sidi), kem_pk2b(epki)) /* IHI4 */ \ MIX2(sid2b(sidi), kem_pk2b(epki)) /* IHI4 */ \
ENCAPS_AND_MIX(sctr, spkr, sptr) /* IHI5 */ \ ENCAPS_AND_MIX(sctr, spkr, sptr) /* IHI5 */ \
ENCRYPT_AND_MIX(pidi_ct, pidi) /* IHI6 */ \ ENCRYPT_AND_MIX(pidiC, pidi) /* IHI6 */ \
MIX2(kem_pk2b(spki), k2b(psk)) /* IHI7 */ \ MIX2(kem_pk2b(spki), k2b(psk)) /* IHI7 */ \
ENCRYPT_AND_MIX(auth, empty) /* IHI8 */ \ ENCRYPT_AND_MIX(auth, empty) /* IHI8 */ \
ih <- InitHello(sidi, epki, sctr, pidi_ct, auth); ih <- InitHello(sidi, epki, sctr, pidiC, auth);
#define INITHELLO_CONSUME() \ #define INITHELLO_CONSUME() \
ck <- lprf1(CK_INIT, kem_pk2b(spkr)); /* IHR1 */ \ ck <- lprf1(CK_INIT, kem_pk2b(spkr)); /* IHR1 */ \
MIX2(sid2b(sidi), kem_pk2b(epki)) /* IHR4 */ \ MIX2(sid2b(sidi), kem_pk2b(epki)) /* IHR4 */ \
DECAPS_AND_MIX(sskr, spkr, sctr) /* IHR5 */ \ DECAPS_AND_MIX(sskr, spkr, sctr) /* IHR5 */ \
DECRYPT_AND_MIX(pid, pidi_ct) /* IHR6 */ \ DECRYPT_AND_MIX(pid, pidiC) /* IHR6 */ \
LOOKUP_SENDER(pid) /* IHR6 */ \ LOOKUP_SENDER(pid) /* IHR6 */ \
MIX2(kem_pk2b(spki), k2b(psk)) /* IHR7 */ \ MIX2(kem_pk2b(spki), k2b(psk)) /* IHR7 */ \
DECRYPT_AND_MIX(DUMMY(empty), auth) DECRYPT_AND_MIX(DUMMY(empty), auth)
@@ -46,17 +46,17 @@ fun RespHello(
MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \ MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \
ENCAPS_AND_MIX(ecti, epki, epti) /* RHR4 */ \ ENCAPS_AND_MIX(ecti, epki, epti) /* RHR4 */ \
ENCAPS_AND_MIX(scti, spki, spti) /* RHR5 */ \ ENCAPS_AND_MIX(scti, spki, spti) /* RHR5 */ \
STORE_BISCUIT(biscuit_ct) /* RHR6 */ \ STORE_BISCUIT(biscuit) /* RHR6 */ \
ENCRYPT_AND_MIX(auth, empty) /* RHR7 */ \ ENCRYPT_AND_MIX(auth, empty) /* RHR7 */ \
rh <- RespHello(sidr, sidi, ecti, scti, biscuit_ct, auth); rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
#define RESPHELLO_CONSUME() \ #define RESPHELLO_CONSUME() \
let RespHello(sidr, sidi, ecti, scti, biscuit_ct, auth) = rh in \ let RespHello(sidr, sidi, ecti, scti, biscuit, auth) = rh in \
/* not handled here */ /* RHI2 */ \ /* not handled here */ /* RHI2 */ \
MIX2(sid2b(sidr), sid2b(sidi)) /* RHI3 */ \ MIX2(sid2b(sidr), sid2b(sidi)) /* RHI3 */ \
DECAPS_AND_MIX(eski, epki, ecti) /* RHI4 */ \ DECAPS_AND_MIX(eski, epki, ecti) /* RHI4 */ \
DECAPS_AND_MIX(sski, spki, scti) /* RHI5 */ \ DECAPS_AND_MIX(sski, spki, scti) /* RHI5 */ \
MIX(biscuit_ct) /* RHI6 */ \ MIX(biscuit) /* RHI6 */ \
DECRYPT_AND_MIX(DUMMY(empty), auth) /* RHI7 */ DECRYPT_AND_MIX(DUMMY(empty), auth) /* RHI7 */
type InitConf_t. type InitConf_t.
@@ -70,11 +70,11 @@ fun InitConf(
#define INITCONF_PRODUCE() \ #define INITCONF_PRODUCE() \
MIX2(sid2b(sidi), sid2b(sidr)) /* ICI3 */ \ MIX2(sid2b(sidi), sid2b(sidr)) /* ICI3 */ \
ENCRYPT_AND_MIX(auth, empty) /* ICI4 */ \ ENCRYPT_AND_MIX(auth, empty) /* ICI4 */ \
ic <- InitConf(sidi, sidr, biscuit_ct, auth); ic <- InitConf(sidi, sidr, biscuit, auth);
#define INITCONF_CONSUME() \ #define INITCONF_CONSUME() \
let InitConf(sidi, sidr, biscuit_ct, auth) = ic in \ let InitConf(sidi, sidr, biscuit, auth) = ic in \
LOAD_BISCUIT(biscuit_no, biscuit_ct)/* ICR1 */ \ LOAD_BISCUIT(biscuit_no, biscuit) /* ICR1 */ \
ENCRYPT_AND_MIX(rh_auth, empty) /* ICIR */ \ ENCRYPT_AND_MIX(rh_auth, empty) /* ICIR */ \
ck_rh <- ck; /* ---- */ /* TODO: Move into oracles.mpv */ \ ck_rh <- ck; /* ---- */ /* TODO: Move into oracles.mpv */ \
MIX2(sid2b(sidi), sid2b(sidr)) /* ICR3 */ \ MIX2(sid2b(sidi), sid2b(sidr)) /* ICR3 */ \

View File

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

View File

@@ -2,4 +2,4 @@
Rosenpass internal library providing traits for cryptographic primitives. Rosenpass internal library providing traits for cryptographic primitives.
This is an internal library; no guarantee is made about its API at this point in time. This is an internal library; not guarantee is made about its API at this point in time.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,42 +8,9 @@ description = "Rosenpass internal ciphers and other cryptographic primitives use
homepage = "https://rosenpass.eu/" homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass" repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md" readme = "readme.md"
rust-version = "1.77.0"
[features] [features]
# whether the types should be defined experiment_libcrux = ["dep:libcrux"]
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_define_chachapoly",
"dep:libcrux",
]
# 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"
harness = false
required-features = ["bench"]
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
@@ -52,22 +19,8 @@ rosenpass-constant-time = { workspace = true }
rosenpass-secret-memory = { workspace = true } rosenpass-secret-memory = { workspace = true }
rosenpass-oqs = { workspace = true } rosenpass-oqs = { workspace = true }
rosenpass-util = { workspace = true } rosenpass-util = { workspace = true }
rosenpass-cipher-traits = { workspace = true }
static_assertions = { workspace = true } static_assertions = { workspace = true }
zeroize = { workspace = true } zeroize = { workspace = true }
chacha20poly1305 = { workspace = true } chacha20poly1305 = { workspace = true }
blake2 = { workspace = true } blake2 = { workspace = true }
sha3 = { workspace = true }
rand = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
libcrux-chacha20poly1305 = { workspace = true, optional = true }
libcrux-blake2 = { workspace = true, optional = true }
libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] }
# this one is only used in testing, so it requires the `experiment_libcrux_chachapoly_test` feature.
libcrux = { workspace = true, optional = true } libcrux = { workspace = true, optional = true }
[dev-dependencies]
rand = { workspace = true }
criterion = { workspace = true }

View File

@@ -1,378 +0,0 @@
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<SK_LEN, PK_LEN, CT_LEN, SHK_LEN>,
>(
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<const NONCE_LEN: usize, T: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>>(
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", "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;
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<H: KeyedHash<KEY_LEN, HASH_LEN>>(
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("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(|| {
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();
})
});
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

121
deny.toml
View File

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

View File

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

View File

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

212
flake.lock generated
View File

@@ -1,5 +1,26 @@
{ {
"nodes": { "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": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@@ -18,65 +39,6 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nix-vm-test": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1734355073,
"narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=",
"owner": "numtide",
"repo": "nix-vm-test",
"rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-vm-test",
"type": "github"
}
},
"nix-vm-test_2": {
"inputs": {
"nixpkgs": [
"rosenpassOld",
"nixpkgs"
]
},
"locked": {
"lastModified": 1734355073,
"narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=",
"owner": "numtide",
"repo": "nix-vm-test",
"rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-vm-test",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1728193676, "lastModified": 1728193676,
@@ -95,77 +57,25 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"fenix": "fenix",
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nix-vm-test": "nix-vm-test", "nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs",
"rosenpassOld": "rosenpassOld",
"rust-overlay": "rust-overlay_2",
"treefmt-nix": "treefmt-nix_2"
} }
}, },
"rosenpassOld": { "rust-analyzer-src": {
"inputs": { "flake": false,
"flake-utils": "flake-utils_2",
"nix-vm-test": "nix-vm-test_2",
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay",
"treefmt-nix": "treefmt-nix"
},
"locked": { "locked": {
"lastModified": 1754748821, "lastModified": 1728249780,
"narHash": "sha256-mMggTZDC97lLvKNOLtDz3GBjjxXFD++e1s0RZsVH/vI=", "narHash": "sha256-J269DvCI5dzBmPrXhAAtj566qt0b22TJtF3TIK+tMsI=",
"owner": "rosenpass", "owner": "rust-lang",
"repo": "rosenpass", "repo": "rust-analyzer",
"rev": "916a9ebb7133f0b22057fb097a473217f261928a", "rev": "2b750da1a1a2c1d2c70896108d7096089842d877",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "rosenpass", "owner": "rust-lang",
"repo": "rosenpass", "ref": "nightly",
"rev": "916a9ebb7133f0b22057fb097a473217f261928a", "repo": "rust-analyzer",
"type": "github"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"rosenpassOld",
"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"
}
},
"rust-overlay_2": {
"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" "type": "github"
} }
}, },
@@ -183,62 +93,6 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"rosenpassOld",
"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"
}
},
"treefmt-nix_2": {
"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", "root": "root",

297
flake.nix
View File

@@ -3,56 +3,37 @@
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
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";
# for rust nightly with llvm-tools-preview # for rust nightly with llvm-tools-preview
rust-overlay.url = "github:oxalica/rust-overlay"; fenix.url = "github:nix-community/fenix";
rust-overlay.inputs.nixpkgs.follows = "nixpkgs"; fenix.inputs.nixpkgs.follows = "nixpkgs";
treefmt-nix.url = "github:numtide/treefmt-nix";
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
# Older version of rosenpass, referenced here for backwards compatibility
rosenpassOld.url = "github:rosenpass/rosenpass?rev=916a9ebb7133f0b22057fb097a473217f261928a";
rosenpassOld.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = outputs = { self, nixpkgs, flake-utils, ... }@inputs:
{
self,
nixpkgs,
flake-utils,
nix-vm-test,
rust-overlay,
treefmt-nix,
rosenpassOld,
...
}@inputs:
nixpkgs.lib.foldl (a: b: nixpkgs.lib.recursiveUpdate a b) { } [ nixpkgs.lib.foldl (a: b: nixpkgs.lib.recursiveUpdate a b) { } [
# #
### Export the overlay.nix from this flake ### ### Export the overlay.nix from this flake ###
# #
{ overlays.default = import ./overlay.nix; } {
overlays.default = import ./overlay.nix;
}
# #
### Actual Rosenpass Package and Docker Container Images ### ### Actual Rosenpass Package and Docker Container Images ###
# #
(flake-utils.lib.eachSystem (flake-utils.lib.eachSystem [
[ "x86_64-linux"
"x86_64-linux" "aarch64-linux"
"aarch64-linux"
# unsuported best-effort # unsuported best-effort
"i686-linux" "i686-linux"
"x86_64-darwin" "x86_64-darwin"
"aarch64-darwin" "aarch64-darwin"
# "x86_64-windows" # "x86_64-windows"
] ]
( (system:
system:
let let
# normal nixpkgs # normal nixpkgs
pkgs = import nixpkgs { pkgs = import nixpkgs {
@@ -63,185 +44,99 @@
}; };
in in
{ {
packages = packages = {
{ default = pkgs.rosenpass;
default = pkgs.rosenpass; rosenpass = pkgs.rosenpass;
rosenpass = pkgs.rosenpass; rosenpass-oci-image = pkgs.rosenpass-oci-image;
rosenpass-oci-image = pkgs.rosenpass-oci-image; rp = pkgs.rp;
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 # 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-rosenpass-static =
aarch64-linux-rp-static = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rp; 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 { # We only offer static builds for linux, as this is not supported on OS X
rosenpass-static = pkgs.pkgsStatic.rosenpass; (nixpkgs.lib.attrsets.optionalAttrs pkgs.stdenv.isLinux {
rosenpass-static-oci-image = pkgs.pkgsStatic.rosenpass-oci-image; rosenpass-static = pkgs.pkgsStatic.rosenpass;
rp-static = pkgs.pkgsStatic.rp; rosenpass-static-oci-image = pkgs.pkgsStatic.rosenpass-oci-image;
}); rp-static = pkgs.pkgsStatic.rp;
});
} }
) ))
)
# #
### Linux specifics ### ### Linux specifics ###
# #
(flake-utils.lib.eachSystem (flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
[ let
"x86_64-linux" pkgs = import nixpkgs {
"aarch64-linux" inherit system;
"i686-linux"
]
(
system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ # apply our own overlay, overriding/inserting our packages as defined in ./pkgs
# apply our own overlay, overriding/inserting our packages as defined in ./pkgs overlays = [ self.overlays.default ];
self.overlays.default };
in
{
nix-vm-test.overlays.default #
### Reading materials ###
#
packages.whitepaper = pkgs.whitepaper;
# apply rust-overlay to get specific versions of the rust toolchain for a MSRV check #
(import rust-overlay) ### Proof and Proof Tools ###
]; #
}; packages.proverif-patched = pkgs.proverif-patched;
packages.proof-proverif = pkgs.proof-proverif;
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 ### ### Devshells ###
# #
packages.whitepaper = pkgs.whitepaper; 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
];
};
devShells.coverage = pkgs.mkShell {
inputsFrom = [ pkgs.rosenpass ];
nativeBuildInputs = [
inputs.fenix.packages.${system}.complete.toolchain
pkgs.cargo-llvm-cov
];
};
#
### Proof and Proof Tools ###
#
packages.proverif-patched = pkgs.proverif-patched;
packages.proof-proverif = pkgs.proof-proverif;
# checks = {
### Devshells ### systemd-rosenpass = pkgs.testers.runNixOSTest ./tests/systemd/rosenpass.nix;
# systemd-rp = pkgs.testers.runNixOSTest ./tests/systemd/rp.nix;
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-msrv
cargo-release
cargo-vet
rustfmt
nodePackages.prettier
nushell # for the .ci/gen-workflow-files.nu script
proverif-patched
pkgs.cargo-llvm-cov
pkgs.grcov
pkgs.rust-bin.stable.latest.complete
];
};
devShells.coverage = pkgs.mkShell {
inputsFrom = [ pkgs.rosenpass ];
nativeBuildInputs = [
pkgs.cargo-llvm-cov
pkgs.grcov
pkgs.rustc.llvmPackages.llvm
];
env = {
inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA;
};
};
devShells.benchmarks = pkgs.mkShell {
inputsFrom = [ pkgs.rosenpass ];
nativeBuildInputs = with pkgs; [
cargo-release
clippy
rustfmt
];
};
# a devshell to hunt unsafe `unsafe` in the code
devShells.miri = pkgs.mkShell {
# inputsFrom = [ self.packages.${system}.rosenpass ];
nativeBuildInputs = with pkgs; [
((rust-bin.selectLatestNightlyWith (toolchain: toolchain.default)).override {
extensions = [
"rust-analysis"
"rust-src"
"miri-preview"
];
})
pkgs.cmake
pkgs.rustPlatform.bindgenHook
];
# Run this to find unsafe `unsafe`:
# MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test --no-fail-fast --lib --bins --tests
#
# - Some test failure is expected.
};
checks = cargo-fmt = pkgs.runCommand "check-cargo-fmt"
import ./tests/integration/integration-checks.nix { { inherit (self.devShells.${system}.default) nativeBuildInputs buildInputs; } ''
inherit system; cargo fmt --manifest-path=${./.}/Cargo.toml --check --all && touch $out
pkgs = inputs.nixpkgs; '';
lib = nixpkgs.lib; nixpkgs-fmt = pkgs.runCommand "check-nixpkgs-fmt"
rosenpassNew = self.packages.${system}.default; { nativeBuildInputs = [ pkgs.nixpkgs-fmt ]; } ''
rosenpassOld = rosenpassOld.packages.${system}.default; nixpkgs-fmt --check ${./.} && touch $out
} '';
// { prettier-check = pkgs.runCommand "check-with-prettier"
systemd-rosenpass = pkgs.testers.runNixOSTest ./tests/systemd/rosenpass.nix; { nativeBuildInputs = [ pkgs.nodePackages.prettier ]; } ''
systemd-rp = pkgs.testers.runNixOSTest ./tests/systemd/rp.nix; cd ${./.} && prettier --check . && touch $out
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; formatter = pkgs.nixpkgs-fmt;
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 {
inherit pkgs;
rosenpass-deb = self.packages.${system}.package-deb;
rosenpass-rpm = self.packages.${system}.package-rpm;
}
);
# for `nix fmt`
formatter = treefmtEval.config.build.wrapper;
}
)
)
]; ];
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,13 +3,13 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target; use libfuzzer_sys::fuzz_target;
use rosenpass_cipher_traits::primitives::Kem; use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::StaticKem; use rosenpass_ciphers::kem::StaticKem;
fuzz_target!(|input: [u8; StaticKem::PK_LEN]| { fuzz_target!(|input: [u8; StaticKem::PK_LEN]| {
let mut ciphertext = [0u8; StaticKem::CT_LEN]; let mut ciphertext = [0u8; StaticKem::CT_LEN];
let mut shared_secret = [0u8; StaticKem::SHK_LEN]; let mut shared_secret = [0u8; StaticKem::SHK_LEN];
// We expect errors while fuzzing therefore we do not check the result. // We expect errors while fuzzing therefore we do not check the result.
let _ = StaticKem.encaps(&mut shared_secret, &mut ciphertext, &input); let _ = StaticKem::encaps(&mut shared_secret, &mut ciphertext, &input);
}); });

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
final: prev: { final: prev: {
# #
### Actual rosenpass software ### ### Actual rosenpass software ###
# #
@@ -26,10 +27,7 @@ final: prev: {
"marzipan(/marzipan.awk)?" "marzipan(/marzipan.awk)?"
"analysis(/.*)?" "analysis(/.*)?"
]; ];
nativeBuildInputs = [ nativeBuildInputs = [ final.proverif final.graphviz ];
final.proverif
final.graphviz
];
CRYPTOVERIF_LIB = final.proverif-patched + "/lib/cryptoverif.pvl"; CRYPTOVERIF_LIB = final.proverif-patched + "/lib/cryptoverif.pvl";
installPhase = '' installPhase = ''
mkdir -p $out mkdir -p $out

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 751 KiB

After

Width:  |  Height:  |  Size: 725 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 218 KiB

View File

@@ -0,0 +1,191 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 2037 1491" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g transform="matrix(0.958104,0,0,0.883458,-169.743,-156.518)">
<rect id="ArtBoard1" x="177.165" y="177.165" width="2125.98" height="1687.51" style="fill:none;"/>
<clipPath id="_clip1">
<rect id="ArtBoard11" serif:id="ArtBoard1" x="177.165" y="177.165" width="2125.98" height="1687.51"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(0.377816,0,0,0.318513,-62.5845,3.62207)">
<path d="M1608.99,599.153C1608.99,575.987 1594.37,557.179 1576.37,557.179L680.292,557.179C662.284,557.179 647.664,575.987 647.664,599.153L647.664,903.661C647.664,926.827 662.284,945.635 680.292,945.635L1576.37,945.635C1594.37,945.635 1608.99,926.827 1608.99,903.661L1608.99,599.153Z" style="fill:rgb(247,4,132);"/>
</g>
<g transform="matrix(1.11885,0,0,0.472336,1334.4,22.5297)">
<path d="M497.076,394.18L497.076,1793.56C497.076,1793.56 -810.094,1791.78 -810.094,1793.56L-810.094,2231.73L497.076,2231.73L497.076,3888.59" style="fill:none;stroke:rgb(255,166,48);stroke-width:15.37px;"/>
</g>
<g transform="matrix(1.10326,0,0,0.239529,-152.083,336.057)">
<g transform="matrix(0.946041,-0,-0,4.72559,298.433,-663.352)">
<path d="M1597.09,252.781L1609.59,265.281L1597.09,277.781" style="fill:none;stroke:rgb(247,4,132);stroke-width:8.33px;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;"/>
<path d="M209.973,265.281L1609.59,265.281" style="fill:none;stroke:rgb(247,4,132);stroke-width:8.33px;stroke-linecap:round;stroke-dasharray:8.33,16.67,0,0;"/>
</g>
</g>
<g transform="matrix(0.54423,0,0,0.514519,523.603,90.8277)">
<path d="M1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:rgb(247,4,132);"/>
<path d="M1624.98,583.163L1624.98,919.651C1624.98,944.11 1607.66,963.968 1586.34,963.968L670.315,963.968C648.993,963.968 631.682,944.11 631.682,919.651L631.682,583.163C631.682,558.704 648.993,538.846 670.315,538.846L1586.34,538.846C1607.66,538.846 1624.98,558.704 1624.98,583.163ZM1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:white;"/>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-323.596,-1172.27)">
<g transform="matrix(50,0,0,50,1497.15,1475.25)">
</g>
<text x="1302.95px" y="1475.25px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:50px;">InitHell<tspan x="1469.15px " y="1475.25px ">o</tspan></text>
</g>
<g transform="matrix(1.10157,0,0,0.239529,-151.245,1006.25)">
<g transform="matrix(0.947489,-0,-0,4.72559,298.129,-3461.31)">
<path d="M1597.09,844.867L1609.59,857.367L1597.09,869.867" style="fill:none;stroke:rgb(247,4,132);stroke-width:8.33px;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;"/>
<path d="M209.973,857.367L1609.59,857.367" style="fill:none;stroke:rgb(247,4,132);stroke-width:8.33px;stroke-linecap:round;stroke-dasharray:8.33,16.67,0,0;"/>
</g>
</g>
<g transform="matrix(0.54423,0,0,0.514519,523.603,752.344)">
<path d="M1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:rgb(247,4,132);"/>
<path d="M1624.98,583.163L1624.98,919.651C1624.98,944.11 1607.66,963.968 1586.34,963.968L670.315,963.968C648.993,963.968 631.682,944.11 631.682,919.651L631.682,583.163C631.682,558.704 648.993,538.846 670.315,538.846L1586.34,538.846C1607.66,538.846 1624.98,558.704 1624.98,583.163ZM1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:white;"/>
</g>
<g transform="matrix(0.19416,0,0,0.275328,1052.99,940.806)">
<path d="M1608.99,571.746C1608.99,563.706 1600.46,557.179 1589.95,557.179L666.712,557.179C656.199,557.179 647.664,563.706 647.664,571.746L647.664,931.068C647.664,939.108 656.199,945.635 666.712,945.635L1589.95,945.635C1600.46,945.635 1608.99,939.108 1608.99,931.068L1608.99,571.746Z" style="fill:rgb(255,211,152);stroke:white;stroke-width:19.24px;stroke-linecap:round;"/>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-345.04,-502.074)">
<g transform="matrix(50,0,0,50,1404.28,1475.25)">
</g>
<text x="1227.23px" y="1475.25px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:50px;">InitC<tspan x="1330.68px " y="1475.25px ">o</tspan>nf</text>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-34.3588,-501.229)">
<g transform="matrix(41.6667,0,0,41.6667,1315.29,1464.53)">
</g>
<text x="1188.09px" y="1464.53px" style="font-family:'Nunito-SemiBold', 'Nunito';font-weight:600;font-size:41.667px;">Biscuit</text>
</g>
<g transform="matrix(8.61155e-18,1.13192,-0.0754413,3.71795e-17,1069.8,-342.031)">
<path d="M497.076,394.18L497.076,1793.56" style="fill:none;stroke:rgb(247,4,132);stroke-width:17.63px;"/>
</g>
<g transform="matrix(8.61155e-18,1.13192,-0.0754413,3.71795e-17,1069.8,-288.169)">
<path d="M497.076,394.18L497.076,1793.56" style="fill:none;stroke:rgb(255,166,48);stroke-width:17.63px;"/>
</g>
<g transform="matrix(1.10808,0,0,1.04133,-187.35,-115.819)">
<path d="M497.076,394.18L497.076,1896.68" style="fill:none;stroke:rgb(247,4,132);stroke-width:12.58px;"/>
</g>
<g transform="matrix(-1.09658,0,0,0.321304,2399.88,618.547)">
<g transform="matrix(-0.9518,0,0,3.52288,2026.95,-1373.72)">
<path d="M220.225,569.992L207.725,557.492L220.225,544.992" style="fill:none;stroke:rgb(255,166,48);stroke-width:8.33px;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;"/>
<path d="M1607.34,557.492L207.725,557.492" style="fill:none;stroke:rgb(255,166,48);stroke-width:8.33px;stroke-linecap:round;stroke-dasharray:8.33,16.67,0,0;"/>
</g>
</g>
<g transform="matrix(0.54423,0,0,0.514519,523.603,421.586)">
<path d="M1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:rgb(255,166,48);"/>
<path d="M1624.98,583.163L1624.98,919.651C1624.98,944.11 1607.66,963.968 1586.34,963.968L670.315,963.968C648.993,963.968 631.682,944.11 631.682,919.651L631.682,583.163C631.682,558.704 648.993,538.846 670.315,538.846L1586.34,538.846C1607.66,538.846 1624.98,558.704 1624.98,583.163ZM1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:white;"/>
</g>
<g transform="matrix(0.19416,0,0,0.275328,1052.99,601.372)">
<path d="M1608.99,571.746C1608.99,563.706 1600.46,557.179 1589.95,557.179L666.712,557.179C656.199,557.179 647.664,563.706 647.664,571.746L647.664,931.068C647.664,939.108 656.199,945.635 666.712,945.635L1589.95,945.635C1600.46,945.635 1608.99,939.108 1608.99,931.068L1608.99,571.746Z" style="fill:rgb(255,211,152);stroke:white;stroke-width:19.24px;stroke-linecap:round;"/>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-345.04,-841.508)">
<g transform="matrix(50,0,0,50,1433.76,1475.25)">
</g>
<text x="1197.76px" y="1475.25px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:50px;">R<tspan x="1230.81px " y="1475.25px ">e</tspan>spHell<tspan x="1405.76px " y="1475.25px ">o</tspan></text>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-34.3588,-841.794)">
<g transform="matrix(41.6667,0,0,41.6667,1315.29,1464.53)">
</g>
<text x="1188.09px" y="1464.53px" style="font-family:'Nunito-SemiBold', 'Nunito';font-weight:600;font-size:41.667px;">Biscuit</text>
</g>
<g transform="matrix(-1.10076,0,0,0.321304,2401.96,1404.06)">
<g transform="matrix(-0.94819,0,0,3.52288,2021.14,-3818.47)">
<path d="M220.225,1263.96L207.725,1251.46L220.225,1238.96" style="fill:none;stroke:rgb(255,166,48);stroke-width:8.33px;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;"/>
<path d="M1607.34,1251.46L207.725,1251.46" style="fill:none;stroke:rgb(255,166,48);stroke-width:8.33px;stroke-linecap:round;stroke-dasharray:8.33,16.67,0,0;"/>
</g>
</g>
<g transform="matrix(0.54423,0,0,0.514519,523.603,1207.09)">
<path d="M1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:rgb(255,166,48);"/>
<path d="M1624.98,583.163L1624.98,919.651C1624.98,944.11 1607.66,963.968 1586.34,963.968L670.315,963.968C648.993,963.968 631.682,944.11 631.682,919.651L631.682,583.163C631.682,558.704 648.993,538.846 670.315,538.846L1586.34,538.846C1607.66,538.846 1624.98,558.704 1624.98,583.163ZM1608.99,583.163C1608.99,568.822 1598.85,557.179 1586.34,557.179L670.315,557.179C657.814,557.179 647.664,568.822 647.664,583.163L647.664,919.651C647.664,933.992 657.814,945.635 670.315,945.635L1586.34,945.635C1598.85,945.635 1608.99,933.992 1608.99,919.651L1608.99,583.163Z" style="fill:white;"/>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-323.596,-115.707)">
<g transform="matrix(50,0,0,50,1528.5,1528)">
</g>
<text x="1274.4px" y="1528px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:50px;">Emp<tspan x="1375.85px 1394.1px " y="1528px 1528px ">ty</tspan>Data</text>
</g>
<g transform="matrix(1.16933e-17,1.13192,-0.102439,3.71795e-17,1384.12,272.481)">
<path d="M497.076,394.18L497.076,1793.56" style="fill:none;stroke:rgb(255,166,48);stroke-width:17.59px;stroke-linecap:round;"/>
</g>
<g transform="matrix(1.16933e-17,1.13192,-0.102439,3.71795e-17,1384.12,612.276)">
<path d="M497.076,394.18L497.076,1793.56" style="fill:none;stroke:rgb(255,166,48);stroke-width:17.59px;stroke-linecap:round;"/>
</g>
<g transform="matrix(0.377816,0,0,0.318513,1464.43,3.62207)">
<path d="M1608.99,599.153C1608.99,575.987 1594.37,557.179 1576.37,557.179L680.292,557.179C662.284,557.179 647.664,575.987 647.664,599.153L647.664,903.661C647.664,926.827 662.284,945.635 680.292,945.635L1576.37,945.635C1594.37,945.635 1608.99,926.827 1608.99,903.661L1608.99,599.153Z" style="fill:rgb(255,166,48);"/>
</g>
<g transform="matrix(1.04373,2.9937e-16,-2.74652e-16,1.13192,767.205,-815.996)">
<text x="1171.58px" y="1474.94px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">r<tspan x="1185.58px " y="1474.94px ">e</tspan>sponder</text>
<g transform="matrix(41.6667,0,0,41.6667,1432.53,1516.61)">
</g>
<text x="1171.58px" y="1516.61px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">aut<tspan x="1230.45px " y="1516.61px ">h</tspan>ent<tspan x="1313.16px 1322.41px 1341.33px 1363.08px 1377.16px " y="1516.61px 1516.61px 1516.61px 1516.61px 1516.61px ">icati</tspan>on</text>
</g>
<g transform="matrix(1.04373,1.80409e-17,1.85964e-17,1.13192,767.205,-611.456)">
<text x="1171.58px" y="1454.11px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">init<tspan x="1227.49px " y="1454.11px ">i</tspan>at<tspan x="1272.24px " y="1454.11px ">o</tspan>r</text>
<text x="1171.58px" y="1495.78px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">aut<tspan x="1230.45px " y="1495.78px ">h</tspan>ent<tspan x="1313.16px 1322.41px 1341.33px 1363.08px 1377.16px " y="1495.78px 1495.78px 1495.78px 1495.78px 1495.78px ">icati</tspan>on,</text>
<g transform="matrix(41.6667,0,0,41.6667,1464.49,1537.44)">
</g>
<text x="1171.58px" y="1537.44px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">f<tspan x="1184.24px 1207.03px 1222.53px 1256.91px 1278.66px 1292.66px " y="1537.44px 1537.44px 1537.44px 1537.44px 1537.44px 1537.44px ">orward</tspan> secr<tspan x="1402.12px " y="1537.44px ">e</tspan>cy</text>
</g>
<g transform="matrix(1.04373,1.80409e-17,1.85964e-17,1.13192,705.967,-92.9691)">
<text x="1171.58px" y="1474.94px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">ackno<tspan x="1278.49px 1313.28px 1324.95px " y="1474.94px 1474.94px 1474.94px ">wle</tspan>dges</text>
<g transform="matrix(41.6667,0,0,41.6667,1314.2,1516.61)">
</g>
<text x="1171.58px" y="1516.61px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">InitC<tspan x="1254.78px " y="1516.61px ">o</tspan>nf</text>
</g>
<g transform="matrix(1.04373,1.72621e-17,1.94353e-17,1.13192,767.205,-321.469)">
<text x="1171.58px" y="1472.39px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">OSK handed</text>
<g transform="matrix(41.6667,0,0,41.6667,1422.78,1514.06)">
</g>
<text x="1171.58px" y="1514.06px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">t<tspan x="1185.33px " y="1514.06px ">o</tspan> W<tspan x="1264.74px 1273.99px 1287.99px " y="1514.06px 1514.06px 1514.06px ">ire</tspan>Guar<tspan x="1398.83px " y="1514.06px ">d</tspan></text>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-159.675,-1425.03)">
<g transform="matrix(33.3333,0,0,33.3333,1376.21,1461.44)">
</g>
<text x="1171.58px" y="1461.44px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:33.333px;fill:rgb(64,63,73);">Init<tspan x="1219.48px " y="1461.44px ">i</tspan>at<tspan x="1256.91px " y="1461.44px ">o</tspan>r Stat<tspan x="1358.41px " y="1461.44px ">e</tspan></text>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-159.675,-1369.01)">
<g transform="matrix(33.3333,0,0,33.3333,1422.81,1461.44)">
</g>
<text x="1171.58px" y="1461.44px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:33.333px;fill:rgb(64,63,73);">R<tspan x="1193.61px " y="1461.44px ">e</tspan>sponder Stat<tspan x="1405.01px " y="1461.44px ">e</tspan></text>
</g>
<g transform="matrix(1.04373,0,0,1.13192,-1040.69,-1406.95)">
<g transform="matrix(50,0,0,50,1434.05,1476.14)">
</g>
<text x="1257.1px" y="1476.14px" style="font-family:'Nunito-SemiBold', 'Nunito';font-weight:600;font-size:50px;fill:white;">Init<tspan x="1330.14px " y="1476.14px ">i</tspan>at<tspan x="1387.14px " y="1476.14px ">o</tspan>r</text>
</g>
<g transform="matrix(1.04373,0,0,1.13192,486.326,-1406.95)">
<g transform="matrix(50,0,0,50,1468.55,1476.14)">
</g>
<text x="1222.6px" y="1476.14px" style="font-family:'Nunito-SemiBold', 'Nunito';font-weight:600;font-size:50px;fill:white;">R<tspan x="1255.85px " y="1476.14px ">e</tspan>sponder</text>
</g>
<g transform="matrix(1.29981,-1.40964,1.29981,1.40964,-996.095,-284.091)">
<path d="M735.267,1542.91L717.276,1524.92L711.619,1548.57L735.267,1542.91Z" style="fill:rgb(179,178,182);"/>
<path d="M736.092,1546.36L712.445,1552.02L708.168,1547.74L713.825,1524.09L719.785,1522.41L737.776,1540.4L736.092,1546.36ZM735.267,1542.91L717.276,1524.92L711.619,1548.57L735.267,1542.91Z" style="fill:white;"/>
</g>
<g transform="matrix(1.29981,-1.40964,1.29981,1.40964,-996.095,-79.5508)">
<path d="M735.267,1542.91L717.276,1524.92L711.619,1548.57L735.267,1542.91Z" style="fill:rgb(179,178,182);"/>
<path d="M736.092,1546.36L712.445,1552.02L708.168,1547.74L713.825,1524.09L719.785,1522.41L737.776,1540.4L736.092,1546.36ZM735.267,1542.91L717.276,1524.92L711.619,1548.57L735.267,1542.91Z" style="fill:white;"/>
</g>
<g transform="matrix(1.29981,-1.40964,1.29981,1.40964,-996.095,207.55)">
<path d="M735.267,1542.91L717.276,1524.92L711.619,1548.57L735.267,1542.91Z" style="fill:rgb(179,178,182);"/>
<path d="M736.092,1546.36L712.445,1552.02L708.168,1547.74L713.825,1524.09L719.785,1522.41L737.776,1540.4L736.092,1546.36ZM735.267,1542.91L717.276,1524.92L711.619,1548.57L735.267,1542.91Z" style="fill:white;"/>
</g>
<g transform="matrix(1.04373,2.04033e-17,1.82707e-17,1.13192,287.154,-312.768)">
<g transform="matrix(41.6667,0,0,41.6667,1473.76,1457.69)">
</g>
<text x="1274.85px" y="1457.69px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">handshak<tspan x="1451.8px " y="1457.69px ">e</tspan></text>
</g>
<g transform="matrix(1.04373,4.8711e-17,3.06091e-17,1.13192,312.355,-241.08)">
<g transform="matrix(41.6667,0,0,41.6667,1449.62,1457.69)">
</g>
<text x="1249.16px" y="1457.69px" style="font-family:'Nunito-Light', 'Nunito';font-weight:300;font-size:41.667px;fill:rgb(64,63,73);">l<tspan x="1262.7px 1273.62px 1296.24px 1319.87px 1332.03px 1357.66px 1382.66px 1406.07px 1427.66px " y="1457.69px 1457.69px 1457.69px 1457.69px 1457.69px 1457.69px 1457.69px 1457.69px 1457.69px ">ive phase</tspan></text>
</g>
<g transform="matrix(1.04373,0,0,1.24761,-135.752,-334.388)">
<g transform="matrix(1,-0,-0,0.90727,299.807,410.028)">
<path d="M1593.36,999.66L1602.74,980.91L1612.11,999.66C1607.42,994.973 1598.05,994.973 1593.36,999.66Z" style="fill:rgb(179,178,182);"/>
<path d="M1602.74,1027.14L1602.74,995.91" style="fill:none;stroke:rgb(179,178,182);stroke-width:6.25px;stroke-linecap:round;"/>
</g>
</g>
<g transform="matrix(-1.04373,1.52788e-16,-1.2782e-16,-1.24761,3835.73,3054.11)">
<g transform="matrix(-1,-1.22465e-16,1.11109e-16,-0.90727,3505.28,2305.97)">
<path d="M1612.11,1090.07L1602.74,1108.82L1593.36,1090.07C1598.05,1094.75 1607.42,1094.75 1612.11,1090.07Z" style="fill:rgb(179,178,182);"/>
<path d="M1602.74,1062.59L1602.74,1093.82" style="fill:none;stroke:rgb(179,178,182);stroke-width:6.25px;stroke-linecap:round;"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 2037 1491" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.5;"> <svg width="100%" height="100%" viewBox="0 0 2037 1491" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g id="ArtBoard1" transform="matrix(0.958104,0,0,0.883458,-169.743,-156.518)"> <g transform="matrix(0.958104,0,0,0.883458,-169.743,-156.518)">
<rect x="177.165" y="177.165" width="2125.98" height="1687.51" style="fill:none;"/> <rect id="ArtBoard1" x="177.165" y="177.165" width="2125.98" height="1687.51" style="fill:none;"/>
<clipPath id="_clip1"> <clipPath id="_clip1">
<rect x="177.165" y="177.165" width="2125.98" height="1687.51"/> <rect id="ArtBoard11" serif:id="ArtBoard1" x="177.165" y="177.165" width="2125.98" height="1687.51"/>
</clipPath> </clipPath>
<g clip-path="url(#_clip1)"> <g clip-path="url(#_clip1)">
<g transform="matrix(1.04373,0,0,1.13192,177.165,177.165)">
<rect x="-16.526" y="0" width="2083.17" height="1490.84" style="fill:white;"/>
</g>
<g transform="matrix(0.377816,0,0,0.318513,-62.5845,3.62207)"> <g transform="matrix(0.377816,0,0,0.318513,-62.5845,3.62207)">
<path d="M1608.99,599.153C1608.99,575.987 1594.37,557.179 1576.37,557.179L680.292,557.179C662.284,557.179 647.664,575.987 647.664,599.153L647.664,903.661C647.664,926.827 662.284,945.635 680.292,945.635L1576.37,945.635C1594.37,945.635 1608.99,926.827 1608.99,903.661L1608.99,599.153Z" style="fill:rgb(247,4,132);"/> <path d="M1608.99,599.153C1608.99,575.987 1594.37,557.179 1576.37,557.179L680.292,557.179C662.284,557.179 647.664,575.987 647.664,599.153L647.664,903.661C647.664,926.827 662.284,945.635 680.292,945.635L1576.37,945.635C1594.37,945.635 1608.99,926.827 1608.99,903.661L1608.99,599.153Z" style="fill:rgb(247,4,132);"/>
</g> </g>

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 97 KiB

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