mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-06 12:50:57 -08:00
Compare commits
452 Commits
dev/rustif
...
dev/karo/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fd3df67ed | ||
|
|
6d47169a5c | ||
|
|
4bcd38a4ea | ||
|
|
730a03957a | ||
|
|
ea071f5363 | ||
|
|
3063d3e4c2 | ||
|
|
1bf0eed90a | ||
|
|
138e6b6553 | ||
|
|
2dde0a2b47 | ||
|
|
3cc3b6009f | ||
|
|
1ab457ed37 | ||
|
|
c9c266fe7c | ||
|
|
8d3c8790fe | ||
|
|
648a94ead8 | ||
|
|
54ac5eecdb | ||
|
|
40c5bbd167 | ||
|
|
a4b8fc2226 | ||
|
|
37f7b3e4e9 | ||
|
|
deafc1c1af | ||
|
|
6bbe85a57b | ||
|
|
e70c5b33a8 | ||
|
|
25fdfef4d0 | ||
|
|
6ab8fafe59 | ||
|
|
c1aacf76b8 | ||
|
|
1bcaf5781f | ||
|
|
de60e5f8f0 | ||
|
|
b50ddda151 | ||
|
|
7282fba3b3 | ||
|
|
0cca389f10 | ||
|
|
8a08d49215 | ||
|
|
8637bc7884 | ||
|
|
4412c2bdd1 | ||
|
|
ecc815dd8e | ||
|
|
b7d7c03e35 | ||
|
|
f6320c3c35 | ||
|
|
19f7905bc9 | ||
|
|
9b5b7ee620 | ||
|
|
4fdd271de7 | ||
|
|
860e65965a | ||
|
|
87144233da | ||
|
|
d0a6e99a1f | ||
|
|
79b634fadf | ||
|
|
99ac3c0902 | ||
|
|
010c14dadf | ||
|
|
45b6132312 | ||
|
|
77f9fd38f3 | ||
|
|
775ed86adc | ||
|
|
40377dce1f | ||
|
|
19293471e8 | ||
|
|
cc5877dd83 | ||
|
|
ebb591aa6f | ||
|
|
07146d9914 | ||
|
|
cd04dbc4eb | ||
|
|
cc22165dc4 | ||
|
|
8496571765 | ||
|
|
ee3a1f580e | ||
|
|
89584645c3 | ||
|
|
3286e49370 | ||
|
|
100d7b6e1c | ||
|
|
921b2bfc39 | ||
|
|
a18658847c | ||
|
|
bdad414c90 | ||
|
|
7c54a37618 | ||
|
|
7a4f700186 | ||
|
|
f535a31cd7 | ||
|
|
ac2aaa5fbd | ||
|
|
e472fa1fcd | ||
|
|
526c930119 | ||
|
|
5f8b00d045 | ||
|
|
b46fca99cb | ||
|
|
70c5ec2c29 | ||
|
|
0e059af5da | ||
|
|
99754f326e | ||
|
|
fd397b9ea0 | ||
|
|
e92fa552e3 | ||
|
|
c438d5a99d | ||
|
|
d4eef998f5 | ||
|
|
c1abfbfd14 | ||
|
|
ae7577c641 | ||
|
|
f07f598e44 | ||
|
|
988f66cf2b | ||
|
|
06969c406d | ||
|
|
b5215aecba | ||
|
|
3e32bbad7c | ||
|
|
650110a04f | ||
|
|
ee669823de | ||
|
|
40940ca1df | ||
|
|
b77eccffc0 | ||
|
|
e17d8cd559 | ||
|
|
c72e8bcda1 | ||
|
|
2bac991305 | ||
|
|
e6d114c557 | ||
|
|
29efbba97a | ||
|
|
3fb1220262 | ||
|
|
1ccf92c538 | ||
|
|
4bb3153761 | ||
|
|
a8ed0e8c66 | ||
|
|
ad6405f865 | ||
|
|
761d5730af | ||
|
|
b45b7bc7f5 | ||
|
|
77a985dc02 | ||
|
|
21e693a9da | ||
|
|
be91b3049c | ||
|
|
4dc24f745c | ||
|
|
61a1cc3825 | ||
|
|
2e01d1df46 | ||
|
|
2c6411a2b1 | ||
|
|
96b12ac261 | ||
|
|
3e734e0d57 | ||
|
|
c9e296794b | ||
|
|
bc6bff499d | ||
|
|
de905056fc | ||
|
|
4e8344660e | ||
|
|
a581f7dfa7 | ||
|
|
bd6a6e5dce | ||
|
|
e0496c12c6 | ||
|
|
f4116f2c20 | ||
|
|
8099bc4bdd | ||
|
|
39d174c605 | ||
|
|
0257aa9e15 | ||
|
|
3299b2bdb4 | ||
|
|
f43b018511 | ||
|
|
0f884b79fa | ||
|
|
ab83d3fae6 | ||
|
|
cc7e8dc510 | ||
|
|
c2d0d34c57 | ||
|
|
5d46c93b2b | ||
|
|
e6d7a7232f | ||
|
|
6ba1be6eae | ||
|
|
c194c74e55 | ||
|
|
96de84e68f | ||
|
|
6215bc1514 | ||
|
|
b0a93d6884 | ||
|
|
bba0c874f2 | ||
|
|
a32efb61d1 | ||
|
|
10bdb5f371 | ||
|
|
b07859f6ec | ||
|
|
65df24a98b | ||
|
|
9396784c0f | ||
|
|
8420d953eb | ||
|
|
e7de4848fb | ||
|
|
92824bb5b0 | ||
|
|
8d20e77173 | ||
|
|
15aafe7563 | ||
|
|
b56af8b696 | ||
|
|
a3e91a95df | ||
|
|
4ea51ab123 | ||
|
|
4b849a4fe4 | ||
|
|
16e67269ba | ||
|
|
76d5093a20 | ||
|
|
0e8945db78 | ||
|
|
ffd81b6a72 | ||
|
|
d1d218ac0f | ||
|
|
0edfb625e8 | ||
|
|
16c0080cdc | ||
|
|
b05c4bbe24 | ||
|
|
639c65ef93 | ||
|
|
332c549305 | ||
|
|
ef973e9d7f | ||
|
|
199ecb814b | ||
|
|
40d955a156 | ||
|
|
cd23e9a2d0 | ||
|
|
4d482aaab7 | ||
|
|
3175b7b783 | ||
|
|
baa35af558 | ||
|
|
b2de384fcf | ||
|
|
c69fd889fb | ||
|
|
13a853ff42 | ||
|
|
13df700ef5 | ||
|
|
2e7f34f4b2 | ||
|
|
292b4bbae0 | ||
|
|
c75d222477 | ||
|
|
478fadb80d | ||
|
|
7c1ada4b10 | ||
|
|
4f4e8e1018 | ||
|
|
971e49b894 | ||
|
|
262e32fe35 | ||
|
|
4dab97d84e | ||
|
|
1a5ffdd495 | ||
|
|
fb91688672 | ||
|
|
27ba729c14 | ||
|
|
60235dc6ea | ||
|
|
36c99c020e | ||
|
|
8c469af6b1 | ||
|
|
e96968b8bc | ||
|
|
81487b103d | ||
|
|
8ea253f86b | ||
|
|
fd8f2e4424 | ||
|
|
a996b08279 | ||
|
|
19a0a22b62 | ||
|
|
b51466eaec | ||
|
|
9552d5a46c | ||
|
|
a1d61bb48e | ||
|
|
e38a6b8ed4 | ||
|
|
639541ab4f | ||
|
|
ec0b5f7fb1 | ||
|
|
0b4699e24a | ||
|
|
d18107b3a9 | ||
|
|
715893e1ac | ||
|
|
92b2f6bc7c | ||
|
|
3498ab2d7b | ||
|
|
9690085156 | ||
|
|
ca972e8b70 | ||
|
|
2fa0a2a72a | ||
|
|
b6203683fc | ||
|
|
e0f75ab97e | ||
|
|
0789c60602 | ||
|
|
e42f90b048 | ||
|
|
29917fd7a6 | ||
|
|
efd0ce51cb | ||
|
|
7739020931 | ||
|
|
ecfecbb8f9 | ||
|
|
e8a81102f4 | ||
|
|
591e5226fd | ||
|
|
62aa9b4351 | ||
|
|
26cb4a587f | ||
|
|
1c14be38dd | ||
|
|
30cb0e9801 | ||
|
|
9824db4f09 | ||
|
|
e3b72487db | ||
|
|
85c447052e | ||
|
|
b2a64ed17a | ||
|
|
91da0dfd2d | ||
|
|
4a170b1983 | ||
|
|
7c83e244f9 | ||
|
|
eb76179dc4 | ||
|
|
d84efa7422 | ||
|
|
61ef5b92bb | ||
|
|
b336a0d264 | ||
|
|
0b7bec75de | ||
|
|
87bbd1eef7 | ||
|
|
2646dc8398 | ||
|
|
4295ec9d80 | ||
|
|
7cb643b181 | ||
|
|
109d624227 | ||
|
|
b96d195f54 | ||
|
|
775b464496 | ||
|
|
e2cd25c184 | ||
|
|
fdcb488d4b | ||
|
|
a8a596ca7e | ||
|
|
9ced9996d2 | ||
|
|
df683f96b2 | ||
|
|
27a8bdbe7b | ||
|
|
bdabae9c33 | ||
|
|
4d7c030476 | ||
|
|
95f22e98ac | ||
|
|
b0dada7613 | ||
|
|
e54ea1feaa | ||
|
|
0fd09c908b | ||
|
|
36628a46d6 | ||
|
|
184cff0e5e | ||
|
|
9819148b6f | ||
|
|
2904c90d4b | ||
|
|
f0dbe2bb54 | ||
|
|
3a0ebd2cbc | ||
|
|
1eefb5f263 | ||
|
|
d45e24e9b6 | ||
|
|
972e82b35f | ||
|
|
101c9bf4b3 | ||
|
|
955d57ea49 | ||
|
|
838f700a74 | ||
|
|
5448cdc565 | ||
|
|
77cd8a9fd1 | ||
|
|
0f89ab7976 | ||
|
|
70fa9bd6d7 | ||
|
|
85a61808de | ||
|
|
cf132bca11 | ||
|
|
7bda010a9b | ||
|
|
36089fd37f | ||
|
|
31d43accd5 | ||
|
|
205c301012 | ||
|
|
d014095469 | ||
|
|
7cece82119 | ||
|
|
284ebb261f | ||
|
|
ba224a2200 | ||
|
|
ca35e47d2a | ||
|
|
181154b470 | ||
|
|
cc8c13e121 | ||
|
|
e2792272e8 | ||
|
|
40861cc2ea | ||
|
|
09aa0e027e | ||
|
|
d44793e07f | ||
|
|
1c65e67be2 | ||
|
|
2ae3d6c271 | ||
|
|
d539be3142 | ||
|
|
a49254a021 | ||
|
|
86300ca936 | ||
|
|
3ddf736b60 | ||
|
|
c64e721c2f | ||
|
|
4c51ead078 | ||
|
|
c5c34523f3 | ||
|
|
6553141637 | ||
|
|
a3de526db8 | ||
|
|
5da0e4115e | ||
|
|
99634d9702 | ||
|
|
46156fcb29 | ||
|
|
e50542193f | ||
|
|
3db9755580 | ||
|
|
556dbd2600 | ||
|
|
6cd42ebf50 | ||
|
|
a220c11e67 | ||
|
|
c9cef05b29 | ||
|
|
96d4f0b545 | ||
|
|
ad947a755c | ||
|
|
0b4b1279cf | ||
|
|
44264a7bb6 | ||
|
|
b095bdaa7c | ||
|
|
9597e485bf | ||
|
|
ab085998bb | ||
|
|
3901e668cb | ||
|
|
b7444bf9b4 | ||
|
|
0051cbd48e | ||
|
|
35f9c3bf68 | ||
|
|
ff44002b7c | ||
|
|
0ce8304c69 | ||
|
|
5f91feb3a4 | ||
|
|
27746781c0 | ||
|
|
93439858d1 | ||
|
|
1223048b48 | ||
|
|
932bde39cc | ||
|
|
1d9e62e56b | ||
|
|
3af722a066 | ||
|
|
df60b0bfc3 | ||
|
|
6274c6fcdd | ||
|
|
cd00f023fb | ||
|
|
baebb8632f | ||
|
|
cb97f90581 | ||
|
|
13563237cb | ||
|
|
447a4f7a44 | ||
|
|
3d13caa37b | ||
|
|
54ecfaddcf | ||
|
|
6bac6a59ff | ||
|
|
e5e04c6d95 | ||
|
|
15ce25ccd2 | ||
|
|
1b383d494c | ||
|
|
605b6463ff | ||
|
|
04eb86af87 | ||
|
|
bf850e3072 | ||
|
|
dd39936220 | ||
|
|
b15f17133f | ||
|
|
b50820ecc0 | ||
|
|
f323839967 | ||
|
|
6e15c38254 | ||
|
|
b7a76849b7 | ||
|
|
d2d72143b5 | ||
|
|
1135cd7bbb | ||
|
|
51f04f749f | ||
|
|
37d1326481 | ||
|
|
d0a84294aa | ||
|
|
a98f64c17d | ||
|
|
d6a7ebe88f | ||
|
|
212336728c | ||
|
|
f48a923dbf | ||
|
|
7b5d0f7d66 | ||
|
|
1e37f89e83 | ||
|
|
b997238f42 | ||
|
|
d915e63445 | ||
|
|
53d7996dd3 | ||
|
|
47b4d394ef | ||
|
|
578d9e2eb5 | ||
|
|
d6b83a4a0b | ||
|
|
959cd50ef6 | ||
|
|
6025623aad | ||
|
|
5a67b4708a | ||
|
|
45145cdd9b | ||
|
|
66e696fea3 | ||
|
|
91d0592ad6 | ||
|
|
8ff9b53365 | ||
|
|
067a839d4b | ||
|
|
38835fb0f8 | ||
|
|
a2b177470c | ||
|
|
1c1e38e2f7 | ||
|
|
46383bdc4d | ||
|
|
2805d686e6 | ||
|
|
b274519bad | ||
|
|
3086c7fb93 | ||
|
|
d21e3af1bb | ||
|
|
b0332971df | ||
|
|
be508b486a | ||
|
|
4314a0915a | ||
|
|
0d2ca37bbb | ||
|
|
7b69afabbc | ||
|
|
e24172d9b5 | ||
|
|
d01c96c1de | ||
|
|
4a3b59fd15 | ||
|
|
11d60bcced | ||
|
|
73a8489232 | ||
|
|
2ac2c84c71 | ||
|
|
a0f79478cc | ||
|
|
7e6985fdc6 | ||
|
|
b958eacaae | ||
|
|
397a776c55 | ||
|
|
19fe7360d2 | ||
|
|
b29720b0c6 | ||
|
|
78e32a6f14 | ||
|
|
5f78857ff5 | ||
|
|
69f62673a5 | ||
|
|
097fd0332d | ||
|
|
303c5a569c | ||
|
|
7aa48b95af | ||
|
|
229224d078 | ||
|
|
e12cd18a42 | ||
|
|
0b1a00a32e | ||
|
|
7c3cd1acf6 | ||
|
|
3856d774ff | ||
|
|
62fab066d4 | ||
|
|
9469b62f58 | ||
|
|
f8bea94330 | ||
|
|
f3c343c472 | ||
|
|
7154af52f9 | ||
|
|
e03fed404f | ||
|
|
42798699e4 | ||
|
|
b99d072879 | ||
|
|
d5b2a9414f | ||
|
|
13cc7e05ed | ||
|
|
096c811491 | ||
|
|
cefe9ce762 | ||
|
|
378fddb645 | ||
|
|
695ef6a769 | ||
|
|
b4d74d64f7 | ||
|
|
0456ded6b9 | ||
|
|
838fd19694 | ||
|
|
94d57f2f87 | ||
|
|
279b3c49fc | ||
|
|
9c40c77f71 | ||
|
|
c79dffa627 | ||
|
|
b8f19c5510 | ||
|
|
f459b91abf | ||
|
|
801ce4cd34 | ||
|
|
a36da78bc8 | ||
|
|
df02f616bf | ||
|
|
87b08bcee1 | ||
|
|
897fa3daf6 | ||
|
|
953b861b4c | ||
|
|
1a61a99575 | ||
|
|
25a7a0736b | ||
|
|
844e9b3c7e | ||
|
|
a723951c71 | ||
|
|
be9ac58bf9 | ||
|
|
75853159fe | ||
|
|
95aba257fd | ||
|
|
34d0bab5c5 | ||
|
|
91d1986126 | ||
|
|
319785cf6e | ||
|
|
df5a6125cd | ||
|
|
80697e6189 | ||
|
|
6212153c48 | ||
|
|
4645ed5569 | ||
|
|
2aeb9067e2 | ||
|
|
c64917fe2e | ||
|
|
a011cc1e1c | ||
|
|
ad75d2218c |
201
.ci/gen-workflow-files.nu
Executable file
201
.ci/gen-workflow-files.nu
Executable file
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env nu
|
||||
|
||||
use std log
|
||||
|
||||
# cd to git root
|
||||
cd (git rev-parse --show-toplevel)
|
||||
|
||||
# check if a subject depends on a potential dependency
|
||||
def depends [
|
||||
subject:string # package to examine
|
||||
maybe_dep:string # maybe a dependency of subject
|
||||
] {
|
||||
not ( nix why-depends --quiet --derivation $subject $maybe_dep | is-empty )
|
||||
}
|
||||
|
||||
# get attribute names of the attribute set
|
||||
def get-attr-names [
|
||||
expr: # nix expression to get attrNames of
|
||||
] {
|
||||
nix eval --json $expr --apply builtins.attrNames | from json
|
||||
}
|
||||
|
||||
def job-id [
|
||||
system:string,
|
||||
derivation:string,
|
||||
] {
|
||||
$"($system)---($derivation)"
|
||||
}
|
||||
|
||||
# map from nixos system to github runner type
|
||||
let systems_map = {
|
||||
# aarch64-darwin
|
||||
# aarch64-linux
|
||||
|
||||
i686-linux: ubuntu-latest,
|
||||
x86_64-darwin: macos-13,
|
||||
x86_64-linux: ubuntu-latest
|
||||
}
|
||||
|
||||
let targets = (get-attr-names ".#packages"
|
||||
| par-each {|system| { $system : (get-attr-names $".#packages.($system)") } }
|
||||
| reduce {|it, acc| $acc | merge $it }
|
||||
)
|
||||
|
||||
mut cachix_workflow = {
|
||||
name: "Nix",
|
||||
permissions: {contents: write},
|
||||
on: {
|
||||
pull_request: null,
|
||||
push: {branches: [main]}
|
||||
},
|
||||
jobs: {},
|
||||
}
|
||||
|
||||
mut release_workflow = {
|
||||
name: "Release",
|
||||
permissions: {contents: write},
|
||||
on: { push: {tags: ["v*"]} },
|
||||
jobs: {},
|
||||
}
|
||||
|
||||
let runner_setup = [
|
||||
{
|
||||
uses: "actions/checkout@v3"
|
||||
}
|
||||
{
|
||||
uses: "cachix/install-nix-action@v22",
|
||||
with: { nix_path: "nixpkgs=channel:nixos-unstable" }
|
||||
}
|
||||
{
|
||||
uses: "cachix/cachix-action@v12",
|
||||
with: {
|
||||
name: rosenpass,
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
for system in ($targets | columns) {
|
||||
if ($systems_map | get -i $system | is-empty) {
|
||||
log info $"skipping ($system), since there are no GH-Actions runners for it"
|
||||
continue
|
||||
}
|
||||
|
||||
# lookup the correct runner for $system
|
||||
let runs_on = [ ($systems_map | get $system) ]
|
||||
|
||||
# add jobs for all derivations
|
||||
let derivations = ($targets | get $system)
|
||||
for derivation in $derivations {
|
||||
|
||||
if ($system == "i686-linux") and ($derivation | str contains "static") {
|
||||
log info $"skipping ($system).($derivation), due to liboqs 0.8 not present in oqs-sys"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($system == "i686-linux") and ($derivation | str contains "release-package") {
|
||||
log info $"skipping ($system).($derivation), due to liboqs 0.8 not present in oqs-sys"
|
||||
continue
|
||||
}
|
||||
|
||||
# job_id for GH-Actions
|
||||
let id = ( job-id $system $derivation )
|
||||
|
||||
# name displayed
|
||||
let name = $"($system).($derivation)"
|
||||
|
||||
# collection of dependencies
|
||||
# TODO currently only considers dependencies on the same $system
|
||||
let needs = ($derivations
|
||||
| filter {|it| $it != $derivation and $it != "default" } # filter out self and default
|
||||
| par-each {|it| {
|
||||
name: $it, # the other derivation
|
||||
# does self depend on $it?
|
||||
needed: (depends $".#packages.($system).($derivation)" $".#packages.($system).($it)")
|
||||
} }
|
||||
| filter {|it| $it.needed}
|
||||
| each {|it| job-id $system $it.name}
|
||||
| sort
|
||||
)
|
||||
|
||||
mut new_job = {
|
||||
name: $"Build ($name)",
|
||||
"runs-on": $runs_on,
|
||||
needs: $needs,
|
||||
steps: ($runner_setup | append [
|
||||
{
|
||||
name: Build,
|
||||
run: $"nix build .#packages.($system).($derivation) --print-build-logs"
|
||||
}
|
||||
])
|
||||
}
|
||||
$cachix_workflow.jobs = ($cachix_workflow.jobs | insert $id $new_job )
|
||||
}
|
||||
|
||||
# add check job
|
||||
$cachix_workflow.jobs = ($cachix_workflow.jobs | insert $"($system)---check" {
|
||||
name: $"Run Nix checks on ($system)",
|
||||
"runs-on": $runs_on,
|
||||
steps: ($runner_setup | append {
|
||||
name: Check,
|
||||
run: "nix flake check . --print-build-logs"
|
||||
})
|
||||
})
|
||||
|
||||
# add release job
|
||||
$release_workflow.jobs = ($release_workflow.jobs | insert $"($system)---release" {
|
||||
name: $"Build release artifacts for ($system)",
|
||||
"runs-on": $runs_on,
|
||||
steps: ($runner_setup | append [
|
||||
{
|
||||
name: "Build release",
|
||||
run: "nix build .#release-package --print-build-logs"
|
||||
}
|
||||
{
|
||||
name: Release,
|
||||
uses: "softprops/action-gh-release@v1",
|
||||
with: {
|
||||
draft: "${{ contains(github.ref_name, 'rc') }}",
|
||||
prerelease: "${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}",
|
||||
files: "result/*"
|
||||
}
|
||||
}
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
# add whitepaper job with upload
|
||||
let system = "x86_64-linux"
|
||||
$cachix_workflow.jobs = ($cachix_workflow.jobs | insert $"($system)---whitepaper-upload" {
|
||||
name: $"Upload whitepaper ($system)",
|
||||
"runs-on": ($systems_map | get $system),
|
||||
"if": "${{ github.ref == 'refs/heads/main' }}",
|
||||
steps: ($runner_setup | append [
|
||||
{
|
||||
name: "Git add git sha and commit",
|
||||
run: "cd papers && ./tex/gitinfo2.sh && git add gitHeadInfo.gin"
|
||||
}
|
||||
{
|
||||
name: Build,
|
||||
run: $"nix build .#packages.($system).whitepaper --print-build-logs"
|
||||
}
|
||||
{
|
||||
name: "Deploy PDF artifacts",
|
||||
uses: "peaceiris/actions-gh-pages@v3",
|
||||
with: {
|
||||
github_token: "${{ secrets.GITHUB_TOKEN }}",
|
||||
publish_dir: result/,
|
||||
publish_branch: papers-pdf,
|
||||
force_orphan: true
|
||||
}
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
log info "saving nix-cachix workflow"
|
||||
$cachix_workflow | to yaml | save --force .github/workflows/nix.yaml
|
||||
$release_workflow | to yaml | save --force .github/workflows/release.yaml
|
||||
|
||||
log info "prettify generated yaml"
|
||||
prettier -w .github/workflows/
|
||||
33
.ci/run-regression.sh
Executable file
33
.ci/run-regression.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
iterations="$1"
|
||||
sleep_time="$2"
|
||||
|
||||
PWD="$(pwd)"
|
||||
EXEC="$PWD/target/release/rosenpass"
|
||||
LOGS="$PWD/output/logs"
|
||||
|
||||
mkdir -p "$LOGS"
|
||||
|
||||
run_command() {
|
||||
local file=$1
|
||||
local log_file="$2"
|
||||
("$EXEC" exchange-config "$file" 2>&1 | tee -a "$log_file") &
|
||||
echo $!
|
||||
}
|
||||
|
||||
pids=()
|
||||
|
||||
(cd output/dut && run_command "configs/dut-$iterations.toml" "$LOGS/dut.log")
|
||||
for (( x=0; x<iterations; x++ )); do
|
||||
(cd output/ate && run_command "configs/ate-$x.toml" "$LOGS/ate-$x.log") & pids+=($!)
|
||||
done
|
||||
|
||||
sleep "$sleep_time"
|
||||
|
||||
lsof -i :9999 | awk 'NR!=1 {print $2}' | xargs kill
|
||||
|
||||
for (( x=0; x<iterations; x++ )); do
|
||||
port=$((x + 50000))
|
||||
lsof -i :$port | awk 'NR!=1 {print $2}' | xargs kill
|
||||
done
|
||||
1
.devcontainer/Dockerfile
Normal file
1
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1 @@
|
||||
FROM ghcr.io/xtruder/nix-devcontainer:v1
|
||||
33
.devcontainer/devcontainer.json
Normal file
33
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,33 @@
|
||||
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or the definition README at
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/master/containers/docker-existing-dockerfile
|
||||
{
|
||||
"name": "devcontainer-project",
|
||||
"dockerFile": "Dockerfile",
|
||||
"context": "${localWorkspaceFolder}",
|
||||
"build": {
|
||||
"args": {
|
||||
"USER_UID": "${localEnv:USER_UID}",
|
||||
"USER_GID": "${localEnv:USER_GID}"
|
||||
}
|
||||
},
|
||||
|
||||
// run arguments passed to docker
|
||||
"runArgs": ["--security-opt", "label=disable"],
|
||||
|
||||
// disable command overriding and updating remote user ID
|
||||
"overrideCommand": false,
|
||||
"userEnvProbe": "loginShell",
|
||||
"updateRemoteUserUID": false,
|
||||
|
||||
// build development environment on creation, make sure you already have shell.nix
|
||||
"onCreateCommand": "nix develop",
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [],
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"]
|
||||
}
|
||||
}
|
||||
}
|
||||
14
.github/codecov.yml
vendored
Normal file
14
.github/codecov.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
codecov:
|
||||
branch: main
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# basic
|
||||
target: auto #default
|
||||
threshold: 5
|
||||
base: auto
|
||||
if_ci_failed: error #success, failure, error, ignore
|
||||
informational: false
|
||||
only_pulls: true
|
||||
patch: off
|
||||
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
63
.github/workflows/dependent-issues.yml
vendored
Normal file
63
.github/workflows/dependent-issues.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
name: Dependent Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- closed
|
||||
- reopened
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- closed
|
||||
- reopened
|
||||
# Makes sure we always add status check for PRs. Useful only if
|
||||
# this action is required to pass before merging. Otherwise, it
|
||||
# can be removed.
|
||||
- synchronize
|
||||
|
||||
# Schedule a daily check. Useful if you reference cross-repository
|
||||
# issues or pull requests. Otherwise, it can be removed.
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: z0al/dependent-issues@v1
|
||||
env:
|
||||
# (Required) The token to use to make API calls to GitHub.
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# (Optional) The token to use to make API calls to GitHub for remote repos.
|
||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||
|
||||
with:
|
||||
# (Optional) The label to use to mark dependent issues
|
||||
label: dependent
|
||||
|
||||
# (Optional) Enable checking for dependencies in issues.
|
||||
# Enable by setting the value to "on". Default "off"
|
||||
check_issues: off
|
||||
|
||||
# (Optional) Ignore dependabot PRs.
|
||||
# Enable by setting the value to "on". Default "off"
|
||||
ignore_dependabot: off
|
||||
|
||||
# (Optional) A comma-separated list of keywords. Default
|
||||
# "depends on, blocked by"
|
||||
keywords: depends on, blocked by
|
||||
|
||||
# (Optional) A custom comment body. It supports `{{ dependencies }}` token.
|
||||
comment: >
|
||||
This PR/issue depends on:
|
||||
|
||||
{{ dependencies }}
|
||||
|
||||
By **[Dependent Issues](https://github.com/z0al/dependent-issues)** (🤖). Happy coding!
|
||||
49
.github/workflows/doc-upload.yml
vendored
Normal file
49
.github/workflows/doc-upload.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: Update website docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "doc/**"
|
||||
|
||||
jobs:
|
||||
update-website:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Clone rosenpass-website repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: rosenpass/rosenpass-website
|
||||
ref: main
|
||||
path: rosenpass-website
|
||||
token: ${{ secrets.PRIVACC }}
|
||||
|
||||
- name: Copy docs to website repo
|
||||
run: |
|
||||
cp -R doc/* rosenpass-website/static/docs/
|
||||
|
||||
- name: Install mandoc
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y mandoc
|
||||
|
||||
- name: Compile man pages to HTML
|
||||
run: |
|
||||
cd rosenpass-website/static/docs/
|
||||
for file in *.1; do
|
||||
mandoc -Thtml "$file" > "${file%.*}.html"
|
||||
done
|
||||
|
||||
- name: Commit changes to website repo
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
author_name: GitHub Actions
|
||||
author_email: actions@github.com
|
||||
message: Update docs
|
||||
cwd: rosenpass-website/static/docs
|
||||
github_token: ${{ secrets.PRIVACC }
|
||||
512
.github/workflows/nix.yaml
vendored
512
.github/workflows/nix.yaml
vendored
@@ -1,74 +1,468 @@
|
||||
name: Nix Related Actions
|
||||
name: Nix
|
||||
permissions:
|
||||
contents: write
|
||||
on:
|
||||
pull_request:
|
||||
pull_request: null
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ matrix.derivation }} on ${{ matrix.nix-system }}
|
||||
i686-linux---default:
|
||||
name: Build i686-linux.default
|
||||
runs-on:
|
||||
- nix
|
||||
- ${{ matrix.nix-system }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
nix-system:
|
||||
- x86_64-linux
|
||||
# - aarch64-linux
|
||||
derivation:
|
||||
- rosenpass
|
||||
- rosenpass-static
|
||||
- rosenpass-oci-image
|
||||
- rosenpass-static-oci-image
|
||||
- proof-proverif
|
||||
- whitepaper
|
||||
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- i686-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Generate gitHeadInfo.gin for the whitepaper
|
||||
if: ${{ matrix.derivation == 'whitepaper' }}
|
||||
run: ( cd papers && ./tex/gitinfo2.sh && git add gitHeadInfo.gin )
|
||||
- name: Build ${{ matrix.derivation }}@${{ matrix.nix-system }}
|
||||
run: |
|
||||
# build the package
|
||||
nix build .#packages.${{ matrix.nix-system }}.${{ matrix.derivation }} --print-build-logs
|
||||
|
||||
# copy over the results
|
||||
if [[ -f $(readlink --canonicalize result ) ]]; then
|
||||
mkdir -- ${{ matrix.derivation }}-${{ matrix.nix-system }}
|
||||
fi
|
||||
cp --recursive -- $(readlink --canonicalize result) ${{ matrix.derivation }}-${{ matrix.nix-system }}
|
||||
chmod --recursive ug+rw -- ${{ matrix.derivation }}-${{ matrix.nix-system }}
|
||||
|
||||
# add version information
|
||||
git rev-parse --abbrev-ref HEAD > ${{ matrix.derivation }}-${{ matrix.nix-system }}/git-version
|
||||
git rev-parse HEAD > ${{ matrix.derivation }}-${{ matrix.nix-system }}/git-sha
|
||||
|
||||
# override the `rp` script to keep compatible with non-nix systems
|
||||
if [[ -f ${{ matrix.derivation }}-${{ matrix.nix-system }}/bin/rp ]]; then
|
||||
cp --force -- rp ${{ matrix.derivation }}-${{ matrix.nix-system }}/bin/
|
||||
fi
|
||||
- name: Upload build results
|
||||
uses: actions/upload-artifact@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
name: ${{ matrix.derivation }}-${{ matrix.nix-system }}
|
||||
path: ${{ matrix.derivation }}-${{ matrix.nix-system }}
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.i686-linux.default --print-build-logs
|
||||
i686-linux---rosenpass:
|
||||
name: Build i686-linux.rosenpass
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.i686-linux.rosenpass --print-build-logs
|
||||
i686-linux---rosenpass-oci-image:
|
||||
name: Build i686-linux.rosenpass-oci-image
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- i686-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.i686-linux.rosenpass-oci-image --print-build-logs
|
||||
i686-linux---check:
|
||||
name: Run Nix checks on i686-linux
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Check
|
||||
run: nix flake check . --print-build-logs
|
||||
x86_64-darwin---default:
|
||||
name: Build x86_64-darwin.default
|
||||
runs-on:
|
||||
- macos-13
|
||||
needs:
|
||||
- x86_64-darwin---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
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@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
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@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
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@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
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@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
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@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Check
|
||||
run: nix flake check . --print-build-logs
|
||||
x86_64-linux---default:
|
||||
name: Build x86_64-linux.default
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- x86_64-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.default --print-build-logs
|
||||
x86_64-linux---proof-proverif:
|
||||
name: Build x86_64-linux.proof-proverif
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- x86_64-linux---proverif-patched
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.proof-proverif --print-build-logs
|
||||
x86_64-linux---proverif-patched:
|
||||
name: Build x86_64-linux.proverif-patched
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.proverif-patched --print-build-logs
|
||||
x86_64-linux---release-package:
|
||||
name: Build x86_64-linux.release-package
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- x86_64-linux---rosenpass-static
|
||||
- x86_64-linux---rosenpass-static-oci-image
|
||||
- x86_64-linux---rp-static
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.release-package --print-build-logs
|
||||
aarch64-linux---release-package:
|
||||
name: Build aarch64-linux.release-package
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- aarch64-linux---rosenpass-oci-image
|
||||
- aarch64-linux---rosenpass
|
||||
- aarch64-linux---rp
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-linux.release-package --print-build-logs
|
||||
x86_64-linux---rosenpass:
|
||||
name: Build x86_64-linux.rosenpass
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.rosenpass --print-build-logs
|
||||
aarch64-linux---rosenpass:
|
||||
name: Build aarch64-linux.rosenpass
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-linux.rosenpass --print-build-logs
|
||||
aarch64-linux---rp:
|
||||
name: Build aarch64-linux.rp
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-linux.rp --print-build-logs
|
||||
x86_64-linux---rosenpass-oci-image:
|
||||
name: Build x86_64-linux.rosenpass-oci-image
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- x86_64-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.rosenpass-oci-image --print-build-logs
|
||||
aarch64-linux---rosenpass-oci-image:
|
||||
name: Build aarch64-linux.rosenpass-oci-image
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- aarch64-linux---rosenpass
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-linux.rosenpass-oci-image --print-build-logs
|
||||
x86_64-linux---rosenpass-static:
|
||||
name: Build x86_64-linux.rosenpass-static
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.rosenpass-static --print-build-logs
|
||||
x86_64-linux---rp-static:
|
||||
name: Build x86_64-linux.rp-static
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.rp-static --print-build-logs
|
||||
x86_64-linux---rosenpass-static-oci-image:
|
||||
name: Build x86_64-linux.rosenpass-static-oci-image
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- x86_64-linux---rosenpass-static
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.rosenpass-static-oci-image --print-build-logs
|
||||
x86_64-linux---whitepaper:
|
||||
name: Build x86_64-linux.whitepaper
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.whitepaper --print-build-logs
|
||||
x86_64-linux---check:
|
||||
name: Run Nix checks on x86_64-linux
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Check
|
||||
run: nix flake check . --print-build-logs
|
||||
x86_64-linux---whitepaper-upload:
|
||||
name: Upload whitepaper x86_64-linux
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Git add git sha and commit
|
||||
run: cd papers && ./tex/gitinfo2.sh && git add gitHeadInfo.gin
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.whitepaper --print-build-logs
|
||||
- name: Deploy PDF artifacts
|
||||
if: ${{ matrix.derivation == 'whitepaper' && github.ref == 'refs/heads/main' }}
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ${{ matrix.derivation }}-${{ matrix.nix-system }}
|
||||
publish_dir: result/
|
||||
publish_branch: papers-pdf
|
||||
force_orphan: true
|
||||
checks:
|
||||
name: Run Nix checks
|
||||
runs-on: nixos
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Checks
|
||||
run: nix flake check . --print-build-logs
|
||||
|
||||
178
.github/workflows/qc.yaml
vendored
178
.github/workflows/qc.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Quality Control
|
||||
name: QC
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
@@ -12,15 +12,69 @@ jobs:
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actionsx/prettier@v2
|
||||
with:
|
||||
args: --check .
|
||||
|
||||
shellcheck:
|
||||
name: Shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
|
||||
rustfmt:
|
||||
name: Rust Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Rust Formatting Script
|
||||
run: bash format_rust_code.sh --mode check
|
||||
|
||||
cargo-bench:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
# 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 bench --workspace --exclude rosenpass-fuzzing
|
||||
|
||||
mandoc:
|
||||
name: mandoc
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install mandoc
|
||||
run: sudo apt-get install -y mandoc
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check rosenpass.1
|
||||
run: doc/check.sh doc/rosenpass.1
|
||||
- name: Check rp.1
|
||||
run: doc/check.sh doc/rp.1
|
||||
|
||||
cargo-audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
cargo-clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -31,17 +85,123 @@ jobs:
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- run: rustup component add clippy
|
||||
- name: Install xmllint
|
||||
run: sudo apt-get install -y libsodium-dev
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
cargo-audit:
|
||||
cargo-doc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/audit-check@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- run: rustup component add clippy
|
||||
# `--no-deps` used as a workaround for a rust compiler bug. See:
|
||||
# - https://github.com/rosenpass/rosenpass/issues/62
|
||||
# - https://github.com/rust-lang/rust/issues/108378
|
||||
- run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items
|
||||
|
||||
cargo-test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-13]
|
||||
# - ubuntu is x86-64
|
||||
# - macos-13 is also x86-64 architecture
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
# 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
|
||||
|
||||
cargo-test-nix-devshell-x86_64-linux:
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: cachix/install-nix-action@v21
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- run: nix develop --command cargo test --workspace --all-features
|
||||
|
||||
cargo-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install nightly toolchain
|
||||
run: |
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
- name: Install cargo-fuzz
|
||||
run: cargo install cargo-fuzz
|
||||
- name: Run fuzzing
|
||||
run: |
|
||||
cargo fuzz run fuzz_aead_enc_into -- -max_total_time=5
|
||||
cargo fuzz run fuzz_blake2b -- -max_total_time=5
|
||||
cargo fuzz run fuzz_handle_msg -- -max_total_time=5
|
||||
ulimit -s 8192000 && RUST_MIN_STACK=33554432000 && cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5
|
||||
cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=5
|
||||
cargo fuzz run fuzz_box_secret_alloc_malloc -- -max_total_time=5
|
||||
cargo fuzz run fuzz_box_secret_alloc_memfdsec -- -max_total_time=5
|
||||
cargo fuzz run fuzz_box_secret_alloc_memfdsec_mallocfb -- -max_total_time=5
|
||||
cargo fuzz run fuzz_vec_secret_alloc_malloc -- -max_total_time=5
|
||||
cargo fuzz run fuzz_vec_secret_alloc_memfdsec -- -max_total_time=5
|
||||
cargo fuzz run fuzz_vec_secret_alloc_memfdsec_mallocfb -- -max_total_time=5
|
||||
|
||||
codecov:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: rustup component add llvm-tools-preview
|
||||
- run: |
|
||||
cargo install cargo-llvm-cov || true
|
||||
cargo llvm-cov --lcov --output-path coverage.lcov
|
||||
# If using tarapulin
|
||||
#- run: cargo install cargo-tarpaulin
|
||||
#- run: cargo tarpaulin --out Xml
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4.0.1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage.lcov
|
||||
verbose: true
|
||||
|
||||
21
.github/workflows/regressions.yml
vendored
Normal file
21
.github/workflows/regressions.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: QC
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
multi-peer:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: cargo build --bin rosenpass --release
|
||||
- run: python misc/generate_configs.py
|
||||
- run: chmod +x .ci/run-regression.sh
|
||||
- run: .ci/run-regression.sh 100 20
|
||||
- run: |
|
||||
[ $(ls -1 output/ate/out | wc -l) -eq 100 ]
|
||||
71
.github/workflows/release.yaml
vendored
71
.github/workflows/release.yaml
vendored
@@ -3,28 +3,69 @@ permissions:
|
||||
contents: write
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
tags:
|
||||
- v*
|
||||
jobs:
|
||||
release:
|
||||
name: Release for ${{ matrix.nix-system }}
|
||||
i686-linux---release:
|
||||
name: Build release artifacts for i686-linux
|
||||
runs-on:
|
||||
- nix
|
||||
- ${{ matrix.nix-system }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
nix-system:
|
||||
- x86_64-linux
|
||||
# - aarch64-linux
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build release-package for ${{ matrix.nix-system }}
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build release
|
||||
run: nix build .#release-package --print-build-logs
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
files: |
|
||||
result/*
|
||||
files: result/*
|
||||
x86_64-darwin---release:
|
||||
name: Build release artifacts for x86_64-darwin
|
||||
runs-on:
|
||||
- macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build release
|
||||
run: nix build .#release-package --print-build-logs
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
files: result/*
|
||||
x86_64-linux---release:
|
||||
name: Build release artifacts for x86_64-linux
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build release
|
||||
run: nix build .#release-package --print-build-logs
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
files: result/*
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -20,3 +20,8 @@ _markdown_*
|
||||
**/result
|
||||
**/result-*
|
||||
.direnv
|
||||
|
||||
# Visual studio code
|
||||
.vscode
|
||||
|
||||
/output
|
||||
|
||||
17
.gitlab-ci.yml
Normal file
17
.gitlab-ci.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# TODO use CI_JOB_TOKEN once https://gitlab.com/groups/gitlab-org/-/epics/6310 is fixed
|
||||
pull-from-gh:
|
||||
only: ["schedules"]
|
||||
variables:
|
||||
REMOTE: "https://github.com/rosenpass/rosenpass.git"
|
||||
LOCAL: " git@gitlab.com:rosenpass/rosenpass.git"
|
||||
GIT_STRATEGY: none
|
||||
before_script:
|
||||
- mkdir ~/.ssh/
|
||||
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||
- echo "$REPO_SSH_KEY" > ~/.ssh/id_ed25519
|
||||
- chmod 600 --recursive ~/.ssh/
|
||||
- git config --global user.email "ci@gitlab.com"
|
||||
- git config --global user.name "CI"
|
||||
script:
|
||||
- git clone --mirror $REMOTE rosenpass
|
||||
- cd rosenpass && git push --mirror $LOCAL
|
||||
38
CONTRIBUTING.md
Normal file
38
CONTRIBUTING.md
Normal file
@@ -0,0 +1,38 @@
|
||||
**Making a new Release of Rosenpass — Cooking Recipe**
|
||||
|
||||
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
|
||||
|
||||
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`
|
||||
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!
|
||||
|
||||
**Frequently Asked Questions (FAQ)**
|
||||
|
||||
- You have untracked files, which `cargo release` complains about?
|
||||
- `git stash --include-untracked`
|
||||
- You cannot push to crates.io because you are not logged in?
|
||||
- Follow the steps displayed in [`cargo login`](https://doc.rust-lang.org/cargo/commands/cargo-login.html)
|
||||
- How is the release page added to [GitHub Releases](https://github.com/rosenpass/rosenpass/releases) itself?
|
||||
- Our CI Pipeline will create the release, once `cargo release` pushed the new version tag to the repo. The new release should pop up almost immediately in [GitHub Releases](https://github.com/rosenpass/rosenpass/releases) after the [Actions/Release](https://github.com/rosenpass/rosenpass/actions/workflows/release.yaml) pipeline started.
|
||||
- No new release pops up in the `Release` sidebar element on the [main page](https://github.com/rosenpass/rosenpass)
|
||||
- Did you push a `rc` release? This view only shows non-draft release, but `rc` releases are considered as draft. See [Releases](https://github.com/rosenpass/rosenpass/releases) page to see all (including draft!) releases.
|
||||
- The release page was created on GitHub, but there are no assets/artifacts other than the source code tar ball/zip?
|
||||
- The artifacts are generated and pushed automatically to the release, but this takes some time (a couple of minutes). You can check the respective CI pipeline: [Actions/Release](https://github.com/rosenpass/rosenpass/actions/workflows/release.yaml), which should start immediately after `cargo release` pushed the new release tag to the repo. The release artifacts only are added later to the release, once all jobs in bespoke pipeline finished.
|
||||
- How are the release artifacts generated, and what are they?
|
||||
- The release artifacts are built using one Nix derivation per platform, `nix build .#release-package`. It contains both statically linked versions of `rosenpass` itself and OCI container images.
|
||||
2540
Cargo.lock
generated
2540
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
109
Cargo.toml
109
Cargo.toml
@@ -1,35 +1,86 @@
|
||||
[package]
|
||||
name = "rosenpass"
|
||||
version = "0.1.1"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Build post-quantum-secure VPNs with WireGuard!"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
[[bench]]
|
||||
name = "handshake"
|
||||
harness = false
|
||||
members = [
|
||||
"rosenpass",
|
||||
"cipher-traits",
|
||||
"ciphers",
|
||||
"util",
|
||||
"constant-time",
|
||||
"oqs",
|
||||
"to",
|
||||
"fuzz",
|
||||
"secret-memory",
|
||||
"rp",
|
||||
"wireguard-broker",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.52", features = ["backtrace"] }
|
||||
base64 = "0.13.0"
|
||||
clap = { version = "3.0.0", features = ["yaml"] }
|
||||
default-members = ["rosenpass", "rp", "wireguard-broker"]
|
||||
|
||||
[workspace.metadata.release]
|
||||
# ensure that adding `--package` as argument to `cargo release` still creates version tags in the form of `vx.y.z`
|
||||
tag-prefix = ""
|
||||
|
||||
[workspace.dependencies]
|
||||
rosenpass = { path = "rosenpass" }
|
||||
rosenpass-util = { path = "util" }
|
||||
rosenpass-constant-time = { path = "constant-time" }
|
||||
rosenpass-cipher-traits = { path = "cipher-traits" }
|
||||
rosenpass-ciphers = { path = "ciphers" }
|
||||
rosenpass-to = { path = "to" }
|
||||
rosenpass-secret-memory = { path = "secret-memory" }
|
||||
rosenpass-oqs = { path = "oqs" }
|
||||
rosenpass-wireguard-broker = { path = "wireguard-broker" }
|
||||
doc-comment = "0.3.3"
|
||||
base64ct = {version = "1.6.0", default-features=false}
|
||||
zeroize = "1.8.1"
|
||||
memoffset = "0.9.1"
|
||||
thiserror = "1.0.63"
|
||||
paste = "1.0.15"
|
||||
env_logger = "0.10.2"
|
||||
toml = "0.7.8"
|
||||
static_assertions = "1.1.0"
|
||||
memoffset = "0.6.5"
|
||||
libsodium-sys-stable = { version = "1.19.26", features = ["use-pkg-config"] }
|
||||
oqs-sys = { version = "0.7.1", default-features = false, features = ['classic_mceliece', 'kyber'] }
|
||||
lazy_static = "1.4.0"
|
||||
thiserror = "1.0.38"
|
||||
paste = "1.0.11"
|
||||
log = { version = "0.4.17", optional = true }
|
||||
env_logger = { version = "0.10.0", optional = true }
|
||||
allocator-api2 = "0.2.14"
|
||||
memsec = { git="https://github.com/rosenpass/memsec.git" ,rev="aceb9baee8aec6844125bd6612f92e9a281373df", features = [ "alloc_ext", ] }
|
||||
rand = "0.8.5"
|
||||
typenum = "1.17.0"
|
||||
log = { version = "0.4.22" }
|
||||
clap = { version = "4.5.13", features = ["derive"] }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||
anyhow = { version = "1.0.86", features = ["backtrace", "std"] }
|
||||
mio = { version = "1.0.1", features = ["net", "os-poll"] }
|
||||
oqs-sys = { version = "0.9.1", default-features = false, features = [
|
||||
'classic_mceliece',
|
||||
'kyber',
|
||||
] }
|
||||
blake2 = "0.10.6"
|
||||
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
|
||||
"std",
|
||||
"heapless",
|
||||
] }
|
||||
zerocopy = { version = "0.7.35", features = ["derive"] }
|
||||
home = "0.5.9"
|
||||
derive_builder = "0.20.0"
|
||||
tokio = { version = "1.39", features = ["macros", "rt-multi-thread"] }
|
||||
postcard= {version = "1.0.8", features = ["alloc"]}
|
||||
libcrux = { version = "0.0.2-pre.2" }
|
||||
hex-literal = { version = "0.4.1" }
|
||||
hex = { version = "0.4.3" }
|
||||
heck = { version = "0.5.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3.5"
|
||||
#Dev dependencies
|
||||
serial_test = "3.1.1"
|
||||
tempfile = "3"
|
||||
stacker = "0.1.15"
|
||||
libfuzzer-sys = "0.4"
|
||||
test_bin = "0.4.0"
|
||||
criterion = "0.4.0"
|
||||
allocator-api2-tests = "0.2.15"
|
||||
procspawn = {version = "1.0.0", features= ["test-support"]}
|
||||
|
||||
[features]
|
||||
default = ["log", "env_logger"]
|
||||
|
||||
#Broker dependencies (might need cleanup or changes)
|
||||
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
|
||||
command-fds = "0.2.3"
|
||||
rustix = { version = "0.38.27", features = ["net", "fs"] }
|
||||
|
||||
25
analysis/03_identity_hiding_initiator.entry.mpv
Normal file
25
analysis/03_identity_hiding_initiator.entry.mpv
Normal 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].
|
||||
96
analysis/03_identity_hiding_responder.entry.mpv
Normal file
96
analysis/03_identity_hiding_responder.entry.mpv
Normal 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].
|
||||
29
analysis/03_identity_hiding_test.entry.mpv
Normal file
29
analysis/03_identity_hiding_test.entry.mpv
Normal 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.
|
||||
136
analysis/04_dos_protection.entry.mpv
Normal file
136
analysis/04_dos_protection.entry.mpv
Normal 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)).
|
||||
|
||||
155
analysis/rosenpass/03_identity_hiding.mpv
Normal file
155
analysis/rosenpass/03_identity_hiding.mpv
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
This identity hiding process tests whether the rosenpass protocol is able to protect the identity of an initiator or responder.
|
||||
The participants in the test are trusted initiators, trusted responders and compromised initiators and responders.
|
||||
The test consists of two phases. In the first phase all of the participants can communicate with each other using the rosenpass protocol.
|
||||
An attacker observes the first phase and is able to intercept and modify messages and choose participants to communicate with each other
|
||||
|
||||
In the second phase if the anonymity of an initiator is being tested then one of two trusted initiators is chosen.
|
||||
The chosen initiator communicates directly with a trusted responder.
|
||||
If an attacker can determine which initiator was chosen then the anonymity of the initiator has been compromised.
|
||||
Otherwise the protocol has successfully protected the initiators’ identity.
|
||||
|
||||
If the anonymity of a responder is being tested then one of two trusted responders is chosen instead.
|
||||
Then an initiator communicates directly with the chosen responder.
|
||||
If an attacker can determine which responder was chosen then the anonymity of the responder is compromised.
|
||||
Otherwise the protocol successfully protects the identity of a responder.
|
||||
|
||||
The Proverif code treats the public key as synonymous with identity.
|
||||
In the above test when a responder or initiator is chosen what is actually chosen is the public/private key pair to use for communication.
|
||||
Traditionally when a responder or initiator is chosen they would be chosen randomly.
|
||||
The way Proverif makes a "choice" is by simulating multiple processes, one process per choice
|
||||
Then the processes are compared and if an association between a public key and a process can be made the test fails.
|
||||
As the choice is at least as bad as choosing the worst possible option the credibility of the test is maintained.
|
||||
The drawback is that Proverif is only able to tell if the identity can be brute forced but misses any probabilistic associations.
|
||||
As usual Proverif also assumes perfect encryption and in particular assumes encryption cannot be linked to identity.
|
||||
|
||||
One of the tradeoffs made here is that the choice function in Proverif is slow but this is in favour of being able to write more precise tests.
|
||||
Another issue is the choice function does not work with queries so a test needs to be run for each set of assumptions.
|
||||
In this case the test uses secure rng and a fresh secure biscuit key.
|
||||
*/
|
||||
|
||||
#include "config.mpv"
|
||||
|
||||
#define CHAINING_KEY_EVENTS 1
|
||||
#define MESSAGE_TRANSMISSION_EVENTS 1
|
||||
#define SESSION_START_EVENTS 0
|
||||
#define RANDOMIZED_CALL_IDS 0
|
||||
#undef FULL_MODEL
|
||||
#undef SIMPLE_MODEL
|
||||
#define SIMPLE_MODEL 1
|
||||
|
||||
#include "prelude/basic.mpv"
|
||||
#include "crypto/key.mpv"
|
||||
#include "rosenpass/oracles.mpv"
|
||||
#include "crypto/kem.mpv"
|
||||
|
||||
#define NEW_TRUSTED_SEED(name) \
|
||||
new MCAT(name, _secret_seed):seed_prec; \
|
||||
name <- make_trusted_seed(MCAT(name, _secret_seed)); \
|
||||
|
||||
free D:channel [private].
|
||||
free secure_biscuit_no:Atom [private].
|
||||
free secure_sidi,secure_sidr:SessionId [private].
|
||||
free secure_psk:key [private].
|
||||
free initiator1, initiator2:kem_sk_prec.
|
||||
free responder1, responder2:kem_sk_prec.
|
||||
|
||||
let secure_init_hello(initiator: kem_sk_tmpl, sidi : SessionId, psk: key_tmpl, responder: kem_sk_tmpl) =
|
||||
|
||||
new epkit:kem_pk; // epki
|
||||
new sctrt:bits; // sctr
|
||||
new pidiCt:bits; // pidiC
|
||||
new autht:bits; // auth
|
||||
|
||||
NEW_TRUSTED_SEED(seski_trusted_seed)
|
||||
NEW_TRUSTED_SEED(ssptr_trusted_seed)
|
||||
new last_cookie:key;
|
||||
new call:Atom;
|
||||
|
||||
Oinitiator_inner(sidi, initiator, psk, responder, seski_trusted_seed, ssptr_trusted_seed, last_cookie, D, call).
|
||||
|
||||
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, pidiC, auth));
|
||||
|
||||
ih <- InitHello(sidi, epki, sctr, pidiC, auth);
|
||||
NEW_TRUSTED_SEED(septi_trusted_seed)
|
||||
NEW_TRUSTED_SEED(sspti_trusted_seed)
|
||||
new last_cookie:key;
|
||||
new call:Atom;
|
||||
|
||||
Oinit_hello_inner(sidr, biscuit_no, responder, psk, initiator, septi_trusted_seed, sspti_trusted_seed, ih, last_cookie, D, call).
|
||||
|
||||
let secure_init_conf(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, psk:key_tmpl, sidi:SessionId, sidr:SessionId) =
|
||||
in(D, InitConf(=sidi, =sidr, biscuit, auth3));
|
||||
ic <- InitConf(sidi,sidr,biscuit, auth3);
|
||||
NEW_TRUSTED_SEED(seski_trusted_seed)
|
||||
NEW_TRUSTED_SEED(ssptr_trusted_seed)
|
||||
new last_cookie:key;
|
||||
call <- Cinit_conf(initiator, psk, responder, ic);
|
||||
|
||||
Oinit_conf_inner(initiator, psk, responder, ic, call).
|
||||
|
||||
let secure_communication(initiator: kem_sk_tmpl, responder:kem_sk_tmpl, key:key) =
|
||||
key_tmpl <- prepare_key(key);
|
||||
(!secure_init_hello(initiator, secure_sidi, key_tmpl, responder))
|
||||
| !secure_resp_hello(initiator, responder, secure_sidi, secure_sidr, secure_biscuit_no, key_tmpl)
|
||||
| !(secure_init_conf(initiator, responder, key_tmpl, secure_sidi, secure_sidr)).
|
||||
|
||||
let participant_communication_initiator(participant:kem_sk_tmpl) =
|
||||
in(C, responder:kem_sk_tmpl);
|
||||
in(C, k:key);
|
||||
secure_communication(participant, responder, k).
|
||||
|
||||
let participant_communication_responder(participant:kem_sk_tmpl) =
|
||||
in(C, initiator:kem_sk_tmpl);
|
||||
in(C, k:key);
|
||||
secure_communication(initiator, participant, k).
|
||||
|
||||
let participants_communication() =
|
||||
initiator1_tmpl <- make_trusted_kem_sk(initiator1);
|
||||
initiator2_tmpl <- make_trusted_kem_sk(initiator2);
|
||||
responder1_tmpl <- make_trusted_kem_sk(responder1);
|
||||
responder2_tmpl <- make_trusted_kem_sk(responder2);
|
||||
|
||||
!participant_communication_initiator(initiator1_tmpl) | !participant_communication_responder(initiator1_tmpl)
|
||||
| !participant_communication_initiator(initiator2_tmpl) | !participant_communication_responder(initiator2_tmpl)
|
||||
| !participant_communication_initiator(responder1_tmpl) | !participant_communication_responder(responder1_tmpl)
|
||||
| !participant_communication_initiator(responder2_tmpl) | !participant_communication_responder(responder2_tmpl).
|
||||
|
||||
let pipeChannel(D:channel, C:channel) =
|
||||
in(D, b:bits);
|
||||
out(C, b).
|
||||
|
||||
let secretCommunication() =
|
||||
|
||||
#ifdef INITIATOR_TEST
|
||||
initiator_seed <- choice[make_trusted_kem_sk(initiator1), make_trusted_kem_sk(initiator2)];
|
||||
#else
|
||||
initiator_seed <- make_trusted_kem_sk(initiator1);
|
||||
#endif
|
||||
#ifdef RESPONDER_TEST
|
||||
responder_seed <- choice[make_trusted_kem_sk(responder1), make_trusted_kem_sk(responder2)];
|
||||
#else
|
||||
responder_seed <- make_trusted_kem_sk(responder1);
|
||||
#endif
|
||||
|
||||
secure_communication(initiator_seed, responder_seed, secure_psk) | !pipeChannel(D, C).
|
||||
|
||||
let reveal_pks() =
|
||||
out(C, setup_kem_pk(make_trusted_kem_sk(responder1)));
|
||||
out(C, setup_kem_pk(make_trusted_kem_sk(responder2)));
|
||||
out(C, setup_kem_pk(make_trusted_kem_sk(initiator1)));
|
||||
out(C, setup_kem_pk(make_trusted_kem_sk(initiator2))).
|
||||
|
||||
let rosenpass_main2() =
|
||||
REP(INITIATOR_BOUND, Oinitiator)
|
||||
| REP(RESPONDER_BOUND, Oinit_hello)
|
||||
| REP(RESPONDER_BOUND, Oinit_conf).
|
||||
|
||||
let identity_hiding_main() =
|
||||
0 | reveal_pks() | rosenpass_main2() | participants_communication() | phase 1; secretCommunication().
|
||||
|
||||
#ifndef CUSTOM_MAIN
|
||||
let main = identity_hiding_main.
|
||||
#endif
|
||||
36
analysis/rosenpass/cookie.mpv
Normal file
36
analysis/rosenpass/cookie.mpv
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
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).
|
||||
|
||||
type CookieMsg_t.
|
||||
fun CookieMsg(
|
||||
SessionId, // sender
|
||||
bits, // nonce
|
||||
bits // cookie
|
||||
) : CookieMsg_t [data].
|
||||
|
||||
|
||||
#define COOKIE_PROCESS(eventLbl, innerFunc) \
|
||||
in(C, Ccookie(mac1, mac2)); \
|
||||
COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (spkm, spkt, last_cookie);) \
|
||||
msgB <- Envelope(mac1, RH2b(rh)); \
|
||||
mac2_key <- create_mac2_key(sskm, spkt) \
|
||||
let RespHello(sidi, sidr, ecti, scti, biscuit, auth) = rh in \
|
||||
if Envelope(mac2_key, msgB) = mac2 then \
|
||||
COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (spkm, last_cookie);) \
|
||||
innerFunc \
|
||||
else \
|
||||
new nonce:bits; \
|
||||
cookie <- create_cookie(sskm, spkm, spkt, nonce, msg) \
|
||||
cookie_msg <- CookieMsg(sidi, nonce, cookie); \
|
||||
COOKIE_EV(event MCAT(eventLbl, _CookieSent) (spkm, cookie, cookie_k, cookie_msg);) \
|
||||
out(C, cookie_msg).
|
||||
|
||||
#define COOKIE_EVENTS(eventLbl) \
|
||||
COOKIE_EV(event MCAT(eventLbl, _UnderLoadEV) (kem_pk, kem_pk, bits).) \
|
||||
COOKIE_EV(event MCAT(eventLbl, _CookieValidated) (kem_pk, bits, key, CookieMsg_t).) \
|
||||
COOKIE_EV(event MCAT(eventLbl, _CookieSent) (kem_pk, bits).)
|
||||
12
cipher-traits/Cargo.toml
Normal file
12
cipher-traits/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "rosenpass-cipher-traits"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal traits for cryptographic primitives"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
5
cipher-traits/readme.md
Normal file
5
cipher-traits/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass internal cryptographic traits
|
||||
|
||||
Rosenpass internal library providing traits for cryptographic primitives.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
45
cipher-traits/src/kem.rs
Normal file
45
cipher-traits/src/kem.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
//! Traits and implementations for Key Encapsulation Mechanisms (KEMs)
|
||||
//!
|
||||
//! KEMs are the interface provided by almost all post-quantum
|
||||
//! secure key exchange mechanisms.
|
||||
//!
|
||||
//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting
|
||||
//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during
|
||||
//!
|
||||
//! encapsulation.
|
||||
//! The [KEM] Trait describes the basic API offered by a Key Encapsulation
|
||||
//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM].
|
||||
|
||||
/// Key Encapsulation Mechanism
|
||||
///
|
||||
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
||||
/// decapsulation.
|
||||
pub trait Kem {
|
||||
type Error;
|
||||
|
||||
/// Secrete Key length
|
||||
const SK_LEN: usize;
|
||||
/// Public Key length
|
||||
const PK_LEN: usize;
|
||||
/// Ciphertext length
|
||||
const CT_LEN: usize;
|
||||
/// Shared Secret length
|
||||
const SHK_LEN: usize;
|
||||
|
||||
/// Generate a keypair consisting of secret key (`sk`) and public key (`pk`)
|
||||
///
|
||||
/// `keygen() -> sk, pk`
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), Self::Error>;
|
||||
|
||||
/// From a public key (`pk`), generate a shared key (`shk`, for local use)
|
||||
/// and a cipher text (`ct`, to be sent to the owner of the `pk`).
|
||||
///
|
||||
/// `encaps(pk) -> shk, ct`
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), Self::Error>;
|
||||
|
||||
/// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key
|
||||
/// (`shk`)
|
||||
///
|
||||
/// `decaps(sk, ct) -> shk`
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
2
cipher-traits/src/lib.rs
Normal file
2
cipher-traits/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod kem;
|
||||
pub use kem::Kem;
|
||||
26
ciphers/Cargo.toml
Normal file
26
ciphers/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "rosenpass-ciphers"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal ciphers and other cryptographic primitives used by rosenpass."
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[features]
|
||||
experiment_libcrux = ["dep:libcrux"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass-constant-time = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-oqs = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
chacha20poly1305 = { workspace = true }
|
||||
blake2 = { workspace = true }
|
||||
libcrux = { workspace = true, optional = true }
|
||||
5
ciphers/readme.md
Normal file
5
ciphers/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass internal cryptographic primitives
|
||||
|
||||
Ciphers and other cryptographic primitives used by rosenpass.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
109
ciphers/src/hash_domain.rs
Normal file
109
ciphers/src/hash_domain.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use anyhow::Result;
|
||||
use rosenpass_secret_memory::Secret;
|
||||
use rosenpass_to::To;
|
||||
|
||||
use crate::subtle::incorrect_hmac_blake2b as hash;
|
||||
|
||||
pub use hash::KEY_LEN;
|
||||
|
||||
// TODO Use a proper Dec interface
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HashDomain([u8; KEY_LEN]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HashDomainNamespace([u8; KEY_LEN]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretHashDomain(Secret<KEY_LEN>);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretHashDomainNamespace(Secret<KEY_LEN>);
|
||||
|
||||
impl HashDomain {
|
||||
pub fn zero() -> Self {
|
||||
Self([0u8; KEY_LEN])
|
||||
}
|
||||
|
||||
pub fn dup(self) -> HashDomainNamespace {
|
||||
HashDomainNamespace(self.0)
|
||||
}
|
||||
|
||||
pub fn turn_secret(self) -> SecretHashDomain {
|
||||
SecretHashDomain(Secret::from_slice(&self.0))
|
||||
}
|
||||
|
||||
// TODO: Protocol! Use domain separation to ensure that
|
||||
pub fn mix(self, v: &[u8]) -> Result<Self> {
|
||||
Ok(Self(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(&self.0, v.secret())
|
||||
}
|
||||
|
||||
pub fn into_value(self) -> [u8; KEY_LEN] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl HashDomainNamespace {
|
||||
pub fn mix(&self, v: &[u8]) -> Result<HashDomain> {
|
||||
Ok(HashDomain(
|
||||
hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(&self.0, v.secret())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHashDomain {
|
||||
pub fn invoke_primitive(k: &[u8], d: &[u8]) -> Result<SecretHashDomain> {
|
||||
let mut r = SecretHashDomain(Secret::zero());
|
||||
hash::hash(k, d).to(r.0.secret_mut())?;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self(Secret::zero())
|
||||
}
|
||||
|
||||
pub fn dup(self) -> SecretHashDomainNamespace {
|
||||
SecretHashDomainNamespace(self.0)
|
||||
}
|
||||
|
||||
pub fn danger_from_secret(k: Secret<KEY_LEN>) -> Self {
|
||||
Self(k)
|
||||
}
|
||||
|
||||
pub fn mix(self, v: &[u8]) -> Result<SecretHashDomain> {
|
||||
Self::invoke_primitive(self.0.secret(), v)
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
Self::invoke_primitive(self.0.secret(), v.secret())
|
||||
}
|
||||
|
||||
pub fn into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn into_secret_slice(mut self, v: &[u8], dst: &[u8]) -> Result<()> {
|
||||
hash::hash(v, dst).to(self.0.secret_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHashDomainNamespace {
|
||||
pub fn mix(&self, v: &[u8]) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(self.0.secret(), v)
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(self.0.secret(), v.secret())
|
||||
}
|
||||
|
||||
// TODO: This entire API is not very nice; we need this for biscuits, but
|
||||
// it might be better to extract a special "biscuit"
|
||||
// labeled subkey and reinitialize the chain with this
|
||||
pub fn danger_into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
32
ciphers/src/lib.rs
Normal file
32
ciphers/src/lib.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use static_assertions::const_assert;
|
||||
|
||||
pub mod subtle;
|
||||
|
||||
pub const KEY_LEN: usize = 32;
|
||||
const_assert!(KEY_LEN == aead::KEY_LEN);
|
||||
const_assert!(KEY_LEN == xaead::KEY_LEN);
|
||||
const_assert!(KEY_LEN == hash_domain::KEY_LEN);
|
||||
|
||||
/// Authenticated encryption with associated data
|
||||
pub mod aead {
|
||||
#[cfg(not(feature = "experiment_libcrux"))]
|
||||
pub use crate::subtle::chacha20poly1305_ietf::{decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN};
|
||||
#[cfg(feature = "experiment_libcrux")]
|
||||
pub use crate::subtle::chacha20poly1305_ietf_libcrux::{
|
||||
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
|
||||
};
|
||||
}
|
||||
|
||||
/// Authenticated encryption with associated data with a constant nonce
|
||||
pub mod xaead {
|
||||
pub use crate::subtle::xchacha20poly1305_ietf::{
|
||||
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod hash_domain;
|
||||
|
||||
pub mod kem {
|
||||
pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem;
|
||||
pub use rosenpass_oqs::Kyber512 as EphemeralKem;
|
||||
}
|
||||
42
ciphers/src/subtle/blake2b.rs
Normal file
42
ciphers/src/subtle/blake2b.rs
Normal 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(())
|
||||
})
|
||||
}
|
||||
43
ciphers/src/subtle/chacha20poly1305_ietf.rs
Normal file
43
ciphers/src/subtle/chacha20poly1305_ietf.rs
Normal 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(())
|
||||
}
|
||||
60
ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs
Normal file
60
ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs
Normal 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(())
|
||||
}
|
||||
46
ciphers/src/subtle/incorrect_hmac_blake2b.rs
Normal file
46
ciphers/src/subtle/incorrect_hmac_blake2b.rs
Normal 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(())
|
||||
})
|
||||
}
|
||||
7
ciphers/src/subtle/mod.rs
Normal file
7
ciphers/src/subtle/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod blake2b;
|
||||
#[cfg(not(feature = "experiment_libcrux"))]
|
||||
pub mod chacha20poly1305_ietf;
|
||||
#[cfg(feature = "experiment_libcrux")]
|
||||
pub mod chacha20poly1305_ietf_libcrux;
|
||||
pub mod incorrect_hmac_blake2b;
|
||||
pub mod xchacha20poly1305_ietf;
|
||||
45
ciphers/src/subtle/xchacha20poly1305_ietf.rs
Normal file
45
ciphers/src/subtle/xchacha20poly1305_ietf.rs
Normal 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(())
|
||||
}
|
||||
2
config-examples/.gitignore
vendored
Normal file
2
config-examples/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
peer-*-*-key
|
||||
peer-*-out
|
||||
18
config-examples/peer-a-config.toml
Normal file
18
config-examples/peer-a-config.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
public_key = "peer-a-public-key"
|
||||
secret_key = "peer-a-secret-key"
|
||||
listen = ["[::]:10001"]
|
||||
verbosity = "Quiet"
|
||||
|
||||
[[peers]]
|
||||
public_key = "peer-b-public-key"
|
||||
endpoint = "localhost:10002"
|
||||
key_out = "peer-a-rp-out-key"
|
||||
# exchange_command = [
|
||||
# "wg",
|
||||
# "set",
|
||||
# "wg0",
|
||||
# "peer",
|
||||
# "<PEER_ID>",
|
||||
# "preshared-key",
|
||||
# "/dev/stdin",
|
||||
# ]
|
||||
18
config-examples/peer-b-config.toml
Normal file
18
config-examples/peer-b-config.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
public_key = "peer-b-public-key"
|
||||
secret_key = "peer-b-secret-key"
|
||||
listen = ["[::]:10002"]
|
||||
verbosity = "Quiet"
|
||||
|
||||
[[peers]]
|
||||
public_key = "peer-a-public-key"
|
||||
endpoint = "localhost:10001"
|
||||
key_out = "peer-b-rp-out-key"
|
||||
# exchange_command = [
|
||||
# "wg",
|
||||
# "set",
|
||||
# "wg0",
|
||||
# "peer",
|
||||
# "<PEER_ID>",
|
||||
# "preshared-key",
|
||||
# "/dev/stdin",
|
||||
# ]
|
||||
22
constant-time/Cargo.toml
Normal file
22
constant-time/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "rosenpass-constant-time"
|
||||
version = "0.1.0"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal utilities for constant time crypto implementations"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
constant_time_tests = []
|
||||
|
||||
[dependencies]
|
||||
rosenpass-to = { workspace = true }
|
||||
memsec = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
5
constant-time/readme.md
Normal file
5
constant-time/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass constant time library
|
||||
|
||||
Rosenpass internal library providing basic constant-time operations.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
39
constant-time/src/compare.rs
Normal file
39
constant-time/src/compare.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use core::ptr;
|
||||
|
||||
/// Little endian memcmp version of quinier/memsec
|
||||
/// https://github.com/quininer/memsec/blob/bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0/src/lib.rs#L30
|
||||
#[inline(never)]
|
||||
pub unsafe fn memcmp_le(b1: *const u8, b2: *const u8, len: usize) -> i32 {
|
||||
let mut res = 0;
|
||||
for i in 0..len {
|
||||
let diff =
|
||||
i32::from(ptr::read_volatile(b1.add(i))) - i32::from(ptr::read_volatile(b2.add(i)));
|
||||
res = (res & (((diff - 1) & !diff) >> 8)) | diff;
|
||||
}
|
||||
((res - 1) >> 8) + (res >> 8) + 1
|
||||
}
|
||||
|
||||
/// compares two slices of memory content and returns an integer indicating the relationship between
|
||||
/// the slices
|
||||
///
|
||||
/// ## Returns
|
||||
/// - <0 if the first byte that does not match both slices has a lower value in `a` than in `b`
|
||||
/// - 0 if the contents are equal
|
||||
/// - >0 if the first byte that does not match both slices has a higher value in `a` than in `b`
|
||||
///
|
||||
/// ## Leaks
|
||||
/// If the two slices have differents lengths, the function will return immediately. This
|
||||
/// effectively leaks the information whether the slices have equal length or not. This is widely
|
||||
/// considered safe.
|
||||
///
|
||||
/// The execution time of the function grows approx. linear with the length of the input. This is
|
||||
/// considered safe.
|
||||
///
|
||||
/// ## Tests
|
||||
/// For discussion on how to ensure the constant-time execution of this function, see
|
||||
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
#[inline]
|
||||
pub fn compare(a: &[u8], b: &[u8]) -> i32 {
|
||||
assert!(a.len() == b.len());
|
||||
unsafe { memcmp_le(a.as_ptr(), b.as_ptr(), a.len()) }
|
||||
}
|
||||
48
constant-time/src/increment.rs
Normal file
48
constant-time/src/increment.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use core::hint::black_box;
|
||||
|
||||
/// Interpret the given slice as a little-endian unsigned integer
|
||||
/// and increment that integer.
|
||||
///
|
||||
/// # Leaks
|
||||
/// TODO: mention here if this function leaks any information, see
|
||||
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
///
|
||||
/// ## Tests
|
||||
/// For discussion on how to ensure the constant-time execution of this function, see
|
||||
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass_constant_time::increment as inc;
|
||||
/// use rosenpass_to::To;
|
||||
///
|
||||
/// fn testcase(v: &[u8], correct: &[u8]) {
|
||||
/// let mut v = v.to_owned();
|
||||
/// inc(&mut v);
|
||||
/// assert_eq!(&v, correct);
|
||||
/// }
|
||||
///
|
||||
/// testcase(b"", b"");
|
||||
/// testcase(b"\x00", b"\x01");
|
||||
/// testcase(b"\x01", b"\x02");
|
||||
/// testcase(b"\xfe", b"\xff");
|
||||
/// testcase(b"\xff", b"\x00");
|
||||
/// testcase(b"\x00\x00", b"\x01\x00");
|
||||
/// testcase(b"\x01\x00", b"\x02\x00");
|
||||
/// testcase(b"\xfe\x00", b"\xff\x00");
|
||||
/// testcase(b"\xff\x00", b"\x00\x01");
|
||||
/// testcase(b"\x00\x00\x00\x00\x00\x00", b"\x01\x00\x00\x00\x00\x00");
|
||||
/// testcase(b"\x00\xa3\x00\x77\x00\x00", b"\x01\xa3\x00\x77\x00\x00");
|
||||
/// testcase(b"\xff\xa3\x00\x77\x00\x00", b"\x00\xa4\x00\x77\x00\x00");
|
||||
/// testcase(b"\xff\xff\xff\x77\x00\x00", b"\x00\x00\x00\x78\x00\x00");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn increment(v: &mut [u8]) {
|
||||
let mut carry = 1u8;
|
||||
for val in v.iter_mut() {
|
||||
let (v, c) = black_box(*val).overflowing_add(black_box(carry));
|
||||
*black_box(val) = v;
|
||||
*black_box(&mut carry) = black_box(black_box(c) as u8);
|
||||
}
|
||||
}
|
||||
17
constant-time/src/lib.rs
Normal file
17
constant-time/src/lib.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
//! constant-time implementations of some primitives
|
||||
//!
|
||||
//! Rosenpass internal library providing basic constant-time operations.
|
||||
//!
|
||||
//! ## TODO
|
||||
//! Figure out methodology to ensure that code is actually constant time, see
|
||||
//! <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
|
||||
mod compare;
|
||||
mod increment;
|
||||
mod memcmp;
|
||||
mod xor;
|
||||
|
||||
pub use compare::compare;
|
||||
pub use increment::increment;
|
||||
pub use memcmp::memcmp;
|
||||
pub use xor::xor;
|
||||
107
constant-time/src/memcmp.rs
Normal file
107
constant-time/src/memcmp.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
/// compares two sclices of memory content and returns whether they are equal
|
||||
///
|
||||
/// ## Leaks
|
||||
/// If the two slices have differents lengths, the function will return immediately. This
|
||||
/// effectively leaks the information whether the slices have equal length or not. This is widely
|
||||
/// considered safe.
|
||||
///
|
||||
/// The execution time of the function grows approx. linear with the length of the input. This is
|
||||
/// considered safe.
|
||||
#[inline]
|
||||
pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
|
||||
a.len() == b.len() && unsafe { memsec::memeq(a.as_ptr(), b.as_ptr(), a.len()) }
|
||||
}
|
||||
|
||||
/// [tests::memcmp_runs_in_constant_time] runs a stasticial test that the equality of the two
|
||||
/// input parameters does not correlate with the run time.
|
||||
///
|
||||
/// For discussion on how to (further) ensure the constant-time execution of this function,
|
||||
/// see <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
#[cfg(all(test, feature = "constant_time_tests"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::time::Instant;
|
||||
|
||||
#[test]
|
||||
/// tests whether [memcmp] actually runs in constant time
|
||||
///
|
||||
/// This test function will run an equal amount of comparisons on two different sets of parameters:
|
||||
/// - completely equal slices
|
||||
/// - completely unequal slices.
|
||||
/// All comparisons are executed in a randomized order. The test will fail if one of the
|
||||
/// two sets is checked for equality significantly faster than the other set
|
||||
/// (absolute correlation coefficient ≥ 0.01)
|
||||
fn memcmp_runs_in_constant_time() {
|
||||
// prepare data to compare
|
||||
let n: usize = 1E6 as usize; // number of comparisons to run
|
||||
let len = 1024; // length of each slice passed as parameters to the tested comparison function
|
||||
let a1 = "a".repeat(len);
|
||||
let a2 = a1.clone();
|
||||
let b = "b".repeat(len);
|
||||
|
||||
let a1 = a1.as_bytes();
|
||||
let a2 = a2.as_bytes();
|
||||
let b = b.as_bytes();
|
||||
|
||||
// vector representing all timing tests
|
||||
//
|
||||
// Each element is a tuple of:
|
||||
// 0: whether the test compared two equal slices
|
||||
// 1: the duration needed for the comparison to run
|
||||
let mut tests = (0..n)
|
||||
.map(|i| (i < n / 2, std::time::Duration::ZERO))
|
||||
.collect::<Vec<_>>();
|
||||
tests.shuffle(&mut thread_rng());
|
||||
|
||||
// run comparisons / call function to test
|
||||
for test in tests.iter_mut() {
|
||||
let now = Instant::now();
|
||||
if test.0 {
|
||||
memcmp(a1, a2);
|
||||
} else {
|
||||
memcmp(a1, b);
|
||||
}
|
||||
test.1 = now.elapsed();
|
||||
// println!("eq: {}, elapsed: {:.2?}", test.0, test.1);
|
||||
}
|
||||
|
||||
// sort by execution time and calculate Pearson correlation coefficient
|
||||
tests.sort_by_key(|v| v.1);
|
||||
let tests = tests
|
||||
.iter()
|
||||
.map(|t| (if t.0 { 1_f64 } else { 0_f64 }, t.1.as_nanos() as f64))
|
||||
.collect::<Vec<_>>();
|
||||
// averages
|
||||
let (avg_x, avg_y): (f64, f64) = (
|
||||
tests.iter().map(|t| t.0).sum::<f64>() / n as f64,
|
||||
tests.iter().map(|t| t.1).sum::<f64>() / n as f64,
|
||||
);
|
||||
assert!((avg_x - 0.5).abs() < 1E-12);
|
||||
// standard deviations
|
||||
let sd_x = 0.5;
|
||||
let sd_y = (1_f64 / n as f64
|
||||
* tests
|
||||
.iter()
|
||||
.map(|t| {
|
||||
let difference = t.1 - avg_y;
|
||||
difference * difference
|
||||
})
|
||||
.sum::<f64>())
|
||||
.sqrt();
|
||||
// covariance
|
||||
let cv = 1_f64 / n as f64
|
||||
* tests
|
||||
.iter()
|
||||
.map(|t| (t.0 - avg_x) * (t.1 - avg_y))
|
||||
.sum::<f64>();
|
||||
// Pearson correlation
|
||||
let correlation = cv / (sd_x * sd_y);
|
||||
println!("correlation: {:.6?}", correlation);
|
||||
assert!(
|
||||
correlation.abs() < 0.01,
|
||||
"execution time correlates with result"
|
||||
)
|
||||
}
|
||||
}
|
||||
34
constant-time/src/xor.rs
Normal file
34
constant-time/src/xor.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use core::hint::black_box;
|
||||
use rosenpass_to::{with_destination, To};
|
||||
|
||||
/// Xors the source into the destination
|
||||
///
|
||||
/// # Panics
|
||||
/// If source and destination are of different sizes.
|
||||
///
|
||||
/// # Leaks
|
||||
/// TODO: mention here if this function leaks any information, see
|
||||
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
///
|
||||
/// ## Tests
|
||||
/// For discussion on how to ensure the constant-time execution of this function, see
|
||||
/// <https://github.com/rosenpass/rosenpass/issues/232>
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass_constant_time::xor;
|
||||
/// use rosenpass_to::To;
|
||||
/// assert_eq!(
|
||||
/// xor(b"world").to_this(|| b"hello".to_vec()),
|
||||
/// b"\x1f\n\x1e\x00\x0b");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn xor(src: &[u8]) -> impl To<[u8], ()> + '_ {
|
||||
with_destination(|dst: &mut [u8]| {
|
||||
assert!(black_box(src.len()) == black_box(dst.len()));
|
||||
for (dv, sv) in dst.iter_mut().zip(src.iter()) {
|
||||
*black_box(dv) ^= black_box(*sv);
|
||||
}
|
||||
})
|
||||
}
|
||||
13
doc/check.sh
Executable file
13
doc/check.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# We have to filter this STYLE error out, because it is very platform specific
|
||||
OUTPUT=$(mandoc -Tlint "$1" | grep --invert-match "STYLE: referenced manual not found")
|
||||
|
||||
if [ -z "$OUTPUT" ]
|
||||
then
|
||||
exit 0
|
||||
else
|
||||
echo "$1 is malformatted, check mandoc -Tlint $1"
|
||||
echo "$OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
114
doc/rosenpass.1
Normal file
114
doc/rosenpass.1
Normal file
@@ -0,0 +1,114 @@
|
||||
.Dd $Mdocdate$
|
||||
.Dt ROSENPASS 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm rosenpass
|
||||
.Nd builds post-quantum-secure VPNs
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op COMMAND
|
||||
.Op Ar OPTIONS ...
|
||||
.Op Ar ARGS ...
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
performs cryptographic key exchanges that are secure against quantum-computers
|
||||
and then outputs the keys.
|
||||
These keys can then be passed to various services, such as wireguard or other
|
||||
vpn services, as pre-shared-keys to achieve security against attackers with
|
||||
quantum computers.
|
||||
.Pp
|
||||
This is a research project and quantum computers are not thought to become
|
||||
practical in fewer than ten years.
|
||||
If you are not specifically tasked with developing post-quantum secure systems,
|
||||
you probably do not need this tool.
|
||||
.Ss COMMANDS
|
||||
.Bl -tag -width Ds
|
||||
.It Ar gen-keys --secret-key <file-path> --public-key <file-path>
|
||||
Generate a keypair to use in the exchange command later.
|
||||
Send the public-key file to your communication partner and keep the private-key
|
||||
file secret!
|
||||
.It Ar exchange private-key <file-path> public-key <file-path> [ OPTIONS ] PEERS
|
||||
Start a process to exchange keys with the specified peers.
|
||||
You should specify at least one peer.
|
||||
.Pp
|
||||
Its
|
||||
.Ar OPTIONS
|
||||
are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Ar listen <ip>[:<port>]
|
||||
Instructs
|
||||
.Nm
|
||||
to listen on the specified interface and port.
|
||||
By default,
|
||||
.Nm
|
||||
will listen on all interfaces and select a random port.
|
||||
.It Ar verbose
|
||||
Extra logging.
|
||||
.El
|
||||
.El
|
||||
.Ss PEER
|
||||
Each
|
||||
.Ar PEER
|
||||
is defined as follows:
|
||||
.Qq peer public-key <file-path> [endpoint <ip>[:<port>]] [preshared-key <file-path>] [outfile <file-path>] [wireguard <dev> <peer> <extra_params>]
|
||||
.Pp
|
||||
Providing a
|
||||
.Ar PEER
|
||||
instructs
|
||||
.Nm
|
||||
to exchange keys with the given peer and write the resulting PSK into the given
|
||||
output file.
|
||||
You must either specify the outfile or wireguard output option.
|
||||
.Pp
|
||||
The parameters of
|
||||
.Ar PEER
|
||||
are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Ar endpoint <ip>[:<port>]
|
||||
Specifies the address where the peer can be reached.
|
||||
This will be automatically updated after the first successful key exchange with
|
||||
the peer.
|
||||
If this is unspecified, the peer must initiate the connection.
|
||||
.It Ar preshared-key <file-path>
|
||||
You may specify a pre-shared key which will be mixed into the final secret.
|
||||
.It Ar outfile <file-path>
|
||||
You may specify a file to write the exchanged keys to.
|
||||
If this option is specified,
|
||||
.Nm
|
||||
will write a notification to standard out every time the key is updated.
|
||||
.It Ar wireguard <dev> <peer> <extra_params>
|
||||
This allows you to directly specify a wireguard peer to deploy the
|
||||
pre-shared-key to.
|
||||
You may specify extra parameters you would pass to
|
||||
.Qq wg set
|
||||
besides the preshared-key parameter which is used by
|
||||
.Nm .
|
||||
This makes it possible to add peers entirely from
|
||||
.Nm .
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh SEE ALSO
|
||||
.Xr rp 1 ,
|
||||
.Xr wg 1
|
||||
.Rs
|
||||
.%A Karolin Varner
|
||||
.%A Benjamin Lipp
|
||||
.%A Wanja Zaeske
|
||||
.%A Lisa Schmidt
|
||||
.%D 2023
|
||||
.%T Rosenpass
|
||||
.%U https://rosenpass.eu/whitepaper.pdf
|
||||
.Re
|
||||
.Sh STANDARDS
|
||||
This tool is the reference implementation of the Rosenpass protocol, as
|
||||
specified within the whitepaper referenced above.
|
||||
.Sh AUTHORS
|
||||
Rosenpass was created by Karolin Varner, Benjamin Lipp, Wanja Zaeske,
|
||||
Marei Peischl, Stephan Ajuvo, and Lisa Schmidt.
|
||||
.Pp
|
||||
This manual page was written by
|
||||
.An Clara Engler
|
||||
.Sh BUGS
|
||||
The bugs are tracked at
|
||||
.Lk https://github.com/rosenpass/rosenpass/issues .
|
||||
119
doc/rp.1
Normal file
119
doc/rp.1
Normal file
@@ -0,0 +1,119 @@
|
||||
.Dd $Mdocdate$
|
||||
.Dt RP 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm rp
|
||||
.Nd high-level interface to rosenpass
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Ar explain
|
||||
.Op Ar verbose
|
||||
.Ar genkey Ar ... | Ar pubkey ... | Ar exchange ...
|
||||
.Nm
|
||||
.Op ...
|
||||
.Ar genkey PRIVATE_KEYS_DIR
|
||||
.Nm
|
||||
.Op ...
|
||||
.Ar pubkey Ar PRIVATE_KEYS_DIR Ar PUBLIC_KEYS_DIR
|
||||
.Nm
|
||||
.Op ...
|
||||
.\" Splitting this across several lines
|
||||
.Ar exchange Ar PRIVATE_KEYS_DIR
|
||||
.Op dev <device>
|
||||
.Op listen <ip>:<port>
|
||||
.\" Because the peer argument is complicated, it would be heel to represent it
|
||||
.\" in mdoc... Using an ugly hack instead, thereby losing semantic.
|
||||
[peer PUBLIC_KEYS_DIR [endpoint <ip>:<port>] [persistent-keepalive <interval>]
|
||||
[allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>] ...]] ...
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program
|
||||
is used to build a VPN with WireGuard and Rosenpass.
|
||||
.Pp
|
||||
The optional
|
||||
.Op explain
|
||||
and
|
||||
.Op verbose
|
||||
options can be used to obtain further help or to enable a detailed view on the
|
||||
operations, respectively.
|
||||
.Ss COMMANDS
|
||||
.Bl -tag -width Ds
|
||||
.It Ar genkey Ar PRIVATE_KEYS_DIR
|
||||
Creates a new directory with appropriate permissions and generates all the
|
||||
necessary private keys required for a peer to participate in a rosenpass
|
||||
connection.
|
||||
.It Ar pubkey Ar PRIVATE_KEYS_DIR Ar PUBLIC_KEYS_DIR
|
||||
Creates a fresh directory at
|
||||
.Ar PUBLIC_KEYS_DIR ,
|
||||
which contains the extracted public keys from the private keys generated by
|
||||
.Ar genkey
|
||||
and located inside
|
||||
.Ar PRIVATE_KEYS_DIR .
|
||||
.It Ar exchange Ar PRIVATE_KEYS_DIR [dev <device>] [listen <ip>:<port>] [PEERS]
|
||||
Starts the VPN on interface
|
||||
.Ar device ,
|
||||
listening on the provided IP and port combination, allowing connections from
|
||||
.Ar PEERS .
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh EXAMPLES
|
||||
In this example, we will assume that the server has an interface bound to
|
||||
192.168.0.1, that accepts incoming connections on port 9999/UDP for Rosenpass
|
||||
and port 10000/UDP for WireGuard.
|
||||
.Pp
|
||||
To create a VPN connection, start by generating secret keys on both hosts.
|
||||
.Bd -literal -offset indent
|
||||
rp genkey server.rosenpass-secret
|
||||
rp genkey client.rosenpass-secret
|
||||
.Ed
|
||||
.Pp
|
||||
Extract the public keys:
|
||||
.Bd -literal -offset indent
|
||||
rp pubkey server.rosenpass-secret server.rosenpass-public
|
||||
rp pubkey client.rosenpass-secret client.rosenpass-public
|
||||
.Ed
|
||||
.Pp
|
||||
Copy the
|
||||
.Qq -public
|
||||
directories to the other peers and then start the VPN.
|
||||
On the server:
|
||||
.Bd -literal -offset indent
|
||||
sudo rp exchange server.rosenpass-secret dev rosenpass0 listen 192.168.0.1:9999 \\
|
||||
peer client.rosenpass-public allowed-ips fe80::/64
|
||||
.Ed
|
||||
.Pp
|
||||
On the client:
|
||||
.Bd -literal -offset indent
|
||||
sudo rp exchange client.rosenpass-secret dev rosenpass 0 \\
|
||||
peer server.rosenpass-public endpoint 192.168.0.1:9999 allowed-ips fe80::/64
|
||||
.Ed
|
||||
.Pp
|
||||
Assign IP addresses:
|
||||
.Bd -literal -offset indent
|
||||
sudo ip a add fe80::1/64 dev rosenpass0 # Server
|
||||
sudo ip a add fe80::2/64 dev rosenpass0 # Client
|
||||
.Ed
|
||||
.Pp
|
||||
Test the connection by pinging the server on the client machine:
|
||||
.Bd -literal -offset indent
|
||||
ping fe80::1%rosenpass0 # Client
|
||||
.Ed
|
||||
.Pp
|
||||
You can watch how rosenpass replaces the WireGuard PSK with the following:
|
||||
.Bd -literal -offset indent
|
||||
watch -n 0.2 'wg show all; wg show all preshared-keys'
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr rosenpass 1 ,
|
||||
.Xr wg 1
|
||||
.Sh AUTHORS
|
||||
Rosenpass was created by Karolin Varner, Benjamin Lipp, Wanja Zaeske,
|
||||
Marei Peischl, Stephan Ajuvo, and Lisa Schmidt.
|
||||
.Pp
|
||||
This manual page was written by
|
||||
.An Clara Engler
|
||||
.Sh BUGS
|
||||
The bugs are tracked at
|
||||
.Lk https://github.com/rosenpass/rosenpass/issues .
|
||||
83
flake.lock
generated
83
flake.lock
generated
@@ -2,17 +2,15 @@
|
||||
"nodes": {
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs": ["nixpkgs"],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1674240251,
|
||||
"narHash": "sha256-AVMmf/CtcGensTZmMicToDpOwySEGNKYgRPC7lu3m8w=",
|
||||
"lastModified": 1712298178,
|
||||
"narHash": "sha256-590fpCPXYAkaAeBz/V91GX4/KGzPObdYtqsTWzT6AhI=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "d8067f4d1d3d30732703209bec5ca7d62aaececc",
|
||||
"rev": "569b5b5781395da08e7064e825953c548c26af76",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -22,12 +20,15 @@
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -36,31 +37,36 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": ["nixpkgs"]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1698420672,
|
||||
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1672968032,
|
||||
"narHash": "sha256-26Jns3GmHem44a06UN5Rj/KOD9qNJThyQrom02Ijur8=",
|
||||
"lastModified": 1712168706,
|
||||
"narHash": "sha256-XP24tOobf6GGElMd0ux90FEBalUtw6NkBSVh/RlA6ik=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2dea8991d89b9f1e78d874945f78ca15f6954289",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1676496762,
|
||||
"narHash": "sha256-GFAxjaTgh8KJ8q7BYaI4EVGI5K98ooW70fG/83rSb08=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1bddde315297c092712b0ef03d9def7a474b28ae",
|
||||
"rev": "1487bdea619e4a7a53a4590c475deabb5a9d1bfb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -69,18 +75,18 @@
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable"
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1674162026,
|
||||
"narHash": "sha256-iY0bxoVE7zAZmp0BB/m5hZW5pWHUfgntDvc1m2zyt/U=",
|
||||
"lastModified": 1712156296,
|
||||
"narHash": "sha256-St7ZQrkrr5lmQX9wC1ZJAFxL8W7alswnyZk9d1se3Us=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "6e52c64031825920983515b9e975e93232739f7f",
|
||||
"rev": "8e581ac348e223488622f4d3003cb2bd412bf27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -89,6 +95,21 @@
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
358
flake.nix
358
flake.nix
@@ -1,8 +1,12 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs-unstable.url = "github:NixOS/nixpkgs";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
# for quicker rust builds
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
naersk.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
# for rust nightly with llvm-tools-preview
|
||||
fenix.url = "github:nix-community/fenix";
|
||||
fenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
@@ -19,103 +23,265 @@
|
||||
"aarch64-linux"
|
||||
|
||||
# unsuported best-effort
|
||||
"i686-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
# "x86_64-windows"
|
||||
]
|
||||
(system:
|
||||
let
|
||||
scoped = (scope: scope.result);
|
||||
lib = nixpkgs.lib;
|
||||
|
||||
# normal nixpkgs
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
# TODO remove overlay once a fix for
|
||||
# https://github.com/NixOS/nixpkgs/issues/216904 got merged
|
||||
overlays = [
|
||||
(
|
||||
final: prev: {
|
||||
iproute2 = prev.iproute2.overrideAttrs (old:
|
||||
let
|
||||
isStatic = prev.stdenv.hostPlatform.isStatic;
|
||||
in
|
||||
{
|
||||
makeFlags = old.makeFlags ++ prev.lib.optional isStatic [
|
||||
"TC_CONFIG_NO_XT=y"
|
||||
];
|
||||
});
|
||||
}
|
||||
)
|
||||
];
|
||||
};
|
||||
|
||||
# parsed Cargo.toml
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./rosenpass/Cargo.toml);
|
||||
|
||||
# source files relevant for rust
|
||||
src = pkgs.lib.sourceByRegex ./. [
|
||||
"Cargo\\.(toml|lock)"
|
||||
"(src|benches)(/.*\\.(rs|md))?"
|
||||
"rp"
|
||||
];
|
||||
# builds a bin path for all dependencies for the `rp` shellscript
|
||||
rpBinPath = p: with p; lib.makeBinPath [
|
||||
coreutils
|
||||
findutils
|
||||
gawk
|
||||
wireguard-tools
|
||||
];
|
||||
src = scoped rec {
|
||||
# File suffices to include
|
||||
extensions = [
|
||||
"lock"
|
||||
"rs"
|
||||
"toml"
|
||||
];
|
||||
# Files to explicitly include
|
||||
files = [
|
||||
"to/README.md"
|
||||
];
|
||||
|
||||
src = ./.;
|
||||
filter = (path: type: scoped rec {
|
||||
inherit (lib) any id removePrefix hasSuffix;
|
||||
anyof = (any id);
|
||||
|
||||
basename = baseNameOf (toString path);
|
||||
relative = removePrefix (toString src + "/") (toString path);
|
||||
|
||||
result = anyof [
|
||||
(type == "directory")
|
||||
(any (ext: hasSuffix ".${ext}" basename) extensions)
|
||||
(any (file: file == relative) files)
|
||||
];
|
||||
});
|
||||
|
||||
result = pkgs.lib.sources.cleanSourceWith { inherit src filter; };
|
||||
};
|
||||
|
||||
# a function to generate a nix derivation for rosenpass against any
|
||||
# given set of nixpkgs
|
||||
rosenpassDerivation = p:
|
||||
let
|
||||
# whether we want to build a statically linked binary
|
||||
isStatic = p.targetPlatform.isStatic;
|
||||
|
||||
# the rust target of `p`
|
||||
target = p.rust.toRustTargetSpec p.targetPlatform;
|
||||
|
||||
# convert a string to shout case
|
||||
shout = string: builtins.replaceStrings [ "-" ] [ "_" ] (pkgs.lib.toUpper string);
|
||||
|
||||
# suitable Rust toolchain
|
||||
toolchain = with inputs.fenix.packages.${system}; combine [
|
||||
stable.cargo
|
||||
stable.rustc
|
||||
targets.${target}.stable.rust-std
|
||||
];
|
||||
|
||||
# naersk with a custom toolchain
|
||||
naersk = pkgs.callPackage inputs.naersk {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
|
||||
# used to trick the build.rs into believing that CMake was ran **again**
|
||||
fakecmake = pkgs.writeScriptBin "cmake" ''
|
||||
#! ${pkgs.stdenv.shell} -e
|
||||
true
|
||||
'';
|
||||
in
|
||||
naersk.buildPackage
|
||||
{
|
||||
# metadata and source
|
||||
name = cargoToml.package.name;
|
||||
version = cargoToml.package.version;
|
||||
inherit src;
|
||||
|
||||
cargoBuildOptions = x: x ++ [ "-p" "rosenpass" ];
|
||||
cargoTestOptions = x: x ++ [ "-p" "rosenpass" ];
|
||||
|
||||
doCheck = true;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
p.stdenv.cc
|
||||
cmake # for oqs build in the oqs-sys crate
|
||||
mandoc # for the built-in manual
|
||||
removeReferencesTo
|
||||
rustPlatform.bindgenHook # for C-bindings in the crypto libs
|
||||
];
|
||||
buildInputs = with p; [ bash ];
|
||||
|
||||
override = x: {
|
||||
preBuild =
|
||||
# nix defaults to building for aarch64 _without_ the armv8-a crypto
|
||||
# extensions, but liboqs depens on these
|
||||
(lib.optionalString (system == "aarch64-linux") ''
|
||||
NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -march=armv8-a+crypto"
|
||||
''
|
||||
);
|
||||
|
||||
# fortify is only compatible with dynamic linking
|
||||
hardeningDisable = lib.optional isStatic "fortify";
|
||||
};
|
||||
|
||||
overrideMain = x: {
|
||||
# CMake detects that it was served a _foreign_ target dir, and CMake
|
||||
# would be executed again upon the second build step of naersk.
|
||||
# By adding our specially optimized CMake version, we reduce the cost
|
||||
# of recompilation by 99 % while, while avoiding any CMake errors.
|
||||
nativeBuildInputs = [ (lib.hiPrio fakecmake) ] ++ x.nativeBuildInputs;
|
||||
|
||||
# make sure that libc is linked, under musl this is not the case per
|
||||
# default
|
||||
preBuild = (lib.optionalString isStatic ''
|
||||
NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -lc"
|
||||
'');
|
||||
};
|
||||
|
||||
# We want to build for a specific target...
|
||||
CARGO_BUILD_TARGET = target;
|
||||
|
||||
# ... which might require a non-default linker:
|
||||
"CARGO_TARGET_${shout target}_LINKER" =
|
||||
let
|
||||
inherit (p.stdenv) cc;
|
||||
in
|
||||
"${cc}/bin/${cc.targetPrefix}cc";
|
||||
|
||||
meta = with pkgs.lib;
|
||||
{
|
||||
inherit (cargoToml.package) description homepage;
|
||||
license = with licenses; [ mit asl20 ];
|
||||
maintainers = [ maintainers.wucke13 ];
|
||||
platforms = platforms.all;
|
||||
};
|
||||
} // (lib.mkIf isStatic {
|
||||
# otherwise pkg-config tries to link non-existent dynamic libs
|
||||
# documented here: https://docs.rs/pkg-config/latest/pkg_config/
|
||||
PKG_CONFIG_ALL_STATIC = true;
|
||||
|
||||
# tell rust to build everything statically linked
|
||||
CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static";
|
||||
});
|
||||
# a function to generate a nix derivation for the rp helper against any
|
||||
# given set of nixpkgs
|
||||
rpDerivation = p:
|
||||
let
|
||||
isStatic = p.stdenv.hostPlatform.isStatic;
|
||||
in
|
||||
p.rustPlatform.buildRustPackage {
|
||||
# metadata and source
|
||||
pname = cargoToml.package.name;
|
||||
version = cargoToml.package.version;
|
||||
inherit src;
|
||||
cargoLock = {
|
||||
lockFile = src + "/Cargo.lock";
|
||||
# whether we want to build a statically linked binary
|
||||
isStatic = p.targetPlatform.isStatic;
|
||||
|
||||
# the rust target of `p`
|
||||
target = p.rust.toRustTargetSpec p.targetPlatform;
|
||||
|
||||
# convert a string to shout case
|
||||
shout = string: builtins.replaceStrings [ "-" ] [ "_" ] (pkgs.lib.toUpper string);
|
||||
|
||||
# suitable Rust toolchain
|
||||
toolchain = with inputs.fenix.packages.${system}; combine [
|
||||
stable.cargo
|
||||
stable.rustc
|
||||
targets.${target}.stable.rust-std
|
||||
];
|
||||
|
||||
# naersk with a custom toolchain
|
||||
naersk = pkgs.callPackage inputs.naersk {
|
||||
cargo = toolchain;
|
||||
rustc = toolchain;
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake # for oqs build in the oqs-sys crate
|
||||
makeWrapper # for the rp shellscript
|
||||
pkg-config # let libsodium-sys-stable find libsodium
|
||||
removeReferencesTo
|
||||
rustPlatform.bindgenHook # for C-bindings in the crypto libs
|
||||
];
|
||||
buildInputs = with p; [ bash libsodium ];
|
||||
# used to trick the build.rs into believing that CMake was ran **again**
|
||||
fakecmake = pkgs.writeScriptBin "cmake" ''
|
||||
#! ${pkgs.stdenv.shell} -e
|
||||
true
|
||||
'';
|
||||
in
|
||||
naersk.buildPackage
|
||||
{
|
||||
# metadata and source
|
||||
name = cargoToml.package.name;
|
||||
version = cargoToml.package.version;
|
||||
inherit src;
|
||||
|
||||
cargoBuildOptions = x: x ++ [ "-p" "rp" ];
|
||||
cargoTestOptions = x: x ++ [ "-p" "rp" ];
|
||||
|
||||
doCheck = true;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
p.stdenv.cc
|
||||
cmake # for oqs build in the oqs-sys crate
|
||||
mandoc # for the built-in manual
|
||||
removeReferencesTo
|
||||
rustPlatform.bindgenHook # for C-bindings in the crypto libs
|
||||
];
|
||||
buildInputs = with p; [ bash ];
|
||||
|
||||
override = x: {
|
||||
preBuild =
|
||||
# nix defaults to building for aarch64 _without_ the armv8-a crypto
|
||||
# extensions, but liboqs depens on these
|
||||
(lib.optionalString (system == "aarch64-linux") ''
|
||||
NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -march=armv8-a+crypto"
|
||||
''
|
||||
);
|
||||
|
||||
# fortify is only compatible with dynamic linking
|
||||
hardeningDisable = lib.optional isStatic "fortify";
|
||||
};
|
||||
|
||||
overrideMain = x: {
|
||||
# CMake detects that it was served a _foreign_ target dir, and CMake
|
||||
# would be executed again upon the second build step of naersk.
|
||||
# By adding our specially optimized CMake version, we reduce the cost
|
||||
# of recompilation by 99 % while, while avoiding any CMake errors.
|
||||
nativeBuildInputs = [ (lib.hiPrio fakecmake) ] ++ x.nativeBuildInputs;
|
||||
|
||||
# make sure that libc is linked, under musl this is not the case per
|
||||
# default
|
||||
preBuild = (lib.optionalString isStatic ''
|
||||
NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -lc"
|
||||
'');
|
||||
};
|
||||
|
||||
# We want to build for a specific target...
|
||||
CARGO_BUILD_TARGET = target;
|
||||
|
||||
# ... which might require a non-default linker:
|
||||
"CARGO_TARGET_${shout target}_LINKER" =
|
||||
let
|
||||
inherit (p.stdenv) cc;
|
||||
in
|
||||
"${cc}/bin/${cc.targetPrefix}cc";
|
||||
|
||||
meta = with pkgs.lib;
|
||||
{
|
||||
inherit (cargoToml.package) description homepage;
|
||||
license = with licenses; [ mit asl20 ];
|
||||
maintainers = [ maintainers.wucke13 ];
|
||||
platforms = platforms.all;
|
||||
};
|
||||
} // (lib.mkIf isStatic {
|
||||
# otherwise pkg-config tries to link non-existent dynamic libs
|
||||
# documented here: https://docs.rs/pkg-config/latest/pkg_config/
|
||||
PKG_CONFIG_ALL_STATIC = true;
|
||||
|
||||
# nix defaults to building for aarch64 _without_ the armv8-a
|
||||
# crypto extensions, but liboqs depens on these
|
||||
preBuild =
|
||||
if system == "aarch64-linux" then ''
|
||||
NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -march=armv8-a+crypto"
|
||||
'' else "";
|
||||
|
||||
preInstall = ''
|
||||
install -D rp $out/bin/rp
|
||||
wrapProgram $out/bin/rp --prefix PATH : "${ rpBinPath p }"
|
||||
'';
|
||||
|
||||
# nix progated the *.dev outputs of buildInputs for static
|
||||
# builds, but that is non-sense for an executables only package
|
||||
postFixup =
|
||||
if isStatic then ''
|
||||
remove-references-to -t ${p.bash.dev} -t ${p.libsodium.dev} \
|
||||
$out/nix-support/propagated-build-inputs
|
||||
'' else "";
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
inherit (cargoToml.package) description homepage;
|
||||
license = with licenses; [ mit asl20 ];
|
||||
maintainers = [ maintainers.wucke13 ];
|
||||
platforms = platforms.all;
|
||||
};
|
||||
};
|
||||
# tell rust to build everything statically linked
|
||||
CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static";
|
||||
});
|
||||
# a function to generate a docker image based of rosenpass
|
||||
rosenpassOCI = name: pkgs.dockerTools.buildImage rec {
|
||||
inherit name;
|
||||
@@ -130,7 +296,8 @@
|
||||
rec {
|
||||
packages = rec {
|
||||
default = rosenpass;
|
||||
rosenpass = rpDerivation pkgs;
|
||||
rosenpass = rosenpassDerivation pkgs;
|
||||
rp = rpDerivation pkgs;
|
||||
rosenpass-oci-image = rosenpassOCI "rosenpass";
|
||||
|
||||
# derivation for the release
|
||||
@@ -141,6 +308,10 @@
|
||||
if pkgs.hostPlatform.isLinux then
|
||||
packages.rosenpass-static
|
||||
else packages.rosenpass;
|
||||
rp =
|
||||
if pkgs.hostPlatform.isLinux then
|
||||
packages.rp-static
|
||||
else packages.rp;
|
||||
oci-image =
|
||||
if pkgs.hostPlatform.isLinux then
|
||||
packages.rosenpass-static-oci-image
|
||||
@@ -149,14 +320,15 @@
|
||||
pkgs.runCommandNoCC "lace-result" { }
|
||||
''
|
||||
mkdir {bin,$out}
|
||||
cp ${./.}/rp bin/
|
||||
tar -cvf $out/rosenpass-${system}-${version}.tar bin/rp \
|
||||
-C ${package} bin/rosenpass
|
||||
tar -cvf $out/rosenpass-${system}-${version}.tar \
|
||||
-C ${package} bin/rosenpass \
|
||||
-C ${rp} bin/rp
|
||||
cp ${oci-image} \
|
||||
$out/rosenpass-oci-image-${system}-${version}.tar.gz
|
||||
'';
|
||||
} // (if pkgs.stdenv.isLinux then rec {
|
||||
rosenpass-static = rpDerivation pkgs.pkgsStatic;
|
||||
rosenpass-static = rosenpassDerivation pkgs.pkgsStatic;
|
||||
rp-static = rpDerivation pkgs.pkgsStatic;
|
||||
rosenpass-static-oci-image = rosenpassOCI "rosenpass-static";
|
||||
} else { });
|
||||
}
|
||||
@@ -178,14 +350,11 @@
|
||||
#
|
||||
packages.whitepaper =
|
||||
let
|
||||
pkgs = import inputs.nixpkgs-unstable {
|
||||
inherit system;
|
||||
};
|
||||
tlsetup = (pkgs.texlive.combine {
|
||||
inherit (pkgs.texlive) scheme-basic acmart amsfonts ccicons
|
||||
csquotes csvsimple doclicense fancyvrb fontspec gobble
|
||||
koma-script ifmtarg latexmk lm markdown mathtools minted noto
|
||||
nunito pgf soul soulutf8 unicode-math lualatex-math
|
||||
nunito pgf soul unicode-math lualatex-math paralist
|
||||
gitinfo2 eso-pic biblatex biblatex-trad biblatex-software
|
||||
xkeyval xurl xifthen biber;
|
||||
});
|
||||
@@ -201,7 +370,6 @@
|
||||
];
|
||||
buildPhase = ''
|
||||
export HOME=$(mktemp -d)
|
||||
export OSFONTDIR="$(kpsewhich --var-value TEXMF)/fonts/{opentype/public/nunito,truetype/google/noto}"
|
||||
latexmk -r tex/CI.rc
|
||||
'';
|
||||
installPhase = ''
|
||||
@@ -222,7 +390,7 @@
|
||||
packages.proof-proverif = pkgs.stdenv.mkDerivation {
|
||||
name = "rosenpass-proverif-proof";
|
||||
version = "unstable";
|
||||
src = pkgs.lib.sourceByRegex ./. [
|
||||
src = pkgs.lib.sources.sourceByRegex ./. [
|
||||
"analyze.sh"
|
||||
"marzipan(/marzipan.awk)?"
|
||||
"analysis(/.*)?"
|
||||
@@ -243,10 +411,12 @@
|
||||
inherit (packages.proof-proverif) CRYPTOVERIF_LIB;
|
||||
inputsFrom = [ packages.default ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
inputs.fenix.packages.${system}.complete.toolchain
|
||||
cmake # override the fakecmake from the main step above
|
||||
cargo-release
|
||||
clippy
|
||||
nodePackages.prettier
|
||||
rustfmt
|
||||
nushell # for the .ci/gen-workflow-files.nu script
|
||||
packages.proverif-patched
|
||||
];
|
||||
};
|
||||
@@ -257,12 +427,10 @@
|
||||
|
||||
|
||||
checks = {
|
||||
# Blocked by https://github.com/rust-lang/rustfmt/issues/4306
|
||||
# @dakoraa wants a coding style suitable for her accessible coding setup
|
||||
# cargo-fmt = pkgs.runCommand "check-cargo-fmt"
|
||||
# { inherit (devShells.default) nativeBuildInputs buildInputs; } ''
|
||||
# cargo fmt --manifest-path=${src}/Cargo.toml --check > $out
|
||||
# '';
|
||||
cargo-fmt = pkgs.runCommand "check-cargo-fmt"
|
||||
{ inherit (self.devShells.${system}.default) nativeBuildInputs buildInputs; } ''
|
||||
cargo fmt --manifest-path=${./.}/Cargo.toml --check --all && touch $out
|
||||
'';
|
||||
nixpkgs-fmt = pkgs.runCommand "check-nixpkgs-fmt"
|
||||
{ nativeBuildInputs = [ pkgs.nixpkgs-fmt ]; } ''
|
||||
nixpkgs-fmt --check ${./.} && touch $out
|
||||
@@ -272,6 +440,8 @@
|
||||
cd ${./.} && prettier --check . && touch $out
|
||||
'';
|
||||
};
|
||||
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
}))
|
||||
];
|
||||
}
|
||||
|
||||
115
format_rust_code.sh
Executable file
115
format_rust_code.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Parse command line options
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--mode)
|
||||
mode="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if mode is specified
|
||||
if [ -z "$mode" ]; then
|
||||
echo "Please specify the mode using --mode option. Valid modes are 'check' and 'fix'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find all Markdown files in the current directory and its subdirectories
|
||||
mapfile -t md_files < <(find . -type f -name "*.md")
|
||||
|
||||
count=0
|
||||
# Iterate through each Markdown file
|
||||
for file in "${md_files[@]}"; do
|
||||
# Use awk to extract Rust code blocks enclosed within triple backticks
|
||||
rust_code_blocks=$(awk '/```rust/{flag=1; next}/```/{flag=0} flag' "$file")
|
||||
|
||||
# Count the number of Rust code blocks
|
||||
num_fences=$(awk '/```rust/{f=1} f{if(/```/){f=0; count++}} END{print count}' "$file")
|
||||
|
||||
if [ -n "$rust_code_blocks" ]; then
|
||||
echo "Processing Rust code in $file"
|
||||
# Iterate through each Rust code block
|
||||
for ((i=1; i <= num_fences ; i++)); do
|
||||
# Extract individual Rust code block using awk
|
||||
current_rust_block=$(awk -v i="$i" '/```rust/{f=1; if (++count == i) next} f&&/```/{f=0;next} f' "$file")
|
||||
# Variable to check if we have added the main function
|
||||
add_main=0
|
||||
# Check if the Rust code block is already inside a function
|
||||
if ! echo "$current_rust_block" | grep -q "fn main()"; then
|
||||
# If not, wrap it in a main function
|
||||
current_rust_block=$'fn main() {\n'"$current_rust_block"$'\n}'
|
||||
add_main=1
|
||||
fi
|
||||
if [ "$mode" == "check" ]; then
|
||||
# Apply changes to the Rust code block
|
||||
formatted_rust_code=$(echo "$current_rust_block" | rustfmt)
|
||||
# Use rustfmt to format the Rust code block, remove first and last lines, and remove the first 4 spaces if added main function
|
||||
if [ "$add_main" == 1 ]; then
|
||||
formatted_rust_code=$(echo "$formatted_rust_code" | sed '1d;$d' | sed 's/^ //')
|
||||
current_rust_block=$(echo "$current_rust_block" | sed '1d;')
|
||||
current_rust_block=$(echo "$current_rust_block" | sed '$d')
|
||||
fi
|
||||
if [ "$formatted_rust_code" == "$current_rust_block" ]; then
|
||||
echo "No changes needed in Rust code block $i in $file"
|
||||
else
|
||||
echo -e "\nChanges needed in Rust code block $i in $file:\n"
|
||||
echo "$formatted_rust_code"
|
||||
count=+1
|
||||
fi
|
||||
|
||||
elif [ "$mode" == "fix" ]; then
|
||||
# Replace current_rust_block with formatted_rust_code in the file
|
||||
formatted_rust_code=$(echo "$current_rust_block" | rustfmt)
|
||||
# Use rustfmt to format the Rust code block, remove first and last lines, and remove the first 4 spaces if added main function
|
||||
if [ "$add_main" == 1 ]; then
|
||||
formatted_rust_code=$(echo "$formatted_rust_code" | sed '1d;$d' | sed 's/^ //')
|
||||
current_rust_block=$(echo "$current_rust_block" | sed '1d;')
|
||||
current_rust_block=$(echo "$current_rust_block" | sed '$d')
|
||||
fi
|
||||
# Check if the formatted code is the same as the current Rust code block
|
||||
if [ "$formatted_rust_code" == "$current_rust_block" ]; then
|
||||
echo "No changes needed in Rust code block $i in $file"
|
||||
else
|
||||
echo "Formatting Rust code block $i in $file"
|
||||
# Replace current_rust_block with formatted_rust_code in the file
|
||||
# Use awk to find the line number of the pattern
|
||||
|
||||
start_line=$(grep -n "^\`\`\`rust" "$file" | sed -n "${i}p" | cut -d: -f1)
|
||||
end_line=$(grep -n "^\`\`\`" "$file" | awk -F: -v start_line="$start_line" '$1 > start_line {print $1; exit;}')
|
||||
|
||||
if [ -n "$start_line" ] && [ -n "$end_line" ]; then
|
||||
# Print lines before the Rust code block
|
||||
head -n "$((start_line - 1))" "$file"
|
||||
|
||||
# Print the formatted Rust code block
|
||||
echo "\`\`\`rust"
|
||||
echo "$formatted_rust_code"
|
||||
echo "\`\`\`"
|
||||
|
||||
# Print lines after the Rust code block
|
||||
tail -n +"$((end_line + 1))" "$file"
|
||||
else
|
||||
# Rust code block not found or end line not found
|
||||
cat "$file"
|
||||
fi > tmpfile && mv tmpfile "$file"
|
||||
|
||||
fi
|
||||
else
|
||||
echo "Unknown mode: $mode. Valid modes are 'check' and 'fix'."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# CI failure if changes are needed
|
||||
if [ $count -gt 0 ]; then
|
||||
echo "CI failed: Changes needed in Rust code blocks."
|
||||
exit 1
|
||||
fi
|
||||
4
fuzz/.gitignore
vendored
Normal file
4
fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
target
|
||||
corpus
|
||||
artifacts
|
||||
coverage
|
||||
1286
fuzz/Cargo.lock
generated
Normal file
1286
fuzz/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
87
fuzz/Cargo.toml
Normal file
87
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,87 @@
|
||||
[package]
|
||||
name = "rosenpass-fuzzing"
|
||||
version = "0.0.1"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
arbitrary = { workspace = true }
|
||||
libfuzzer-sys = { workspace = true }
|
||||
stacker = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-ciphers = { workspace = true }
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_handle_msg"
|
||||
path = "fuzz_targets/handle_msg.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_blake2b"
|
||||
path = "fuzz_targets/blake2b.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_aead_enc_into"
|
||||
path = "fuzz_targets/aead_enc_into.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_mceliece_encaps"
|
||||
path = "fuzz_targets/mceliece_encaps.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_kyber_encaps"
|
||||
path = "fuzz_targets/kyber_encaps.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_box_secret_alloc_malloc"
|
||||
path = "fuzz_targets/box_secret_alloc_malloc.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_vec_secret_alloc_malloc"
|
||||
path = "fuzz_targets/vec_secret_alloc_malloc.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_box_secret_alloc_memfdsec"
|
||||
path = "fuzz_targets/box_secret_alloc_memfdsec.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_vec_secret_alloc_memfdsec"
|
||||
path = "fuzz_targets/vec_secret_alloc_memfdsec.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_box_secret_alloc_memfdsec_mallocfb"
|
||||
path = "fuzz_targets/box_secret_alloc_memfdsec_mallocfb.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_vec_secret_alloc_memfdsec_mallocfb"
|
||||
path = "fuzz_targets/vec_secret_alloc_memfdsec_mallocfb.rs"
|
||||
test = false
|
||||
doc = false
|
||||
28
fuzz/fuzz_targets/aead_enc_into.rs
Normal file
28
fuzz/fuzz_targets/aead_enc_into.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
#![no_main]
|
||||
extern crate arbitrary;
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_ciphers::aead;
|
||||
|
||||
#[derive(arbitrary::Arbitrary, Debug)]
|
||||
pub struct Input {
|
||||
pub key: [u8; 32],
|
||||
pub nonce: [u8; 12],
|
||||
pub ad: Box<[u8]>,
|
||||
pub plaintext: Box<[u8]>,
|
||||
}
|
||||
|
||||
fuzz_target!(|input: Input| {
|
||||
let mut ciphertext = vec![0u8; input.plaintext.len() + 16];
|
||||
|
||||
aead::encrypt(
|
||||
ciphertext.as_mut_slice(),
|
||||
&input.key,
|
||||
&input.nonce,
|
||||
&input.ad,
|
||||
&input.plaintext,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
20
fuzz/fuzz_targets/blake2b.rs
Normal file
20
fuzz/fuzz_targets/blake2b.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
extern crate arbitrary;
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_ciphers::subtle::blake2b;
|
||||
use rosenpass_to::To;
|
||||
|
||||
#[derive(arbitrary::Arbitrary, Debug)]
|
||||
pub struct Blake2b {
|
||||
pub key: [u8; 32],
|
||||
pub data: Box<[u8]>,
|
||||
}
|
||||
|
||||
fuzz_target!(|input: Blake2b| {
|
||||
let mut out = [0u8; 32];
|
||||
|
||||
blake2b::hash(&input.key, &input.data).to(&mut out).unwrap();
|
||||
});
|
||||
12
fuzz/fuzz_targets/box_secret_alloc_malloc.rs
Normal file
12
fuzz/fuzz_targets/box_secret_alloc_malloc.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_secret_memory::alloc::secret_box;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
use std::sync::Once;
|
||||
static ONCE: Once = Once::new();
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
ONCE.call_once(secret_policy_use_only_malloc_secrets);
|
||||
let _ = secret_box(data);
|
||||
});
|
||||
13
fuzz/fuzz_targets/box_secret_alloc_memfdsec.rs
Normal file
13
fuzz/fuzz_targets/box_secret_alloc_memfdsec.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_secret_memory::alloc::secret_box;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
use std::sync::Once;
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
ONCE.call_once(secret_policy_use_only_memfd_secrets);
|
||||
let _ = secret_box(data);
|
||||
});
|
||||
13
fuzz/fuzz_targets/box_secret_alloc_memfdsec_mallocfb.rs
Normal file
13
fuzz/fuzz_targets/box_secret_alloc_memfdsec_mallocfb.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_secret_memory::alloc::secret_box;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
use std::sync::Once;
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
ONCE.call_once(secret_policy_try_use_memfd_secrets);
|
||||
let _ = secret_box(data);
|
||||
});
|
||||
24
fuzz/fuzz_targets/handle_msg.rs
Normal file
24
fuzz/fuzz_targets/handle_msg.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
#![no_main]
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass::protocol::CryptoServer;
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::StaticKem;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
use rosenpass_secret_memory::{PublicBox, Secret};
|
||||
use std::sync::Once;
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
fuzz_target!(|rx_buf: &[u8]| {
|
||||
ONCE.call_once(secret_policy_use_only_malloc_secrets);
|
||||
let sk = Secret::from_slice(&[0; StaticKem::SK_LEN]);
|
||||
let pk = PublicBox::from_slice(&[0; StaticKem::PK_LEN]);
|
||||
|
||||
let mut cs = CryptoServer::new(sk, pk);
|
||||
let mut tx_buf = [0; 10240];
|
||||
|
||||
// We expect errors while fuzzing therefore we do not check the result.
|
||||
let _ = cs.handle_msg(rx_buf, &mut tx_buf);
|
||||
});
|
||||
20
fuzz/fuzz_targets/kyber_encaps.rs
Normal file
20
fuzz/fuzz_targets/kyber_encaps.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![no_main]
|
||||
extern crate arbitrary;
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::EphemeralKem;
|
||||
|
||||
#[derive(arbitrary::Arbitrary, Debug)]
|
||||
pub struct Input {
|
||||
pub pk: [u8; EphemeralKem::PK_LEN],
|
||||
}
|
||||
|
||||
fuzz_target!(|input: Input| {
|
||||
let mut ciphertext = [0u8; EphemeralKem::CT_LEN];
|
||||
let mut shared_secret = [0u8; EphemeralKem::SHK_LEN];
|
||||
|
||||
EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap();
|
||||
});
|
||||
15
fuzz/fuzz_targets/mceliece_encaps.rs
Normal file
15
fuzz/fuzz_targets/mceliece_encaps.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#![no_main]
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::StaticKem;
|
||||
|
||||
fuzz_target!(|input: [u8; StaticKem::PK_LEN]| {
|
||||
let mut ciphertext = [0u8; StaticKem::CT_LEN];
|
||||
let mut shared_secret = [0u8; StaticKem::SHK_LEN];
|
||||
|
||||
// We expect errors while fuzzing therefore we do not check the result.
|
||||
let _ = StaticKem::encaps(&mut shared_secret, &mut ciphertext, &input);
|
||||
});
|
||||
15
fuzz/fuzz_targets/vec_secret_alloc_malloc.rs
Normal file
15
fuzz/fuzz_targets/vec_secret_alloc_malloc.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#![no_main]
|
||||
|
||||
use std::sync::Once;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_secret_memory::alloc::secret_vec;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
ONCE.call_once(secret_policy_use_only_malloc_secrets);
|
||||
let mut vec = secret_vec();
|
||||
vec.extend_from_slice(data);
|
||||
});
|
||||
15
fuzz/fuzz_targets/vec_secret_alloc_memfdsec.rs
Normal file
15
fuzz/fuzz_targets/vec_secret_alloc_memfdsec.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#![no_main]
|
||||
|
||||
use std::sync::Once;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_secret_memory::alloc::secret_vec;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
ONCE.call_once(secret_policy_use_only_memfd_secrets);
|
||||
let mut vec = secret_vec();
|
||||
vec.extend_from_slice(data);
|
||||
});
|
||||
15
fuzz/fuzz_targets/vec_secret_alloc_memfdsec_mallocfb.rs
Normal file
15
fuzz/fuzz_targets/vec_secret_alloc_memfdsec_mallocfb.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
#![no_main]
|
||||
|
||||
use std::sync::Once;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_secret_memory::alloc::secret_vec;
|
||||
use rosenpass_secret_memory::policy::*;
|
||||
|
||||
static ONCE: Once = Once::new();
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
ONCE.call_once(secret_policy_try_use_memfd_secrets);
|
||||
let mut vec = secret_vec();
|
||||
vec.extend_from_slice(data);
|
||||
});
|
||||
40
misc/README.md
Normal file
40
misc/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Additional files
|
||||
|
||||
This folder contains additional files that are used in the project.
|
||||
|
||||
## `generate_configs.py`
|
||||
|
||||
The script is used to generate configuration files for a benchmark setup
|
||||
consisting of a device under testing (DUT) and automatic test equipment (ATE),
|
||||
basically a strong machine capable of running multiple Rosenpass instances at
|
||||
once.
|
||||
|
||||
At the top of the script multiple variables can be set to configure the DUT IP
|
||||
address and more. Once configured you may run `python3 generate_configs.py` to
|
||||
create the configuration files.
|
||||
|
||||
A new folder called `output/` is created containing the subfolder `dut/` and
|
||||
`ate/`. The former has to be copied on the DUT, ideally reproducible hardware
|
||||
like a Raspberry Pi, while the latter is copied to the ATE, i.e. a laptop.
|
||||
|
||||
### Running a benchmark
|
||||
|
||||
On the ATE a run script is required since multiple instances of `rosenpass` are
|
||||
started with different configurations in parallel. The scripts are named after
|
||||
the number of instances they start, e.g. `run-50.sh` starts 50 instances.
|
||||
|
||||
```shell
|
||||
# on the ATE aka laptop
|
||||
cd output/ate
|
||||
./run-10.sh
|
||||
```
|
||||
|
||||
On the DUT you start a single Rosenpass instance with the configuration matching
|
||||
the ATE number of peers.
|
||||
|
||||
```shell
|
||||
# on the DUT aka Raspberry Pi
|
||||
rosenpass exchange-config configs/dut-10.toml
|
||||
```
|
||||
|
||||
Use whatever measurement tool you like to monitor the DUT and ATE.
|
||||
105
misc/generate_configs.py
Normal file
105
misc/generate_configs.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from pathlib import Path
|
||||
from subprocess import run
|
||||
import os
|
||||
|
||||
config = dict(
|
||||
peer_counts=[1, 5, 10, 50, 100, 500],
|
||||
peer_count_max=100,
|
||||
ate_ip="127.0.0.1",
|
||||
dut_ip="127.0.0.1",
|
||||
dut_port=9999,
|
||||
path_to_rosenpass_bin=os.getcwd() + "/target/release/rosenpass",
|
||||
)
|
||||
|
||||
print(config)
|
||||
|
||||
output_dir = Path("output")
|
||||
output_dir.mkdir(exist_ok=True)
|
||||
|
||||
template_dut = """
|
||||
public_key = "keys/dut-public-key"
|
||||
secret_key = "keys/dut-secret-key"
|
||||
listen = ["{dut_ip}:{dut_port}"]
|
||||
verbosity = "Quiet"
|
||||
"""
|
||||
template_dut_peer = """
|
||||
[[peers]] # ATE-{i}
|
||||
public_key = "keys/ate-{i}-public-key"
|
||||
endpoint = "{ate_ip}:{ate_port}"
|
||||
key_out = "out/key_out_{i}"
|
||||
"""
|
||||
|
||||
template_ate = """
|
||||
public_key = "keys/ate-{i}-public-key"
|
||||
secret_key = "keys/ate-{i}-secret-key"
|
||||
listen = ["{ate_ip}:{ate_port}"]
|
||||
verbosity = "Quiet"
|
||||
|
||||
[[peers]] # DUT
|
||||
public_key = "keys/dut-public-key"
|
||||
endpoint = "{dut_ip}:{dut_port}"
|
||||
key_out = "out/key_out_{i}"
|
||||
"""
|
||||
|
||||
(output_dir / "dut" / "keys").mkdir(exist_ok=True, parents=True)
|
||||
(output_dir / "dut" / "out").mkdir(exist_ok=True, parents=True)
|
||||
(output_dir / "dut" / "configs").mkdir(exist_ok=True, parents=True)
|
||||
(output_dir / "ate" / "keys").mkdir(exist_ok=True, parents=True)
|
||||
(output_dir / "ate" / "out").mkdir(exist_ok=True, parents=True)
|
||||
(output_dir / "ate" / "configs").mkdir(exist_ok=True, parents=True)
|
||||
|
||||
for peer_count in config["peer_counts"]:
|
||||
dut_config = template_dut.format(**config)
|
||||
for i in range(peer_count):
|
||||
dut_config += template_dut_peer.format(**config, i=i, ate_port=50000 + i)
|
||||
|
||||
(output_dir / "dut" / "configs" / f"dut-{peer_count}.toml").write_text(dut_config)
|
||||
|
||||
if not (output_dir / "dut" / "keys" / "dut-public-key").exists():
|
||||
print("Generate DUT keys")
|
||||
run(
|
||||
[
|
||||
config["path_to_rosenpass_bin"],
|
||||
"gen-keys",
|
||||
f"configs/dut-{peer_count}.toml",
|
||||
],
|
||||
cwd=output_dir / "dut",
|
||||
)
|
||||
else:
|
||||
print("DUT keys already exist")
|
||||
|
||||
# copy the DUT public key to the ATE
|
||||
(output_dir / "ate" / "keys" / "dut-public-key").write_bytes(
|
||||
(output_dir / "dut" / "keys" / "dut-public-key").read_bytes()
|
||||
)
|
||||
|
||||
ate_script = "(trap 'kill 0' SIGINT; \\\n"
|
||||
|
||||
for i in range(config["peer_count_max"]):
|
||||
(output_dir / "ate" / "configs" / f"ate-{i}.toml").write_text(
|
||||
template_ate.format(**config, i=i, ate_port=50000 + i)
|
||||
)
|
||||
|
||||
if not (output_dir / "ate" / "keys" / f"ate-{i}-public-key").exists():
|
||||
# generate ATE keys
|
||||
run(
|
||||
[config["path_to_rosenpass_bin"], "gen-keys", f"configs/ate-{i}.toml"],
|
||||
cwd=output_dir / "ate",
|
||||
)
|
||||
else:
|
||||
print(f"ATE-{i} keys already exist")
|
||||
|
||||
# copy the ATE public keys to the DUT
|
||||
(output_dir / "dut" / "keys" / f"ate-{i}-public-key").write_bytes(
|
||||
(output_dir / "ate" / "keys" / f"ate-{i}-public-key").read_bytes()
|
||||
)
|
||||
|
||||
ate_script += (
|
||||
f"{config['path_to_rosenpass_bin']} exchange-config configs/ate-{i}.toml & \\\n"
|
||||
)
|
||||
|
||||
if (i + 1) in config["peer_counts"]:
|
||||
write_script = ate_script
|
||||
write_script += "wait)"
|
||||
|
||||
(output_dir / "ate" / f"run-{i+1}.sh").write_text(write_script)
|
||||
16
oqs/Cargo.toml
Normal file
16
oqs/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "rosenpass-oqs"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal bindings to liboqs"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
oqs-sys = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
5
oqs/readme.md
Normal file
5
oqs/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass internal liboqs bindings
|
||||
|
||||
Rosenpass internal library providing bindings to liboqs.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
80
oqs/src/kem_macro.rs
Normal file
80
oqs/src/kem_macro.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
macro_rules! oqs_kem {
|
||||
($name:ident) => { ::paste::paste!{
|
||||
mod [< $name:snake >] {
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_util::result::Guaranteed;
|
||||
|
||||
pub enum [< $name:camel >] {}
|
||||
|
||||
/// # Panic & Safety
|
||||
///
|
||||
/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte
|
||||
/// slices only identified using raw pointers. It must be ensured that the raw
|
||||
/// pointers point into byte slices of sufficient length, to avoid UB through
|
||||
/// overwriting of arbitrary data. This is ensured through assertions in the
|
||||
/// implementation.
|
||||
///
|
||||
/// __Note__: This requirement is stricter than necessary, it would suffice
|
||||
/// to only check that the buffers are big enough, allowing them to be even
|
||||
/// bigger. However, from a correctness point of view it does not make sense to
|
||||
/// allow bigger buffers.
|
||||
impl Kem for [< $name:camel >] {
|
||||
type Error = ::std::convert::Infallible;
|
||||
|
||||
const SK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_secret_key >] as usize;
|
||||
const PK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_public_key >] as usize;
|
||||
const CT_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_ciphertext >] as usize;
|
||||
const SHK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_shared_secret >] as usize;
|
||||
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Guaranteed<()> {
|
||||
assert_eq!(sk.len(), Self::SK_LEN);
|
||||
assert_eq!(pk.len(), Self::PK_LEN);
|
||||
unsafe {
|
||||
oqs_call!(
|
||||
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ keypair >],
|
||||
pk.as_mut_ptr(),
|
||||
sk.as_mut_ptr()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Guaranteed<()> {
|
||||
assert_eq!(shk.len(), Self::SHK_LEN);
|
||||
assert_eq!(ct.len(), Self::CT_LEN);
|
||||
assert_eq!(pk.len(), Self::PK_LEN);
|
||||
unsafe {
|
||||
oqs_call!(
|
||||
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ encaps >],
|
||||
ct.as_mut_ptr(),
|
||||
shk.as_mut_ptr(),
|
||||
pk.as_ptr()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Guaranteed<()> {
|
||||
assert_eq!(shk.len(), Self::SHK_LEN);
|
||||
assert_eq!(sk.len(), Self::SK_LEN);
|
||||
assert_eq!(ct.len(), Self::CT_LEN);
|
||||
unsafe {
|
||||
oqs_call!(
|
||||
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >],
|
||||
shk.as_mut_ptr(),
|
||||
ct.as_ptr(),
|
||||
sk.as_ptr()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub use [< $name:snake >] :: [< $name:camel >];
|
||||
}}
|
||||
}
|
||||
21
oqs/src/lib.rs
Normal file
21
oqs/src/lib.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
macro_rules! oqs_call {
|
||||
($name:path, $($args:expr),*) => {{
|
||||
use oqs_sys::common::OQS_STATUS::*;
|
||||
|
||||
match $name($($args),*) {
|
||||
OQS_SUCCESS => {}, // nop
|
||||
OQS_EXTERNAL_LIB_ERROR_OPENSSL => {
|
||||
panic!("OpenSSL error in liboqs' {}.", stringify!($name));
|
||||
},
|
||||
OQS_ERROR => {
|
||||
panic!("Unknown error in liboqs' {}.", stringify!($name));
|
||||
}
|
||||
}
|
||||
}};
|
||||
($name:ident) => { oqs_call!($name, ) };
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod kem_macro;
|
||||
oqs_kem!(kyber_512);
|
||||
oqs_kem!(classic_mceliece_460896);
|
||||
BIN
papers/assets/2023-03-20-rg-tutorial-screenshot.png
Normal file
BIN
papers/assets/2023-03-20-rg-tutorial-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
BIN
papers/assets/2023-03-20-symbolic-analysis-screenshot.png
Normal file
BIN
papers/assets/2023-03-20-symbolic-analysis-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 227 KiB |
Binary file not shown.
BIN
papers/graphics/rosenpass-wp-hashing-tree.afdesign
Normal file
BIN
papers/graphics/rosenpass-wp-hashing-tree.afdesign
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 725 KiB After Width: | Height: | Size: 725 KiB |
@@ -1345,7 +1345,7 @@
|
||||
<g transform="matrix(1,0,0,1,420.66,-1031.32)">
|
||||
<g transform="matrix(31.25,0,0,31.25,1431.32,1459.33)">
|
||||
</g>
|
||||
<text x="1179.63px" y="1459.33px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:31.25px;">"k<tspan x="1207.79px 1224.25px " y="1459.33px 1459.33px ">ey</tspan> chaining init"</text>
|
||||
<text x="1179.63px" y="1459.33px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:31.25px;">"chaining k<tspan x="1334px 1350.47px " y="1459.33px 1459.33px ">ey</tspan> init"</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.389246,0,0,0.136584,299.374,1166.87)">
|
||||
@@ -1437,7 +1437,7 @@
|
||||
<g transform="matrix(0.99675,0,0,0.996238,-597.124,-172.692)">
|
||||
<g transform="matrix(31.25,0,0,31.25,1492.94,1459.33)">
|
||||
</g>
|
||||
<text x="1187.16px" y="1459.33px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:31.25px;">"k<tspan x="1215.32px 1231.79px " y="1459.33px 1459.33px ">ey</tspan> chaining e<tspan x="1398.88px " y="1459.33px ">x</tspan>tr<tspan x="1437.88px " y="1459.33px ">a</tspan>ct"</text>
|
||||
<text x="1187.16px" y="1459.33px" style="font-family:'Nunito-Medium', 'Nunito';font-weight:500;font-size:31.25px;">"chaining k<tspan x="1341.54px 1358px " y="1459.33px 1459.33px ">ey</tspan> e<tspan x="1398.88px " y="1459.33px ">x</tspan>tr<tspan x="1437.88px " y="1459.33px ">a</tspan>ct"</text>
|
||||
</g>
|
||||
<g transform="matrix(0.99675,0,0,0.996238,-380.054,-779.158)">
|
||||
<g transform="matrix(31.25,0,0,31.25,1463.54,1459.33)">
|
||||
|
||||
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 218 KiB |
Binary file not shown.
Binary file not shown.
@@ -79,6 +79,8 @@
|
||||
letter-csv .initial:n = ,
|
||||
letter-content .tl_set:N = \l_letter_csv_content_tl,
|
||||
letter-content .initial:n=,
|
||||
tableofcontents .bool_gset:N = \g__ptxcd_tableofcontents_bool,
|
||||
tableofcontents .initial:n = true,
|
||||
}
|
||||
|
||||
\tl_new:N \l__markdown_sequence_tl
|
||||
|
||||
@@ -171,8 +171,17 @@ version={4.0},
|
||||
\ExplSyntaxOn
|
||||
\SetTemplatePreamble{
|
||||
\hypersetup{pdftitle=\inserttitle,pdfauthor=The~Rosenpass~Project}
|
||||
\title{\vspace*{-2.5cm}\includegraphics[width=4cm]{RosenPass-Logo}}
|
||||
\author{\csname insertauthor\endcsname}
|
||||
\exp_args:NV\tl_if_eq:nnTF \inserttitle{Rosenpass} {
|
||||
\title{\vspace*{-2.5cm}\includegraphics[width=4cm]{RosenPass-Logo}}
|
||||
} {
|
||||
\titlehead{\centerline{\includegraphics[width=4cm]{RosenPass-Logo}}}
|
||||
\title{\inserttitle}
|
||||
}
|
||||
\ifx\csname insertauthor\endcsname\relax
|
||||
\author{}
|
||||
\else
|
||||
\author{\parbox{\linewidth}{\centering\insertauthor}}
|
||||
\fi
|
||||
\subject{\csname insertsubject\endcsname}
|
||||
\date{\vspace{-1cm}}
|
||||
}
|
||||
@@ -374,29 +383,28 @@ version={4.0},
|
||||
}
|
||||
}
|
||||
}
|
||||
\makeatother
|
||||
\ExplSyntaxOff
|
||||
|
||||
% end of namepartpicturesetup
|
||||
|
||||
\newcommand{\captionbox}[1]{{\setlength{\fboxsep}{.5ex}\colorbox{rosenpass-gray}{#1}}}
|
||||
|
||||
\makeatletter
|
||||
\renewenvironment{abstract}{
|
||||
\small
|
||||
\begin{center}\normalfont\sectfont\nobreak\abstractname\@endparpenalty\@M\end{center}%
|
||||
}{
|
||||
\par
|
||||
}
|
||||
\makeatother
|
||||
|
||||
|
||||
\SetTemplateBegin{
|
||||
\maketitle
|
||||
\begin{abstract}
|
||||
\noindent\csname insertabstract\endcsname
|
||||
\end{abstract}
|
||||
\tableofcontents
|
||||
\bool_if:NT \g__ptxcd_tableofcontents_bool \tableofcontents
|
||||
\clearpage
|
||||
}
|
||||
\makeatother
|
||||
\ExplSyntaxOff
|
||||
|
||||
\SetTemplateEnd{
|
||||
}
|
||||
\SetTemplateEnd{}
|
||||
|
||||
@@ -6,14 +6,15 @@ author:
|
||||
- Benjamin Lipp = Max Planck Institute for Security and Privacy (MPI-SP)
|
||||
- Wanja Zaeske
|
||||
- Lisa Schmidt = {Scientific Illustrator – \\url{mullana.de}}
|
||||
- Prabhpreet Dua
|
||||
abstract: |
|
||||
Rosenpass is used to create post-quantum-secure VPNs. Rosenpass computes a shared key, WireGuard (WG) [@wg] uses the shared key to establish a secure connection. Rosenpass can also be used without WireGuard, deriving post-quantum-secure symmetric keys for some other application. The Rosenpass protocol builds on “Post-quantum WireGuard” (PQWG) [@pqwg] and improves it by using a cookie mechanism to provide security against state disruption attacks.
|
||||
Rosenpass is used to create post-quantum-secure VPNs. Rosenpass computes a shared key, WireGuard (WG) [@wg] uses the shared key to establish a secure connection. Rosenpass can also be used without WireGuard, deriving post-quantum-secure symmetric keys for another application. The Rosenpass protocol builds on “Post-quantum WireGuard” (PQWG) [@pqwg] and improves it by using a cookie mechanism to provide security against state disruption attacks.
|
||||
|
||||
The WireGuard implementation enjoys great trust from the cryptography community and has excellent performance characteristics. To preserve these features, the Rosenpass application runs side-by-side with WireGuard and supplies a new post-quantum-secure pre-shared key (PSK) every two minutes. WireGuard itself still performs the pre-quantum-secure key exchange and transfers any transport data with no involvement from Rosenpass at all.
|
||||
|
||||
The Rosenpass project consists of a protocol description, an implementation written in Rust, and a symbolic analysis of the protocol’s security using ProVerif [@proverif]. We are working on a cryptographic security proof using CryptoVerif [@cryptoverif].
|
||||
|
||||
This document is a guide to engineers and researchers implementing the protocol; a scientific paper discussing the security properties of Rosenpass is work in progress.
|
||||
This document is a guide for engineers and researchers implementing the protocol; a scientific paper discussing the security properties of Rosenpass is work in progress.
|
||||
---
|
||||
|
||||
\enlargethispage{5mm}
|
||||
@@ -169,7 +170,7 @@ Rosenpass uses a cryptographic hash function for multiple purposes:
|
||||
* Computing the cookie to guard against denial of service attacks. This is a feature adopted from WireGuard, but not yet included in the implementation of Rosenpass.
|
||||
* Computing the peer ID
|
||||
* Key derivation during and after the handshake
|
||||
* Computing the additional data for the biscuit encryption, to prove some privacy for its contents
|
||||
* Computing the additional data for the biscuit encryption, to provide some privacy for its contents
|
||||
|
||||
Using one hash function for multiple purposes can cause real-world security issues and even key recovery attacks [@oraclecloning]. We choose a tree-based domain separation scheme based on a keyed hash function – the previously introduced primitive `hash` – to make sure all our hash function calls can be seen as distinct.
|
||||
|
||||
@@ -218,6 +219,7 @@ The server needs to store the following variables:
|
||||
* `spkm`
|
||||
* `biscuit_key` – Randomly chosen key used to encrypt biscuits
|
||||
* `biscuit_ctr` – Retransmission protection for biscuits
|
||||
* `cookie_secret`- A randomized cookie secret to derive cookies sent to peer when under load. This secret changes every 120 seconds
|
||||
|
||||
Not mandated per se, but required in practice:
|
||||
|
||||
@@ -237,12 +239,13 @@ For each peer, the server stores:
|
||||
The initiator stores the following local state for each ongoing handshake:
|
||||
|
||||
* A reference to the peer structure
|
||||
* A state indicator to keep track of the message expected from the responder next
|
||||
* A state indicator to keep track of the next message expected from the responder
|
||||
* `sidi` – Initiator session ID
|
||||
* `sidr` – Responder session ID
|
||||
* `ck` – The chaining key
|
||||
* `eski` – The initiator's ephemeral secret key
|
||||
* `epki` – The initiator's ephemeral public key
|
||||
* `cookie_value`- Cookie value sent by an initiator peer under load, used to compute cookie field in outgoing handshake to peer under load. This value expires 120 seconds from when a peer sends this value using the CookieReply message
|
||||
|
||||
The responder stores no state. While the responder has access to all of the above variables except for `eski`, the responder discards them after generating the RespHello message. Instead, the responder state is contained inside a cookie called a biscuit. This value is returned to the responder inside the InitConf packet. The biscuit consists of:
|
||||
|
||||
@@ -428,12 +431,92 @@ The responder code handling InitConf needs to deal with the biscuits and package
|
||||
|
||||
ICR5 and ICR6 perform biscuit replay protection using the biscuit number. This is not handled in `load_biscuit()` itself because there is the case that `biscuit_no = biscuit_used` which needs to be dealt with for retransmission handling.
|
||||
|
||||
### Denial of Service Mitigation and Cookies
|
||||
|
||||
Rosenpass derives its cookie-based DoS mitigation technique for a responder when receiving InitHello messages from Wireguard [@wg].
|
||||
|
||||
When the responder is under load, it may choose to not process further InitHello handshake messages, but instead to respond with a cookie reply message (see Figure \ref{img:MessageTypes}).
|
||||
|
||||
The sender of the exchange then uses this cookie in order to resend the message and have it accepted the following time by the reciever.
|
||||
|
||||
For an initiator, Rosenpass ignores all messages when under load.
|
||||
|
||||
#### Cookie Reply Message
|
||||
|
||||
The cookie reply message is sent by the responder on receiving an InitHello message when under load. It consists of the `sidi` of the initiator, a random 24-byte bitstring `nonce` and encrypting `cookie_value` into a `cookie_encrypted` reply field which consists of the following:
|
||||
|
||||
```pseudorust
|
||||
cookie_value = lhash("cookie-value", cookie_secret, initiator_host_info)[0..16]
|
||||
cookie_encrypted = XAEAD(lhash("cookie-key", spkm), nonce, cookie_value, mac_peer)
|
||||
```
|
||||
|
||||
where `cookie_secret` is a secret variable that changes every two minutes to a random value. `initiator_host_info` is used to identify the initiator host, and is implementation-specific for the client. This paramaters used to identify the host must be carefully chosen to ensure there is a unique mapping, especially when using IPv4 and IPv6 addresses to identify the host (such as taking care of IPv6 link-local addresses). `cookie_value` is a truncated 16 byte value from the above hash operation. `mac_peer` is the `mac` field of the peer's handshake message to which message is the reply.
|
||||
|
||||
#### Envelope `mac` Field
|
||||
|
||||
Similar to `mac.1` in Wireguard handshake messages, the `mac` field of a Rosenpass envelope from a handshake packet sender's point of view consists of the following:
|
||||
|
||||
```pseudorust
|
||||
mac = lhash("mac", spkt, MAC_WIRE_DATA)[0..16]
|
||||
```
|
||||
|
||||
where `MAC_WIRE_DATA` represents all bytes of msg prior to `mac` field in the envelope.
|
||||
|
||||
If a client receives an invalid `mac` value for any message, it will discard the message.
|
||||
|
||||
#### Envelope cookie field
|
||||
|
||||
The initiator, on receiving a CookieReply message, decrypts `cookie_encrypted` and stores the `cookie_value` for the session into `peer[sid].cookie_value` for a limited time (120 seconds). This value is then used to set `cookie` field set for subsequent messages and retransmissions to the responder as follows:
|
||||
|
||||
```pseudorust
|
||||
if (peer.cookie_value.is_none() || seconds_since_update(peer[sid].cookie_value) >= 120) {
|
||||
cookie.zeroize(); //zeroed out 16 bytes bitstring
|
||||
}
|
||||
else {
|
||||
cookie = lhash("cookie",peer.cookie_value.unwrap(),COOKIE_WIRE_DATA)
|
||||
}
|
||||
```
|
||||
|
||||
Here, `seconds_since_update(peer.cookie_value)` is the amount of time in seconds ellapsed since last cookie was received, and `COOKIE_WIRE_DATA` are the message contents of all bytes of the retransmitted message prior to the `cookie` field.
|
||||
|
||||
The inititator can use an invalid value for the `cookie` value, when the responder is not under load, and the responder must ignore this value.
|
||||
However, when the responder is under load, it may reject InitHello messages with the invalid `cookie` value, and issue a cookie reply message.
|
||||
|
||||
### Conditions to trigger DoS Mechanism
|
||||
|
||||
This whitepaper does not mandate any specific mechanism to detect responder contention (also mentioned as the under load condition) that would trigger use of the cookie mechanism.
|
||||
|
||||
For the reference implemenation, Rosenpass has derived inspiration from the linux implementation of Wireguard. This implementation suggests that the reciever keep track of the number of messages it is processing at a given time.
|
||||
|
||||
On receiving an incoming message, if the length of the message queue to be processed exceeds a threshold `MAX_QUEUED_INCOMING_HANDSHAKES_THRESHOLD`, the client is considered under load and its state is stored as under load. In addition, the timestamp of this instant when the client was last under load is stored. When recieving subsequent messages, if the client is still in an under load state, the client will check if the time ellpased since the client was last under load has exceeded `LAST_UNDER_LOAD_WINDOW` seconds. If this is the case, the client will update its state to normal operation, and process the message in a normal fashion.
|
||||
|
||||
Currently, the following constants are derived from the Linux kernel implementation of Wireguard:
|
||||
|
||||
```pseudorust
|
||||
MAX_QUEUED_INCOMING_HANDSHAKES_THRESHOLD = 4096
|
||||
LAST_UNDER_LOAD_WINDOW = 1 //seconds
|
||||
```
|
||||
|
||||
## Dealing with Packet Loss
|
||||
|
||||
The initiator deals with packet loss by storing the messages it sends to the responder and retransmitting them in randomized, exponentially increasing intervals until they get a response. Receiving RespHello terminates retransmission of InitHello. A Data or EmptyData message serves as acknowledgement of receiving InitConf and terminates its retransmission.
|
||||
|
||||
The responder does not need to do anything special to handle RespHello retransmission – if the RespHello package is lost, the initiator retransmits InitHello and the responder can generate another RespHello package from that. InitConf retransmission needs to be handled specifically in the responder code because accepting an InitConf retransmission would reset the live session including the nonce counter, which would cause nonce reuse. Implementations must detect the case that `biscuit_no = biscuit_used` in ICR5, skip execution of ICR6 and ICR7, and just transmit another EmptyData package to confirm that the initiator can stop transmitting InitConf.
|
||||
|
||||
### Interaction with cookie reply system
|
||||
|
||||
The cookie reply system does not interfere with the retransmission logic discussed above.
|
||||
|
||||
When the initator is under load, it will ignore processing any incoming messages.
|
||||
|
||||
When a responder is under load and it receives an InitHello handshake message, the InitHello message will be discarded and a cookie reply message is sent. The initiator, then on the reciept of the cookie reply message, will store a decrypted `cookie_value` to set the `cookie` field to subsequently sent messages. As per the retransmission mechanism above, the initiator will send a retransmitted InitHello message with a valid `cookie` value appended. On receiving the retransmitted handshake message, the responder will validate the `cookie` value and resume with the handshake process.
|
||||
|
||||
When the responder is under load and it recieves an InitConf message, the message will be directly processed without checking the validity of the cookie field.
|
||||
|
||||
# Changelog
|
||||
|
||||
- Added section "Denial of Service Mitigation and Cookies", and modify "Dealing with Packet Loss" for DoS cookie mechanism
|
||||
|
||||
\printbibliography
|
||||
|
||||
\setupimage{landscape,fullpage,label=img:HandlingCode}
|
||||
|
||||
24
readme.md
24
readme.md
@@ -14,22 +14,22 @@ This repository contains
|
||||
|
||||
## Getting started
|
||||
|
||||
First, [install rosenpass](#Getting-Rosenpass). Then, check out the help funtions of `rp` & `rosenpass`:
|
||||
First, [install rosenpass](#Getting-Rosenpass). Then, check out the help functions of `rp` & `rosenpass`:
|
||||
|
||||
```sh
|
||||
rp help
|
||||
rosenpass help
|
||||
```
|
||||
|
||||
Follow [quickstart instructions](https://rosenpass.eu/#start) to get a VPN up and running.
|
||||
Follow [quick start instructions](https://rosenpass.eu/#start) to get a VPN up and running.
|
||||
|
||||
## Software architecture
|
||||
|
||||
The [rosenpass tool](./src/) is written in Rust and uses liboqs[^liboqs] and libsodium[^libsodium]. The tool establishes a symmetric key and provides it to WireGuard. Since it supplies WireGuard with key through the PSK feature using Rosenpass+WireGuard is cryptographically no less secure than using WireGuard on its own ("hybrid security"). Rosenpass refreshes the symmetric key every two minutes.
|
||||
The [rosenpass tool](./src/) is written in Rust and uses liboqs[^liboqs]. The tool establishes a symmetric key and provides it to WireGuard. Since it supplies WireGuard with key through the PSK feature using Rosenpass+WireGuard is cryptographically no less secure than using WireGuard on its own ("hybrid security"). Rosenpass refreshes the symmetric key every two minutes.
|
||||
|
||||
As with any application a small risk of critical security issues (such as buffer overflows, remote code execution) exists; the Rosenpass application is written in the Rust programming language which is much less prone to such issues. Rosenpass can also write keys to files instead of supplying them to WireGuard With a bit of scripting the stand alone mode of the implementation can be used to run the application in a Container, VM or on another host. This mode can also be used to integrate tools other than WireGuard with Rosenpass.
|
||||
|
||||
The [`rp`](./rp) tool written in bash makes it easy to create a VPN using WireGuard and Rosenpass.
|
||||
The [`rp`](./rp) tool written in Rust makes it easy to create a VPN using WireGuard and Rosenpass.
|
||||
|
||||
`rp` is easy to get started with but has a few drawbacks; it runs as root, demanding access to both WireGuard
|
||||
and Rosenpass private keys, takes control of the interface and works with exactly one interface. If you do not feel confident about running Rosenpass as root, you should use the stand-alone mode to create a more secure setup using containers, jails, or virtual machines.
|
||||
@@ -54,23 +54,31 @@ We are working on a cryptographic proof of security, but we already provide a sy
|
||||
(manual) $ ./analyze.sh
|
||||
```
|
||||
|
||||
The analysis is implemented according to modern software engineering principles: Using the C preprocessor, we where able to split the analysis into multiple files and uses some metaprogramming to avoid repetition.
|
||||
The analysis is implemented according to modern software engineering principles: Using the C preprocessor, we where able to split the analysis into multiple files and uses some meta programming to avoid repetition.
|
||||
The code uses a variety of optimizations to speed up analysis such as using secret functions to model trusted/malicious setup. We split the model into two separate entry points which can be analyzed in parallel. Each is much faster than both models combined.
|
||||
A wrapper script provides instant feedback about which queries execute as expected in color: A red cross if a query fails and a green check if it succeeds.
|
||||
|
||||
[^liboqs]: https://openquantumsafe.org/liboqs/
|
||||
[^libsodium]: https://doc.libsodium.org/
|
||||
[^wg]: https://www.wireguard.com/
|
||||
[^pqwg]: https://eprint.iacr.org/2020/379
|
||||
[^pqwg-statedis]: Unless supplied with a pre-shared-key, but this defeates the purpose of a key exchange protocol
|
||||
[^pqwg-statedis]: Unless supplied with a pre-shared-key, but this defeats the purpose of a key exchange protocol
|
||||
[^wg-statedis]: https://lists.zx2c4.com/pipermail/wireguard/2021-August/006916.htmlA
|
||||
|
||||
# Getting Rosenpass
|
||||
|
||||
Rosenpass is packaged for more and more distros, maybe also for the distro of your choice?
|
||||
Documentation and installation guides can be found at the [Rosenpass website](https://rosenpass.eu/docs).
|
||||
|
||||
Rosenpass is packaged for more and more distributions, maybe also for the distribution of your choice?
|
||||
|
||||
[](https://repology.org/project/rosenpass/versions)
|
||||
|
||||
# Mirrors
|
||||
|
||||
Don't want to use GitHub or only have an IPv6 connection? Rosenpass has set up two mirrors for this:
|
||||
|
||||
- [NotABug](https://notabug.org/rosenpass/rosenpass)
|
||||
- [GitLab](https://gitlab.com/rosenpass/rosenpass/)
|
||||
|
||||
# Supported by
|
||||
|
||||
Funded through <a href="https://nlnet.nl/">NLNet</a> with financial support for the European Commission's <a href="https://nlnet.nl/assure">NGI Assure</a> program.
|
||||
|
||||
74
rosenpass/Cargo.toml
Normal file
74
rosenpass/Cargo.toml
Normal file
@@ -0,0 +1,74 @@
|
||||
[package]
|
||||
name = "rosenpass"
|
||||
version = "0.2.1"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Build post-quantum-secure VPNs with WireGuard!"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[[bin]]
|
||||
name = "rosenpass"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "rosenpass-gen-ipc-msg-types"
|
||||
path = "src/bin/gen-ipc-msg-types.rs"
|
||||
required-features = ["experiment_api", "internal_bin_gen_ipc_msg_types"]
|
||||
|
||||
[[test]]
|
||||
name = "api-integration-tests"
|
||||
required-features = ["experiment_api", "internal_testing"]
|
||||
|
||||
[[bench]]
|
||||
name = "handshake"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
rosenpass-util = { workspace = true }
|
||||
rosenpass-constant-time = { workspace = true }
|
||||
rosenpass-ciphers = { workspace = true }
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
memoffset = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
log = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
mio = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
zerocopy = { workspace = true }
|
||||
home = { workspace = true }
|
||||
derive_builder = {workspace = true}
|
||||
rosenpass-wireguard-broker = {workspace = true}
|
||||
zeroize = { workspace = true }
|
||||
hex-literal = { workspace = true, optional = true }
|
||||
hex = { workspace = true, optional = true }
|
||||
heck = { workspace = true, optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true }
|
||||
test_bin = { workspace = true }
|
||||
stacker = { workspace = true }
|
||||
serial_test = {workspace = true}
|
||||
procspawn = {workspace = true}
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[features]
|
||||
enable_broker_api = ["rosenpass-wireguard-broker/enable_broker_api"]
|
||||
experiment_memfd_secret = []
|
||||
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
|
||||
experiment_api = ["hex-literal"]
|
||||
internal_testing = []
|
||||
internal_bin_gen_ipc_msg_types = ["hex", "heck"]
|
||||
@@ -1,17 +1,18 @@
|
||||
use anyhow::Result;
|
||||
use rosenpass::{
|
||||
pqkem::{CCAKEM, KEM},
|
||||
protocol::{CcaPk, CcaSk, HandleMsgResult, MsgBuf, PeerPtr, Server, SymKey},
|
||||
sodium::sodium_init,
|
||||
};
|
||||
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SPk, SSk, SymKey};
|
||||
use std::ops::DerefMut;
|
||||
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::StaticKem;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
|
||||
|
||||
fn handle(
|
||||
tx: &mut Server,
|
||||
tx: &mut CryptoServer,
|
||||
msgb: &mut MsgBuf,
|
||||
msgl: usize,
|
||||
rx: &mut Server,
|
||||
rx: &mut CryptoServer,
|
||||
resb: &mut MsgBuf,
|
||||
) -> Result<(Option<SymKey>, Option<SymKey>)> {
|
||||
let HandleMsgResult {
|
||||
@@ -30,7 +31,7 @@ fn handle(
|
||||
Ok((txk, rxk.or(xch)))
|
||||
}
|
||||
|
||||
fn hs(ini: &mut Server, res: &mut Server) -> Result<()> {
|
||||
fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> {
|
||||
let (mut inib, mut resb) = (MsgBuf::zero(), MsgBuf::zero());
|
||||
let sz = ini.initiate_handshake(PeerPtr(0), &mut *inib)?;
|
||||
let (kini, kres) = handle(ini, &mut inib, sz, res, &mut resb)?;
|
||||
@@ -38,32 +39,35 @@ fn hs(ini: &mut Server, res: &mut Server) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn keygen() -> Result<(CcaSk, CcaPk)> {
|
||||
let (mut sk, mut pk) = (CcaSk::zero(), CcaPk::zero());
|
||||
CCAKEM::keygen(sk.secret_mut(), pk.secret_mut())?;
|
||||
fn keygen() -> Result<(SSk, SPk)> {
|
||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?;
|
||||
Ok((sk, pk))
|
||||
}
|
||||
|
||||
fn make_server_pair() -> Result<(Server, Server)> {
|
||||
fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> {
|
||||
let psk = SymKey::random();
|
||||
let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?);
|
||||
let (mut a, mut b) = (Server::new(ska, pka.clone()), Server::new(skb, pkb.clone()));
|
||||
let (mut a, mut b) = (
|
||||
CryptoServer::new(ska, pka.clone()),
|
||||
CryptoServer::new(skb, pkb.clone()),
|
||||
);
|
||||
a.add_peer(Some(psk.clone()), pkb)?;
|
||||
b.add_peer(Some(psk), pka)?;
|
||||
Ok((a, b))
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
sodium_init().unwrap();
|
||||
secret_policy_try_use_memfd_secrets();
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
c.bench_function("cca_secret_alloc", |bench| {
|
||||
bench.iter(|| {
|
||||
CcaSk::zero();
|
||||
SSk::zero();
|
||||
})
|
||||
});
|
||||
c.bench_function("cca_public_alloc", |bench| {
|
||||
bench.iter(|| {
|
||||
CcaPk::zero();
|
||||
SPk::zero();
|
||||
})
|
||||
});
|
||||
c.bench_function("keygen", |bench| {
|
||||
52
rosenpass/build.rs
Normal file
52
rosenpass/build.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use anyhow::bail;
|
||||
use anyhow::Result;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
/// Invokes a troff compiler to compile a manual page
|
||||
fn render_man(compiler: &str, man: &str) -> Result<String> {
|
||||
let out = Command::new(compiler).args(["-Tascii", man]).output()?;
|
||||
if !out.status.success() {
|
||||
bail!("{} returned an error", compiler);
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(out.stdout)?)
|
||||
}
|
||||
|
||||
/// Generates the manual page
|
||||
fn generate_man() -> String {
|
||||
// This function is purposely stupid and redundant
|
||||
|
||||
let man = render_man("mandoc", "./doc/rosenpass.1");
|
||||
if let Ok(man) = man {
|
||||
return man;
|
||||
}
|
||||
|
||||
let man = render_man("groff", "./doc/rosenpass.1");
|
||||
if let Ok(man) = man {
|
||||
return man;
|
||||
}
|
||||
|
||||
"Cannot render manual page. Please visit https://rosenpass.eu/docs/manuals/\n".into()
|
||||
}
|
||||
|
||||
fn man() {
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let man = generate_man();
|
||||
let path = out_dir.join("rosenpass.1.ascii");
|
||||
|
||||
let mut file = File::create(&path).unwrap();
|
||||
file.write_all(man.as_bytes()).unwrap();
|
||||
|
||||
println!("cargo:rustc-env=ROSENPASS_MAN={}", path.display());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// For now, rerun the build script on every time, as the build script
|
||||
// is not very expensive right now.
|
||||
println!("cargo:rerun-if-changed=./");
|
||||
man();
|
||||
}
|
||||
1
rosenpass/readme.md
Symbolic link
1
rosenpass/readme.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../readme.md
|
||||
116
rosenpass/src/api/boilerplate/byte_slice_ext.rs
Normal file
116
rosenpass/src/api/boilerplate/byte_slice_ext.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
use zerocopy::{ByteSlice, Ref};
|
||||
|
||||
use rosenpass_util::zerocopy::{RefMaker, ZerocopySliceExt};
|
||||
|
||||
use super::{
|
||||
PingRequest, PingResponse, RawMsgType, RefMakerRawMsgTypeExt, RequestMsgType, RequestRef,
|
||||
ResponseMsgType, ResponseRef,
|
||||
};
|
||||
|
||||
pub trait ByteSliceRefExt: ByteSlice {
|
||||
fn msg_type_maker(self) -> RefMaker<Self, RawMsgType> {
|
||||
self.zk_ref_maker()
|
||||
}
|
||||
|
||||
fn msg_type(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||
self.zk_parse()
|
||||
}
|
||||
|
||||
fn msg_type_from_prefix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||
self.zk_parse_prefix()
|
||||
}
|
||||
|
||||
fn msg_type_from_suffix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||
self.zk_parse_suffix()
|
||||
}
|
||||
|
||||
fn request_msg_type(self) -> anyhow::Result<RequestMsgType> {
|
||||
self.msg_type_maker().parse_request_msg_type()
|
||||
}
|
||||
|
||||
fn request_msg_type_from_prefix(self) -> anyhow::Result<RequestMsgType> {
|
||||
self.msg_type_maker()
|
||||
.from_prefix()?
|
||||
.parse_request_msg_type()
|
||||
}
|
||||
|
||||
fn request_msg_type_from_suffix(self) -> anyhow::Result<RequestMsgType> {
|
||||
self.msg_type_maker()
|
||||
.from_suffix()?
|
||||
.parse_request_msg_type()
|
||||
}
|
||||
|
||||
fn response_msg_type(self) -> anyhow::Result<ResponseMsgType> {
|
||||
self.msg_type_maker().parse_response_msg_type()
|
||||
}
|
||||
|
||||
fn response_msg_type_from_prefix(self) -> anyhow::Result<ResponseMsgType> {
|
||||
self.msg_type_maker()
|
||||
.from_prefix()?
|
||||
.parse_response_msg_type()
|
||||
}
|
||||
|
||||
fn response_msg_type_from_suffix(self) -> anyhow::Result<ResponseMsgType> {
|
||||
self.msg_type_maker()
|
||||
.from_suffix()?
|
||||
.parse_response_msg_type()
|
||||
}
|
||||
|
||||
fn parse_request(self) -> anyhow::Result<RequestRef<Self>> {
|
||||
RequestRef::parse(self)
|
||||
}
|
||||
|
||||
fn parse_request_from_prefix(self) -> anyhow::Result<RequestRef<Self>> {
|
||||
RequestRef::parse_from_prefix(self)
|
||||
}
|
||||
|
||||
fn parse_request_from_suffix(self) -> anyhow::Result<RequestRef<Self>> {
|
||||
RequestRef::parse_from_suffix(self)
|
||||
}
|
||||
|
||||
fn parse_response(self) -> anyhow::Result<ResponseRef<Self>> {
|
||||
ResponseRef::parse(self)
|
||||
}
|
||||
|
||||
fn parse_response_from_prefix(self) -> anyhow::Result<ResponseRef<Self>> {
|
||||
ResponseRef::parse_from_prefix(self)
|
||||
}
|
||||
|
||||
fn parse_response_from_suffix(self) -> anyhow::Result<ResponseRef<Self>> {
|
||||
ResponseRef::parse_from_suffix(self)
|
||||
}
|
||||
|
||||
fn ping_request_maker(self) -> RefMaker<Self, PingRequest> {
|
||||
self.zk_ref_maker()
|
||||
}
|
||||
|
||||
fn ping_request(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||
self.zk_parse()
|
||||
}
|
||||
|
||||
fn ping_request_from_prefix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||
self.zk_parse_prefix()
|
||||
}
|
||||
|
||||
fn ping_request_from_suffix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||
self.zk_parse_suffix()
|
||||
}
|
||||
|
||||
fn ping_response_maker(self) -> RefMaker<Self, PingResponse> {
|
||||
self.zk_ref_maker()
|
||||
}
|
||||
|
||||
fn ping_response(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||
self.zk_parse()
|
||||
}
|
||||
|
||||
fn ping_response_from_prefix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||
self.zk_parse_prefix()
|
||||
}
|
||||
|
||||
fn ping_response_from_suffix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||
self.zk_parse_suffix()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: ByteSlice> ByteSliceRefExt for B {}
|
||||
29
rosenpass/src/api/boilerplate/message_trait.rs
Normal file
29
rosenpass/src/api/boilerplate/message_trait.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use zerocopy::{ByteSliceMut, Ref};
|
||||
|
||||
use rosenpass_util::zerocopy::RefMaker;
|
||||
|
||||
use super::RawMsgType;
|
||||
|
||||
pub trait Message {
|
||||
type Payload;
|
||||
type MessageClass: Into<RawMsgType>;
|
||||
const MESSAGE_TYPE: Self::MessageClass;
|
||||
|
||||
fn from_payload(payload: Self::Payload) -> Self;
|
||||
fn init(&mut self);
|
||||
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>>;
|
||||
}
|
||||
|
||||
pub trait ZerocopyResponseMakerSetupMessageExt<B, T> {
|
||||
fn setup_msg(self) -> anyhow::Result<Ref<B, T>>;
|
||||
}
|
||||
|
||||
impl<B, T> ZerocopyResponseMakerSetupMessageExt<B, T> for RefMaker<B, T>
|
||||
where
|
||||
B: ByteSliceMut,
|
||||
T: Message,
|
||||
{
|
||||
fn setup_msg(self) -> anyhow::Result<Ref<B, T>> {
|
||||
T::setup(self.into_buf())
|
||||
}
|
||||
}
|
||||
117
rosenpass/src/api/boilerplate/message_type.rs
Normal file
117
rosenpass/src/api/boilerplate/message_type.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use hex_literal::hex;
|
||||
use rosenpass_util::zerocopy::RefMaker;
|
||||
use zerocopy::ByteSlice;
|
||||
|
||||
use crate::RosenpassError::{self, InvalidApiMessageType};
|
||||
|
||||
pub type RawMsgType = u128;
|
||||
|
||||
// constants generated by gen-ipc-msg-types:
|
||||
// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Ping Request
|
||||
pub const PING_REQUEST: RawMsgType =
|
||||
RawMsgType::from_le_bytes(hex!("2397 3ecc c441 704d 0b02 ea31 45d3 4999"));
|
||||
// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Ping Response
|
||||
pub const PING_RESPONSE: RawMsgType =
|
||||
RawMsgType::from_le_bytes(hex!("4ec7 f6f0 2bbc ba64 48f1 da14 c7cf 0260"));
|
||||
|
||||
pub trait MessageAttributes {
|
||||
fn message_size(&self) -> usize;
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub enum RequestMsgType {
|
||||
Ping,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub enum ResponseMsgType {
|
||||
Ping,
|
||||
}
|
||||
|
||||
impl MessageAttributes for RequestMsgType {
|
||||
fn message_size(&self) -> usize {
|
||||
match self {
|
||||
Self::Ping => std::mem::size_of::<super::PingRequest>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageAttributes for ResponseMsgType {
|
||||
fn message_size(&self) -> usize {
|
||||
match self {
|
||||
Self::Ping => std::mem::size_of::<super::PingResponse>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RawMsgType> for RequestMsgType {
|
||||
type Error = RosenpassError;
|
||||
|
||||
fn try_from(value: RawMsgType) -> Result<Self, Self::Error> {
|
||||
use RequestMsgType as E;
|
||||
Ok(match value {
|
||||
self::PING_REQUEST => E::Ping,
|
||||
_ => return Err(InvalidApiMessageType(value)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RequestMsgType> for RawMsgType {
|
||||
fn from(val: RequestMsgType) -> Self {
|
||||
use RequestMsgType as E;
|
||||
match val {
|
||||
E::Ping => self::PING_REQUEST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RawMsgType> for ResponseMsgType {
|
||||
type Error = RosenpassError;
|
||||
|
||||
fn try_from(value: RawMsgType) -> Result<Self, Self::Error> {
|
||||
use ResponseMsgType as E;
|
||||
Ok(match value {
|
||||
self::PING_RESPONSE => E::Ping,
|
||||
_ => return Err(InvalidApiMessageType(value)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResponseMsgType> for RawMsgType {
|
||||
fn from(val: ResponseMsgType) -> Self {
|
||||
use ResponseMsgType as E;
|
||||
match val {
|
||||
E::Ping => self::PING_RESPONSE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RawMsgTypeExt {
|
||||
fn into_request_msg_type(self) -> Result<RequestMsgType, RosenpassError>;
|
||||
fn into_response_msg_type(self) -> Result<ResponseMsgType, RosenpassError>;
|
||||
}
|
||||
|
||||
impl RawMsgTypeExt for RawMsgType {
|
||||
fn into_request_msg_type(self) -> Result<RequestMsgType, RosenpassError> {
|
||||
self.try_into()
|
||||
}
|
||||
|
||||
fn into_response_msg_type(self) -> Result<ResponseMsgType, RosenpassError> {
|
||||
self.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RefMakerRawMsgTypeExt {
|
||||
fn parse_request_msg_type(self) -> anyhow::Result<RequestMsgType>;
|
||||
fn parse_response_msg_type(self) -> anyhow::Result<ResponseMsgType>;
|
||||
}
|
||||
|
||||
impl<B: ByteSlice> RefMakerRawMsgTypeExt for RefMaker<B, RawMsgType> {
|
||||
fn parse_request_msg_type(self) -> anyhow::Result<RequestMsgType> {
|
||||
Ok(self.parse()?.read().try_into()?)
|
||||
}
|
||||
|
||||
fn parse_response_msg_type(self) -> anyhow::Result<ResponseMsgType> {
|
||||
Ok(self.parse()?.read().try_into()?)
|
||||
}
|
||||
}
|
||||
17
rosenpass/src/api/boilerplate/mod.rs
Normal file
17
rosenpass/src/api/boilerplate/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
mod byte_slice_ext;
|
||||
mod message_trait;
|
||||
mod message_type;
|
||||
mod payload;
|
||||
mod request_ref;
|
||||
mod request_response;
|
||||
mod response_ref;
|
||||
mod server;
|
||||
|
||||
pub use byte_slice_ext::*;
|
||||
pub use message_trait::*;
|
||||
pub use message_type::*;
|
||||
pub use payload::*;
|
||||
pub use request_ref::*;
|
||||
pub use request_response::*;
|
||||
pub use response_ref::*;
|
||||
pub use server::*;
|
||||
96
rosenpass/src/api/boilerplate/payload.rs
Normal file
96
rosenpass/src/api/boilerplate/payload.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use rosenpass_util::zerocopy::ZerocopyMutSliceExt;
|
||||
use zerocopy::{AsBytes, ByteSliceMut, FromBytes, FromZeroes, Ref};
|
||||
|
||||
use super::{Message, RawMsgType, RequestMsgType, ResponseMsgType};
|
||||
|
||||
/// Size required to fit any message in binary form
|
||||
pub const MAX_REQUEST_LEN: usize = 2500; // TODO fix this
|
||||
pub const MAX_RESPONSE_LEN: usize = 2500; // TODO fix this
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||
pub struct Envelope<M: AsBytes + FromBytes> {
|
||||
/// Which message this is
|
||||
pub msg_type: RawMsgType,
|
||||
/// The actual Paylod
|
||||
pub payload: M,
|
||||
}
|
||||
|
||||
pub type RequestEnvelope<M> = Envelope<M>;
|
||||
pub type ResponseEnvelope<M> = Envelope<M>;
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||
pub struct PingRequestPayload {
|
||||
/// Randomly generated connection id
|
||||
pub echo: [u8; 256],
|
||||
}
|
||||
|
||||
pub type PingRequest = RequestEnvelope<PingRequestPayload>;
|
||||
|
||||
impl PingRequest {
|
||||
pub fn new(echo: [u8; 256]) -> Self {
|
||||
Self::from_payload(PingRequestPayload { echo })
|
||||
}
|
||||
}
|
||||
|
||||
impl Message for PingRequest {
|
||||
type Payload = PingRequestPayload;
|
||||
type MessageClass = RequestMsgType;
|
||||
const MESSAGE_TYPE: Self::MessageClass = RequestMsgType::Ping;
|
||||
|
||||
fn from_payload(payload: Self::Payload) -> Self {
|
||||
Self {
|
||||
msg_type: Self::MESSAGE_TYPE.into(),
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>> {
|
||||
let mut r: Ref<B, Self> = buf.zk_zeroized()?;
|
||||
r.init();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||
pub struct PingResponsePayload {
|
||||
/// Randomly generated connection id
|
||||
pub echo: [u8; 256],
|
||||
}
|
||||
|
||||
pub type PingResponse = ResponseEnvelope<PingResponsePayload>;
|
||||
|
||||
impl PingResponse {
|
||||
pub fn new(echo: [u8; 256]) -> Self {
|
||||
Self::from_payload(PingResponsePayload { echo })
|
||||
}
|
||||
}
|
||||
|
||||
impl Message for PingResponse {
|
||||
type Payload = PingResponsePayload;
|
||||
type MessageClass = ResponseMsgType;
|
||||
const MESSAGE_TYPE: Self::MessageClass = ResponseMsgType::Ping;
|
||||
|
||||
fn from_payload(payload: Self::Payload) -> Self {
|
||||
Self {
|
||||
msg_type: Self::MESSAGE_TYPE.into(),
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>> {
|
||||
let mut r: Ref<B, Self> = buf.zk_zeroized()?;
|
||||
r.init();
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||
}
|
||||
}
|
||||
107
rosenpass/src/api/boilerplate/request_ref.rs
Normal file
107
rosenpass/src/api/boilerplate/request_ref.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use anyhow::ensure;
|
||||
|
||||
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||
|
||||
use super::{ByteSliceRefExt, MessageAttributes, PingRequest, RequestMsgType};
|
||||
|
||||
struct RequestRefMaker<B> {
|
||||
buf: B,
|
||||
msg_type: RequestMsgType,
|
||||
}
|
||||
|
||||
impl<B: ByteSlice> RequestRef<B> {
|
||||
pub fn parse(buf: B) -> anyhow::Result<Self> {
|
||||
RequestRefMaker::new(buf)?.parse()
|
||||
}
|
||||
|
||||
pub fn parse_from_prefix(buf: B) -> anyhow::Result<Self> {
|
||||
RequestRefMaker::new(buf)?.from_prefix()?.parse()
|
||||
}
|
||||
|
||||
pub fn parse_from_suffix(buf: B) -> anyhow::Result<Self> {
|
||||
RequestRefMaker::new(buf)?.from_suffix()?.parse()
|
||||
}
|
||||
|
||||
pub fn message_type(&self) -> RequestMsgType {
|
||||
match self {
|
||||
Self::Ping(_) => RequestMsgType::Ping,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<Ref<B, PingRequest>> for RequestRef<B> {
|
||||
fn from(v: Ref<B, PingRequest>) -> Self {
|
||||
Self::Ping(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: ByteSlice> RequestRefMaker<B> {
|
||||
fn new(buf: B) -> anyhow::Result<Self> {
|
||||
let msg_type = buf.deref().request_msg_type_from_prefix()?;
|
||||
Ok(Self { buf, msg_type })
|
||||
}
|
||||
|
||||
fn target_size(&self) -> usize {
|
||||
self.msg_type.message_size()
|
||||
}
|
||||
|
||||
fn parse(self) -> anyhow::Result<RequestRef<B>> {
|
||||
Ok(match self.msg_type {
|
||||
RequestMsgType::Ping => RequestRef::Ping(self.buf.ping_request()?),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_prefix(self) -> anyhow::Result<Self> {
|
||||
self.ensure_fit()?;
|
||||
let point = self.target_size();
|
||||
let Self { buf, msg_type } = self;
|
||||
let (buf, _) = buf.split_at(point);
|
||||
Ok(Self { buf, msg_type })
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_suffix(self) -> anyhow::Result<Self> {
|
||||
self.ensure_fit()?;
|
||||
let point = self.buf.len() - self.target_size();
|
||||
let Self { buf, msg_type } = self;
|
||||
let (buf, _) = buf.split_at(point);
|
||||
Ok(Self { buf, msg_type })
|
||||
}
|
||||
|
||||
pub fn ensure_fit(&self) -> anyhow::Result<()> {
|
||||
let have = self.buf.len();
|
||||
let need = self.target_size();
|
||||
ensure!(
|
||||
need <= have,
|
||||
"Buffer is undersized at {have} bytes (need {need} bytes)!"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RequestRef<B> {
|
||||
Ping(Ref<B, PingRequest>),
|
||||
}
|
||||
|
||||
impl<B> RequestRef<B>
|
||||
where
|
||||
B: ByteSlice,
|
||||
{
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
match self {
|
||||
Self::Ping(r) => r.bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> RequestRef<B>
|
||||
where
|
||||
B: ByteSliceMut,
|
||||
{
|
||||
pub fn bytes_mut(&mut self) -> &[u8] {
|
||||
match self {
|
||||
Self::Ping(r) => r.bytes_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
103
rosenpass/src/api/boilerplate/request_response.rs
Normal file
103
rosenpass/src/api/boilerplate/request_response.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use rosenpass_util::zerocopy::{
|
||||
RefMaker, ZerocopyEmancipateExt, ZerocopyEmancipateMutExt, ZerocopySliceExt,
|
||||
};
|
||||
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||
|
||||
use super::{Message, PingRequest, PingResponse};
|
||||
use super::{RequestRef, ResponseRef, ZerocopyResponseMakerSetupMessageExt};
|
||||
|
||||
pub trait RequestMsg: Sized + Message {
|
||||
type ResponseMsg: ResponseMsg;
|
||||
|
||||
fn zk_response_maker<B: ByteSlice>(buf: B) -> RefMaker<B, Self::ResponseMsg> {
|
||||
buf.zk_ref_maker()
|
||||
}
|
||||
|
||||
fn setup_response<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
|
||||
Self::zk_response_maker(buf).setup_msg()
|
||||
}
|
||||
|
||||
fn setup_response_from_prefix<B: ByteSliceMut>(
|
||||
buf: B,
|
||||
) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
|
||||
Self::zk_response_maker(buf).from_prefix()?.setup_msg()
|
||||
}
|
||||
|
||||
fn setup_response_from_suffix<B: ByteSliceMut>(
|
||||
buf: B,
|
||||
) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
|
||||
Self::zk_response_maker(buf).from_prefix()?.setup_msg()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResponseMsg: Message {
|
||||
type RequestMsg: RequestMsg;
|
||||
}
|
||||
|
||||
impl RequestMsg for PingRequest {
|
||||
type ResponseMsg = PingResponse;
|
||||
}
|
||||
|
||||
impl ResponseMsg for PingResponse {
|
||||
type RequestMsg = PingRequest;
|
||||
}
|
||||
|
||||
pub type PingPair<B1, B2> = (Ref<B1, PingRequest>, Ref<B2, PingResponse>);
|
||||
|
||||
pub enum RequestResponsePair<B1, B2> {
|
||||
Ping(PingPair<B1, B2>),
|
||||
}
|
||||
|
||||
impl<B1, B2> From<PingPair<B1, B2>> for RequestResponsePair<B1, B2> {
|
||||
fn from(v: PingPair<B1, B2>) -> Self {
|
||||
RequestResponsePair::Ping(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B1, B2> RequestResponsePair<B1, B2>
|
||||
where
|
||||
B1: ByteSlice,
|
||||
B2: ByteSlice,
|
||||
{
|
||||
pub fn both(&self) -> (RequestRef<&[u8]>, ResponseRef<&[u8]>) {
|
||||
match self {
|
||||
Self::Ping((req, res)) => {
|
||||
let req = RequestRef::Ping(req.emancipate());
|
||||
let res = ResponseRef::Ping(res.emancipate());
|
||||
(req, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self) -> RequestRef<&[u8]> {
|
||||
self.both().0
|
||||
}
|
||||
|
||||
pub fn response(&self) -> ResponseRef<&[u8]> {
|
||||
self.both().1
|
||||
}
|
||||
}
|
||||
|
||||
impl<B1, B2> RequestResponsePair<B1, B2>
|
||||
where
|
||||
B1: ByteSliceMut,
|
||||
B2: ByteSliceMut,
|
||||
{
|
||||
pub fn both_mut(&mut self) -> (RequestRef<&mut [u8]>, ResponseRef<&mut [u8]>) {
|
||||
match self {
|
||||
Self::Ping((req, res)) => {
|
||||
let req = RequestRef::Ping(req.emancipate_mut());
|
||||
let res = ResponseRef::Ping(res.emancipate_mut());
|
||||
(req, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_mut(&mut self) -> RequestRef<&mut [u8]> {
|
||||
self.both_mut().0
|
||||
}
|
||||
|
||||
pub fn response_mut(&mut self) -> ResponseRef<&mut [u8]> {
|
||||
self.both_mut().1
|
||||
}
|
||||
}
|
||||
108
rosenpass/src/api/boilerplate/response_ref.rs
Normal file
108
rosenpass/src/api/boilerplate/response_ref.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
// TODO: This is copied verbatim from ResponseRef…not pretty
|
||||
use anyhow::ensure;
|
||||
|
||||
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||
|
||||
use super::{ByteSliceRefExt, MessageAttributes, PingResponse, ResponseMsgType};
|
||||
|
||||
struct ResponseRefMaker<B> {
|
||||
buf: B,
|
||||
msg_type: ResponseMsgType,
|
||||
}
|
||||
|
||||
impl<B: ByteSlice> ResponseRef<B> {
|
||||
pub fn parse(buf: B) -> anyhow::Result<Self> {
|
||||
ResponseRefMaker::new(buf)?.parse()
|
||||
}
|
||||
|
||||
pub fn parse_from_prefix(buf: B) -> anyhow::Result<Self> {
|
||||
ResponseRefMaker::new(buf)?.from_prefix()?.parse()
|
||||
}
|
||||
|
||||
pub fn parse_from_suffix(buf: B) -> anyhow::Result<Self> {
|
||||
ResponseRefMaker::new(buf)?.from_suffix()?.parse()
|
||||
}
|
||||
|
||||
pub fn message_type(&self) -> ResponseMsgType {
|
||||
match self {
|
||||
Self::Ping(_) => ResponseMsgType::Ping,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> From<Ref<B, PingResponse>> for ResponseRef<B> {
|
||||
fn from(v: Ref<B, PingResponse>) -> Self {
|
||||
Self::Ping(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: ByteSlice> ResponseRefMaker<B> {
|
||||
fn new(buf: B) -> anyhow::Result<Self> {
|
||||
let msg_type = buf.deref().response_msg_type_from_prefix()?;
|
||||
Ok(Self { buf, msg_type })
|
||||
}
|
||||
|
||||
fn target_size(&self) -> usize {
|
||||
self.msg_type.message_size()
|
||||
}
|
||||
|
||||
fn parse(self) -> anyhow::Result<ResponseRef<B>> {
|
||||
Ok(match self.msg_type {
|
||||
ResponseMsgType::Ping => ResponseRef::Ping(self.buf.ping_response()?),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_prefix(self) -> anyhow::Result<Self> {
|
||||
self.ensure_fit()?;
|
||||
let point = self.target_size();
|
||||
let Self { buf, msg_type } = self;
|
||||
let (buf, _) = buf.split_at(point);
|
||||
Ok(Self { buf, msg_type })
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_suffix(self) -> anyhow::Result<Self> {
|
||||
self.ensure_fit()?;
|
||||
let point = self.buf.len() - self.target_size();
|
||||
let Self { buf, msg_type } = self;
|
||||
let (buf, _) = buf.split_at(point);
|
||||
Ok(Self { buf, msg_type })
|
||||
}
|
||||
|
||||
pub fn ensure_fit(&self) -> anyhow::Result<()> {
|
||||
let have = self.buf.len();
|
||||
let need = self.target_size();
|
||||
ensure!(
|
||||
need <= have,
|
||||
"Buffer is undersized at {have} bytes (need {need} bytes)!"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ResponseRef<B> {
|
||||
Ping(Ref<B, PingResponse>),
|
||||
}
|
||||
|
||||
impl<B> ResponseRef<B>
|
||||
where
|
||||
B: ByteSlice,
|
||||
{
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
match self {
|
||||
Self::Ping(r) => r.bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> ResponseRef<B>
|
||||
where
|
||||
B: ByteSliceMut,
|
||||
{
|
||||
pub fn bytes_mut(&mut self) -> &[u8] {
|
||||
match self {
|
||||
Self::Ping(r) => r.bytes_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
40
rosenpass/src/api/boilerplate/server.rs
Normal file
40
rosenpass/src/api/boilerplate/server.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use zerocopy::{ByteSlice, ByteSliceMut};
|
||||
|
||||
use super::{ByteSliceRefExt, Message, PingRequest, PingResponse, RequestRef, RequestResponsePair};
|
||||
|
||||
pub trait Server {
|
||||
fn ping(&mut self, req: &PingRequest, res: &mut PingResponse) -> anyhow::Result<()>;
|
||||
|
||||
fn dispatch<ReqBuf, ResBuf>(
|
||||
&mut self,
|
||||
p: &mut RequestResponsePair<ReqBuf, ResBuf>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
ReqBuf: ByteSlice,
|
||||
ResBuf: ByteSliceMut,
|
||||
{
|
||||
match p {
|
||||
RequestResponsePair::Ping((req, res)) => self.ping(req, res),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_message<ReqBuf, ResBuf>(&mut self, req: ReqBuf, res: ResBuf) -> anyhow::Result<usize>
|
||||
where
|
||||
ReqBuf: ByteSlice,
|
||||
ResBuf: ByteSliceMut,
|
||||
{
|
||||
let req = req.parse_request_from_prefix()?;
|
||||
// TODO: This is not pretty; This match should be moved into RequestRef
|
||||
let mut pair = match req {
|
||||
RequestRef::Ping(req) => {
|
||||
let mut res = res.ping_response_from_prefix()?;
|
||||
res.init();
|
||||
RequestResponsePair::Ping((req, res))
|
||||
}
|
||||
};
|
||||
self.dispatch(&mut pair)?;
|
||||
|
||||
let res_len = pair.request().bytes().len();
|
||||
Ok(res_len)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user