mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-06 21:00:52 -08:00
Compare commits
891 Commits
v0.2.2
...
dev/karo/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72e6542958 | ||
|
|
15ae4b4ae5 | ||
|
|
b5107c77d8 | ||
|
|
335584b187 | ||
|
|
3c0e167347 | ||
|
|
6f6fdef542 | ||
|
|
c839126e29 | ||
|
|
a1698f36a6 | ||
|
|
2d6550da0f | ||
|
|
bae336d633 | ||
|
|
6c929f7ddc | ||
|
|
41eb620751 | ||
|
|
8561aaf137 | ||
|
|
f0ee7a33c9 | ||
|
|
1d4a70f863 | ||
|
|
f4e8e4314b | ||
|
|
1b9be7519b | ||
|
|
c689f8e78a | ||
|
|
edcbf290fc | ||
|
|
31a5dbe420 | ||
|
|
a85f9b8e63 | ||
|
|
21ea526435 | ||
|
|
35e956e340 | ||
|
|
3371d7f00f | ||
|
|
3f2a9bb96b | ||
|
|
8dfa67a2dd | ||
|
|
f31d635df8 | ||
|
|
75702dfc03 | ||
|
|
3af479a27e | ||
|
|
e76e5b253f | ||
|
|
0d944afbd8 | ||
|
|
8d81be56f3 | ||
|
|
16b3914c46 | ||
|
|
ae060f7cfb | ||
|
|
afa6212264 | ||
|
|
3c744c253b | ||
|
|
53e6553c8b | ||
|
|
4cd2cdfcff | ||
|
|
3e03e47935 | ||
|
|
7003671cde | ||
|
|
91fc50c1e1 | ||
|
|
b1a7d94295 | ||
|
|
48b7bb2f14 | ||
|
|
77e3682820 | ||
|
|
8bad02bcda | ||
|
|
864407f90b | ||
|
|
4deee59e90 | ||
|
|
c82ed332f6 | ||
|
|
5ced547a07 | ||
|
|
bdaedc4e2a | ||
|
|
4e77e67f10 | ||
|
|
f33c3a6928 | ||
|
|
348650d507 | ||
|
|
c318cf7bac | ||
|
|
d9a6430472 | ||
|
|
9656fa7025 | ||
|
|
53ddad30f1 | ||
|
|
7e8e502bca | ||
|
|
d81649c1d1 | ||
|
|
da642186f2 | ||
|
|
ad6d053015 | ||
|
|
240a1f923d | ||
|
|
a538dee0c3 | ||
|
|
08ea045325 | ||
|
|
6b61823255 | ||
|
|
96ac01ff2e | ||
|
|
811c1746c1 | ||
|
|
91707cc430 | ||
|
|
73df0ceca7 | ||
|
|
9cc7a58ee7 | ||
|
|
5106ffd549 | ||
|
|
7fc6fd2f52 | ||
|
|
77b50b70b1 | ||
|
|
cf061bd0f5 | ||
|
|
196d459a2b | ||
|
|
5097d9fce1 | ||
|
|
cdf6e8369f | ||
|
|
d5eb996423 | ||
|
|
6c49f38e29 | ||
|
|
e021b9f11d | ||
|
|
49f384c380 | ||
|
|
7e590dd30e | ||
|
|
d98815fa7f | ||
|
|
dd105a4491 | ||
|
|
64ff326e14 | ||
|
|
37e71a4051 | ||
|
|
e90bc1b636 | ||
|
|
bbd7e7bb72 | ||
|
|
3d724f04d4 | ||
|
|
db9d0b642b | ||
|
|
50501f37fd | ||
|
|
39f99fbfea | ||
|
|
3ea1a824cc | ||
|
|
d496490916 | ||
|
|
740489544d | ||
|
|
22b980a61f | ||
|
|
a45812b2cd | ||
|
|
1025de2c64 | ||
|
|
b8e9519e26 | ||
|
|
c3def9744f | ||
|
|
e3d3584adb | ||
|
|
a1982e0245 | ||
|
|
4896cd6130 | ||
|
|
9aab9d2d2a | ||
|
|
108ca440fe | ||
|
|
03e408b7c2 | ||
|
|
67f387a190 | ||
|
|
745c3962bb | ||
|
|
f6971aa5ad | ||
|
|
b46cd636d2 | ||
|
|
f22f4aad7d | ||
|
|
a83589d76a | ||
|
|
508d46f2bc | ||
|
|
3fc3083a54 | ||
|
|
faa45a8540 | ||
|
|
77632d0725 | ||
|
|
7218b0a3f4 | ||
|
|
4266cbfb72 | ||
|
|
070d299329 | ||
|
|
15699710a0 | ||
|
|
ae418ffba7 | ||
|
|
e3f7773bac | ||
|
|
9ab754eb0b | ||
|
|
b055457d01 | ||
|
|
b3403e7120 | ||
|
|
abd5210ae4 | ||
|
|
03464e1be7 | ||
|
|
54fc904c15 | ||
|
|
ceff8b711a | ||
|
|
c84bbed3bd | ||
|
|
d453002230 | ||
|
|
e81612d2e3 | ||
|
|
d558bdb633 | ||
|
|
e8fb7206fc | ||
|
|
b47d3a9deb | ||
|
|
f7fb09bc44 | ||
|
|
db6530ef77 | ||
|
|
8f519b042d | ||
|
|
954162b61f | ||
|
|
c65abe7bd9 | ||
|
|
80885d81d7 | ||
|
|
d023108d3b | ||
|
|
417df7aa7f | ||
|
|
9dd00e04c1 | ||
|
|
1a8e220aa8 | ||
|
|
de0022f092 | ||
|
|
dbb891a2ed | ||
|
|
531ae0ef70 | ||
|
|
8bb54b9cca | ||
|
|
7566eadef8 | ||
|
|
ebf6403ea7 | ||
|
|
62d408eade | ||
|
|
d1cf6af531 | ||
|
|
5e6c85d73d | ||
|
|
3205f8c572 | ||
|
|
b21a95dbbd | ||
|
|
006946442a | ||
|
|
33901d598a | ||
|
|
944be10bd2 | ||
|
|
23cf60c7ec | ||
|
|
6f71767529 | ||
|
|
38f371e3d7 | ||
|
|
2dba9205e7 | ||
|
|
30c3de3f87 | ||
|
|
b16619b1d3 | ||
|
|
576ad5f6d0 | ||
|
|
6494518460 | ||
|
|
185e92108e | ||
|
|
253243a8c8 | ||
|
|
075d9ffff3 | ||
|
|
01a1408044 | ||
|
|
b84e0beae8 | ||
|
|
949a3e4d23 | ||
|
|
d61b137761 | ||
|
|
a1f41953b7 | ||
|
|
46ebb6f46c | ||
|
|
32ae8f7051 | ||
|
|
b94ddd980d | ||
|
|
44e46895aa | ||
|
|
2ddd1488b3 | ||
|
|
c9aad280b2 | ||
|
|
d7398d9bcf | ||
|
|
6d25c13fd1 | ||
|
|
2d2d109246 | ||
|
|
30e158f594 | ||
|
|
cf74584f51 | ||
|
|
793cfd227f | ||
|
|
54c8e91db4 | ||
|
|
1b0179e751 | ||
|
|
760ecdc457 | ||
|
|
6a9bbddde3 | ||
|
|
530f81b9d5 | ||
|
|
b96df1588c | ||
|
|
5a2555a327 | ||
|
|
ac3f21c4bd | ||
|
|
b36d30d89d | ||
|
|
62fe529d36 | ||
|
|
76d01ffaf9 | ||
|
|
576b17cd9c | ||
|
|
cbc1bb4be2 | ||
|
|
c8a084157e | ||
|
|
09f1353dcc | ||
|
|
43225c1fe8 | ||
|
|
8e41cfc0b4 | ||
|
|
69538622b4 | ||
|
|
45a7c17cdd | ||
|
|
b8ecdab8dc | ||
|
|
af9d83b472 | ||
|
|
f81e329a11 | ||
|
|
5e2c72ef99 | ||
|
|
88e7d1d1cb | ||
|
|
43a930d3f7 | ||
|
|
b5f6d07650 | ||
|
|
be3c3d3d61 | ||
|
|
fe60cea959 | ||
|
|
441988cf43 | ||
|
|
b40b7f4f2f | ||
|
|
da76d88170 | ||
|
|
e35955f99c | ||
|
|
87587399ed | ||
|
|
9fdba31b32 | ||
|
|
0bfe47e5b8 | ||
|
|
771dce3ac7 | ||
|
|
436c6e6f87 | ||
|
|
f093406c34 | ||
|
|
eadf70ee38 | ||
|
|
7ac0883970 | ||
|
|
b1658b83a0 | ||
|
|
27650e95a7 | ||
|
|
6ab4e1152c | ||
|
|
2c64da23f1 | ||
|
|
03cc609a1e | ||
|
|
3effcb313e | ||
|
|
fded3b2e79 | ||
|
|
1471bb6a9f | ||
|
|
7edf84bd4a | ||
|
|
5187e50bb7 | ||
|
|
fd5806ba55 | ||
|
|
8e50d38b38 | ||
|
|
377f2f40d2 | ||
|
|
9bae080c4d | ||
|
|
3392da5163 | ||
|
|
3109cf1ffc | ||
|
|
d2539e445f | ||
|
|
6dc58cc6c1 | ||
|
|
e3d16966c9 | ||
|
|
a5e6af4b49 | ||
|
|
24a71977f0 | ||
|
|
5f0ac579d7 | ||
|
|
4df994b5f0 | ||
|
|
e4e0a9e661 | ||
|
|
742e037936 | ||
|
|
b5848af799 | ||
|
|
4982e40084 | ||
|
|
c1ae3268c6 | ||
|
|
524ec68f3f | ||
|
|
184603aa2c | ||
|
|
ec6706ffeb | ||
|
|
7571670e71 | ||
|
|
0d7dd99d96 | ||
|
|
c78a9cb777 | ||
|
|
dd0db53e8b | ||
|
|
422acf9891 | ||
|
|
877c15a018 | ||
|
|
55d7f8b1c1 | ||
|
|
199ff63a06 | ||
|
|
47b556e317 | ||
|
|
f87e2cb31b | ||
|
|
58e1c8fbff | ||
|
|
c89c7d7acf | ||
|
|
a5b876f119 | ||
|
|
c2f50f47b3 | ||
|
|
53168dc62d | ||
|
|
2cfe703118 | ||
|
|
a2d7c3aaa6 | ||
|
|
1aa111570e | ||
|
|
a91d61f9f0 | ||
|
|
ff7827c24e | ||
|
|
255e377d29 | ||
|
|
50505d81cc | ||
|
|
10484cc6d4 | ||
|
|
d27e602f43 | ||
|
|
73f6b33dbb | ||
|
|
a279dfc0b1 | ||
|
|
caf2f6bfec | ||
|
|
d398ad369e | ||
|
|
00696321ff | ||
|
|
d807a1bca7 | ||
|
|
4daf97b2ee | ||
|
|
b394e302ab | ||
|
|
198bc2d5f2 | ||
|
|
fc2f535eae | ||
|
|
302e249f08 | ||
|
|
d8fe3eba5f | ||
|
|
35519e7baa | ||
|
|
78af5d1dc4 | ||
|
|
61b8b28e86 | ||
|
|
26f77924f8 | ||
|
|
2e0e2cfa0c | ||
|
|
9cc860fdeb | ||
|
|
a537eb3e1b | ||
|
|
ea233bf137 | ||
|
|
db8796ab40 | ||
|
|
0353c82729 | ||
|
|
ae3fbde0a3 | ||
|
|
51d4dede15 | ||
|
|
4725a2d628 | ||
|
|
a6bac74d48 | ||
|
|
b9a34f4238 | ||
|
|
46e855b266 | ||
|
|
c0b91fd729 | ||
|
|
97dff8453d | ||
|
|
a3d4686104 | ||
|
|
cee0678817 | ||
|
|
a996f194c7 | ||
|
|
447be89414 | ||
|
|
ef4f550abc | ||
|
|
4737cd2b2a | ||
|
|
9336794e4d | ||
|
|
096bac6ee5 | ||
|
|
161826979a | ||
|
|
c435b772d2 | ||
|
|
8805ef7c38 | ||
|
|
cca02dc8d1 | ||
|
|
d4350195eb | ||
|
|
1c5e4ecf95 | ||
|
|
b15947b815 | ||
|
|
cacbf8535c | ||
|
|
f6d9da4a18 | ||
|
|
68f73e264d | ||
|
|
d5f68dcbd2 | ||
|
|
96581ed118 | ||
|
|
553b058759 | ||
|
|
85286c146f | ||
|
|
0f58b36c5b | ||
|
|
737781c8bc | ||
|
|
4ea1c76b81 | ||
|
|
5251721bcf | ||
|
|
a789f801ab | ||
|
|
be06f8adec | ||
|
|
03d3c70e2e | ||
|
|
94ba99d89b | ||
|
|
667a994253 | ||
|
|
9561ea4a47 | ||
|
|
fb641f8568 | ||
|
|
6e16956bc7 | ||
|
|
eeb738b649 | ||
|
|
2d20ad6335 | ||
|
|
df3d1821c8 | ||
|
|
6048ebd3d9 | ||
|
|
cd7558594f | ||
|
|
022cdc4ffa | ||
|
|
06d4e289a5 | ||
|
|
f9dce3fc9a | ||
|
|
d9f3c8fb96 | ||
|
|
0ea9f1061e | ||
|
|
737f0bc9f9 | ||
|
|
32ebd18107 | ||
|
|
f04cff6d57 | ||
|
|
2c1a0a7451 | ||
|
|
74fdb44680 | ||
|
|
c3adbb7cf3 | ||
|
|
fa583ec6ae | ||
|
|
aa76db1e1c | ||
|
|
c5699b5259 | ||
|
|
d3c52fdf64 | ||
|
|
b18f05ae19 | ||
|
|
d8839ba341 | ||
|
|
7022a93378 | ||
|
|
c9da9b8591 | ||
|
|
b483612cb7 | ||
|
|
a30805f8a0 | ||
|
|
a9b0a90ab5 | ||
|
|
2bc138e614 | ||
|
|
f97781039f | ||
|
|
5eda161cf2 | ||
|
|
a473fe6d9b | ||
|
|
e2c46f1ff0 | ||
|
|
c8b804b39d | ||
|
|
e56798b04c | ||
|
|
b76d18e3c8 | ||
|
|
a9792c3143 | ||
|
|
cb2c1c12ee | ||
|
|
08514d69e5 | ||
|
|
baf2d68070 | ||
|
|
cc7f7a4b4d | ||
|
|
5b701631b5 | ||
|
|
402158b706 | ||
|
|
e95636bf70 | ||
|
|
744e2bcf3e | ||
|
|
8c82ca18fb | ||
|
|
208e79c3a7 | ||
|
|
6ee023c9e9 | ||
|
|
6f75d34934 | ||
|
|
6b364a35dc | ||
|
|
2b6d10f0aa | ||
|
|
cb380b89d1 | ||
|
|
f703933e7f | ||
|
|
d02a5d2eb7 | ||
|
|
c7273e6f88 | ||
|
|
85eca49a5b | ||
|
|
9943f1336b | ||
|
|
bb2a0732cc | ||
|
|
1275b992a0 | ||
|
|
196767964f | ||
|
|
d4e9770fe6 | ||
|
|
8e2f6991d1 | ||
|
|
af0db88939 | ||
|
|
6601742903 | ||
|
|
9436281350 | ||
|
|
f3399907b9 | ||
|
|
0cea8c5eff | ||
|
|
5b3f4da23e | ||
|
|
c13badb697 | ||
|
|
cc7757a0db | ||
|
|
d267916445 | ||
|
|
03bc89a582 | ||
|
|
19b31bcdf0 | ||
|
|
939d216027 | ||
|
|
05fbaff2dc | ||
|
|
1d1c0e9da7 | ||
|
|
e19b724673 | ||
|
|
f879ad5020 | ||
|
|
29e7087cb5 | ||
|
|
637a08d222 | ||
|
|
6416c247f4 | ||
|
|
4b3b7e41e4 | ||
|
|
325fb915f0 | ||
|
|
43cb0c09c5 | ||
|
|
0836a2eb28 | ||
|
|
ca7df013d5 | ||
|
|
1209d68718 | ||
|
|
8806494899 | ||
|
|
582d27351a | ||
|
|
61136d79eb | ||
|
|
71bd406201 | ||
|
|
ce63cf534a | ||
|
|
d3ff19bdb9 | ||
|
|
3b6d0822d6 | ||
|
|
533afea129 | ||
|
|
da5b281b96 | ||
|
|
b9e873e534 | ||
|
|
a3b339b180 | ||
|
|
b4347c1382 | ||
|
|
0745019e10 | ||
|
|
2369006342 | ||
|
|
0fa6176d06 | ||
|
|
22bdeaf8f1 | ||
|
|
5731272844 | ||
|
|
bc7cef9de0 | ||
|
|
4cdcc35c3e | ||
|
|
a8f1292cbf | ||
|
|
ae5c5ed2b4 | ||
|
|
c483452a6a | ||
|
|
4ce331d299 | ||
|
|
d81eb7e2ed | ||
|
|
61043500ba | ||
|
|
9c4752559d | ||
|
|
6aec7acdb8 | ||
|
|
337cc1b4b4 | ||
|
|
387a266a49 | ||
|
|
179970b905 | ||
|
|
8b769e04c1 | ||
|
|
810bdf5519 | ||
|
|
d3a666bea0 | ||
|
|
2b8f780584 | ||
|
|
6aea3c0c1f | ||
|
|
e4fdfcae08 | ||
|
|
48e629fff7 | ||
|
|
6321bb36fc | ||
|
|
2f9ff487ba | ||
|
|
c0c06cd1dc | ||
|
|
e9772effa6 | ||
|
|
cf68f15674 | ||
|
|
dd5d45cdc9 | ||
|
|
17a6aed8a6 | ||
|
|
3f9926e353 | ||
|
|
f4ab2ac891 | ||
|
|
de51c1005f | ||
|
|
1e2cd589b1 | ||
|
|
02bc485d97 | ||
|
|
3ae52b9824 | ||
|
|
cbf361206b | ||
|
|
398da99df2 | ||
|
|
acfbb67abe | ||
|
|
c407b8b006 | ||
|
|
bc7213d8c0 | ||
|
|
69e68aad2c | ||
|
|
9b07f5803b | ||
|
|
5ce572b739 | ||
|
|
d9f8fa0092 | ||
|
|
a5208795f6 | ||
|
|
0959148305 | ||
|
|
f2bc3a8b64 | ||
|
|
06529df2c0 | ||
|
|
128c77f77a | ||
|
|
501cc9bb05 | ||
|
|
9ad5277a90 | ||
|
|
0cbcaeaf98 | ||
|
|
687ef3f6f8 | ||
|
|
b0706354d3 | ||
|
|
c1e86daec8 | ||
|
|
18a286e688 | ||
|
|
cb92313391 | ||
|
|
5cd30b4c13 | ||
|
|
76d8d38744 | ||
|
|
f63f0bbc2e | ||
|
|
4a449e6502 | ||
|
|
1e6d2df004 | ||
|
|
3fa9aadda2 | ||
|
|
0c79a4ce95 | ||
|
|
036960b5b1 | ||
|
|
e7258849cb | ||
|
|
8c88f68990 | ||
|
|
cf20536576 | ||
|
|
72e18e3ec2 | ||
|
|
6040156a0e | ||
|
|
d3b318b413 | ||
|
|
3a49345138 | ||
|
|
4ec7813259 | ||
|
|
db31da14d3 | ||
|
|
4c20efc8a8 | ||
|
|
c81d484294 | ||
|
|
cc578169d6 | ||
|
|
91527702f1 | ||
|
|
0179f1c673 | ||
|
|
2238919657 | ||
|
|
d913e19883 | ||
|
|
1555d0897b | ||
|
|
abdbf8f3da | ||
|
|
9f78531979 | ||
|
|
624d8d2f44 | ||
|
|
9bbf9433e6 | ||
|
|
77760d71df | ||
|
|
53e560191f | ||
|
|
93cd266c68 | ||
|
|
594f894206 | ||
|
|
a831e01a5c | ||
|
|
0884641d64 | ||
|
|
ae85d0ed2b | ||
|
|
163f66f20e | ||
|
|
3caff91515 | ||
|
|
24eebe29a1 | ||
|
|
1d2fa7d038 | ||
|
|
edf1e774c1 | ||
|
|
7a31b57227 | ||
|
|
d5a8c85abe | ||
|
|
48f7ff93e3 | ||
|
|
5f6c36e773 | ||
|
|
7b3b7612cf | ||
|
|
c1704b1464 | ||
|
|
2785aaf783 | ||
|
|
15002a74cc | ||
|
|
0fe2d9825b | ||
|
|
ab805dae75 | ||
|
|
08653c3338 | ||
|
|
520c8c6eaa | ||
|
|
258efe408c | ||
|
|
fd0f35b279 | ||
|
|
8808ed5dbc | ||
|
|
6fc45cab53 | ||
|
|
1f7196e473 | ||
|
|
c359b87d0c | ||
|
|
355b48169b | ||
|
|
274d245bed | ||
|
|
065b0fcc8a | ||
|
|
191fb10663 | ||
|
|
3faa84117f | ||
|
|
fda75a0184 | ||
|
|
96b1f6c0d3 | ||
|
|
fb73c68626 | ||
|
|
42b0e23695 | ||
|
|
c58f832727 | ||
|
|
7b6a9eebc1 | ||
|
|
4554dc4bb3 | ||
|
|
465c6beaab | ||
|
|
1853e0a3c0 | ||
|
|
245d4d1a0f | ||
|
|
d5d15cd9bc | ||
|
|
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 | ||
|
|
35f9c3bf68 | ||
|
|
ff44002b7c | ||
|
|
0ce8304c69 | ||
|
|
5f91feb3a4 | ||
|
|
baebb8632f | ||
|
|
cb97f90581 | ||
|
|
3d13caa37b | ||
|
|
54ecfaddcf |
9
.ci/boot_race/a.toml
Normal file
9
.ci/boot_race/a.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
public_key = "rp-a-public-key"
|
||||
secret_key = "rp-a-secret-key"
|
||||
listen = ["127.0.0.1:9999"]
|
||||
verbosity = "Verbose"
|
||||
|
||||
[[peers]]
|
||||
public_key = "rp-b-public-key"
|
||||
endpoint = "127.0.0.1:9998"
|
||||
key_out = "rp-b-key-out.txt"
|
||||
9
.ci/boot_race/b.toml
Normal file
9
.ci/boot_race/b.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
public_key = "rp-b-public-key"
|
||||
secret_key = "rp-b-secret-key"
|
||||
listen = ["127.0.0.1:9998"]
|
||||
verbosity = "Verbose"
|
||||
|
||||
[[peers]]
|
||||
public_key = "rp-a-public-key"
|
||||
endpoint = "127.0.0.1:9999"
|
||||
key_out = "rp-a-key-out.txt"
|
||||
48
.ci/boot_race/run.sh
Normal file
48
.ci/boot_race/run.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
iterations="$1"
|
||||
sleep_time="$2"
|
||||
config_a="$3"
|
||||
config_b="$4"
|
||||
|
||||
PWD="$(pwd)"
|
||||
EXEC="$PWD/target/release/rosenpass"
|
||||
|
||||
i=0
|
||||
while [ "$i" -ne "$iterations" ]; do
|
||||
echo "=> Iteration $i"
|
||||
|
||||
# flush the PSK files
|
||||
echo "A" >rp-a-key-out.txt
|
||||
echo "B" >rp-b-key-out.txt
|
||||
|
||||
# start the two instances
|
||||
echo "Starting instance A"
|
||||
"$EXEC" exchange-config "$config_a" &
|
||||
PID_A=$!
|
||||
sleep "$sleep_time"
|
||||
echo "Starting instance B"
|
||||
"$EXEC" exchange-config "$config_b" &
|
||||
PID_B=$!
|
||||
|
||||
# give the key exchange some time to complete
|
||||
sleep 3
|
||||
|
||||
# kill the instances
|
||||
kill $PID_A
|
||||
kill $PID_B
|
||||
|
||||
# compare the keys
|
||||
if cmp -s rp-a-key-out.txt rp-b-key-out.txt; then
|
||||
echo "Keys match"
|
||||
else
|
||||
echo "::warning title=Key Exchange Race Condition::The key exchange resulted in different keys. Delay was ${sleep_time}s."
|
||||
# TODO: set this to 1 when the race condition is fixed
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# give the instances some time to shut down
|
||||
sleep 2
|
||||
|
||||
i=$((i + 1))
|
||||
done
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env nu
|
||||
|
||||
use log *
|
||||
use std log
|
||||
|
||||
# cd to git root
|
||||
cd (git rev-parse --show-toplevel)
|
||||
@@ -32,9 +32,9 @@ let systems_map = {
|
||||
# aarch64-darwin
|
||||
# aarch64-linux
|
||||
|
||||
i686-linux: ubuntu-latest,
|
||||
i686-linux: ubicloud-standard-2-ubuntu-2204,
|
||||
x86_64-darwin: macos-13,
|
||||
x86_64-linux: ubuntu-latest
|
||||
x86_64-linux: ubicloud-standard-2-ubuntu-2204
|
||||
}
|
||||
|
||||
let targets = (get-attr-names ".#packages"
|
||||
@@ -61,14 +61,13 @@ mut release_workflow = {
|
||||
|
||||
let runner_setup = [
|
||||
{
|
||||
uses: "actions/checkout@v3"
|
||||
uses: "actions/checkout@v4"
|
||||
}
|
||||
{
|
||||
uses: "cachix/install-nix-action@v22",
|
||||
with: { nix_path: "nixpkgs=channel:nixos-unstable" }
|
||||
uses: "cachix/install-nix-action@v30",
|
||||
}
|
||||
{
|
||||
uses: "cachix/cachix-action@v12",
|
||||
uses: "cachix/cachix-action@v15",
|
||||
with: {
|
||||
name: rosenpass,
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
@@ -116,6 +115,7 @@ for system in ($targets | columns) {
|
||||
} }
|
||||
| filter {|it| $it.needed}
|
||||
| each {|it| job-id $system $it.name}
|
||||
| sort
|
||||
)
|
||||
|
||||
mut new_job = {
|
||||
@@ -153,7 +153,7 @@ for system in ($targets | columns) {
|
||||
}
|
||||
{
|
||||
name: Release,
|
||||
uses: "softprops/action-gh-release@v1",
|
||||
uses: "softprops/action-gh-release@v2",
|
||||
with: {
|
||||
draft: "${{ contains(github.ref_name, 'rc') }}",
|
||||
prerelease: "${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}",
|
||||
@@ -181,7 +181,7 @@ $cachix_workflow.jobs = ($cachix_workflow.jobs | insert $"($system)---whitepaper
|
||||
}
|
||||
{
|
||||
name: "Deploy PDF artifacts",
|
||||
uses: "peaceiris/actions-gh-pages@v3",
|
||||
uses: "peaceiris/actions-gh-pages@v4",
|
||||
with: {
|
||||
github_token: "${{ secrets.GITHUB_TOKEN }}",
|
||||
publish_dir: result/,
|
||||
@@ -197,4 +197,4 @@ $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/
|
||||
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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
1
.dockerignore
Symbolic link
1
.dockerignore
Symbolic link
@@ -0,0 +1 @@
|
||||
.gitignore
|
||||
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
|
||||
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
103
.github/workflows/bench-primitives.yml
vendored
Normal file
103
.github/workflows/bench-primitives.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
name: rosenpass-ciphers - primitives - benchmark
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
#pull_request:
|
||||
push:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
prim-benchmark:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
system: ["x86_64-linux", "i686-linux"]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Install nix
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27 # A popular action for installing Nix
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Set up environment
|
||||
|
||||
- name: 🛠️ Prepare Benchmark Path
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BRANCH_NAME: ${{ github.ref_name }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
case "$EVENT_NAME" in
|
||||
"push")
|
||||
echo "BENCH_PATH=branch/$BRANCH_NAME" >> $GITHUB_ENV
|
||||
;;
|
||||
"pull_request")
|
||||
echo "BENCH_PATH=pull/$PR_NUMBER" >> $GITHUB_ENV
|
||||
;;
|
||||
*)
|
||||
echo "don't know benchmark path for event of type $EVENT_NAME, aborting"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
# Benchmarks ...
|
||||
|
||||
- name: 🏃🏻♀️ Benchmarks (using Nix as shell)
|
||||
working-directory: ciphers
|
||||
run: nix develop ".#devShells.${{ matrix.system }}.benchmarks" --command cargo bench -F bench --bench primitives --verbose -- --output-format bencher | tee ../bench-primitives.txt
|
||||
|
||||
- name: Extract benchmarks
|
||||
uses: cryspen/benchmark-data-extract-transform@v2
|
||||
with:
|
||||
name: rosenpass-ciphers primitives benchmarks
|
||||
tool: "cargo"
|
||||
os: ${{ matrix.system }}
|
||||
output-file-path: bench-primitives.txt
|
||||
data-out-path: bench-primitives-os.json
|
||||
|
||||
- name: Fix up 'os' label in benchmark data
|
||||
run: jq 'map(with_entries(.key |= if . == "os" then "operating system" else . end))' <bench-primitives-os.json >bench-primitives.json
|
||||
|
||||
- name: Upload benchmarks
|
||||
uses: cryspen/benchmark-upload-and-plot-action@v3
|
||||
with:
|
||||
name: Crypto Primitives Benchmarks
|
||||
group-by: "operating system,primitive,algorithm"
|
||||
schema: "operating system,primitive,algorithm,implementation,operation,length"
|
||||
input-data-path: bench-primitives.json
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# NOTE: pushes to current repository
|
||||
gh-repository: github.com/${{ github.repository }}
|
||||
auto-push: true
|
||||
fail-on-alert: true
|
||||
base-path: benchmarks/
|
||||
|
||||
ciphers-primitives-bench-status:
|
||||
if: ${{ always() }}
|
||||
needs: [prim-benchmark]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Successful
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
run: exit 0
|
||||
- name: Failing
|
||||
if: ${{ (contains(needs.*.result, 'failure')) }}
|
||||
run: exit 1
|
||||
90
.github/workflows/bench-protocol.yml
vendored
Normal file
90
.github/workflows/bench-protocol.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: rosenpass - protocol - benchmark
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
#pull_request:
|
||||
push:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
proto-benchmark:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
system: ["x86_64-linux", "i686-linux"]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Install nix
|
||||
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@v27 # A popular action for installing Nix
|
||||
with:
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Set up environment
|
||||
|
||||
- name: 🛠️ Prepare Benchmark Path
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BRANCH_NAME: ${{ github.ref_name }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
case "$EVENT_NAME" in
|
||||
"push")
|
||||
echo "BENCH_PATH=branch/$BRANCH_NAME" >> $GITHUB_ENV
|
||||
;;
|
||||
"pull_request")
|
||||
echo "BENCH_PATH=pull/$PR_NUMBER" >> $GITHUB_ENV
|
||||
;;
|
||||
*)
|
||||
echo "don't know benchmark path for event of type $EVENT_NAME, aborting"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
# Benchmarks ...
|
||||
|
||||
- name: 🏃🏻♀️ Benchmarks
|
||||
run: nix develop ".#devShells.${{ matrix.system }}.benchmarks" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose >bench-protocol.json
|
||||
|
||||
- name: Upload benchmarks
|
||||
uses: cryspen/benchmark-upload-and-plot-action@v3
|
||||
with:
|
||||
name: Protocol Benchmarks
|
||||
group-by: "operating system,architecture,protocol version,run time"
|
||||
schema: "operating system,architecture,protocol version,run time,name"
|
||||
input-data-path: bench-protocol.json
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# NOTE: pushes to current repository
|
||||
gh-repository: github.com/${{ github.repository }}
|
||||
auto-push: true
|
||||
fail-on-alert: true
|
||||
base-path: benchmarks/
|
||||
|
||||
ciphers-protocol-bench-status:
|
||||
if: ${{ always() }}
|
||||
needs: [proto-benchmark]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Successful
|
||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||
run: exit 0
|
||||
- name: Failing
|
||||
if: ${{ (contains(needs.*.result, 'failure')) }}
|
||||
run: exit 1
|
||||
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!
|
||||
4
.github/workflows/doc-upload.yml
vendored
4
.github/workflows/doc-upload.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Clone rosenpass-website repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: rosenpass/rosenpass-website
|
||||
ref: main
|
||||
|
||||
288
.github/workflows/docker.yaml
vendored
Normal file
288
.github/workflows/docker.yaml
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
name: Build Docker Images
|
||||
|
||||
# Run this job on all non-pull-request events,
|
||||
# or if Docker-related files are changed in a pull request.
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
tags:
|
||||
- "v*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "docker/Dockerfile"
|
||||
- ".github/workflows/docker.yaml"
|
||||
branches:
|
||||
- "main"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
# --------------------------------
|
||||
# 1. BUILD & TEST
|
||||
# --------------------------------
|
||||
build-and-test-rp:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, arm64]
|
||||
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build (no push) and Load
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
# no pushing here, so we can test locally
|
||||
push: false
|
||||
# load the built image into the local Docker daemon on the runner
|
||||
load: true
|
||||
target: rosenpass
|
||||
tags: rosenpass:test
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
- name: Integration Test - Standalone Key Exchange
|
||||
run: |
|
||||
# Create separate workdirs
|
||||
mkdir -p workdir-server workdir-client
|
||||
|
||||
# Create a Docker network
|
||||
docker network create -d bridge rp
|
||||
|
||||
echo "=== GENERATE SERVER KEYS ==="
|
||||
docker run --rm \
|
||||
-v $PWD/workdir-server:/workdir \
|
||||
rosenpass:test gen-keys \
|
||||
--public-key=workdir/server-public \
|
||||
--secret-key=workdir/server-secret
|
||||
|
||||
echo "=== GENERATE CLIENT KEYS ==="
|
||||
docker run --rm \
|
||||
-v $PWD/workdir-client:/workdir \
|
||||
rosenpass:test gen-keys \
|
||||
--public-key=workdir/client-public \
|
||||
--secret-key=workdir/client-secret
|
||||
|
||||
echo "=== SHARE PUBLIC KEYS ==="
|
||||
cp workdir-client/client-public workdir-server/client-public
|
||||
cp workdir-server/server-public workdir-client/server-public
|
||||
|
||||
echo "=== START SERVER CONTAINER ==="
|
||||
docker run -d --rm \
|
||||
--name rpserver \
|
||||
--network rp \
|
||||
-v $PWD/workdir-server:/workdir \
|
||||
rosenpass:test exchange \
|
||||
private-key workdir/server-secret \
|
||||
public-key workdir/server-public \
|
||||
listen 0.0.0.0:9999 \
|
||||
peer public-key workdir/client-public \
|
||||
outfile workdir/server-sharedkey
|
||||
|
||||
# Get the container IP of the server
|
||||
SERVER_IP=$(docker inspect --format='{{.NetworkSettings.Networks.rp.IPAddress}}' rpserver)
|
||||
echo "SERVER_IP=$SERVER_IP"
|
||||
|
||||
echo "=== START CLIENT CONTAINER ==="
|
||||
docker run -d --rm \
|
||||
--name rpclient \
|
||||
--network rp \
|
||||
-v $PWD/workdir-client:/workdir \
|
||||
rosenpass:test exchange \
|
||||
private-key workdir/client-secret \
|
||||
public-key workdir/client-public \
|
||||
peer public-key workdir/server-public \
|
||||
endpoint ${SERVER_IP}:9999 \
|
||||
outfile workdir/client-sharedkey
|
||||
|
||||
echo "=== COMPARE SHARED KEYS ==="
|
||||
echo "Waiting up to 30 seconds for the server to generate 'server-sharedkey'..."
|
||||
for i in $(seq 1 30); do
|
||||
if [ -f "workdir-server/server-sharedkey" ]; then
|
||||
echo "server-sharedkey found!"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
sudo cmp workdir-server/server-sharedkey workdir-client/client-sharedkey
|
||||
|
||||
echo "Standalone Key Exchange test OK."
|
||||
# --------------------------------
|
||||
# 2. PUSH (only if tests pass)
|
||||
# --------------------------------
|
||||
docker-image-rp:
|
||||
needs:
|
||||
- build-and-test-rp
|
||||
# Skip if this is not a PR. Then we want to push this image.
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
# Use a matrix to build for both AMD64 and ARM64
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, arm64]
|
||||
# Switch the runner based on the architecture
|
||||
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository_owner }}/rp
|
||||
labels: |
|
||||
maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
|
||||
org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
|
||||
org.opencontainers.image.title=Rosenpass
|
||||
org.opencontainers.image.description=The rp command-line integrates Rosenpass and WireGuard to help you create a VPN
|
||||
org.opencontainers.image.vendor=Rosenpass e.V.
|
||||
org.opencontainers.image.licenses=MIT OR Apache-2.0
|
||||
org.opencontainers.image.url=https://rosenpass.eu
|
||||
org.opencontainers.image.documentation=https://rosenpass.eu/docs/
|
||||
org.opencontainers.image.source=https://github.com/rosenpass/rosenpass
|
||||
|
||||
- name: Log in to registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ghcr.io/${{ github.repository_owner }}/rp
|
||||
target: rp
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-rp-${{ matrix.arch }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
docker-image-rosenpass:
|
||||
needs:
|
||||
- build-and-test-rp
|
||||
# Skip if this is not a PR. Then we want to push this image.
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
# Use a matrix to build for both AMD64 and ARM64
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, arm64]
|
||||
# Switch the runner based on the architecture
|
||||
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository_owner }}/rosenpass
|
||||
labels: |
|
||||
maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
|
||||
org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
|
||||
org.opencontainers.image.title=Rosenpass
|
||||
org.opencontainers.image.description=Reference implementation of the protocol rosenpass protocol
|
||||
org.opencontainers.image.vendor=Rosenpass e.V.
|
||||
org.opencontainers.image.licenses=MIT OR Apache-2.0
|
||||
org.opencontainers.image.url=https://rosenpass.eu
|
||||
org.opencontainers.image.documentation=https://rosenpass.eu/docs/
|
||||
org.opencontainers.image.source=https://github.com/rosenpass/rosenpass
|
||||
|
||||
- name: Log in to registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: ghcr.io/${{ github.repository_owner }}/rosenpass
|
||||
target: rosenpass
|
||||
platforms: linux/${{ matrix.arch }}
|
||||
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-rosenpass-${{ matrix.arch }}
|
||||
path: ${{ runner.temp }}/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge-digests:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- docker-image-rosenpass
|
||||
- docker-image-rp
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
strategy:
|
||||
matrix:
|
||||
target: [rp, rosenpass]
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/digests
|
||||
pattern: digests-${{ matrix.target }}-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Log in to registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}
|
||||
tags: |
|
||||
type=edge,branch=main
|
||||
type=sha,branch=main
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}:${{ steps.meta.outputs.version }}
|
||||
19
.github/workflows/manual-mac-pr.yaml
vendored
Normal file
19
.github/workflows/manual-mac-pr.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: PR Validation on Mac
|
||||
on:
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
checks: write
|
||||
contents: write
|
||||
concurrency:
|
||||
group: manual-mac-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
qc:
|
||||
uses: ./.github/workflows/qc-mac.yaml
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
nix:
|
||||
uses: ./.github/workflows/nix-mac.yaml
|
||||
permissions:
|
||||
contents: write
|
||||
114
.github/workflows/nix-mac.yaml
vendored
Normal file
114
.github/workflows/nix-mac.yaml
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
name: Nix on Mac
|
||||
permissions:
|
||||
contents: write
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
aarch64-darwin---default:
|
||||
name: Build aarch64-darwin.default
|
||||
runs-on:
|
||||
- warp-macos-13-arm64-6x
|
||||
needs:
|
||||
- aarch64-darwin---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-darwin.default --print-build-logs
|
||||
aarch64-darwin---release-package:
|
||||
name: Build aarch64-darwin.release-package
|
||||
runs-on:
|
||||
- warp-macos-13-arm64-6x
|
||||
needs:
|
||||
- aarch64-darwin---rosenpass
|
||||
- aarch64-darwin---rp
|
||||
- aarch64-darwin---rosenpass-oci-image
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-darwin.release-package --print-build-logs
|
||||
aarch64-darwin---rosenpass:
|
||||
name: Build aarch64-darwin.rosenpass
|
||||
runs-on:
|
||||
- warp-macos-13-arm64-6x
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-darwin.rosenpass --print-build-logs
|
||||
aarch64-darwin---rp:
|
||||
name: Build aarch64-darwin.rp
|
||||
runs-on:
|
||||
- warp-macos-13-arm64-6x
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-darwin.rp --print-build-logs
|
||||
aarch64-darwin---rosenpass-oci-image:
|
||||
name: Build aarch64-darwin.rosenpass-oci-image
|
||||
runs-on:
|
||||
- warp-macos-13-arm64-6x
|
||||
needs:
|
||||
- aarch64-darwin---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.aarch64-darwin.rosenpass-oci-image --print-build-logs
|
||||
aarch64-darwin---check:
|
||||
name: Run Nix checks on aarch64-darwin
|
||||
runs-on:
|
||||
- warp-macos-13-arm64-6x
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Check
|
||||
run: nix flake check . --print-build-logs
|
||||
317
.github/workflows/nix.yaml
vendored
317
.github/workflows/nix.yaml
vendored
@@ -6,19 +6,24 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
i686-linux---default:
|
||||
name: Build i686-linux.default
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- i686-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -27,14 +32,14 @@ jobs:
|
||||
i686-linux---rosenpass:
|
||||
name: Build i686-linux.rosenpass
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -43,15 +48,15 @@ jobs:
|
||||
i686-linux---rosenpass-oci-image:
|
||||
name: Build i686-linux.rosenpass-oci-image
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- i686-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -60,96 +65,13 @@ jobs:
|
||||
i686-linux---check:
|
||||
name: Run Nix checks on i686-linux
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
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---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---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
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -158,15 +80,15 @@ jobs:
|
||||
x86_64-linux---default:
|
||||
name: Build x86_64-linux.default
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- x86_64-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -175,15 +97,15 @@ jobs:
|
||||
x86_64-linux---proof-proverif:
|
||||
name: Build x86_64-linux.proof-proverif
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- x86_64-linux---proverif-patched
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -192,14 +114,14 @@ jobs:
|
||||
x86_64-linux---proverif-patched:
|
||||
name: Build x86_64-linux.proverif-patched
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -208,82 +130,187 @@ jobs:
|
||||
x86_64-linux---release-package:
|
||||
name: Build x86_64-linux.release-package
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- x86_64-linux---rosenpass-static-oci-image
|
||||
- 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
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
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:
|
||||
# - ubicloud-standard-2-arm-ubuntu-2204
|
||||
# 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@v4
|
||||
# - uses: cachix/install-nix-action@v30
|
||||
# with:
|
||||
# nix_path: nixpkgs=channel:nixos-unstable
|
||||
# extra_nix_config: |
|
||||
# system = aarch64-linux
|
||||
# - uses: cachix/cachix-action@v15
|
||||
# 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
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
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:
|
||||
- ubicloud-standard-2-arm-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v15
|
||||
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:
|
||||
- ubicloud-standard-2-arm-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v15
|
||||
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
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- x86_64-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
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:
|
||||
- ubicloud-standard-2-arm-ubuntu-2204
|
||||
needs:
|
||||
- aarch64-linux---rosenpass
|
||||
steps:
|
||||
- run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
extra_nix_config: |
|
||||
system = aarch64-linux
|
||||
- uses: cachix/cachix-action@v15
|
||||
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
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
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:
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-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
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs:
|
||||
- x86_64-linux---rosenpass-static
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -292,14 +319,14 @@ jobs:
|
||||
x86_64-linux---whitepaper:
|
||||
name: Build x86_64-linux.whitepaper
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -308,13 +335,13 @@ jobs:
|
||||
x86_64-linux---check:
|
||||
name: Run Nix checks on x86_64-linux
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -322,14 +349,14 @@ jobs:
|
||||
run: nix flake check . --print-build-logs
|
||||
x86_64-linux---whitepaper-upload:
|
||||
name: Upload whitepaper x86_64-linux
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -338,7 +365,7 @@ jobs:
|
||||
- name: Build
|
||||
run: nix build .#packages.x86_64-linux.whitepaper --print-build-logs
|
||||
- name: Deploy PDF artifacts
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: result/
|
||||
|
||||
32
.github/workflows/qc-mac.yaml
vendored
Normal file
32
.github/workflows/qc-mac.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: QC Mac
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cargo-test-mac:
|
||||
runs-on: warp-macos-13-arm64-6x
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
# liboqs requires quite a lot of stack memory, thus we adjust
|
||||
# the default stack size picked for new threads (which is used
|
||||
# by `cargo test`) to be _big enough_. Setting it to 8 MiB
|
||||
- run: RUST_MIN_STACK=8388608 cargo test --workspace --all-features
|
||||
177
.github/workflows/qc.yaml
vendored
177
.github/workflows/qc.yaml
vendored
@@ -4,40 +4,96 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actionsx/prettier@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actionsx/prettier@v3
|
||||
with:
|
||||
args: --check .
|
||||
|
||||
shellcheck:
|
||||
name: Shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
|
||||
cargo-audit:
|
||||
runs-on: ubuntu-latest
|
||||
rustfmt:
|
||||
name: Rust code formatting
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install nightly toolchain
|
||||
run: |
|
||||
rustup toolchain install nightly
|
||||
rustup override set nightly
|
||||
- run: rustup component add rustfmt
|
||||
- name: Run Cargo Fmt
|
||||
run: cargo fmt --all --check
|
||||
- name: Run Rust Markdown code block Formatting Script
|
||||
run: bash format_rust_code.sh --mode check
|
||||
|
||||
cargo-bench:
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
# liboqs requires quite a lot of stack memory, thus we adjust
|
||||
# the default stack size picked for new threads (which is used
|
||||
# by `cargo test`) to be _big enough_. Setting it to 8 MiB
|
||||
- run: RUST_MIN_STACK=8388608 cargo bench --workspace --exclude rosenpass-fuzzing
|
||||
|
||||
mandoc:
|
||||
name: mandoc
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- name: Install mandoc
|
||||
run: sudo apt-get install -y mandoc
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check rp.1
|
||||
run: doc/check.sh doc/rp.1
|
||||
|
||||
cargo-audit:
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
cargo-clippy:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
@@ -47,18 +103,16 @@ jobs:
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- run: rustup component add clippy
|
||||
- name: Install libsodium
|
||||
run: sudo apt-get install -y libsodium-dev
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
cargo-doc:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
@@ -68,18 +122,16 @@ jobs:
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- run: rustup component add clippy
|
||||
- name: Install libsodium
|
||||
run: sudo apt-get install -y libsodium-dev
|
||||
# `--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: ubuntu-latest
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
@@ -88,19 +140,17 @@ jobs:
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install libsodium
|
||||
run: sudo apt-get install -y libsodium-dev
|
||||
# liboqs requires quite a lot of stack memory, thus we adjust
|
||||
# the default stack size picked for new threads (which is used
|
||||
# by `cargo test`) to be _big enough_. Setting it to 8 MiB
|
||||
- run: RUST_MIN_STACK=8388608 cargo test
|
||||
- run: RUST_MIN_STACK=8388608 cargo test --workspace --all-features
|
||||
|
||||
cargo-test-nix-devshell-x86_64-linux:
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
- ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
@@ -109,11 +159,78 @@ jobs:
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- run: nix develop --command cargo test
|
||||
- run: nix develop --command cargo test --workspace --all-features
|
||||
|
||||
cargo-fuzz:
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install nightly toolchain
|
||||
run: |
|
||||
rustup toolchain install nightly
|
||||
rustup override set 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: ubicloud-standard-2-ubuntu-2204
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: nightly
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install nightly toolchain
|
||||
run: |
|
||||
rustup toolchain install nightly
|
||||
rustup override set nightly
|
||||
- run: rustup component add llvm-tools-preview
|
||||
- run: |
|
||||
cargo install cargo-llvm-cov || true
|
||||
cargo install grcov || true
|
||||
./coverage_report.sh
|
||||
# If using tarapulin
|
||||
#- run: cargo install cargo-tarpaulin
|
||||
#- run: cargo tarpaulin --out Xml
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: ./target/grcov/lcov
|
||||
verbose: true
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
37
.github/workflows/regressions.yml
vendored
Normal file
37
.github/workflows/regressions.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Regressions
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
multi-peer:
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 ]
|
||||
|
||||
boot-race:
|
||||
runs-on: ubicloud-standard-2-ubuntu-2204
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cargo build --bin rosenpass --release
|
||||
- run: chmod +x .ci/boot_race/run.sh
|
||||
- run: cargo run --release --bin rosenpass gen-keys .ci/boot_race/a.toml
|
||||
- run: cargo run --release --bin rosenpass gen-keys .ci/boot_race/b.toml
|
||||
- run: .ci/boot_race/run.sh 5 2 .ci/boot_race/a.toml .ci/boot_race/b.toml
|
||||
- run: .ci/boot_race/run.sh 5 1 .ci/boot_race/a.toml .ci/boot_race/b.toml
|
||||
- run: .ci/boot_race/run.sh 5 0 .ci/boot_race/a.toml .ci/boot_race/b.toml
|
||||
49
.github/workflows/release.yaml
vendored
49
.github/workflows/release.yaml
vendored
@@ -11,18 +11,16 @@ jobs:
|
||||
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
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build release
|
||||
run: nix build .#release-package --print-build-logs
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
@@ -32,18 +30,16 @@ jobs:
|
||||
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
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build release
|
||||
run: nix build .#release-package --print-build-logs
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
@@ -53,19 +49,40 @@ jobs:
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v22
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
- uses: cachix/cachix-action@v15
|
||||
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
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
files: result/*
|
||||
linux-packages:
|
||||
name: Build and upload DEB and RPM packages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v30
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- name: Build DEB & RPM package
|
||||
run: |
|
||||
mkdir packages
|
||||
for f in $(nix build .#package-deb .#package-rpm --print-out-paths); do cp "$f" "packages/${f#*-}"; done
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: ${{ contains(github.ref_name, 'rc') }}
|
||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||
files: |
|
||||
packages/*
|
||||
|
||||
177
.github/workflows/supply-chain.yml
vendored
Normal file
177
.github/workflows/supply-chain.yml
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
name: Supply-Chain
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
cargo-deny:
|
||||
name: Deny dependencies with vulnerabilities or incompatible licenses
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
cargo-supply-chain:
|
||||
name: Supply Chain Report
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cache/cargo-supply-chain/
|
||||
key: cargo-supply-chain-cache
|
||||
- name: Install nightly toolchain
|
||||
run: |
|
||||
rustup toolchain install nightly
|
||||
rustup override set nightly
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.tool_cache }}/cargo-supply-chain
|
||||
key: cargo-supply-chain-bin
|
||||
- name: Add the tool cache directory to the search path
|
||||
run: echo "${{ runner.tool_cache }}/cargo-supply-chain/bin" >> $GITHUB_PATH
|
||||
- name: Ensure that the tool cache is populated with the cargo-supply-chain binary
|
||||
run: cargo install --root ${{ runner.tool_cache }}/cargo-supply-chain cargo-supply-chain
|
||||
- name: Update data for cargo-supply-chain
|
||||
run: cargo supply-chain update
|
||||
- name: Generate cargo-supply-chain report about publishers
|
||||
run: cargo supply-chain publishers
|
||||
- name: Generate cargo-supply-chain report about crates
|
||||
run: cargo supply-chain crates
|
||||
# The setup for cargo-vet follows the recommendations in the cargo-vet documentation: https://mozilla.github.io/cargo-vet/configuring-ci.html
|
||||
cargo-vet:
|
||||
name: Vet Dependencies
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
key: cargo-vet-cache
|
||||
- name: Install nightly toolchain
|
||||
run: |
|
||||
rustup toolchain install nightly
|
||||
rustup override set nightly
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.tool_cache }}/cargo-vet
|
||||
key: cargo-vet-bin
|
||||
- name: Add the tool cache directory to the search path
|
||||
run: echo "${{ runner.tool_cache }}/cargo-vet/bin" >> $GITHUB_PATH
|
||||
- name: Ensure that the tool cache is populated with the cargo-vet binary
|
||||
run: cargo install --root ${{ runner.tool_cache }}/cargo-vet cargo-vet
|
||||
- name: Check which event triggered this CI run, a push or a pull request.
|
||||
run: |
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
IS_PR="false"
|
||||
IS_PUSH="false"
|
||||
if [[ "$EVENT_NAME" == "pull_request" ]]; then
|
||||
echo "This CI run was triggered in the context of a pull request."
|
||||
IS_PR="true"
|
||||
elif [[ "$EVENT_NAME" == "push" ]]; then
|
||||
echo "This CI run was triggered in the context of a push."
|
||||
IS_PUSH="true"
|
||||
else
|
||||
echo "ERROR: This CI run was not triggered in the context of a pull request or a push. Exiting with error."
|
||||
exit 1
|
||||
fi
|
||||
echo "IS_PR=$IS_PR" >> $GITHUB_ENV
|
||||
echo "IS_PUSH=$IS_PUSH" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Check if last commit was by Dependabot
|
||||
run: |
|
||||
# Depending on the trigger for, the relevant commit has to be deduced differently.
|
||||
if [[ "$IS_PR" == true ]]; then
|
||||
# This is the commit ID for the last commit to the head branch of the pull request.
|
||||
# If we used github.sha here instead, it would point to a merge commit between the PR and the main branch, which is only created for the CI run.
|
||||
SHA="${{ github.event.pull_request.head.sha }}"
|
||||
REF="${{ github.head_ref }}"
|
||||
elif [[ "$IS_PUSH" == "true" ]]; then
|
||||
SHA="${{ github.sha }}" # This is the last commit to the branch.
|
||||
REF=${GITHUB_REF#refs/heads/}
|
||||
else
|
||||
echo "ERROR: This action only supports pull requests and push events as triggers. Exiting with error."
|
||||
exit 1
|
||||
fi
|
||||
echo "Commit SHA is $SHA"
|
||||
echo "Branch is $REF"
|
||||
echo "REF=$REF" >> $GITHUB_ENV
|
||||
|
||||
COMMIT_AUTHOR=$(gh api repos/${{ github.repository }}/commits/$SHA --jq .author.login) # .author.login might be null, but for dependabot it will always be there and cannot be spoofed in contrast to .commit.author.name
|
||||
echo "The author of the last commit is $COMMIT_AUTHOR"
|
||||
if [[ "$COMMIT_AUTHOR" == "dependabot[bot]" ]]; then
|
||||
echo "The last commit was made by dependabot"
|
||||
LAST_COMMIT_IS_BY_DEPENDABOT=true
|
||||
else
|
||||
echo "The last commit was made by $COMMIT_AUTHOR not by dependabot"
|
||||
LAST_COMMIT_IS_BY_DEPENDABOT=false
|
||||
fi
|
||||
echo "LAST_COMMIT_IS_BY_DEPENDABOT=$LAST_COMMIT_IS_BY_DEPENDABOT" >> $GITHUB_ENV
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
- name: Check if the last commit's message ends in "--regenerate-exemptions"
|
||||
run: |
|
||||
# Get commit message
|
||||
COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s")
|
||||
if [[ "$COMMIT_MESSAGE" == *"--regenerate-exemptions" ]]; then
|
||||
echo "The last commit message ends in --regenerate-exemptions"
|
||||
REGEN_EXEMP=true
|
||||
else
|
||||
echo "The last commit message does not end in --regenerate-exemptions"
|
||||
REGEN_EXEMP=false
|
||||
fi
|
||||
echo "REGEN_EXEMP=$REGEN_EXEMP" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Check if the CI run happens in the context of a dependabot PR # Even if a PR is created by dependabot, the last commit can, and often should be, the regeneration of the cargo vet exemptions. It could also be from an individual making manual changes.
|
||||
run: |
|
||||
IN_DEPENDABOT_PR_CONTEXT="false"
|
||||
if [[ $IS_PR == "true" && "${{ github.event.pull_request.user.login }}" == "dependabot[bot]" ]]; then
|
||||
IN_DEPENDABOT_PR_CONTEXT="true"
|
||||
echo "This CI run is in the context of PR by dependabot."
|
||||
else
|
||||
echo "This CI run is NOT in the context of PR by dependabot."
|
||||
IN_DEPENDABOT_PR_CONTEXT="false"
|
||||
fi
|
||||
echo "IN_DEPENDABOT_PR_CONTEXT=$IN_DEPENDABOT_PR_CONTEXT" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- uses: actions/checkout@v4
|
||||
if: env.IN_DEPENDABOT_PR_CONTEXT == 'true'
|
||||
with:
|
||||
token: ${{ secrets.CI_BOT_PAT }}
|
||||
- name: In case of a dependabot PR, ensure that we are not in a detached HEAD state
|
||||
if: env.IN_DEPENDABOT_PR_CONTEXT == 'true'
|
||||
run: |
|
||||
git fetch origin $REF # ensure that we are up to date.
|
||||
git switch $REF # ensure that we are NOT in a detached HEAD state. This is important for the commit action in the end
|
||||
shell: bash
|
||||
- name: Regenerate cargo vet exemptions if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested.
|
||||
if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true') # Run only for Dependabot PRs or if specifically requested
|
||||
run: cargo vet regenerate exemptions
|
||||
- name: Commit and push changes if we are in the context of a PR created by dependabot and the last commit is by dependabot or a regeneration of cargo vet exemptions was explicitly requested.
|
||||
if: env.IN_DEPENDABOT_PR_CONTEXT == 'true' && (env.LAST_COMMIT_IS_BY_DEPENDABOT == 'true' || env.REGEN_EXEMP=='true')
|
||||
uses: stefanzweifel/git-auto-commit-action@v6
|
||||
with:
|
||||
commit_message: Regenerate cargo vet exemptions
|
||||
commit_user_name: rosenpass-ci-bot[bot]
|
||||
commit_user_email: noreply@rosenpass.eu
|
||||
commit_author: Rosenpass CI Bot <noreply@rosenpass.eu>
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.CI_BOT_PAT }}
|
||||
- name: Invoke cargo-vet
|
||||
run: cargo vet --locked
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -20,3 +20,9 @@ _markdown_*
|
||||
**/result
|
||||
**/result-*
|
||||
.direnv
|
||||
|
||||
# Visual studio code
|
||||
.vscode
|
||||
|
||||
/output
|
||||
.nixos-test-history
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.direnv/
|
||||
flake.lock
|
||||
papers/whitepaper.md
|
||||
target/
|
||||
src/usage.md
|
||||
target/
|
||||
|
||||
41
CONTRIBUTING.md
Normal file
41
CONTRIBUTING.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Contributing to Rosenpass
|
||||
|
||||
## Common operations
|
||||
|
||||
### Apply code formatting
|
||||
|
||||
Format rust code:
|
||||
|
||||
```bash
|
||||
cargo fmt
|
||||
```
|
||||
|
||||
Format rust code in markdown files:
|
||||
|
||||
```bash
|
||||
./format_rust_code.sh --mode fix
|
||||
```
|
||||
|
||||
### Spawn a development environment with nix
|
||||
|
||||
```bash
|
||||
nix develop .#fullEnv
|
||||
```
|
||||
|
||||
You need to [install this nix package manager](https://wiki.archlinux.org/title/Nix) first.
|
||||
|
||||
### Run our test
|
||||
|
||||
Make sure to increase the stack size available; some of our cryptography operations require a lot of stack memory.
|
||||
|
||||
```bash
|
||||
RUST_MIN_STACK=8388608 cargo test --workspace --all-features
|
||||
```
|
||||
|
||||
### Generate coverage reports
|
||||
|
||||
Keep in mind that many of Rosenpass' tests are doctests, so to get an accurate read on our code coverage, you have to include doctests:
|
||||
|
||||
```bash
|
||||
./coverage_report.sh
|
||||
```
|
||||
2770
Cargo.lock
generated
2770
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
94
Cargo.toml
94
Cargo.toml
@@ -2,9 +2,101 @@
|
||||
resolver = "2"
|
||||
|
||||
members = [
|
||||
"rosenpass",
|
||||
"rosenpass",
|
||||
"cipher-traits",
|
||||
"ciphers",
|
||||
"util",
|
||||
"constant-time",
|
||||
"oqs",
|
||||
"to",
|
||||
"fuzz",
|
||||
"secret-memory",
|
||||
"rp",
|
||||
"wireguard-broker",
|
||||
]
|
||||
|
||||
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.69"
|
||||
paste = "1.0.15"
|
||||
env_logger = "0.10.2"
|
||||
toml = "0.7.8"
|
||||
static_assertions = "1.1.0"
|
||||
allocator-api2 = "0.2.14"
|
||||
memsec = { git = "https://github.com/rosenpass/memsec.git", rev = "aceb9baee8aec6844125bd6612f92e9a281373df", features = [
|
||||
"alloc_ext",
|
||||
] }
|
||||
rand = "0.8.5"
|
||||
typenum = "1.17.0"
|
||||
log = { version = "0.4.22" }
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
clap_mangen = "0.2.29"
|
||||
clap_complete = "4.5.40"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
arbitrary = { version = "1.4.1", features = ["derive"] }
|
||||
anyhow = { version = "1.0.95", features = ["backtrace", "std"] }
|
||||
mio = { version = "1.0.3", features = ["net", "os-poll"] }
|
||||
signal-hook-mio = { version = "0.2.4", features = ["support-v1_0"] }
|
||||
signal-hook = "0.3.17"
|
||||
oqs-sys = { version = "0.9.1", default-features = false, features = [
|
||||
'classic_mceliece',
|
||||
'kyber',
|
||||
] }
|
||||
blake2 = "0.10.6"
|
||||
sha3 = "0.10.8"
|
||||
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
|
||||
"std",
|
||||
"heapless",
|
||||
] }
|
||||
zerocopy = { version = "0.7.35", features = ["derive"] }
|
||||
home = "=0.5.9" # 5.11 requires rustc 1.81
|
||||
derive_builder = "0.20.1"
|
||||
tokio = { version = "1.46", features = ["macros", "rt-multi-thread"] }
|
||||
postcard = { version = "1.1.1", features = ["alloc"] }
|
||||
libcrux = { version = "0.0.2-pre.2" }
|
||||
libcrux-chacha20poly1305 = { version = "0.0.2-beta.3" }
|
||||
libcrux-ml-kem = { version = "0.0.2-beta.3" }
|
||||
libcrux-blake2 = { git = "https://github.com/cryspen/libcrux.git", rev = "10ce653e9476" }
|
||||
libcrux-test-utils = { git = "https://github.com/cryspen/libcrux.git", rev = "0ab6d2dd9c1f" }
|
||||
hex-literal = { version = "0.4.1" }
|
||||
hex = { version = "0.4.3" }
|
||||
heck = { version = "0.5.0" }
|
||||
libc = { version = "0.2" }
|
||||
uds = { git = "https://github.com/rosenpass/uds" }
|
||||
lazy_static = "1.5"
|
||||
|
||||
#Dev dependencies
|
||||
serial_test = "3.2.0"
|
||||
tempfile = "3"
|
||||
stacker = "0.1.17"
|
||||
libfuzzer-sys = "0.4"
|
||||
test_bin = "0.4.0"
|
||||
criterion = "0.5.1"
|
||||
allocator-api2-tests = "0.2.15"
|
||||
procspawn = { version = "1.0.1", features = ["test-support"] }
|
||||
|
||||
#Broker dependencies (might need cleanup or changes)
|
||||
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
|
||||
command-fds = "0.2.3"
|
||||
rustix = { version = "0.38.42", features = ["net", "fs", "process"] }
|
||||
futures = "0.3"
|
||||
futures-util = "0.3"
|
||||
x25519-dalek = "2"
|
||||
|
||||
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).)
|
||||
20
cipher-traits/Cargo.toml
Normal file
20
cipher-traits/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[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"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rosenpass-oqs = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
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; no guarantee is made about its API at this point in time.
|
||||
137
cipher-traits/src/algorithms.rs
Normal file
137
cipher-traits/src/algorithms.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
//! This module contains the traits for all the cryptographic algorithms used throughout Rosenpass.
|
||||
//! These traits are marker traits that signal intent. They can also be used for trait objects.
|
||||
|
||||
/// Constants and trait for the Incorrect HMAC over Blake2b, with 256 key and hash length.
|
||||
pub mod keyed_hash_incorrect_hmac_blake2b {
|
||||
use crate::primitives::keyed_hash::*;
|
||||
|
||||
// These constants describe how they are used here, not what the algorithm defines.
|
||||
|
||||
/// The key length used in [`KeyedHashIncorrectHmacBlake2b`].
|
||||
pub const KEY_LEN: usize = 32;
|
||||
/// The hash length used in [`KeyedHashIncorrectHmacBlake2b`].
|
||||
pub const HASH_LEN: usize = 32;
|
||||
|
||||
/// A [`KeyedHash`] that is an incorrect HMAC over Blake2 (a custom Rosenpass construction)
|
||||
pub trait KeyedHashIncorrectHmacBlake2b: KeyedHash<KEY_LEN, HASH_LEN> {}
|
||||
}
|
||||
|
||||
/// Constants and trait for Blake2b, with 256 key and hash length.
|
||||
pub mod keyed_hash_blake2b {
|
||||
use crate::primitives::keyed_hash::*;
|
||||
|
||||
// These constants describe how they are used here, not what the algorithm defines.
|
||||
|
||||
/// The key length used in [`KeyedHashBlake2b`].
|
||||
pub const KEY_LEN: usize = 32;
|
||||
/// The hash length used in [`KeyedHashBlake2b`].
|
||||
pub const HASH_LEN: usize = 32;
|
||||
|
||||
/// A [`KeyedHash`] that is Blake2b
|
||||
pub trait KeyedHashBlake2b: KeyedHash<KEY_LEN, HASH_LEN> {}
|
||||
}
|
||||
|
||||
/// Constants and trait for SHAKE256, with 256 key and hash length.
|
||||
pub mod keyed_hash_shake256 {
|
||||
use crate::primitives::keyed_hash::*;
|
||||
|
||||
// These constants describe how they are used here, not what the algorithm defines.
|
||||
|
||||
/// The key length used in [`KeyedHashShake256`].
|
||||
pub const KEY_LEN: usize = 32;
|
||||
/// The hash length used in [`KeyedHashShake256`].
|
||||
pub const HASH_LEN: usize = 32;
|
||||
|
||||
/// A [`KeyedHash`] that is SHAKE256.
|
||||
pub trait KeyedHashShake256: KeyedHash<KEY_LEN, HASH_LEN> {}
|
||||
}
|
||||
|
||||
/// Constants and trait for the ChaCha20Poly1305 AEAD
|
||||
pub mod aead_chacha20poly1305 {
|
||||
use crate::primitives::aead::*;
|
||||
|
||||
// See https://datatracker.ietf.org/doc/html/rfc7539#section-2.8
|
||||
|
||||
/// The key length used in [`AeadChaCha20Poly1305`].
|
||||
pub const KEY_LEN: usize = 32;
|
||||
/// The nonce length used in [`AeadChaCha20Poly1305`].
|
||||
pub const NONCE_LEN: usize = 12;
|
||||
/// The tag length used in [`AeadChaCha20Poly1305`].
|
||||
pub const TAG_LEN: usize = 16;
|
||||
|
||||
/// An [`Aead`] that is ChaCha20Poly1305.
|
||||
pub trait AeadChaCha20Poly1305: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {}
|
||||
}
|
||||
|
||||
/// Constants and trait for the XChaCha20Poly1305 AEAD (i.e. ChaCha20Poly1305 with extended nonce
|
||||
/// lengths)
|
||||
pub mod aead_xchacha20poly1305 {
|
||||
use crate::primitives::aead::*;
|
||||
|
||||
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03
|
||||
|
||||
/// The key length used in [`AeadXChaCha20Poly1305`].
|
||||
pub const KEY_LEN: usize = 32;
|
||||
/// The nonce length used in [`AeadXChaCha20Poly1305`].
|
||||
pub const NONCE_LEN: usize = 24;
|
||||
/// The tag length used in [`AeadXChaCha20Poly1305`].
|
||||
pub const TAG_LEN: usize = 16;
|
||||
|
||||
/// An [`Aead`] that is XChaCha20Poly1305.
|
||||
pub trait AeadXChaCha20Poly1305: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {}
|
||||
}
|
||||
|
||||
/// Constants and trait for the Kyber512 KEM
|
||||
pub mod kem_kyber512 {
|
||||
use crate::primitives::kem::*;
|
||||
|
||||
// page 39 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf
|
||||
// (which is ml-kem instead of kyber, but it's the same)
|
||||
|
||||
/// The secret key length used in [`KemKyber512`].
|
||||
pub const SK_LEN: usize = 1632;
|
||||
|
||||
/// The public key length used in [`KemKyber512`].
|
||||
pub const PK_LEN: usize = 800;
|
||||
|
||||
/// The ciphertext length used in [`KemKyber512`].
|
||||
pub const CT_LEN: usize = 768;
|
||||
|
||||
/// The shared key length used in [`KemKyber512`].
|
||||
pub const SHK_LEN: usize = 32;
|
||||
|
||||
/// A [`Kem`] that is Kyber512.
|
||||
pub trait KemKyber512: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {}
|
||||
}
|
||||
|
||||
/// Constants and trait for the Classic McEliece 460896 KEM
|
||||
pub mod kem_classic_mceliece460896 {
|
||||
use crate::primitives::kem::*;
|
||||
|
||||
// page 6 of https://classic.mceliece.org/mceliece-impl-20221023.pdf
|
||||
|
||||
/// The secret key length used in [`KemClassicMceliece460896`].
|
||||
pub const SK_LEN: usize = 13608;
|
||||
|
||||
/// The public key length used in [`KemClassicMceliece460896`].
|
||||
pub const PK_LEN: usize = 524160;
|
||||
|
||||
/// The ciphertext length used in [`KemClassicMceliece460896`].
|
||||
pub const CT_LEN: usize = 156;
|
||||
|
||||
/// The shared key length used in [`KemClassicMceliece460896`].
|
||||
pub const SHK_LEN: usize = 32;
|
||||
|
||||
/// A [`Kem`] that is ClassicMceliece460896.
|
||||
pub trait KemClassicMceliece460896: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {}
|
||||
}
|
||||
|
||||
pub use aead_chacha20poly1305::AeadChaCha20Poly1305;
|
||||
pub use aead_xchacha20poly1305::AeadXChaCha20Poly1305;
|
||||
|
||||
pub use kem_classic_mceliece460896::KemClassicMceliece460896;
|
||||
pub use kem_kyber512::KemKyber512;
|
||||
|
||||
pub use keyed_hash_blake2b::KeyedHashBlake2b;
|
||||
pub use keyed_hash_incorrect_hmac_blake2b::KeyedHashIncorrectHmacBlake2b;
|
||||
pub use keyed_hash_shake256::KeyedHashShake256;
|
||||
163
cipher-traits/src/kem.rs
Normal file
163
cipher-traits/src/kem.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
//! Traits and implementations for Key Encapsulation Mechanisms (KEMs)
|
||||
//!
|
||||
//! KEMs are the interface provided by almost all post-quantum
|
||||
//! secure key exchange mechanisms.
|
||||
//!
|
||||
//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting
|
||||
//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during
|
||||
//! encapsulation.
|
||||
//!
|
||||
//! The [Kem] Trait describes the basic API offered by a Key Encapsulation
|
||||
//! Mechanism. Two implementations for it are provided:
|
||||
//! [Kyber512](../../rosenpass_oqs/kyber_512/enum.Kyber512.html) and
|
||||
//! [ClassicMceliece460896](../../rosenpass_oqs/classic_mceliece_460896/enum.ClassicMceliece460896.html).
|
||||
//!
|
||||
//! An example where Alice generates a keypair and gives her public key to Bob, for Bob to
|
||||
//! encapsulate a symmetric key and Alice to decapsulate it would look as follows.
|
||||
//! In the example, we are using Kyber512, but any KEM that correctly implements the [Kem]
|
||||
//! trait could be used as well.
|
||||
//!```rust
|
||||
//! use rosenpass_cipher_traits::Kem;
|
||||
//! use rosenpass_oqs::Kyber512;
|
||||
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
|
||||
//!
|
||||
//! type MyKem = Kyber512;
|
||||
//! secret_policy_use_only_malloc_secrets();
|
||||
//! let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
|
||||
//! let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
|
||||
//! MyKem::keygen(alice_sk.secret_mut(), &mut alice_pk)?;
|
||||
//!
|
||||
//! let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
|
||||
//! MyKem::encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
|
||||
//!
|
||||
//! let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! MyKem::decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
|
||||
//!
|
||||
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
|
||||
//! # Ok::<(), anyhow::Error>(())
|
||||
//!```
|
||||
//!
|
||||
//! Implementing the [Kem]-trait for a KEM is easy. Mostly, you must format the KEM's
|
||||
//! keys, and ciphertext as `u8` slices. Below, we provide an example for how the trait can
|
||||
//! be implemented using a **HORRIBLY INSECURE** DummyKem that only uses static values for keys
|
||||
//! and ciphertexts as an example.
|
||||
//!```rust
|
||||
//!# use rosenpass_cipher_traits::Kem;
|
||||
//!
|
||||
//! struct DummyKem {}
|
||||
//! impl Kem for DummyKem {
|
||||
//!
|
||||
//! // For this DummyKem, using String for errors is sufficient.
|
||||
//! type Error = String;
|
||||
//!
|
||||
//! // For this DummyKem, we will use a single `u8` for everything
|
||||
//! const SK_LEN: usize = 1;
|
||||
//! const PK_LEN: usize = 1;
|
||||
//! const CT_LEN: usize = 1;
|
||||
//! const SHK_LEN: usize = 1;
|
||||
//!
|
||||
//! fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), Self::Error> {
|
||||
//! if sk.len() != Self::SK_LEN {
|
||||
//! return Err("sk does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if pk.len() != Self::PK_LEN {
|
||||
//! return Err("pk does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! sk[0] = 42;
|
||||
//! pk[0] = 21;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), Self::Error> {
|
||||
//! if pk.len() != Self::PK_LEN {
|
||||
//! return Err("pk does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if ct.len() != Self::CT_LEN {
|
||||
//! return Err("ct does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if shk.len() != Self::SHK_LEN {
|
||||
//! return Err("shk does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if pk[0] != 21 {
|
||||
//! return Err("Invalid public key!".to_string());
|
||||
//! }
|
||||
//! ct[0] = 7;
|
||||
//! shk[0] = 17;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), Self::Error> {
|
||||
//! if sk.len() != Self::SK_LEN {
|
||||
//! return Err("sk does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if ct.len() != Self::CT_LEN {
|
||||
//! return Err("ct does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if shk.len() != Self::SHK_LEN {
|
||||
//! return Err("shk does not have the correct length!".to_string());
|
||||
//! }
|
||||
//! if sk[0] != 42 {
|
||||
//! return Err("Invalid public key!".to_string());
|
||||
//! }
|
||||
//! if ct[0] != 7 {
|
||||
//! return Err("Invalid ciphertext!".to_string());
|
||||
//! }
|
||||
//! shk[0] = 17;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
|
||||
//! #
|
||||
//! # type MyKem = DummyKem;
|
||||
//! # secret_policy_use_only_malloc_secrets();
|
||||
//! # let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
|
||||
//! # let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
|
||||
//! # MyKem::keygen(alice_sk.secret_mut(), &mut alice_pk)?;
|
||||
//!
|
||||
//! # let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! # let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
|
||||
//! # MyKem::encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
|
||||
//! #
|
||||
//! # let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! # MyKem::decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
|
||||
//! #
|
||||
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
|
||||
//! #
|
||||
//! # Ok::<(), String>(())
|
||||
//!```
|
||||
//!
|
||||
|
||||
/// 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>;
|
||||
}
|
||||
5
cipher-traits/src/lib.rs
Normal file
5
cipher-traits/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
//! This trait contains traits, constants and wrappers that provid= the interface between Rosenpass
|
||||
//! as a consumer of cryptographic libraries and the implementations of cryptographic algorithms.
|
||||
|
||||
pub mod algorithms;
|
||||
pub mod primitives;
|
||||
10
cipher-traits/src/primitives.rs
Normal file
10
cipher-traits/src/primitives.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
//! Traits for cryptographic primitives used in Rosenpass, specifically KEM, AEAD and keyed
|
||||
//! hashing.
|
||||
|
||||
pub(crate) mod aead;
|
||||
pub(crate) mod kem;
|
||||
pub(crate) mod keyed_hash;
|
||||
|
||||
pub use aead::{Aead, AeadWithNonceInCiphertext, Error as AeadError};
|
||||
pub use kem::{Error as KemError, Kem};
|
||||
pub use keyed_hash::*;
|
||||
175
cipher-traits/src/primitives/aead.rs
Normal file
175
cipher-traits/src/primitives/aead.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use rosenpass_to::{ops::copy_slice, To as _};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Models authenticated encryption with assiciated data (AEAD) functionality.
|
||||
///
|
||||
/// The methods of this trait take a `&self` argument as a receiver. This has two reasons:
|
||||
/// 1. It makes type inference a lot smoother
|
||||
/// 2. It allows to use the functionality through a trait object or having an enum that has
|
||||
/// variants for multiple options (like e.g. the `KeyedHash` enum in `rosenpass-ciphers`).
|
||||
///
|
||||
/// Since the caller needs an instance of the type to use the functionality, implementors are
|
||||
/// adviced to implement the [`Default`] trait where possible.
|
||||
///
|
||||
/// Example for encrypting a message with a specific [`Aead`] instance:
|
||||
/// ```
|
||||
/// use rosenpass_cipher_traits::primitives::Aead;
|
||||
///
|
||||
/// const KEY_LEN: usize = 32;
|
||||
/// const NONCE_LEN: usize = 12;
|
||||
/// const TAG_LEN: usize = 16;
|
||||
///
|
||||
/// fn encrypt_message_given_an_aead<AeadImpl>(
|
||||
/// aead: &AeadImpl,
|
||||
/// msg: &str,
|
||||
/// nonce: &[u8; NONCE_LEN],
|
||||
/// encrypted: &mut [u8]
|
||||
/// ) where AeadImpl: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {
|
||||
/// let key = [0u8; KEY_LEN]; // This is not a secure key!
|
||||
/// let ad = b""; // we don't need associated data here
|
||||
/// aead.encrypt(encrypted, &key, nonce, ad, msg.as_bytes()).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If only the type (but no instance) is available, then we can still encrypt, as long as the type
|
||||
/// also is [`Default`]:
|
||||
/// ```
|
||||
/// use rosenpass_cipher_traits::primitives::Aead;
|
||||
///
|
||||
/// const KEY_LEN: usize = 32;
|
||||
/// const NONCE_LEN: usize = 12;
|
||||
/// const TAG_LEN: usize = 16;
|
||||
///
|
||||
/// fn encrypt_message_without_aead<AeadImpl>(
|
||||
/// msg: &str,
|
||||
/// nonce: &[u8; NONCE_LEN],
|
||||
/// encrypted: &mut [u8]
|
||||
/// ) where AeadImpl: Default + Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {
|
||||
/// let key = [0u8; KEY_LEN]; // This is not a secure key!
|
||||
/// let ad = b""; // we don't need associated data here
|
||||
/// AeadImpl::default().encrypt(encrypted, &key, nonce, ad, msg.as_bytes()).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Aead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize> {
|
||||
const KEY_LEN: usize = KEY_LEN;
|
||||
const NONCE_LEN: usize = NONCE_LEN;
|
||||
const TAG_LEN: usize = TAG_LEN;
|
||||
|
||||
/// Encrypts `plaintext` using the given `key` and `nonce`, taking into account the additional
|
||||
/// data `ad` and writes the result into `ciphertext`.
|
||||
///
|
||||
/// `ciphertext` must be exactly `TAG_LEN` longer than `plaintext`.
|
||||
fn encrypt(
|
||||
&self,
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Decrypts `ciphertexttext` using the given `key` and `nonce`, taking into account the additional
|
||||
/// data `ad` and writes the result into `plaintext`.
|
||||
///
|
||||
/// `ciphertext` must be exactly `TAG_LEN` longer than `plaintext`.
|
||||
fn decrypt(
|
||||
&self,
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Provides an AEAD API where the nonce is part of the ciphertext.
|
||||
///
|
||||
/// The old xaead API had the ciphertext begin with the `nonce`. In order to not having to change
|
||||
/// the calling code too much, we add a wrapper trait that provides this API and implement it for
|
||||
/// all AEAD.
|
||||
pub trait AeadWithNonceInCiphertext<
|
||||
const KEY_LEN: usize,
|
||||
const NONCE_LEN: usize,
|
||||
const TAG_LEN: usize,
|
||||
>: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>
|
||||
{
|
||||
/// Encrypts `plaintext` using the given `key` and `nonce`, taking into account the additional
|
||||
/// data `ad` and writes the result into `ciphertext`.
|
||||
///
|
||||
/// `ciphertext` must be exactly `TAG_LEN` + `NONCE_LEN` longer than `plaintext`.
|
||||
fn encrypt_with_nonce_in_ctxt(
|
||||
&self,
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
// The comparison looks complicated, but we need to do it this way to prevent
|
||||
// over/underflows.
|
||||
if ciphertext.len() < NONCE_LEN + TAG_LEN
|
||||
|| ciphertext.len() - TAG_LEN - NONCE_LEN < plaintext.len()
|
||||
{
|
||||
return Err(Error::InvalidLengths);
|
||||
}
|
||||
|
||||
let (n, rest) = ciphertext.split_at_mut(NONCE_LEN);
|
||||
copy_slice(nonce).to(n);
|
||||
|
||||
self.encrypt(rest, key, nonce, ad, plaintext)
|
||||
}
|
||||
|
||||
/// Decrypts `ciphertexttext` using the given `key` and `nonce`, taking into account the additional
|
||||
/// data `ad` and writes the result into `plaintext`.
|
||||
///
|
||||
/// `ciphertext` must be exactly `TAG_LEN` + `NONCE_LEN` longer than `plaintext`.
|
||||
fn decrypt_with_nonce_in_ctxt(
|
||||
&self,
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> Result<(), Error> {
|
||||
// The comparison looks complicated, but we need to do it this way to prevent
|
||||
// over/underflows.
|
||||
if ciphertext.len() < NONCE_LEN + TAG_LEN
|
||||
|| ciphertext.len() - TAG_LEN - NONCE_LEN < plaintext.len()
|
||||
{
|
||||
return Err(Error::InvalidLengths);
|
||||
}
|
||||
|
||||
let (nonce, rest) = ciphertext.split_at(NONCE_LEN);
|
||||
// We know this should be the right length (we just split it), and everything else would be
|
||||
// very unexpected.
|
||||
let nonce = nonce.try_into().map_err(|_| Error::InternalError)?;
|
||||
|
||||
self.decrypt(plaintext, key, nonce, ad, rest)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
const KEY_LEN: usize,
|
||||
const NONCE_LEN: usize,
|
||||
const TAG_LEN: usize,
|
||||
T: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>,
|
||||
> AeadWithNonceInCiphertext<KEY_LEN, NONCE_LEN, TAG_LEN> for T
|
||||
{
|
||||
}
|
||||
|
||||
/// The error returned by AEAD operations
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// An internal error occurred. This should never be happen and indicates an error in the
|
||||
/// AEAD implementation.
|
||||
#[error("internal error")]
|
||||
InternalError,
|
||||
|
||||
/// Could not decrypt a message because the message is not a valid ciphertext for the given
|
||||
/// key.
|
||||
#[error("decryption error")]
|
||||
DecryptError,
|
||||
|
||||
/// The provided buffers have the wrong lengths.
|
||||
#[error("buffers have invalid length")]
|
||||
InvalidLengths,
|
||||
}
|
||||
212
cipher-traits/src/primitives/kem.rs
Normal file
212
cipher-traits/src/primitives/kem.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
//! Traits and implementations for Key Encapsulation Mechanisms (KEMs)
|
||||
//!
|
||||
//! KEMs are the interface provided by almost all post-quantum
|
||||
//! secure key exchange mechanisms.
|
||||
//!
|
||||
//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting
|
||||
//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during
|
||||
//! encapsulation.
|
||||
//!
|
||||
//! The [Kem] Trait describes the basic API offered by a Key Encapsulation
|
||||
//! Mechanism. Two implementations for it are provided:
|
||||
//! [Kyber512](../../rosenpass_oqs/kyber_512/enum.Kyber512.html) and
|
||||
//! [ClassicMceliece460896](../../rosenpass_oqs/classic_mceliece_460896/enum.ClassicMceliece460896.html).
|
||||
//!
|
||||
//! An example where Alice generates a keypair and gives her public key to Bob, for Bob to
|
||||
//! encapsulate a symmetric key and Alice to decapsulate it would look as follows.
|
||||
//! In the example, we are using Kyber512, but any KEM that correctly implements the [Kem]
|
||||
//! trait could be used as well.
|
||||
//!```rust
|
||||
//! use rosenpass_cipher_traits::primitives::Kem;
|
||||
//! use rosenpass_oqs::Kyber512;
|
||||
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
|
||||
//!
|
||||
//! type MyKem = Kyber512;
|
||||
//! secret_policy_use_only_malloc_secrets();
|
||||
//! let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
|
||||
//! let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
|
||||
//! MyKem::default().keygen(alice_sk.secret_mut(), &mut alice_pk)?;
|
||||
//!
|
||||
//! let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
|
||||
//! MyKem::default().encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
|
||||
//!
|
||||
//! let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! MyKem::default().decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
|
||||
//!
|
||||
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
|
||||
//! # Ok::<(), anyhow::Error>(())
|
||||
//!```
|
||||
//!
|
||||
//! Implementing the [Kem]-trait for a KEM is easy. Mostly, you must format the KEM's
|
||||
//! keys, and ciphertext as `u8` slices. Below, we provide an example for how the trait can
|
||||
//! be implemented using a **HORRIBLY INSECURE** DummyKem that only uses static values for keys
|
||||
//! and ciphertexts as an example.
|
||||
//!```rust
|
||||
//!# use rosenpass_cipher_traits::primitives::{Kem, KemError as Error};
|
||||
//!
|
||||
//! struct DummyKem {}
|
||||
//! impl Kem<1,1,1,1> for DummyKem {
|
||||
//!
|
||||
//! // For this DummyKem, we will use a single `u8` for everything
|
||||
//! const SK_LEN: usize = 1;
|
||||
//! const PK_LEN: usize = 1;
|
||||
//! const CT_LEN: usize = 1;
|
||||
//! const SHK_LEN: usize = 1;
|
||||
//!
|
||||
//! fn keygen(&self, sk: &mut [u8;1], pk: &mut [u8;1]) -> Result<(), Error> {
|
||||
//! sk[0] = 42;
|
||||
//! pk[0] = 21;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn encaps(&self, shk: &mut [u8;1], ct: &mut [u8;1], pk: &[u8;1]) -> Result<(), Error> {
|
||||
//! if pk[0] != 21 {
|
||||
//! return Err(Error::InvalidArgument);
|
||||
//! }
|
||||
//! ct[0] = 7;
|
||||
//! shk[0] = 17;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn decaps(&self, shk: &mut [u8;1 ], sk: &[u8;1], ct: &[u8;1]) -> Result<(), Error> {
|
||||
//! if sk[0] != 42 {
|
||||
//! return Err(Error::InvalidArgument);
|
||||
//! }
|
||||
//! if ct[0] != 7 {
|
||||
//! return Err(Error::InvalidArgument);
|
||||
//! }
|
||||
//! shk[0] = 17;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl Default for DummyKem {
|
||||
//! fn default() -> Self {
|
||||
//! Self{}
|
||||
//! }
|
||||
//! }
|
||||
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
|
||||
//! #
|
||||
//! # type MyKem = DummyKem;
|
||||
//! # secret_policy_use_only_malloc_secrets();
|
||||
//! # let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
|
||||
//! # let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
|
||||
//! # MyKem::default().keygen(alice_sk.secret_mut(), &mut alice_pk)?;
|
||||
//!
|
||||
//! # let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! # let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
|
||||
//! # MyKem::default().encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
|
||||
//! #
|
||||
//! # let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
|
||||
//! # MyKem::default().decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
|
||||
//! #
|
||||
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
|
||||
//! #
|
||||
//! # Ok::<(), Error>(())
|
||||
//!```
|
||||
//!
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Key Encapsulation Mechanism
|
||||
///
|
||||
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
||||
/// decapsulation. The parameters are made available as associated constants for convenience.
|
||||
///
|
||||
/// The methods of this trait take a `&self` argument as a receiver. This has two reasons:
|
||||
/// 1. It makes type inference a lot smoother
|
||||
/// 2. It allows to use the functionality through a trait object or having an enum that has
|
||||
/// variants for multiple options (like e.g. the `KeyedHash` enum in `rosenpass-ciphers`).
|
||||
///
|
||||
/// Since the caller needs an instance of the type to use the functionality, implementors are
|
||||
/// adviced to implement the [`Default`] trait where possible.
|
||||
///
|
||||
/// Example for encrypting a message with a specific [`Kem`] instance:
|
||||
/// ```
|
||||
/// use rosenpass_cipher_traits::primitives::Kem;
|
||||
///
|
||||
/// const SK_LEN: usize = 1632;
|
||||
/// const PK_LEN: usize = 800;
|
||||
/// const CT_LEN: usize = 768;
|
||||
/// const SHK_LEN: usize = 32;
|
||||
///
|
||||
/// fn encaps_given_a_kem<KemImpl>(
|
||||
/// kem: &KemImpl,
|
||||
/// pk: &[u8; PK_LEN],
|
||||
/// ct: &mut [u8; CT_LEN]
|
||||
/// ) -> [u8; SHK_LEN] where KemImpl: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN>{
|
||||
/// let mut shk = [0u8; SHK_LEN];
|
||||
/// kem.encaps(&mut shk, ct, pk).unwrap();
|
||||
/// shk
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If only the type (but no instance) is available, then we can still use the trait, as long as
|
||||
/// the type also is [`Default`]:
|
||||
/// ```
|
||||
/// use rosenpass_cipher_traits::primitives::Kem;
|
||||
///
|
||||
/// const SK_LEN: usize = 1632;
|
||||
/// const PK_LEN: usize = 800;
|
||||
/// const CT_LEN: usize = 768;
|
||||
/// const SHK_LEN: usize = 32;
|
||||
///
|
||||
/// fn encaps_without_kem<KemImpl>(
|
||||
/// pk: &[u8; PK_LEN],
|
||||
/// ct: &mut [u8; CT_LEN]
|
||||
/// ) -> [u8; SHK_LEN]
|
||||
/// where KemImpl: Default + Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {
|
||||
/// let mut shk = [0u8; SHK_LEN];
|
||||
/// KemImpl::default().encaps(&mut shk, ct, pk).unwrap();
|
||||
/// shk
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Kem<const SK_LEN: usize, const PK_LEN: usize, const CT_LEN: usize, const SHK_LEN: usize> {
|
||||
/// The length of the secret (decapsulation) key.
|
||||
const SK_LEN: usize = SK_LEN;
|
||||
|
||||
/// The length of the public (encapsulation) key.
|
||||
const PK_LEN: usize = PK_LEN;
|
||||
|
||||
/// The length of the ciphertext.
|
||||
const CT_LEN: usize = CT_LEN;
|
||||
|
||||
/// The legnth of the resulting shared key.
|
||||
const SHK_LEN: usize = SHK_LEN;
|
||||
|
||||
/// Generate a keypair consisting of secret key (`sk`) and public key (`pk`)
|
||||
///
|
||||
/// `keygen() -> sk, pk`
|
||||
fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), Error>;
|
||||
|
||||
/// From a public key (`pk`), generate a shared key (`shk`, for local use)
|
||||
/// and a cipher text (`ct`, to be sent to the owner of the `pk`).
|
||||
///
|
||||
/// `encaps(pk) -> shk, ct`
|
||||
fn encaps(
|
||||
&self,
|
||||
shk: &mut [u8; SHK_LEN],
|
||||
ct: &mut [u8; CT_LEN],
|
||||
pk: &[u8; PK_LEN],
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key
|
||||
/// (`shk`)
|
||||
///
|
||||
/// `decaps(sk, ct) -> shk`
|
||||
fn decaps(
|
||||
&self,
|
||||
shk: &mut [u8; SHK_LEN],
|
||||
sk: &[u8; SK_LEN],
|
||||
ct: &[u8; CT_LEN],
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("invalid argument")]
|
||||
InvalidArgument,
|
||||
#[error("internal error")]
|
||||
InternalError,
|
||||
}
|
||||
159
cipher-traits/src/primitives/keyed_hash.rs
Normal file
159
cipher-traits/src/primitives/keyed_hash.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Models a keyed hash function using an associated function (i.e. without `&self` receiver).
|
||||
pub trait KeyedHash<const KEY_LEN: usize, const HASH_LEN: usize> {
|
||||
/// The error type used to signal what went wrong.
|
||||
type Error;
|
||||
|
||||
/// Performs a keyed hash using `key` and `data` and writes the output to `out`
|
||||
fn keyed_hash(
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Models a keyed hash function using a method (i.e. with a `&self` receiver).
|
||||
///
|
||||
/// This makes type inference easier, but also requires having a [`KeyedHashInstance`] value,
|
||||
/// instead of just the [`KeyedHash`] type.
|
||||
pub trait KeyedHashInstance<const KEY_LEN: usize, const HASH_LEN: usize> {
|
||||
/// The error type used to signal what went wrong.
|
||||
type Error;
|
||||
|
||||
/// Performs a keyed hash using `key` and `data` and writes the output to `out`
|
||||
fn keyed_hash(
|
||||
&self,
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// This is a helper to allow for type parameter inference when calling functions
|
||||
/// that need a [KeyedHash].
|
||||
///
|
||||
/// Really just binds the [KeyedHash] trait to a dummy variable, so the type of this dummy variable
|
||||
/// can be used for type inference. Less typing work.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct InferKeyedHash<Static, const KEY_LEN: usize, const HASH_LEN: usize>
|
||||
where
|
||||
Static: KeyedHash<KEY_LEN, HASH_LEN>,
|
||||
{
|
||||
pub _phantom_keyed_hasher: PhantomData<Static>,
|
||||
}
|
||||
|
||||
impl<Static, const KEY_LEN: usize, const HASH_LEN: usize> InferKeyedHash<Static, KEY_LEN, HASH_LEN>
|
||||
where
|
||||
Static: KeyedHash<KEY_LEN, HASH_LEN>,
|
||||
{
|
||||
pub const KEY_LEN: usize = KEY_LEN;
|
||||
pub const HASH_LEN: usize = HASH_LEN;
|
||||
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
_phantom_keyed_hasher: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// This just forwards to [KeyedHash::keyed_hash] of the type parameter `Static`
|
||||
fn keyed_hash_internal<'a>(
|
||||
&self,
|
||||
key: &'a [u8; KEY_LEN],
|
||||
data: &'a [u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Static::Error> {
|
||||
Static::keyed_hash(key, data, out)
|
||||
}
|
||||
|
||||
/// Returns the key length of the keyed hash function.
|
||||
pub const fn key_len(self) -> usize {
|
||||
Self::KEY_LEN
|
||||
}
|
||||
|
||||
/// Returns the hash length of the keyed hash function.
|
||||
pub const fn hash_len(self) -> usize {
|
||||
Self::HASH_LEN
|
||||
}
|
||||
}
|
||||
|
||||
impl<const KEY_LEN: usize, const HASH_LEN: usize, Static: KeyedHash<KEY_LEN, HASH_LEN>>
|
||||
KeyedHashInstance<KEY_LEN, HASH_LEN> for InferKeyedHash<Static, KEY_LEN, HASH_LEN>
|
||||
{
|
||||
type Error = Static::Error;
|
||||
|
||||
fn keyed_hash(
|
||||
&self,
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Static::Error> {
|
||||
self.keyed_hash_internal(key, data, out)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper traits /////////////////////////////////////////////
|
||||
|
||||
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Default
|
||||
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
|
||||
where
|
||||
Static: KeyedHash<KEY_LEN, OUT_LEN>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Clone
|
||||
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
|
||||
where
|
||||
Static: KeyedHash<KEY_LEN, OUT_LEN>,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Copy
|
||||
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
|
||||
where
|
||||
Static: KeyedHash<KEY_LEN, OUT_LEN>,
|
||||
{
|
||||
}
|
||||
|
||||
use rosenpass_to::{with_destination, To};
|
||||
|
||||
/// Extends the [`KeyedHash`] trait with a [`To`]-flavoured function.
|
||||
pub trait KeyedHashTo<const KEY_LEN: usize, const HASH_LEN: usize>:
|
||||
KeyedHash<KEY_LEN, HASH_LEN>
|
||||
{
|
||||
fn keyed_hash_to(
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
) -> impl To<[u8; HASH_LEN], Result<(), Self::Error>> {
|
||||
with_destination(|out| Self::keyed_hash(key, data, out))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const KEY_LEN: usize, const HASH_LEN: usize, T: KeyedHash<KEY_LEN, HASH_LEN>>
|
||||
KeyedHashTo<KEY_LEN, HASH_LEN> for T
|
||||
{
|
||||
}
|
||||
|
||||
/// Extends the [`KeyedHashInstance`] trait with a [`To`]-flavoured function.
|
||||
pub trait KeyedHashInstanceTo<const KEY_LEN: usize, const HASH_LEN: usize>:
|
||||
KeyedHashInstance<KEY_LEN, HASH_LEN>
|
||||
{
|
||||
fn keyed_hash_to(
|
||||
&self,
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
) -> impl To<[u8; HASH_LEN], Result<(), Self::Error>> {
|
||||
with_destination(|out| self.keyed_hash(key, data, out))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const KEY_LEN: usize, const HASH_LEN: usize, T: KeyedHashInstance<KEY_LEN, HASH_LEN>>
|
||||
KeyedHashInstanceTo<KEY_LEN, HASH_LEN> for T
|
||||
{
|
||||
}
|
||||
73
ciphers/Cargo.toml
Normal file
73
ciphers/Cargo.toml
Normal file
@@ -0,0 +1,73 @@
|
||||
[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"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[features]
|
||||
# whether the types should be defined
|
||||
experiment_libcrux_define_blake2 = ["dep:libcrux-blake2", "dep:thiserror"]
|
||||
experiment_libcrux_define_kyber = ["dep:libcrux-ml-kem", "dep:rand"]
|
||||
experiment_libcrux_define_chachapoly = ["dep:libcrux-chacha20poly1305"]
|
||||
|
||||
# whether the types should be used by default
|
||||
experiment_libcrux_blake2 = ["experiment_libcrux_define_blake2"]
|
||||
experiment_libcrux_kyber = ["experiment_libcrux_define_kyber"]
|
||||
experiment_libcrux_chachapoly = ["experiment_libcrux_define_chachapoly"]
|
||||
experiment_libcrux_chachapoly_test = [
|
||||
"experiment_libcrux_define_chachapoly",
|
||||
"dep:libcrux",
|
||||
]
|
||||
|
||||
# shorthands
|
||||
experiment_libcrux_define_all = [
|
||||
"experiment_libcrux_define_blake2",
|
||||
"experiment_libcrux_define_chachapoly",
|
||||
"experiment_libcrux_define_kyber",
|
||||
]
|
||||
experiment_libcrux_all = [
|
||||
"experiment_libcrux_blake2",
|
||||
"experiment_libcrux_chachapoly",
|
||||
"experiment_libcrux_chachapoly_test",
|
||||
"experiment_libcrux_kyber",
|
||||
]
|
||||
|
||||
bench = ["experiment_libcrux_define_all"]
|
||||
|
||||
[[bench]]
|
||||
name = "primitives"
|
||||
harness = false
|
||||
required-features = ["bench"]
|
||||
|
||||
[dependencies]
|
||||
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 }
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
chacha20poly1305 = { workspace = true }
|
||||
blake2 = { workspace = true }
|
||||
sha3 = { workspace = true }
|
||||
rand = { workspace = true, optional = true }
|
||||
thiserror = { workspace = true, optional = true }
|
||||
|
||||
libcrux-chacha20poly1305 = { workspace = true, optional = true }
|
||||
libcrux-blake2 = { workspace = true, optional = true }
|
||||
libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] }
|
||||
|
||||
# this one is only used in testing, so it requires the `experiment_libcrux_chachapoly_test` feature.
|
||||
libcrux = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { workspace = true }
|
||||
criterion = { workspace = true }
|
||||
378
ciphers/benches/primitives.rs
Normal file
378
ciphers/benches/primitives.rs
Normal file
@@ -0,0 +1,378 @@
|
||||
criterion::criterion_main!(keyed_hash::benches, aead::benches, kem::benches);
|
||||
|
||||
fn benchid(base: KvPairs, last: KvPairs) -> String {
|
||||
format!("{base},{last}")
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct KvPair<'a>(&'a str, &'a str);
|
||||
|
||||
impl std::fmt::Display for KvPair<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{k}={v}", k = self.0, v = self.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct KvPairs<'a>(&'a [KvPair<'a>]);
|
||||
|
||||
impl std::fmt::Display for KvPairs<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.0.len() {
|
||||
0 => Ok(()),
|
||||
1 => write!(f, "{}", &self.0[0]),
|
||||
_ => {
|
||||
let mut delim = "";
|
||||
for pair in self.0 {
|
||||
write!(f, "{delim}{pair}")?;
|
||||
delim = ",";
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod kem {
|
||||
criterion::criterion_group!(
|
||||
benches,
|
||||
bench_kyber512_libcrux,
|
||||
bench_kyber512_oqs,
|
||||
bench_classicmceliece460896_oqs
|
||||
);
|
||||
|
||||
use criterion::Criterion;
|
||||
|
||||
fn bench_classicmceliece460896_oqs(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"classicmceliece460896",
|
||||
"oqs",
|
||||
rosenpass_oqs::ClassicMceliece460896,
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_kyber512_libcrux(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"kyber512",
|
||||
"libcrux",
|
||||
rosenpass_ciphers::subtle::libcrux::kyber512::Kyber512,
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_kyber512_oqs(c: &mut Criterion) {
|
||||
template(c, "kyber512", "oqs", rosenpass_oqs::Kyber512);
|
||||
}
|
||||
|
||||
use rosenpass_cipher_traits::primitives::Kem;
|
||||
|
||||
fn template<
|
||||
const SK_LEN: usize,
|
||||
const PK_LEN: usize,
|
||||
const CT_LEN: usize,
|
||||
const SHK_LEN: usize,
|
||||
T: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN>,
|
||||
>(
|
||||
c: &mut Criterion,
|
||||
alg_name: &str,
|
||||
impl_name: &str,
|
||||
scheme: T,
|
||||
) {
|
||||
use super::{benchid, KvPair, KvPairs};
|
||||
|
||||
let base = [
|
||||
KvPair("primitive", "kem"),
|
||||
KvPair("algorithm", alg_name),
|
||||
KvPair("implementation", impl_name),
|
||||
KvPair("length", "-1"),
|
||||
];
|
||||
|
||||
let kem_benchid = |op| benchid(KvPairs(&base), KvPairs(&[KvPair("operation", op)]));
|
||||
|
||||
c.bench_function(&kem_benchid("keygen"), |bench| {
|
||||
let mut sk = [0; SK_LEN];
|
||||
let mut pk = [0; PK_LEN];
|
||||
|
||||
bench.iter(|| {
|
||||
scheme.keygen(&mut sk, &mut pk).unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
c.bench_function(&kem_benchid("encaps"), |bench| {
|
||||
let mut sk = [0; SK_LEN];
|
||||
let mut pk = [0; PK_LEN];
|
||||
let mut ct = [0; CT_LEN];
|
||||
let mut shk = [0; SHK_LEN];
|
||||
|
||||
scheme.keygen(&mut sk, &mut pk).unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
scheme.encaps(&mut shk, &mut ct, &pk).unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
c.bench_function(&kem_benchid("decaps"), |bench| {
|
||||
let mut sk = [0; SK_LEN];
|
||||
let mut pk = [0; PK_LEN];
|
||||
let mut ct = [0; CT_LEN];
|
||||
let mut shk = [0; SHK_LEN];
|
||||
let mut shk2 = [0; SHK_LEN];
|
||||
|
||||
scheme.keygen(&mut sk, &mut pk).unwrap();
|
||||
scheme.encaps(&mut shk, &mut ct, &pk).unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
scheme.decaps(&mut shk2, &sk, &ct).unwrap();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
mod aead {
|
||||
criterion::criterion_group!(
|
||||
benches,
|
||||
bench_chachapoly_libcrux,
|
||||
bench_chachapoly_rustcrypto,
|
||||
bench_xchachapoly_rustcrypto,
|
||||
);
|
||||
|
||||
use criterion::Criterion;
|
||||
|
||||
const KEY_LEN: usize = rosenpass_ciphers::Aead::KEY_LEN;
|
||||
const TAG_LEN: usize = rosenpass_ciphers::Aead::TAG_LEN;
|
||||
|
||||
fn bench_xchachapoly_rustcrypto(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"xchacha20poly1305",
|
||||
"rustcrypto",
|
||||
rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::XChaCha20Poly1305,
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_chachapoly_rustcrypto(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"chacha20poly1305",
|
||||
"rustcrypto",
|
||||
rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305,
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_chachapoly_libcrux(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"chacha20poly1305",
|
||||
"libcrux",
|
||||
rosenpass_ciphers::subtle::libcrux::chacha20poly1305_ietf::ChaCha20Poly1305,
|
||||
);
|
||||
}
|
||||
|
||||
use rosenpass_cipher_traits::primitives::Aead;
|
||||
|
||||
fn template<const NONCE_LEN: usize, T: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>>(
|
||||
c: &mut Criterion,
|
||||
alg_name: &str,
|
||||
impl_name: &str,
|
||||
scheme: T,
|
||||
) {
|
||||
use crate::{benchid, KvPair, KvPairs};
|
||||
|
||||
let base = [
|
||||
KvPair("primitive", "aead"),
|
||||
KvPair("algorithm", alg_name),
|
||||
KvPair("implementation", impl_name),
|
||||
];
|
||||
let aead_benchid = |op, len| {
|
||||
benchid(
|
||||
KvPairs(&base),
|
||||
KvPairs(&[KvPair("operation", op), KvPair("length", len)]),
|
||||
)
|
||||
};
|
||||
|
||||
let key = [12; KEY_LEN];
|
||||
let nonce = [23; NONCE_LEN];
|
||||
let ad = [];
|
||||
|
||||
c.bench_function(&aead_benchid("encrypt", "0byte"), |bench| {
|
||||
const DATA_LEN: usize = 0;
|
||||
|
||||
let ptxt = [];
|
||||
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||
|
||||
bench.iter(|| {
|
||||
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
c.bench_function(&aead_benchid("decrypt", "0byte"), |bench| {
|
||||
const DATA_LEN: usize = 0;
|
||||
|
||||
let ptxt = [];
|
||||
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||
let mut ptxt_out = [0u8; DATA_LEN];
|
||||
|
||||
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
scheme
|
||||
.decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function(&aead_benchid("encrypt", "32byte"), |bench| {
|
||||
const DATA_LEN: usize = 32;
|
||||
|
||||
let ptxt = [34u8; DATA_LEN];
|
||||
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||
|
||||
bench.iter(|| {
|
||||
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
c.bench_function(&aead_benchid("decrypt", "32byte"), |bench| {
|
||||
const DATA_LEN: usize = 32;
|
||||
|
||||
let ptxt = [34u8; DATA_LEN];
|
||||
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||
let mut ptxt_out = [0u8; DATA_LEN];
|
||||
|
||||
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
scheme
|
||||
.decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
c.bench_function(&aead_benchid("encrypt", "1024byte"), |bench| {
|
||||
const DATA_LEN: usize = 1024;
|
||||
|
||||
let ptxt = [34u8; DATA_LEN];
|
||||
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||
|
||||
bench.iter(|| {
|
||||
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||
});
|
||||
});
|
||||
c.bench_function(&aead_benchid("decrypt", "1024byte"), |bench| {
|
||||
const DATA_LEN: usize = 1024;
|
||||
|
||||
let ptxt = [34u8; DATA_LEN];
|
||||
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||
let mut ptxt_out = [0u8; DATA_LEN];
|
||||
|
||||
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
scheme
|
||||
.decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mod keyed_hash {
|
||||
criterion::criterion_group!(
|
||||
benches,
|
||||
bench_blake2b_rustcrypto,
|
||||
bench_blake2b_libcrux,
|
||||
bench_shake256_rustcrypto,
|
||||
);
|
||||
|
||||
const KEY_LEN: usize = 32;
|
||||
const HASH_LEN: usize = 32;
|
||||
|
||||
use criterion::Criterion;
|
||||
|
||||
fn bench_shake256_rustcrypto(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"shake256",
|
||||
"rustcrypto",
|
||||
&rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::SHAKE256Core,
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_blake2b_rustcrypto(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"blake2b",
|
||||
"rustcrypto",
|
||||
&rosenpass_ciphers::subtle::rust_crypto::blake2b::Blake2b,
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_blake2b_libcrux(c: &mut Criterion) {
|
||||
template(
|
||||
c,
|
||||
"blake2b",
|
||||
"libcrux",
|
||||
&rosenpass_ciphers::subtle::libcrux::blake2b::Blake2b,
|
||||
);
|
||||
}
|
||||
|
||||
use rosenpass_cipher_traits::primitives::KeyedHash;
|
||||
|
||||
fn template<H: KeyedHash<KEY_LEN, HASH_LEN>>(
|
||||
c: &mut Criterion,
|
||||
alg_name: &str,
|
||||
impl_name: &str,
|
||||
_: &H,
|
||||
) where
|
||||
H::Error: std::fmt::Debug,
|
||||
{
|
||||
use crate::{benchid, KvPair, KvPairs};
|
||||
|
||||
let key = [12u8; KEY_LEN];
|
||||
let mut out = [0u8; HASH_LEN];
|
||||
|
||||
let base = [
|
||||
KvPair("primitive", "keyedhash"),
|
||||
KvPair("algorithm", alg_name),
|
||||
KvPair("implementation", impl_name),
|
||||
KvPair("operation", "hash"),
|
||||
];
|
||||
let keyedhash_benchid = |len| benchid(KvPairs(&base), KvPairs(&[KvPair("length", len)]));
|
||||
|
||||
c.bench_function(&keyedhash_benchid("0byte"), |bench| {
|
||||
let bytes = [];
|
||||
|
||||
bench.iter(|| {
|
||||
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||
})
|
||||
})
|
||||
.bench_function(&keyedhash_benchid("32byte"), |bench| {
|
||||
let bytes = [34u8; 32];
|
||||
|
||||
bench.iter(|| {
|
||||
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||
})
|
||||
})
|
||||
.bench_function(&keyedhash_benchid("64byte"), |bench| {
|
||||
let bytes = [34u8; 64];
|
||||
|
||||
bench.iter(|| {
|
||||
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||
})
|
||||
})
|
||||
.bench_function(&keyedhash_benchid("128byte"), |bench| {
|
||||
let bytes = [34u8; 128];
|
||||
|
||||
bench.iter(|| {
|
||||
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||
})
|
||||
})
|
||||
.bench_function(&keyedhash_benchid("1024byte"), |bench| {
|
||||
let bytes = [34u8; 1024];
|
||||
|
||||
bench.iter(|| {
|
||||
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
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.
|
||||
276
ciphers/src/hash_domain.rs
Normal file
276
ciphers/src/hash_domain.rs
Normal file
@@ -0,0 +1,276 @@
|
||||
//!
|
||||
//!```rust
|
||||
//! # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace};
|
||||
//! use rosenpass_ciphers::KeyedHash;
|
||||
//! use rosenpass_secret_memory::Secret;
|
||||
//! # rosenpass_secret_memory::secret_policy_use_only_malloc_secrets();
|
||||
//!
|
||||
//! const PROTOCOL_IDENTIFIER: &str = "MY_PROTOCOL:IDENTIFIER";
|
||||
//! // create use once hash domain for the protocol identifier
|
||||
//! let mut hash_domain = HashDomain::zero(KeyedHash::keyed_shake256());
|
||||
//! hash_domain = hash_domain.mix(PROTOCOL_IDENTIFIER.as_bytes())?;
|
||||
//! // upgrade to reusable hash domain
|
||||
//! let hash_domain_namespace: HashDomainNamespace = hash_domain.dup();
|
||||
//! // derive new key
|
||||
//! let key_identifier = "my_key_identifier";
|
||||
//! let key = hash_domain_namespace.mix(key_identifier.as_bytes())?.into_value();
|
||||
//! // derive a new key based on a secret
|
||||
//! const MY_SECRET_LEN: usize = 21;
|
||||
//! let my_secret_bytes = "my super duper secret".as_bytes();
|
||||
//! let my_secret: Secret<21> = Secret::from_slice("my super duper secret".as_bytes());
|
||||
//! let secret_hash_domain: SecretHashDomain = hash_domain_namespace.mix_secret(my_secret)?;
|
||||
//! // derive a new key based on the secret key
|
||||
//! let new_key_identifier = "my_new_key_identifier".as_bytes();
|
||||
//! let new_key = secret_hash_domain.mix(new_key_identifier)?.into_secret();
|
||||
//!
|
||||
//! # Ok::<(), anyhow::Error>(())
|
||||
//!```
|
||||
//!
|
||||
|
||||
use anyhow::Result;
|
||||
use rosenpass_secret_memory::Secret;
|
||||
use rosenpass_to::To as _;
|
||||
|
||||
pub use crate::{KeyedHash, KEY_LEN};
|
||||
|
||||
use rosenpass_cipher_traits::primitives::KeyedHashInstanceTo;
|
||||
|
||||
// TODO Use a proper Dec interface
|
||||
/// A use-once hash domain for a specified key that can be used directly.
|
||||
/// The key must consist of [KEY_LEN] many bytes. If the key must remain secret,
|
||||
/// use [SecretHashDomain] instead.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HashDomain([u8; KEY_LEN], KeyedHash);
|
||||
/// A reusable hash domain for a namespace identified by the key.
|
||||
/// The key must consist of [KEY_LEN] many bytes. If the key must remain secret,
|
||||
/// use [SecretHashDomainNamespace] instead.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HashDomainNamespace([u8; KEY_LEN], KeyedHash);
|
||||
/// A use-once hash domain for a specified key that can be used directly
|
||||
/// by wrapping it in [Secret]. The key must consist of [KEY_LEN] many bytes.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretHashDomain(Secret<KEY_LEN>, KeyedHash);
|
||||
/// A reusable secure hash domain for a namespace identified by the key and that keeps the key secure
|
||||
/// by wrapping it in [Secret]. The key must consist of [KEY_LEN] many bytes.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretHashDomainNamespace(Secret<KEY_LEN>, KeyedHash);
|
||||
|
||||
impl HashDomain {
|
||||
/// Creates a nw [HashDomain] initialized with a all-zeros key.
|
||||
pub fn zero(choice: KeyedHash) -> Self {
|
||||
Self([0u8; KEY_LEN], choice)
|
||||
}
|
||||
|
||||
/// Turns this [HashDomain] into a [HashDomainNamespace], keeping the key.
|
||||
pub fn dup(self) -> HashDomainNamespace {
|
||||
HashDomainNamespace(self.0, self.1)
|
||||
}
|
||||
|
||||
/// Turns this [HashDomain] into a [SecretHashDomain] by wrapping the key into a [Secret]
|
||||
/// and creating a new [SecretHashDomain] from it.
|
||||
pub fn turn_secret(self) -> SecretHashDomain {
|
||||
SecretHashDomain(Secret::from_slice(&self.0), self.1)
|
||||
}
|
||||
|
||||
// TODO: Protocol! Use domain separation to ensure that
|
||||
/// Creates a new [HashDomain] by mixing in a new key `v`. Specifically,
|
||||
/// it evaluates [hash::hash] with this HashDomain's key as the key and `v`
|
||||
/// as the `data` and uses the result as the key for the new [HashDomain].
|
||||
///
|
||||
pub fn mix(self, v: &[u8]) -> Result<Self> {
|
||||
let mut new_key: [u8; KEY_LEN] = [0u8; KEY_LEN];
|
||||
self.1.keyed_hash_to(&self.0, v).to(&mut new_key)?;
|
||||
Ok(Self(new_key, self.1))
|
||||
}
|
||||
|
||||
/// Version of [Self::mix] that accepts an iterator and mixes all values from the iterator into
|
||||
/// this hash domain.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_ciphers::{hash_domain::HashDomain, KeyedHash};
|
||||
///
|
||||
/// let hasher = HashDomain::zero(KeyedHash::keyed_shake256());
|
||||
/// assert_eq!(
|
||||
/// hasher.clone().mix(b"Hello")?.mix(b"World")?.into_value(),
|
||||
/// hasher.clone().mix_many([b"Hello", b"World"])?.into_value()
|
||||
/// );
|
||||
///
|
||||
/// Ok::<(), anyhow::Error>(())
|
||||
/// ```
|
||||
pub fn mix_many<I, T>(mut self, it: I) -> Result<Self>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
for e in it {
|
||||
self = self.mix(e.as_ref())?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
|
||||
/// by calling [SecretHashDomain::invoke_primitive] with this
|
||||
/// [HashDomain]'s key as `k` and `v` as `d`.
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(&self.0, v.secret(), self.1)
|
||||
}
|
||||
|
||||
/// Gets the key of this [HashDomain].
|
||||
pub fn into_value(self) -> [u8; KEY_LEN] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl HashDomainNamespace {
|
||||
/// Creates a new [HashDomain] by mixing in a new key `v`. Specifically,
|
||||
/// it evaluates [hash::hash] with the key of this HashDomainNamespace key as the key and `v`
|
||||
/// as the `data` and uses the result as the key for the new [HashDomain].
|
||||
pub fn mix(&self, v: &[u8]) -> Result<HashDomain> {
|
||||
let mut new_key: [u8; KEY_LEN] = [0u8; KEY_LEN];
|
||||
self.1.keyed_hash_to(&self.0, v).to(&mut new_key)?;
|
||||
Ok(HashDomain(new_key, self.1.clone()))
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
|
||||
/// by calling [SecretHashDomain::invoke_primitive] with the key of this
|
||||
/// [HashDomainNamespace] as `k` and `v` as `d`.
|
||||
///
|
||||
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(&self.0, v.secret(), self.1.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHashDomain {
|
||||
/// Create a new [SecretHashDomain] with the given key `k` and data `d` by calling
|
||||
/// [hash::hash] with `k` as the `key` and `d` s the `data`, and using the result
|
||||
/// as the content for the new [SecretHashDomain].
|
||||
/// Both `k` and `d` have to be exactly [KEY_LEN] bytes in length.
|
||||
/// TODO: docu
|
||||
pub fn invoke_primitive(
|
||||
k: &[u8],
|
||||
d: &[u8],
|
||||
hash_choice: KeyedHash,
|
||||
) -> Result<SecretHashDomain> {
|
||||
let mut new_secret_key = Secret::zero();
|
||||
hash_choice
|
||||
.keyed_hash_to(k.try_into()?, d)
|
||||
.to(new_secret_key.secret_mut())?;
|
||||
let r = SecretHashDomain(new_secret_key, hash_choice);
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] that is initialized with an all zeros key.
|
||||
pub fn zero(hash_choice: KeyedHash) -> Self {
|
||||
Self(Secret::zero(), hash_choice)
|
||||
}
|
||||
|
||||
/// Turns this [SecretHashDomain] into a [SecretHashDomainNamespace].
|
||||
pub fn dup(self) -> SecretHashDomainNamespace {
|
||||
SecretHashDomainNamespace(self.0, self.1)
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] from a [Secret] `k`.
|
||||
///
|
||||
/// It requires that `k` consist of exactly [KEY_LEN] bytes.
|
||||
pub fn danger_from_secret(k: Secret<KEY_LEN>, hash_choice: KeyedHash) -> Self {
|
||||
Self(k, hash_choice)
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] by mixing in a new key `v`. Specifically,
|
||||
/// it evaluates [hash::hash] with this [SecretHashDomain]'s key as the key and `v`
|
||||
/// as the `data` and uses the result as the key for the new [SecretHashDomain].
|
||||
///
|
||||
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
|
||||
pub fn mix(self, v: &[u8]) -> Result<SecretHashDomain> {
|
||||
Self::invoke_primitive(self.0.secret(), v, self.1)
|
||||
}
|
||||
|
||||
/// Version of [Self::mix] that accepts an iterator and mixes all values from the iterator into
|
||||
/// this hash domain.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_ciphers::{hash_domain::HashDomain, KeyedHash};
|
||||
///
|
||||
/// rosenpass_secret_memory::secret_policy_use_only_malloc_secrets();
|
||||
///
|
||||
/// let hasher = HashDomain::zero(KeyedHash::keyed_shake256());
|
||||
/// assert_eq!(
|
||||
/// hasher
|
||||
/// .clone()
|
||||
/// .turn_secret()
|
||||
/// .mix(b"Hello")?
|
||||
/// .mix(b"World")?
|
||||
/// .into_secret()
|
||||
/// .secret(),
|
||||
/// hasher
|
||||
/// .clone()
|
||||
/// .turn_secret()
|
||||
/// .mix_many([b"Hello", b"World"])?
|
||||
/// .into_secret()
|
||||
/// .secret(),
|
||||
/// );
|
||||
|
||||
/// Ok::<(), anyhow::Error>(())
|
||||
/// ```
|
||||
pub fn mix_many<I, T>(mut self, it: I) -> Result<Self>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
for e in it {
|
||||
self = self.mix(e.as_ref())?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
|
||||
/// by calling [SecretHashDomain::invoke_primitive] with the key of this
|
||||
/// [HashDomainNamespace] as `k` and `v` as `d`.
|
||||
///
|
||||
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
Self::invoke_primitive(self.0.secret(), v.secret(), self.1)
|
||||
}
|
||||
|
||||
/// Get the secret key data from this [SecretHashDomain].
|
||||
pub fn into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHashDomainNamespace {
|
||||
/// Creates a new [SecretHashDomain] by mixing in a new key `v`. Specifically,
|
||||
/// it evaluates [hash::hash] with the key of this HashDomainNamespace key as the key and `v`
|
||||
/// as the `data` and uses the result as the key for the new [HashDomain].
|
||||
///
|
||||
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
|
||||
pub fn mix(&self, v: &[u8]) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(self.0.secret(), v, self.1.clone())
|
||||
}
|
||||
|
||||
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
|
||||
/// by calling [SecretHashDomain::invoke_primitive] with the key of this
|
||||
/// [HashDomainNamespace] as `k` and `v` as `d`.
|
||||
///
|
||||
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(self.0.secret(), v.secret(), self.1.clone())
|
||||
}
|
||||
|
||||
// TODO: This entire API is not very nice; we need this for biscuits, but
|
||||
// it might be better to extract a special "biscuit"
|
||||
// labeled subkey and reinitialize the chain with this
|
||||
/// Get the secret key data from this [SecretHashDomain].
|
||||
pub fn danger_into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn keyed_hash(&self) -> &KeyedHash {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
46
ciphers/src/lib.rs
Normal file
46
ciphers/src/lib.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use rosenpass_cipher_traits::primitives::Aead as AeadTrait;
|
||||
use static_assertions::const_assert;
|
||||
|
||||
pub mod subtle;
|
||||
|
||||
/// All keyed primitives in this crate use 32 byte keys
|
||||
pub const KEY_LEN: usize = 32;
|
||||
const_assert!(KEY_LEN == Aead::KEY_LEN);
|
||||
const_assert!(KEY_LEN == XAead::KEY_LEN);
|
||||
const_assert!(KEY_LEN == hash_domain::KEY_LEN);
|
||||
|
||||
/// Keyed hashing
|
||||
///
|
||||
/// This should only be used for implementation details; anything with relevance
|
||||
/// to the cryptographic protocol should use the facilities in [hash_domain], (though
|
||||
/// hash domain uses this module internally)
|
||||
pub use crate::subtle::keyed_hash::KeyedHash;
|
||||
|
||||
/// Authenticated encryption with associated data (AEAD)
|
||||
/// Chacha20poly1305 is used.
|
||||
#[cfg(feature = "experiment_libcrux_chachapoly")]
|
||||
pub use subtle::libcrux::chacha20poly1305_ietf::ChaCha20Poly1305 as Aead;
|
||||
|
||||
/// Authenticated encryption with associated data (AEAD)
|
||||
/// Chacha20poly1305 is used.
|
||||
#[cfg(not(feature = "experiment_libcrux_chachapoly"))]
|
||||
pub use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as Aead;
|
||||
|
||||
/// Authenticated encryption with associated data with a extended-length nonce (XAEAD)
|
||||
/// XChacha20poly1305 is used.
|
||||
pub use crate::subtle::rust_crypto::xchacha20poly1305_ietf::XChaCha20Poly1305 as XAead;
|
||||
|
||||
/// Use Classic-McEcliece-460986 as the Static KEM.
|
||||
///
|
||||
/// See [rosenpass_oqs::ClassicMceliece460896] for more details.
|
||||
pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem;
|
||||
|
||||
/// Use Kyber-512 as the Static KEM
|
||||
///
|
||||
/// See [rosenpass_oqs::Kyber512] for more details.
|
||||
#[cfg(not(feature = "experiment_libcrux_kyber"))]
|
||||
pub use rosenpass_oqs::Kyber512 as EphemeralKem;
|
||||
#[cfg(feature = "experiment_libcrux_kyber")]
|
||||
pub use subtle::libcrux::kyber512::Kyber512 as EphemeralKem;
|
||||
|
||||
pub mod hash_domain;
|
||||
79
ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs
Normal file
79
ciphers/src/subtle/custom/incorrect_hmac_blake2b.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use rosenpass_cipher_traits::{
|
||||
algorithms::KeyedHashIncorrectHmacBlake2b,
|
||||
primitives::{InferKeyedHash, KeyedHash, KeyedHashTo},
|
||||
};
|
||||
use rosenpass_constant_time::xor;
|
||||
use rosenpass_to::{ops::copy_slice, To};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
#[cfg(not(feature = "experiment_libcrux_blake2"))]
|
||||
use crate::subtle::rust_crypto::blake2b::Blake2b;
|
||||
#[cfg(not(feature = "experiment_libcrux_blake2"))]
|
||||
use anyhow::Error;
|
||||
|
||||
#[cfg(feature = "experiment_libcrux_blake2")]
|
||||
use crate::subtle::libcrux::blake2b::{Blake2b, Error};
|
||||
|
||||
/// The key length, 32 bytes or 256 bits.
|
||||
pub const KEY_LEN: usize = 32;
|
||||
|
||||
/// The hash length, 32 bytes or 256 bits.
|
||||
pub const HASH_LEN: usize = 32;
|
||||
|
||||
/// This is a woefully incorrect implementation of hmac_blake2b.
|
||||
/// See <https://github.com/rosenpass/rosenpass/issues/68#issuecomment-1563612222>
|
||||
///
|
||||
/// It accepts 32 byte keys, exclusively.
|
||||
///
|
||||
/// This will be replaced, likely by Kekkac at some point soon.
|
||||
/// <https://github.com/rosenpass/rosenpass/pull/145>
|
||||
///
|
||||
/// # Examples
|
||||
///```rust
|
||||
/// # use rosenpass_ciphers::subtle::custom::incorrect_hmac_blake2b::IncorrectHmacBlake2bCore;
|
||||
/// use rosenpass_cipher_traits::primitives::KeyedHashTo;
|
||||
/// use rosenpass_to::To;
|
||||
/// let key: [u8; 32] = [0; 32];
|
||||
/// let data: [u8; 32] = [255; 32];
|
||||
/// // buffer for the hash output
|
||||
/// let mut hash_data: [u8; 32] = [0u8; 32];
|
||||
///
|
||||
/// assert!(IncorrectHmacBlake2bCore::keyed_hash_to(&key, &data).to(&mut hash_data).is_ok(), "Hashing has to return OK result");
|
||||
/// # let expected_hash: &[u8] = &[5, 152, 135, 141, 151, 106, 147, 8, 220, 95, 38, 66, 29, 33, 3,
|
||||
/// 104, 250, 114, 131, 119, 27, 56, 59, 44, 11, 67, 230, 113, 112, 20, 80, 103];
|
||||
/// # assert_eq!(hash_data, expected_hash);
|
||||
///```
|
||||
///
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct IncorrectHmacBlake2bCore;
|
||||
|
||||
impl KeyedHash<KEY_LEN, HASH_LEN> for IncorrectHmacBlake2bCore {
|
||||
type Error = Error;
|
||||
|
||||
fn keyed_hash(
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error> {
|
||||
const IPAD: [u8; KEY_LEN] = [0x36u8; KEY_LEN];
|
||||
const OPAD: [u8; KEY_LEN] = [0x5Cu8; KEY_LEN];
|
||||
|
||||
type Key = Zeroizing<[u8; KEY_LEN]>;
|
||||
let mut tmp_key = Key::default();
|
||||
|
||||
copy_slice(key).to(tmp_key.as_mut());
|
||||
xor(&IPAD).to(tmp_key.as_mut());
|
||||
let mut outer_data = Key::default();
|
||||
Blake2b::keyed_hash_to(&tmp_key, data).to(&mut outer_data)?;
|
||||
|
||||
copy_slice(key).to(tmp_key.as_mut());
|
||||
xor(&OPAD).to(tmp_key.as_mut());
|
||||
Blake2b::keyed_hash_to(&tmp_key, outer_data.as_ref()).to(out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub type IncorrectHmacBlake2b = InferKeyedHash<IncorrectHmacBlake2bCore, KEY_LEN, HASH_LEN>;
|
||||
|
||||
impl KeyedHashIncorrectHmacBlake2b for IncorrectHmacBlake2bCore {}
|
||||
3
ciphers/src/subtle/custom/mod.rs
Normal file
3
ciphers/src/subtle/custom/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
//! Own implementations of custom algorithms
|
||||
|
||||
pub mod incorrect_hmac_blake2b;
|
||||
65
ciphers/src/subtle/keyed_hash.rs
Normal file
65
ciphers/src/subtle/keyed_hash.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
//! This module provides types that enabling choosing the keyed hash building block to be used at
|
||||
//! runtime (using enums) instead of at compile time (using generics).
|
||||
|
||||
use anyhow::Result;
|
||||
use rosenpass_cipher_traits::primitives::KeyedHashInstance;
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::subtle::{
|
||||
custom::incorrect_hmac_blake2b::IncorrectHmacBlake2b, rust_crypto::keyed_shake256::SHAKE256_32,
|
||||
};
|
||||
|
||||
/// Length of symmetric key throughout Rosenpass.
|
||||
pub const KEY_LEN: usize = 32;
|
||||
|
||||
/// The hash is used as a symmetric key and should have the same length.
|
||||
pub const HASH_LEN: usize = KEY_LEN;
|
||||
|
||||
/// Provides a way to pick which keyed hash to use at runtime.
|
||||
/// Implements [`KeyedHashInstance`] to allow hashing using the respective algorithm.
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum KeyedHash {
|
||||
/// A hasher backed by [`SHAKE256_32`].
|
||||
KeyedShake256(SHAKE256_32),
|
||||
/// A hasher backed by [`IncorrectHmacBlake2b`].
|
||||
IncorrectHmacBlake2b(IncorrectHmacBlake2b),
|
||||
}
|
||||
|
||||
impl KeyedHash {
|
||||
/// Creates an [`KeyedHash`] backed by SHAKE256.
|
||||
pub fn keyed_shake256() -> Self {
|
||||
Self::KeyedShake256(Default::default())
|
||||
}
|
||||
|
||||
/// Creates an [`KeyedHash`] backed by Blake2B.
|
||||
pub fn incorrect_hmac_blake2b() -> Self {
|
||||
Self::IncorrectHmacBlake2b(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyedHashInstance<KEY_LEN, HASH_LEN> for KeyedHash {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn keyed_hash(
|
||||
&self,
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error> {
|
||||
match self {
|
||||
Self::KeyedShake256(h) => h.keyed_hash(key, data, out)?,
|
||||
Self::IncorrectHmacBlake2b(h) => h.keyed_hash(key, data, out)?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for KeyedHash {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::KeyedShake256(_) => write!(f, "KeyedShake256_32"),
|
||||
Self::IncorrectHmacBlake2b(_) => write!(f, "IncorrectHmacBlake2b"),
|
||||
}
|
||||
}
|
||||
}
|
||||
88
ciphers/src/subtle/libcrux/blake2b.rs
Normal file
88
ciphers/src/subtle/libcrux/blake2b.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
//! Implementation of the [`KeyedHashBlake2b`] trait based on the [`libcrux_blake2`] crate.
|
||||
|
||||
use libcrux_blake2::Blake2bBuilder;
|
||||
|
||||
use rosenpass_cipher_traits::algorithms::KeyedHashBlake2b;
|
||||
use rosenpass_cipher_traits::primitives::KeyedHash;
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::HASH_LEN;
|
||||
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::KEY_LEN;
|
||||
|
||||
/// Describles which error occurred
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// An unexpected internal error occurred. Should never be returned and points to a bug in the
|
||||
/// implementation.
|
||||
#[error("internal error")]
|
||||
InternalError,
|
||||
|
||||
/// Indicates that the provided data was too long.
|
||||
#[error("data is too long")]
|
||||
DataTooLong,
|
||||
}
|
||||
|
||||
/// Hasher for the given `data` with the Blake2b hash function.
|
||||
pub struct Blake2b;
|
||||
|
||||
impl KeyedHash<KEY_LEN, HASH_LEN> for Blake2b {
|
||||
type Error = Error;
|
||||
|
||||
fn keyed_hash(
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error> {
|
||||
let mut h = Blake2bBuilder::new_keyed_const(key)
|
||||
// this may fail if the key length is invalid, but 32 is fine
|
||||
.map_err(|_| Error::InternalError)?
|
||||
.build_const_digest_len()
|
||||
.map_err(|_|
|
||||
// this can only fail if the output length is invalid, but 32 is fine.
|
||||
Error::InternalError)?;
|
||||
|
||||
h.update(data).map_err(|_| Error::DataTooLong)?;
|
||||
h.finalize(out);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyedHashBlake2b for Blake2b {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod equivalence_tests {
|
||||
use super::*;
|
||||
use rand::RngCore;
|
||||
|
||||
#[test]
|
||||
fn fuzz_equivalence_libcrux_old_new() {
|
||||
let datas: [&[u8]; 3] = [
|
||||
b"".as_slice(),
|
||||
b"test".as_slice(),
|
||||
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
|
||||
];
|
||||
|
||||
let mut key = [0; KEY_LEN];
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut hash_left = [0; 32];
|
||||
let mut hash_right = [0; 32];
|
||||
|
||||
for data in datas {
|
||||
for _ in 0..1000 {
|
||||
rng.fill_bytes(&mut key);
|
||||
|
||||
crate::subtle::rust_crypto::blake2b::Blake2b::keyed_hash(
|
||||
&key,
|
||||
data,
|
||||
&mut hash_left,
|
||||
)
|
||||
.unwrap();
|
||||
crate::subtle::libcrux::blake2b::Blake2b::keyed_hash(&key, data, &mut hash_right)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(hash_left, hash_right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
274
ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs
Normal file
274
ciphers/src/subtle/libcrux/chacha20poly1305_ietf.rs
Normal file
@@ -0,0 +1,274 @@
|
||||
//! Implementation of the [`AeadChaCha20Poly1305`] trait based on the [`libcrux_chacha20poly1305`] crate.
|
||||
|
||||
use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305;
|
||||
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN};
|
||||
|
||||
/// An implementation of the ChaCha20Poly1305 AEAD based on libcrux
|
||||
pub struct ChaCha20Poly1305;
|
||||
|
||||
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
|
||||
fn encrypt(
|
||||
&self,
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> Result<(), AeadError> {
|
||||
let (ctxt, tag) = libcrux_chacha20poly1305::encrypt(key, plaintext, ciphertext, ad, nonce)
|
||||
.map_err(|_| AeadError::InternalError)?;
|
||||
|
||||
// return an error of the destination buffer is longer than expected
|
||||
// because the caller wouldn't know where the end is
|
||||
if ctxt.len() + tag.len() != ciphertext.len() {
|
||||
return Err(AeadError::InternalError);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
&self,
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> Result<(), AeadError> {
|
||||
let ptxt = libcrux_chacha20poly1305::decrypt(key, plaintext, ciphertext, ad, nonce)
|
||||
.map_err(|_| AeadError::DecryptError)?;
|
||||
|
||||
// return an error of the destination buffer is longer than expected
|
||||
// because the caller wouldn't know where the end is
|
||||
if ptxt.len() != plaintext.len() {
|
||||
return Err(AeadError::DecryptError);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {}
|
||||
|
||||
/// The idea of these tests is to check that the above implemenatation behaves, by and large, the
|
||||
/// same as the one from the old libcrux and the one from RustCrypto. You can consider them janky,
|
||||
/// self-rolled property-based tests.
|
||||
#[cfg(test)]
|
||||
mod equivalence_tests {
|
||||
use super::*;
|
||||
use rand::RngCore;
|
||||
|
||||
#[test]
|
||||
fn proptest_equivalence_libcrux_rustcrypto() {
|
||||
use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as RustCryptoChaCha20Poly1305;
|
||||
let ptxts: [&[u8]; 3] = [
|
||||
b"".as_slice(),
|
||||
b"test".as_slice(),
|
||||
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
|
||||
];
|
||||
let mut key = [0; KEY_LEN];
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut ctxt_left = [0; 64 + TAG_LEN];
|
||||
let mut ctxt_right = [0; 64 + TAG_LEN];
|
||||
|
||||
let mut ptxt_left = [0; 64];
|
||||
let mut ptxt_right = [0; 64];
|
||||
|
||||
let nonce = [0; NONCE_LEN];
|
||||
let ad = b"";
|
||||
|
||||
for ptxt in ptxts {
|
||||
for _ in 0..1000 {
|
||||
rng.fill_bytes(&mut key);
|
||||
let ctxt_left = &mut ctxt_left[..ptxt.len() + TAG_LEN];
|
||||
let ctxt_right = &mut ctxt_right[..ptxt.len() + TAG_LEN];
|
||||
|
||||
let ptxt_left = &mut ptxt_left[..ptxt.len()];
|
||||
let ptxt_right = &mut ptxt_right[..ptxt.len()];
|
||||
|
||||
RustCryptoChaCha20Poly1305
|
||||
.encrypt(ctxt_left, &key, &nonce, ad, ptxt)
|
||||
.unwrap();
|
||||
ChaCha20Poly1305
|
||||
.encrypt(ctxt_right, &key, &nonce, ad, ptxt)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ctxt_left, ctxt_right);
|
||||
|
||||
RustCryptoChaCha20Poly1305
|
||||
.decrypt(ptxt_left, &key, &nonce, ad, ctxt_left)
|
||||
.unwrap();
|
||||
ChaCha20Poly1305
|
||||
.decrypt(ptxt_right, &key, &nonce, ad, ctxt_right)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ptxt_left, ptxt);
|
||||
assert_eq!(ptxt_right, ptxt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "experiment_libcrux_chachapoly_test")]
|
||||
fn proptest_equivalence_libcrux_old_new() {
|
||||
let ptxts: [&[u8]; 3] = [
|
||||
b"".as_slice(),
|
||||
b"test".as_slice(),
|
||||
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
|
||||
];
|
||||
let mut key = [0; KEY_LEN];
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut ctxt_left = [0; 64 + TAG_LEN];
|
||||
let mut ctxt_right = [0; 64 + TAG_LEN];
|
||||
|
||||
let mut ptxt_left = [0; 64];
|
||||
let mut ptxt_right = [0; 64];
|
||||
|
||||
let nonce = [0; NONCE_LEN];
|
||||
let ad = b"";
|
||||
|
||||
for ptxt in ptxts {
|
||||
for _ in 0..1000 {
|
||||
rng.fill_bytes(&mut key);
|
||||
let ctxt_left = &mut ctxt_left[..ptxt.len() + TAG_LEN];
|
||||
let ctxt_right = &mut ctxt_right[..ptxt.len() + TAG_LEN];
|
||||
|
||||
let ptxt_left = &mut ptxt_left[..ptxt.len()];
|
||||
let ptxt_right = &mut ptxt_right[..ptxt.len()];
|
||||
|
||||
encrypt(ctxt_left, &key, &nonce, ad, ptxt).unwrap();
|
||||
ChaCha20Poly1305
|
||||
.encrypt(ctxt_right, &key, &nonce, ad, ptxt)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ctxt_left, ctxt_right);
|
||||
|
||||
decrypt(ptxt_left, &key, &nonce, ad, ctxt_left).unwrap();
|
||||
ChaCha20Poly1305
|
||||
.decrypt(ptxt_right, &key, &nonce, ad, ctxt_right)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(ptxt_left, ptxt);
|
||||
assert_eq!(ptxt_right, ptxt);
|
||||
}
|
||||
}
|
||||
|
||||
// The old libcrux functions:
|
||||
|
||||
// The functions below are from the old libcrux backend. I am keeping them around so we can
|
||||
// check if they behave the same.
|
||||
use rosenpass_to::ops::copy_slice;
|
||||
use rosenpass_to::To;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Encrypts using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
|
||||
/// Key and nonce MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
|
||||
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN]. The last [TAG_LEN] bytes
|
||||
/// written in `ciphertext` are the tag guaranteeing integrity. `ciphertext` MUST have a capacity of
|
||||
/// `plaintext.len()` + [TAG_LEN].
|
||||
///
|
||||
/// # Examples
|
||||
///```rust
|
||||
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
|
||||
///
|
||||
/// const PLAINTEXT_LEN: usize = 43;
|
||||
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
|
||||
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
|
||||
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
|
||||
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
|
||||
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
|
||||
/// let mut ciphertext_buffer = [0u8; PLAINTEXT_LEN + TAG_LEN];
|
||||
///
|
||||
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
|
||||
/// assert!(res.is_ok());
|
||||
/// # let expected_ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
|
||||
/// # 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
|
||||
/// # 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
|
||||
/// # 8, 114, 85, 4, 25];
|
||||
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
|
||||
///```
|
||||
///
|
||||
#[inline]
|
||||
pub fn encrypt(
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8],
|
||||
nonce: &[u8],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> anyhow::Result<()> {
|
||||
let (ciphertext, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
|
||||
|
||||
use libcrux::aead as C;
|
||||
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
|
||||
let crux_iv = C::Iv(nonce.try_into().unwrap());
|
||||
|
||||
copy_slice(plaintext).to(ciphertext);
|
||||
let crux_tag = libcrux::aead::encrypt(&crux_key, ciphertext, crux_iv, ad).unwrap();
|
||||
copy_slice(crux_tag.as_ref()).to(mac);
|
||||
|
||||
match crux_key {
|
||||
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
|
||||
/// `ad`. using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
|
||||
///
|
||||
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
|
||||
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN].
|
||||
///
|
||||
/// # Examples
|
||||
///```rust
|
||||
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
|
||||
/// let ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
|
||||
/// 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
|
||||
/// 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
|
||||
/// 8, 114, 85, 4, 25]; // this is the ciphertext generated by the example for the encryption
|
||||
/// const PLAINTEXT_LEN: usize = 43;
|
||||
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN, ciphertext.len());
|
||||
///
|
||||
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
|
||||
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
|
||||
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
|
||||
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
|
||||
///
|
||||
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, nonce, additional_data, ciphertext);
|
||||
/// assert!(res.is_ok());
|
||||
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
|
||||
/// assert_eq!(expected_plaintext, plaintext_buffer);
|
||||
///
|
||||
///```
|
||||
#[inline]
|
||||
pub fn decrypt(
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8],
|
||||
nonce: &[u8],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> anyhow::Result<()> {
|
||||
let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
|
||||
|
||||
use libcrux::aead as C;
|
||||
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
|
||||
let crux_iv = C::Iv(nonce.try_into().unwrap());
|
||||
let crux_tag = C::Tag::from_slice(mac).unwrap();
|
||||
|
||||
copy_slice(ciphertext).to(plaintext);
|
||||
libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap();
|
||||
|
||||
match crux_key {
|
||||
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
|
||||
_ => panic!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
133
ciphers/src/subtle/libcrux/kyber512.rs
Normal file
133
ciphers/src/subtle/libcrux/kyber512.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
//! Implementation of the [`KemKyber512`] trait based on the [`libcrux_ml_kem`] crate.
|
||||
|
||||
use libcrux_ml_kem::kyber512;
|
||||
use rand::RngCore;
|
||||
|
||||
use rosenpass_cipher_traits::algorithms::KemKyber512;
|
||||
use rosenpass_cipher_traits::primitives::{Kem, KemError};
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::kem_kyber512::{CT_LEN, PK_LEN, SHK_LEN, SK_LEN};
|
||||
|
||||
/// An implementation of the Kyber512 KEM based on libcrux
|
||||
pub struct Kyber512;
|
||||
|
||||
impl Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> for Kyber512 {
|
||||
fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), KemError> {
|
||||
let mut randomness = [0u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE];
|
||||
rand::thread_rng().fill_bytes(&mut randomness);
|
||||
|
||||
let key_pair = kyber512::generate_key_pair(randomness);
|
||||
|
||||
let new_sk: &[u8; SK_LEN] = key_pair.sk();
|
||||
let new_pk: &[u8; PK_LEN] = key_pair.pk();
|
||||
|
||||
sk.clone_from_slice(new_sk);
|
||||
pk.clone_from_slice(new_pk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encaps(
|
||||
&self,
|
||||
shk: &mut [u8; SHK_LEN],
|
||||
ct: &mut [u8; CT_LEN],
|
||||
pk: &[u8; PK_LEN],
|
||||
) -> Result<(), KemError> {
|
||||
let mut randomness = [0u8; libcrux_ml_kem::SHARED_SECRET_SIZE];
|
||||
rand::thread_rng().fill_bytes(&mut randomness);
|
||||
|
||||
let (new_ct, new_shk) = kyber512::encapsulate(&pk.into(), randomness);
|
||||
let new_ct: &[u8; CT_LEN] = new_ct.as_slice();
|
||||
|
||||
shk.clone_from_slice(&new_shk);
|
||||
ct.clone_from_slice(new_ct);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decaps(
|
||||
&self,
|
||||
shk: &mut [u8; SHK_LEN],
|
||||
sk: &[u8; SK_LEN],
|
||||
ct: &[u8; CT_LEN],
|
||||
) -> Result<(), KemError> {
|
||||
let new_shk: [u8; SHK_LEN] = kyber512::decapsulate(&sk.into(), &ct.into());
|
||||
shk.clone_from(&new_shk);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Kyber512 {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl KemKyber512 for Kyber512 {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod equivalence_tests {
|
||||
use super::*;
|
||||
|
||||
// Test that libcrux and OQS produce the same results
|
||||
#[test]
|
||||
fn proptest_equivalence_libcrux_oqs() {
|
||||
use rosenpass_oqs::Kyber512 as OqsKyber512;
|
||||
|
||||
let (mut sk1, mut pk1) = ([0; SK_LEN], [0; PK_LEN]);
|
||||
let (mut sk2, mut pk2) = ([0; SK_LEN], [0; PK_LEN]);
|
||||
|
||||
let mut ct_left = [0; CT_LEN];
|
||||
let mut ct_right = [0; CT_LEN];
|
||||
|
||||
let mut shk_enc_left = [0; SHK_LEN];
|
||||
let mut shk_enc_right = [0; SHK_LEN];
|
||||
|
||||
// naming schema: shk_dec_{encapsing lib}_{decapsing lib}
|
||||
// should be the same if the encapsing lib was the same.
|
||||
let mut shk_dec_left_left = [0; SHK_LEN];
|
||||
let mut shk_dec_left_right = [0; SHK_LEN];
|
||||
let mut shk_dec_right_left = [0; SHK_LEN];
|
||||
let mut shk_dec_right_right = [0; SHK_LEN];
|
||||
|
||||
for _ in 0..1000 {
|
||||
let sk1 = &mut sk1;
|
||||
let pk1 = &mut pk1;
|
||||
let sk2 = &mut sk2;
|
||||
let pk2 = &mut pk2;
|
||||
|
||||
let ct_left = &mut ct_left;
|
||||
let ct_right = &mut ct_right;
|
||||
|
||||
let shk_enc_left = &mut shk_enc_left;
|
||||
let shk_enc_right = &mut shk_enc_right;
|
||||
|
||||
let shk_dec_left_left = &mut shk_dec_left_left;
|
||||
let shk_dec_left_right = &mut shk_dec_left_right;
|
||||
let shk_dec_right_left = &mut shk_dec_right_left;
|
||||
let shk_dec_right_right = &mut shk_dec_right_right;
|
||||
|
||||
Kyber512.keygen(sk1, pk1).unwrap();
|
||||
Kyber512.keygen(sk2, pk2).unwrap();
|
||||
|
||||
Kyber512.encaps(shk_enc_left, ct_left, pk2).unwrap();
|
||||
OqsKyber512.encaps(shk_enc_right, ct_right, pk2).unwrap();
|
||||
|
||||
Kyber512.decaps(shk_dec_left_left, sk2, ct_left).unwrap();
|
||||
Kyber512.decaps(shk_dec_right_left, sk2, ct_right).unwrap();
|
||||
|
||||
OqsKyber512
|
||||
.decaps(shk_dec_left_right, sk2, ct_left)
|
||||
.unwrap();
|
||||
OqsKyber512
|
||||
.decaps(shk_dec_right_right, sk2, ct_right)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(shk_enc_left, shk_dec_left_left);
|
||||
assert_eq!(shk_enc_left, shk_dec_left_right);
|
||||
|
||||
assert_eq!(shk_enc_right, shk_dec_right_left);
|
||||
assert_eq!(shk_enc_right, shk_dec_right_right);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
ciphers/src/subtle/libcrux/mod.rs
Normal file
14
ciphers/src/subtle/libcrux/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
//! Implementations backed by libcrux, a verified crypto library.
|
||||
//!
|
||||
//! [Website](https://cryspen.com/libcrux/)
|
||||
//!
|
||||
//! [Github](https://github.com/cryspen/libcrux)
|
||||
|
||||
#[cfg(feature = "experiment_libcrux_define_blake2")]
|
||||
pub mod blake2b;
|
||||
|
||||
#[cfg(feature = "experiment_libcrux_define_chachapoly")]
|
||||
pub mod chacha20poly1305_ietf;
|
||||
|
||||
#[cfg(feature = "experiment_libcrux_define_kyber")]
|
||||
pub mod kyber512;
|
||||
16
ciphers/src/subtle/mod.rs
Normal file
16
ciphers/src/subtle/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
//! Contains the implementations of the crypto algorithms used throughout Rosenpass.
|
||||
|
||||
pub mod keyed_hash;
|
||||
|
||||
pub use custom::incorrect_hmac_blake2b;
|
||||
pub use rust_crypto::{blake2b, keyed_shake256};
|
||||
|
||||
pub mod custom;
|
||||
pub mod rust_crypto;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "experiment_libcrux_define_blake2",
|
||||
feature = "experiment_libcrux_define_chachapoly",
|
||||
feature = "experiment_libcrux_define_kyber",
|
||||
))]
|
||||
pub mod libcrux;
|
||||
44
ciphers/src/subtle/rust_crypto/blake2b.rs
Normal file
44
ciphers/src/subtle/rust_crypto/blake2b.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use blake2::digest::crypto_common::generic_array::GenericArray;
|
||||
use blake2::digest::crypto_common::typenum::U32;
|
||||
use blake2::digest::{FixedOutput, Mac};
|
||||
use blake2::Blake2bMac;
|
||||
|
||||
use rosenpass_cipher_traits::primitives::KeyedHash;
|
||||
use rosenpass_to::{ops::copy_slice, To};
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::{HASH_LEN, KEY_LEN};
|
||||
|
||||
/// Specify that the used implementation of BLAKE2b is the MAC version of BLAKE2b
|
||||
/// with output and key length of 32 bytes (see [Blake2bMac]).
|
||||
type Impl = Blake2bMac<U32>;
|
||||
|
||||
/// Hashes the given `data` with the [Blake2bMac] hash function under the given `key`.
|
||||
/// The both the length of the output the length of the key 32 bytes (or 256 bits).
|
||||
pub struct Blake2b;
|
||||
|
||||
impl KeyedHash<KEY_LEN, HASH_LEN> for Blake2b {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn keyed_hash(
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error> {
|
||||
let mut h = Impl::new_from_slice(key)?;
|
||||
h.update(data);
|
||||
|
||||
// Jesus christ, blake2 crate, your usage of GenericArray might be nice and fancy,
|
||||
// but it introduces a ton of complexity. This cost me half an hour just to figure
|
||||
// out the right way to use the imports while allowing for zeroization.
|
||||
// An API based on slices might actually be simpler.
|
||||
let mut tmp = Zeroizing::new([0u8; HASH_LEN]);
|
||||
let tmp = GenericArray::from_mut_slice(tmp.as_mut());
|
||||
h.finalize_into(tmp);
|
||||
copy_slice(tmp.as_ref()).to(out);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl rosenpass_cipher_traits::algorithms::KeyedHashBlake2b for Blake2b {}
|
||||
79
ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs
Normal file
79
ciphers/src/subtle/rust_crypto/chacha20poly1305_ietf.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use rosenpass_to::ops::copy_slice;
|
||||
use rosenpass_to::To;
|
||||
|
||||
use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305;
|
||||
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
|
||||
|
||||
use chacha20poly1305::aead::generic_array::GenericArray;
|
||||
use chacha20poly1305::ChaCha20Poly1305 as AeadImpl;
|
||||
use chacha20poly1305::{AeadInPlace, KeyInit};
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN};
|
||||
|
||||
/// Implements the [`Aead`] and [`AeadChaCha20Poly1305`] traits backed by the RustCrypto
|
||||
/// implementation.
|
||||
pub struct ChaCha20Poly1305;
|
||||
|
||||
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
|
||||
fn encrypt(
|
||||
&self,
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> Result<(), AeadError> {
|
||||
// The comparison looks complicated, but we need to do it this way to prevent
|
||||
// over/underflows.
|
||||
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
|
||||
return Err(AeadError::InvalidLengths);
|
||||
}
|
||||
|
||||
let nonce = GenericArray::from_slice(nonce);
|
||||
let (ct, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
|
||||
copy_slice(plaintext).to(ct);
|
||||
|
||||
// This only fails if the length is wrong, which really shouldn't happen and would
|
||||
// constitute an internal error.
|
||||
let encrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
|
||||
|
||||
let mac_value = encrypter
|
||||
.encrypt_in_place_detached(nonce, ad, ct)
|
||||
.map_err(|_| AeadError::InternalError)?;
|
||||
copy_slice(&mac_value[..]).to(mac);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
&self,
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> Result<(), AeadError> {
|
||||
// The comparison looks complicated, but we need to do it this way to prevent
|
||||
// over/underflows.
|
||||
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
|
||||
return Err(AeadError::InvalidLengths);
|
||||
}
|
||||
|
||||
let nonce = GenericArray::from_slice(nonce);
|
||||
let (ct, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
|
||||
let tag = GenericArray::from_slice(mac);
|
||||
copy_slice(ct).to(plaintext);
|
||||
|
||||
// This only fails if the length is wrong, which really shouldn't happen and would
|
||||
// constitute an internal error.
|
||||
let decrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
|
||||
|
||||
decrypter
|
||||
.decrypt_in_place_detached(nonce, ad, plaintext, tag)
|
||||
.map_err(|_| AeadError::DecryptError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {}
|
||||
117
ciphers/src/subtle/rust_crypto/keyed_shake256.rs
Normal file
117
ciphers/src/subtle/rust_crypto/keyed_shake256.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use anyhow::ensure;
|
||||
use rosenpass_cipher_traits::primitives::{InferKeyedHash, KeyedHash};
|
||||
use sha3::digest::{ExtendableOutput, Update, XofReader};
|
||||
use sha3::Shake256;
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::keyed_hash_shake256::{HASH_LEN, KEY_LEN};
|
||||
|
||||
/// An implementation of the [`KeyedHash`] trait backed by the RustCrypto implementation of SHAKE256.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SHAKE256Core<const KEY_LEN: usize, const HASH_LEN: usize>;
|
||||
|
||||
impl<const KEY_LEN: usize, const HASH_LEN: usize> KeyedHash<KEY_LEN, HASH_LEN>
|
||||
for SHAKE256Core<KEY_LEN, HASH_LEN>
|
||||
{
|
||||
type Error = anyhow::Error;
|
||||
|
||||
/// Provides a keyed hash function based on SHAKE256. To work for the protocol, the output length
|
||||
/// and key length are fixed to 32 bytes (also see [KEY_LEN] and [HASH_LEN]).
|
||||
///
|
||||
/// Note that the SHAKE256 is designed for 64 bytes output length, which we truncate to 32 bytes
|
||||
/// to work well with the overall protocol. Referring to Table 4 of FIPS 202, this offers the
|
||||
/// same collision resistance as SHAKE128, but 256 bits of preimage resistance. We therefore
|
||||
/// prefer a truncated SHAKE256 over SHAKE128.
|
||||
///
|
||||
/// #Examples
|
||||
/// ```rust
|
||||
/// # use rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::SHAKE256Core;
|
||||
/// use rosenpass_cipher_traits::primitives::KeyedHash;
|
||||
/// const KEY_LEN: usize = 32;
|
||||
/// const HASH_LEN: usize = 32;
|
||||
/// let key: [u8; 32] = [0; KEY_LEN];
|
||||
/// let data: [u8; 32] = [255; 32]; // arbitrary data, could also be longer
|
||||
/// // buffer for the hash output
|
||||
/// let mut hash_data: [u8; 32] = [0u8; HASH_LEN];
|
||||
///
|
||||
/// assert!(SHAKE256Core::<32, 32>::keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result");
|
||||
/// # let expected_hash: &[u8] = &[174, 4, 47, 188, 1, 228, 179, 246, 67, 43, 255, 94, 155, 11,
|
||||
/// 187, 161, 38, 110, 217, 23, 4, 62, 172, 30, 218, 187, 249, 80, 171, 21, 145, 238];
|
||||
/// # assert_eq!(hash_data, expected_hash);
|
||||
/// ```
|
||||
fn keyed_hash(
|
||||
key: &[u8; KEY_LEN],
|
||||
data: &[u8],
|
||||
out: &mut [u8; HASH_LEN],
|
||||
) -> Result<(), Self::Error> {
|
||||
// Since SHAKE256 is a XOF, we fix the output length manually to what is required for the
|
||||
// protocol.
|
||||
ensure!(out.len() == HASH_LEN);
|
||||
// Not bothering with padding; the implementation
|
||||
// uses appropriately sized keys.
|
||||
ensure!(key.len() == KEY_LEN);
|
||||
let mut shake256 = Shake256::default();
|
||||
shake256.update(key);
|
||||
shake256.update(data);
|
||||
|
||||
// Since we use domain separation extensively, related outputs of the truncated XOF
|
||||
// are not a concern. This follows the NIST recommendations in Section A.2 of the FIPS 202
|
||||
// standard, (pages 24/25, i.e., 32/33 in the PDF).
|
||||
shake256.finalize_xof().read(out);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const KEY_LEN: usize, const HASH_LEN: usize> SHAKE256Core<KEY_LEN, HASH_LEN> {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const KEY_LEN: usize, const HASH_LEN: usize> Default for SHAKE256Core<KEY_LEN, HASH_LEN> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// This type provides the same functionality as [SHAKE256Core], but bound to an instance.
|
||||
/// In contrast to [SHAKE256Core], this allows for type interference and thus allows the user of the
|
||||
/// type to omit explicit type parameters when instantiating the type or using it.
|
||||
///
|
||||
/// The instantiation is based on the [InferKeyedHash] trait.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::{SHAKE256};
|
||||
/// use rosenpass_cipher_traits::primitives::KeyedHashInstance;
|
||||
/// const KEY_LEN: usize = 32;
|
||||
/// const HASH_LEN: usize = 32;
|
||||
/// let key: [u8; KEY_LEN] = [0; KEY_LEN];
|
||||
/// let data: [u8; 32] = [255; 32]; // arbitrary data, could also be longer
|
||||
/// // buffer for the hash output
|
||||
/// let mut hash_data: [u8; 32] = [0u8; HASH_LEN];
|
||||
/// assert!(SHAKE256::new().keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result");
|
||||
/// # let expected_hash: &[u8] = &[174, 4, 47, 188, 1, 228, 179, 246, 67, 43, 255, 94, 155, 11, 187,
|
||||
/// 161, 38, 110, 217, 23, 4, 62, 172, 30, 218, 187, 249, 80, 171, 21, 145, 238];
|
||||
/// # assert_eq!(hash_data, expected_hash);
|
||||
/// ```
|
||||
pub type SHAKE256<const KEY_LEN: usize, const HASH_LEN: usize> =
|
||||
InferKeyedHash<SHAKE256Core<KEY_LEN, HASH_LEN>, KEY_LEN, HASH_LEN>;
|
||||
|
||||
/// The SHAKE256_32 type is a specific instance of the [SHAKE256] type with the key length and hash
|
||||
/// length fixed to 32 bytes.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_ciphers::subtle::keyed_shake256::{SHAKE256_32};
|
||||
/// use rosenpass_cipher_traits::primitives::KeyedHashInstance;
|
||||
/// const KEY_LEN: usize = 32;
|
||||
/// const HASH_LEN: usize = 32;
|
||||
/// let key: [u8; 32] = [0; KEY_LEN];
|
||||
/// let data: [u8; 32] = [255; 32]; // arbitrary data, could also be longer
|
||||
/// // buffer for the hash output
|
||||
/// let mut hash_data: [u8; 32] = [0u8; HASH_LEN];
|
||||
///
|
||||
/// assert!(SHAKE256_32::new().keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result");
|
||||
/// # let expected_hash: &[u8] = &[174, 4, 47, 188, 1, 228, 179, 246, 67, 43, 255, 94, 155, 11, 187,
|
||||
/// 161, 38, 110, 217, 23, 4, 62, 172, 30, 218, 187, 249, 80, 171, 21, 145, 238];
|
||||
/// # assert_eq!(hash_data, expected_hash);
|
||||
/// ```
|
||||
pub type SHAKE256_32 = SHAKE256<32, 32>;
|
||||
7
ciphers/src/subtle/rust_crypto/mod.rs
Normal file
7
ciphers/src/subtle/rust_crypto/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! Implementations backed by RustCrypto
|
||||
|
||||
pub mod blake2b;
|
||||
pub mod keyed_shake256;
|
||||
|
||||
pub mod chacha20poly1305_ietf;
|
||||
pub mod xchacha20poly1305_ietf;
|
||||
164
ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs
Normal file
164
ciphers/src/subtle/rust_crypto/xchacha20poly1305_ietf.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use rosenpass_to::ops::copy_slice;
|
||||
use rosenpass_to::To;
|
||||
|
||||
use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::AeadXChaCha20Poly1305;
|
||||
use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext};
|
||||
|
||||
use chacha20poly1305::aead::generic_array::GenericArray;
|
||||
use chacha20poly1305::XChaCha20Poly1305 as AeadImpl;
|
||||
use chacha20poly1305::{AeadInPlace, KeyInit};
|
||||
|
||||
pub use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::{
|
||||
KEY_LEN, NONCE_LEN, TAG_LEN,
|
||||
};
|
||||
/// Implements the [`Aead`] and [`AeadXChaCha20Poly1305`] traits backed by the RustCrypto
|
||||
/// implementation.
|
||||
pub struct XChaCha20Poly1305;
|
||||
|
||||
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for XChaCha20Poly1305 {
|
||||
fn encrypt(
|
||||
&self,
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> Result<(), AeadError> {
|
||||
// The comparison looks complicated, but we need to do it this way to prevent
|
||||
// over/underflows.
|
||||
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
|
||||
return Err(AeadError::InvalidLengths);
|
||||
}
|
||||
|
||||
let (ct, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
|
||||
copy_slice(plaintext).to(ct);
|
||||
|
||||
let nonce = GenericArray::from_slice(nonce);
|
||||
|
||||
// This only fails if the length is wrong, which really shouldn't happen and would
|
||||
// constitute an internal error.
|
||||
let encrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
|
||||
|
||||
let mac_value = encrypter
|
||||
.encrypt_in_place_detached(nonce, ad, ct)
|
||||
.map_err(|_| AeadError::InternalError)?;
|
||||
copy_slice(&mac_value[..]).to(mac);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
&self,
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> Result<(), AeadError> {
|
||||
// The comparison looks complicated, but we need to do it this way to prevent
|
||||
// over/underflows.
|
||||
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
|
||||
return Err(AeadError::InvalidLengths);
|
||||
}
|
||||
|
||||
let (ct, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
|
||||
let nonce = GenericArray::from_slice(nonce);
|
||||
let tag = GenericArray::from_slice(mac);
|
||||
copy_slice(ct).to(plaintext);
|
||||
|
||||
// This only fails if the length is wrong, which really shouldn't happen and would
|
||||
// constitute an internal error.
|
||||
let decrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
|
||||
|
||||
decrypter
|
||||
.decrypt_in_place_detached(nonce, ad, plaintext, tag)
|
||||
.map_err(|_| AeadError::DecryptError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadXChaCha20Poly1305 for XChaCha20Poly1305 {}
|
||||
|
||||
/// Encrypts using XChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
|
||||
/// `key` and `nonce` MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
|
||||
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN].
|
||||
/// In contrast to [chacha20poly1305_ietf::encrypt](crate::subtle::chacha20poly1305_ietf::encrypt) and
|
||||
/// [chacha20poly1305_ietf_libcrux::encrypt](crate::subtle::chacha20poly1305_ietf_libcrux::encrypt),
|
||||
/// `nonce` is also written into `ciphertext` and therefore ciphertext MUST have a length
|
||||
/// of at least [NONCE_LEN] + `plaintext.len()` + [TAG_LEN].
|
||||
///
|
||||
/// # Examples
|
||||
///```rust
|
||||
/// # use rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
|
||||
/// const PLAINTEXT_LEN: usize = 43;
|
||||
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
|
||||
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
|
||||
/// let key: &[u8; KEY_LEN] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
|
||||
/// let nonce: &[u8; NONCE_LEN] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
|
||||
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
|
||||
/// let mut ciphertext_buffer = [0u8; NONCE_LEN + PLAINTEXT_LEN + TAG_LEN];
|
||||
///
|
||||
///
|
||||
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
|
||||
/// # assert!(res.is_ok());
|
||||
/// # let expected_ciphertext: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/// # 0, 0, 0, 0, 8, 241, 229, 253, 200, 81, 248, 30, 183, 149, 134, 168, 149, 87, 109, 49, 159, 108,
|
||||
/// # 206, 89, 51, 232, 232, 197, 163, 253, 254, 208, 73, 76, 253, 13, 247, 162, 133, 184, 177, 44,
|
||||
/// # 73, 138, 176, 193, 61, 248, 61, 183, 164, 192, 214, 168, 4, 1, 62, 243, 36, 48, 149, 164, 6];
|
||||
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
|
||||
///```
|
||||
#[inline]
|
||||
pub fn encrypt(
|
||||
ciphertext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
nonce: &[u8; NONCE_LEN],
|
||||
ad: &[u8],
|
||||
plaintext: &[u8],
|
||||
) -> anyhow::Result<()> {
|
||||
XChaCha20Poly1305
|
||||
.encrypt_with_nonce_in_ctxt(ciphertext, key, nonce, ad, plaintext)
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
|
||||
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
|
||||
/// `ad`. using XChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
|
||||
///
|
||||
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
|
||||
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN] - [NONCE_LEN].
|
||||
///
|
||||
/// In contrast to [chacha20poly1305_ietf::decrypt](crate::subtle::chacha20poly1305_ietf::decrypt) and
|
||||
/// [chacha20poly1305_ietf_libcrux::decrypt](crate::subtle::chacha20poly1305_ietf_libcrux::decrypt),
|
||||
/// `ciperhtext` MUST include the as it is not given otherwise.
|
||||
///
|
||||
/// # Examples
|
||||
///```rust
|
||||
/// # use rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
|
||||
/// let ciphertext: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/// # 0, 0, 0, 0, 8, 241, 229, 253, 200, 81, 248, 30, 183, 149, 134, 168, 149, 87, 109, 49, 159, 108,
|
||||
/// # 206, 89, 51, 232, 232, 197, 163, 253, 254, 208, 73, 76, 253, 13, 247, 162, 133, 184, 177, 44,
|
||||
/// # 73, 138, 176, 193, 61, 248, 61, 183, 164, 192, 214, 168, 4, 1, 62, 243, 36, 48, 149, 164, 6];
|
||||
/// // this is the ciphertext generated by the example for the encryption
|
||||
/// const PLAINTEXT_LEN: usize = 43;
|
||||
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN + NONCE_LEN, ciphertext.len());
|
||||
///
|
||||
/// let key: &[u8; KEY_LEN] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
|
||||
/// let nonce: &[u8; NONCE_LEN] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
|
||||
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
|
||||
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
|
||||
///
|
||||
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, additional_data, ciphertext);
|
||||
/// assert!(res.is_ok());
|
||||
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
|
||||
/// assert_eq!(expected_plaintext, plaintext_buffer);
|
||||
///
|
||||
///```
|
||||
#[inline]
|
||||
pub fn decrypt(
|
||||
plaintext: &mut [u8],
|
||||
key: &[u8; KEY_LEN],
|
||||
ad: &[u8],
|
||||
ciphertext: &[u8],
|
||||
) -> anyhow::Result<()> {
|
||||
XChaCha20Poly1305
|
||||
.decrypt_with_nonce_in_ctxt(plaintext, key, ad, ciphertext)
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
26
constant-time/Cargo.toml
Normal file
26
constant-time/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[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"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
# 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 = { workspace = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }
|
||||
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.
|
||||
114
constant-time/src/compare.rs
Normal file
114
constant-time/src/compare.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
//! Constant-time comparison
|
||||
|
||||
use core::ptr;
|
||||
|
||||
/// Little endian memcmp version of [quinier/memsec](https://github.com/quininer/memsec/blob/bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0/src/lib.rs#L30)
|
||||
///
|
||||
/// # Panic & Safety
|
||||
///
|
||||
/// Both input arrays must be at least of the indicated length.
|
||||
///
|
||||
/// See [std::ptr::read_volatile] on safety.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// let a = [1, 2, 3, 4];
|
||||
/// let b = [1, 2, 3, 4];
|
||||
/// let c = [1, 2, 2, 5];
|
||||
/// let d = [1, 2, 2, 4];
|
||||
///
|
||||
/// unsafe {
|
||||
/// use rosenpass_constant_time::memcmp_le;
|
||||
/// assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 4), 0);
|
||||
/// assert!(memcmp_le(a.as_ptr(), c.as_ptr(), 4) < 0);
|
||||
/// assert!(memcmp_le(a.as_ptr(), d.as_ptr(), 4) > 0);
|
||||
/// assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 2), 0);
|
||||
/// }
|
||||
/// ```
|
||||
#[inline(never)]
|
||||
pub unsafe fn memcmp_le(b1: *const u8, b2: *const u8, len: usize) -> i32 {
|
||||
let mut res = 0;
|
||||
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
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn memcmp_le_test() {
|
||||
// use rosenpass_constant_time::memcmp_le;
|
||||
let a = [0, 1, 0, 0];
|
||||
let b = [0, 0, 0, 1];
|
||||
assert_eq!(-1, unsafe { memcmp_le(a.as_ptr(), b.as_ptr(), 4) });
|
||||
assert_eq!(0, unsafe { memcmp_le(a.as_ptr(), a.as_ptr(), 4) });
|
||||
assert_eq!(1, unsafe { memcmp_le(b.as_ptr(), a.as_ptr(), 4) });
|
||||
}
|
||||
|
||||
/// 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>
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_constant_time::compare;
|
||||
/// let a = [0, 1, 0, 0];
|
||||
/// let b = [0, 0, 0, 1];
|
||||
/// assert_eq!(-1, compare(&a, &b));
|
||||
/// assert_eq!(0, compare(&a, &a));
|
||||
/// assert_eq!(1, compare(&b, &a));
|
||||
/// ```
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// This function will panic if the input arrays are of different lengths.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// use rosenpass_constant_time::compare;
|
||||
/// let a = [0, 1, 0];
|
||||
/// let b = [0, 0, 0, 1];
|
||||
/// compare(&a, &b);
|
||||
/// ```
|
||||
#[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()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::compare::memcmp_le;
|
||||
|
||||
#[test]
|
||||
fn memcmp_le_test() {
|
||||
let a = [1, 2, 3, 4];
|
||||
let b = [1, 2, 3, 4];
|
||||
let c = [1, 2, 2, 5];
|
||||
let d = [1, 2, 2, 4];
|
||||
|
||||
unsafe {
|
||||
assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 4), 0);
|
||||
assert!(memcmp_le(a.as_ptr(), c.as_ptr(), 4) < 0);
|
||||
assert!(memcmp_le(a.as_ptr(), d.as_ptr(), 4) > 0);
|
||||
assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 2), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
constant-time/src/increment.rs
Normal file
58
constant-time/src/increment.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
//! Incrementing numbers
|
||||
|
||||
use core::hint::black_box;
|
||||
|
||||
/// Interpret the given slice as a little-endian unsigned integer
|
||||
/// and increment that integer.
|
||||
///
|
||||
/// # Leaks
|
||||
/// This function may leak timing information in the following ways:
|
||||
///
|
||||
/// - The function execution time is linearly proportional to the input length
|
||||
/// - The number of carry operations that occur may affect timing slightly
|
||||
/// - Memory access patterns are sequential and predictable
|
||||
///
|
||||
/// The carry operation timing variation is mitigated through the use of black_box,
|
||||
/// but the linear scaling with input size is inherent to the operation.
|
||||
/// These timing characteristics are generally considered acceptable for most
|
||||
/// cryptographic counter implementations.
|
||||
///
|
||||
/// ## Tests
|
||||
/// For discussion on how to ensure the constant-time execution of this function, see
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
46
constant-time/src/lib.rs
Normal file
46
constant-time/src/lib.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#![warn(missing_docs)]
|
||||
#![warn(clippy::missing_docs_in_private_items)]
|
||||
//! 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>
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! use rosenpass_constant_time::{memcmp, compare};
|
||||
//!
|
||||
//! let a = [1, 2, 3, 4];
|
||||
//! let b = [1, 2, 3, 4];
|
||||
//! let c = [1, 2, 3, 5];
|
||||
//!
|
||||
//! // Compare for equality
|
||||
//! assert!(memcmp(&a, &b));
|
||||
//! assert!(!memcmp(&a, &c));
|
||||
//!
|
||||
//! // Compare lexicographically
|
||||
//! assert_eq!(compare(&a, &c), -1); // a < c
|
||||
//! assert_eq!(compare(&c, &a), 1); // c > a
|
||||
//! assert_eq!(compare(&a, &b), 0); // a == b
|
||||
//! ```
|
||||
//!
|
||||
//! # Security Notes
|
||||
//!
|
||||
//! While these functions aim to be constant-time, they may leak timing information in some cases:
|
||||
//!
|
||||
//! - Length mismatches between inputs are immediately detectable
|
||||
//! - Execution time scales linearly with input size
|
||||
|
||||
mod compare;
|
||||
mod increment;
|
||||
mod memcmp;
|
||||
mod xor;
|
||||
|
||||
pub use compare::compare;
|
||||
pub use compare::memcmp_le;
|
||||
pub use increment::increment;
|
||||
pub use memcmp::memcmp;
|
||||
pub use xor::xor;
|
||||
125
constant-time/src/memcmp.rs
Normal file
125
constant-time/src/memcmp.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! memcmp
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_constant_time::memcmp;
|
||||
/// let a = [0, 0, 0, 0];
|
||||
/// let b = [0, 0, 0, 1];
|
||||
/// let c = [0, 0, 0];
|
||||
/// assert!(memcmp(&a, &a));
|
||||
/// assert!(!memcmp(&a, &b));
|
||||
/// assert!(!memcmp(&a, &c));
|
||||
/// ```
|
||||
#[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"))]
|
||||
// Stopgap measure against https://github.com/rosenpass/rosenpass/issues/634
|
||||
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use core::hint::black_box;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::time::Instant;
|
||||
|
||||
#[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
|
||||
const LEN: usize = 1024; // length of each slice passed as parameters to the tested comparison function
|
||||
|
||||
let a = [b'a'; LEN];
|
||||
let b = [b'b'; LEN];
|
||||
|
||||
let mut tmp = [0u8; LEN];
|
||||
|
||||
// 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 src = match test.0 {
|
||||
true => a,
|
||||
false => b,
|
||||
};
|
||||
tmp.copy_from_slice(&src);
|
||||
|
||||
let now = Instant::now();
|
||||
memcmp(black_box(&a), black_box(&tmp));
|
||||
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);
|
||||
#[cfg(not(coverage))]
|
||||
assert!(
|
||||
correlation.abs() < 0.01,
|
||||
"execution time correlates with result"
|
||||
)
|
||||
}
|
||||
}
|
||||
47
constant-time/src/xor.rs
Normal file
47
constant-time/src/xor.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
//! xor
|
||||
|
||||
use core::hint::black_box;
|
||||
use rosenpass_to::{with_destination, To};
|
||||
|
||||
/// Xors the source into the destination
|
||||
///
|
||||
/// Performs a constant-time XOR operation between two byte slices
|
||||
///
|
||||
/// Takes a source slice and XORs it with the destination slice in-place using the
|
||||
/// rosenpass_to trait for destination management.
|
||||
///
|
||||
/// # Panics
|
||||
/// If source and destination are of different sizes.
|
||||
///
|
||||
/// # Leaks
|
||||
/// This function may leak timing information in the following ways:
|
||||
///
|
||||
/// - The function execution time is linearly proportional to the input length
|
||||
/// - Length mismatches between source and destination are immediately detectable via panic
|
||||
/// - Memory access patterns follow a predictable sequential pattern
|
||||
///
|
||||
/// These leaks are generally considered acceptable in most cryptographic contexts
|
||||
/// as they don't reveal information about the actual content being XORed.
|
||||
///
|
||||
/// ## Tests
|
||||
/// For discussion on how to ensure the constant-time execution of this function, see
|
||||
/// <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);
|
||||
}
|
||||
})
|
||||
}
|
||||
46
coverage_report.sh
Executable file
46
coverage_report.sh
Executable file
@@ -0,0 +1,46 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
OUTPUT_DIR="target/grcov"
|
||||
|
||||
log() {
|
||||
echo >&2 "$@"
|
||||
}
|
||||
|
||||
exc() {
|
||||
echo '$' "$@"
|
||||
"$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
exc cd "$(dirname "$0")"
|
||||
|
||||
local open="0"
|
||||
if [[ "$1" == "--open" ]]; then
|
||||
open="1"
|
||||
fi
|
||||
|
||||
exc cargo llvm-cov --all-features --workspace --doctests --branch
|
||||
|
||||
exc rm -rf target/llvm-cov-target/debug/deps/doctestbins
|
||||
exc mv -v target/llvm-cov-target/doctestbins target/llvm-cov-target/debug/deps/
|
||||
exc rm -rf "${OUTPUT_DIR}"
|
||||
exc mkdir -p "${OUTPUT_DIR}"
|
||||
exc grcov target/llvm-cov-target/ --llvm -s . --branch \
|
||||
--binary-path ./target/llvm-cov-target/debug/deps \
|
||||
--ignore-not-existing --ignore '../*' --ignore "/*" \
|
||||
--excl-line '^\s*#\[(derive|repr)\(' \
|
||||
-t lcov,html,markdown -o "${OUTPUT_DIR}"
|
||||
|
||||
if (( "${open}" == 1 )); then
|
||||
xdg-open "${PWD}/${OUTPUT_DIR}/html/index.html"
|
||||
fi
|
||||
|
||||
log ""
|
||||
log "Generated reports in \"${PWD}/${OUTPUT_DIR}\"."
|
||||
log "Open \"${PWD}/${OUTPUT_DIR}/html/index.html\" to view HTML report."
|
||||
log ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
121
deny.toml
Normal file
121
deny.toml
Normal file
@@ -0,0 +1,121 @@
|
||||
# The graph table configures how the dependency graph is constructed and thus
|
||||
# which crates the checks are performed against
|
||||
[graph]
|
||||
# If true, metadata will be collected with `--all-features`. Note that this can't
|
||||
# be toggled off if true, if you want to conditionally enable `--all-features` it
|
||||
# is recommended to pass `--all-features` on the cmd line instead
|
||||
all-features = true
|
||||
# If true, metadata will be collected with `--no-default-features`. The same
|
||||
# caveat with `all-features` applies
|
||||
no-default-features = false
|
||||
|
||||
# The output table provides options for how/if diagnostics are outputted
|
||||
[output]
|
||||
# When outputting inclusion graphs in diagnostics that include features, this
|
||||
# option can be used to specify the depth at which feature edges will be added.
|
||||
# This option is included since the graphs can be quite large and the addition
|
||||
# of features from the crate(s) to all of the graph roots can be far too verbose.
|
||||
# This option can be overridden via `--feature-depth` on the cmd line
|
||||
feature-depth = 1
|
||||
|
||||
# This section is considered when running `cargo deny check advisories`
|
||||
# More documentation for the advisories section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
|
||||
[advisories]
|
||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||
# output a note when they are encountered.
|
||||
ignore = ["RUSTSEC-2024-0370", "RUSTSEC-2024-0436", "RUSTSEC-2023-0089"]
|
||||
# If this is true, then cargo deny will use the git executable to fetch advisory database.
|
||||
# If this is false, then it uses a built-in git library.
|
||||
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
|
||||
# See Git Authentication for more information about setting up git authentication.
|
||||
#git-fetch-with-cli = true
|
||||
|
||||
# This section is considered when running `cargo deny check #licenses`
|
||||
# More documentation for the licenses section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
|
||||
[licenses]
|
||||
# List of explicitly allowed licenses
|
||||
# See https://spdx.org/licenses/ for list of possible licenses
|
||||
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
||||
allow = [
|
||||
"MIT",
|
||||
"Apache-2.0",
|
||||
"Apache-2.0 WITH LLVM-exception",
|
||||
"BSD-3-Clause",
|
||||
"ISC",
|
||||
]
|
||||
# The confidence threshold for detecting a license from license text.
|
||||
# The higher the value, the more closely the license text must be to the
|
||||
# canonical license text of a valid SPDX license file.
|
||||
# [possible values: any between 0.0 and 1.0].
|
||||
confidence-threshold = 0.8
|
||||
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
|
||||
# aren't accepted for every possible crate as with the normal allow list
|
||||
exceptions = [
|
||||
# Each entry is the crate and version constraint, and its specific allow
|
||||
# list
|
||||
{ allow = ["Unicode-DFS-2016", "Unicode-3.0"], crate = "unicode-ident" },
|
||||
{ allow = ["NCSA"], crate = "libfuzzer-sys" },
|
||||
|
||||
]
|
||||
|
||||
[licenses.private]
|
||||
# If true, ignores workspace crates that aren't published, or are only
|
||||
# published to private registries.
|
||||
# To see how to mark a crate as unpublished (to the official registry),
|
||||
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
|
||||
ignore = true
|
||||
|
||||
# This section is considered when running `cargo deny check bans`.
|
||||
# More documentation about the 'bans' section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
|
||||
[bans]
|
||||
# Lint level for when multiple versions of the same crate are detected
|
||||
multiple-versions = "warn"
|
||||
# Lint level for when a crate version requirement is `*`
|
||||
wildcards = "allow"
|
||||
# The graph highlighting used when creating dotgraphs for crates
|
||||
# with multiple versions
|
||||
# * lowest-version - The path to the lowest versioned duplicate is highlighted
|
||||
# * simplest-path - The path to the version with the fewest edges is highlighted
|
||||
# * all - Both lowest-version and simplest-path are used
|
||||
highlight = "all"
|
||||
# The default lint level for `default` features for crates that are members of
|
||||
# the workspace that is being checked. This can be overridden by allowing/denying
|
||||
# `default` on a crate-by-crate basis if desired.
|
||||
workspace-default-features = "allow"
|
||||
# The default lint level for `default` features for external crates that are not
|
||||
# members of the workspace. This can be overridden by allowing/denying `default`
|
||||
# on a crate-by-crate basis if desired.
|
||||
external-default-features = "allow"
|
||||
# List of crates that are allowed. Use with care!
|
||||
allow = []
|
||||
# List of crates to deny
|
||||
deny = []
|
||||
|
||||
skip-tree = []
|
||||
|
||||
# This section is considered when running `cargo deny check sources`.
|
||||
# More documentation about the 'sources' section can be found here:
|
||||
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
|
||||
[sources]
|
||||
# Lint level for what to happen when a crate from a crate registry that is not
|
||||
# in the allow list is encountered
|
||||
unknown-registry = "warn"
|
||||
# Lint level for what to happen when a crate from a git repository that is not
|
||||
# in the allow list is encountered
|
||||
unknown-git = "warn"
|
||||
# List of URLs for allowed crate registries. Defaults to the crates.io index
|
||||
# if not specified. If it is specified but empty, no registries are allowed.
|
||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||
# List of URLs for allowed Git repositories
|
||||
allow-git = ["git+https://github.com/rosenpass/memsec.git?branch=master"]
|
||||
|
||||
[sources.allow-org]
|
||||
# github.com organizations to allow git sources for
|
||||
github = []
|
||||
# gitlab.com organizations to allow git sources for
|
||||
gitlab = []
|
||||
# bitbucket.org organizations to allow git sources for
|
||||
bitbucket = []
|
||||
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
|
||||
105
doc/rosenpass.1
105
doc/rosenpass.1
@@ -1,105 +0,0 @@
|
||||
.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 keygen private-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
|
||||
.Sh STANDARDS
|
||||
This tool is the reference implementation of the Rosenpass protocol, written
|
||||
by Karolin Varner, Benjamin Lipp, Wanja Zaeske, and Lisa Schmidt.
|
||||
.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 Emil Engler
|
||||
.Sh BUGS
|
||||
The bugs are tracked at
|
||||
.Lk https://github.com/rosenpass/rosenpass/issues .
|
||||
2
doc/rp.1
2
doc/rp.1
@@ -113,7 +113,7 @@ 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 Emil Engler
|
||||
.An Clara Engler
|
||||
.Sh BUGS
|
||||
The bugs are tracked at
|
||||
.Lk https://github.com/rosenpass/rosenpass/issues .
|
||||
|
||||
45
docker/Dockerfile
Normal file
45
docker/Dockerfile
Normal file
@@ -0,0 +1,45 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_IMAGE=debian:bookworm-slim
|
||||
|
||||
# Stage 1: Base image with cargo-chef installed
|
||||
FROM rust:latest AS chef
|
||||
RUN cargo install cargo-chef
|
||||
# install software required for liboqs-rust
|
||||
RUN apt-get update && apt-get install -y clang cmake && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Stage 2: Prepare the cargo-chef recipe
|
||||
FROM chef AS planner
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
# Stage 3: Cache dependencies using the recipe
|
||||
FROM chef AS cacher
|
||||
WORKDIR /app
|
||||
COPY --from=planner /app/recipe.json recipe.json
|
||||
RUN cargo chef cook --release --recipe-path recipe.json
|
||||
|
||||
# Stage 4: Build the application
|
||||
FROM cacher AS builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN cargo build --release
|
||||
|
||||
# Stage 5: Install runtime-dependencies in the base image
|
||||
FROM ${BASE_IMAGE} AS base_image_with_dependencies
|
||||
|
||||
RUN apt-get update && apt-get install -y iproute2 && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Final Stage (rosenpass): Copy the rosenpass binary
|
||||
FROM base_image_with_dependencies AS rosenpass
|
||||
COPY --from=builder /app/target/release/rosenpass /usr/local/bin/rosenpass
|
||||
ENTRYPOINT [ "/usr/local/bin/rosenpass" ]
|
||||
|
||||
# Final Stage (rp): Copy the rp binary
|
||||
FROM base_image_with_dependencies AS rp
|
||||
|
||||
RUN apt-get update && apt-get install -y wireguard && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY --from=builder /app/target/release/rp /usr/local/bin/rp
|
||||
ENTRYPOINT [ "/usr/local/bin/rp" ]
|
||||
203
docker/USAGE.md
Normal file
203
docker/USAGE.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Rosenpass in Docker
|
||||
|
||||
Rosenpass provides post-quantum-secure key exchange for VPNs. It generates symmetric keys used by [WireGuard](https://www.wireguard.com/papers/wireguard.pdf) or other applications. The protocol enhances "Post-Quantum WireGuard" ([PQWG](https://eprint.iacr.org/2020/379)) with a cookie mechanism for better security against state disruption attacks.
|
||||
|
||||
Prebuilt Docker images are available for easy deployment:
|
||||
|
||||
- [`ghcr.io/rosenpass/rosenpass`](https://github.com/rosenpass/rosenpass/pkgs/container/rosenpass) – the core key exchange tool
|
||||
- [`ghcr.io/rosenpass/rp`](https://github.com/rosenpass/rosenpass/pkgs/container/rp) – a frontend for setting up WireGuard VPNs
|
||||
|
||||
The entrypoint of the `rosenpass` image is the `rosenpass` executable, whose documentation can be found [here](https://rosenpass.eu/docs/rosenpass-tool/manuals/rp_manual/).
|
||||
Similarly, the entrypoint of the `rp` image is the `rp` executable, with its documentation available [here](https://rosenpass.eu/docs/rosenpass-tool/manuals/rp1/).
|
||||
|
||||
## Usage - Standalone Key Exchange
|
||||
|
||||
The `ghcr.io/rosenpass/rosenpass` image can be used in a server-client setup to exchange quantum-secure shared keys.
|
||||
This setup uses rosenpass as a standalone application, without using any other component such as wireguard.
|
||||
What follows, is a simple setup for illustrative purposes.
|
||||
|
||||
Create a docker network that is used to connect the containers:
|
||||
|
||||
```bash
|
||||
docker network create -d bridge rp
|
||||
export NET=rp
|
||||
```
|
||||
|
||||
Generate the server and client key pairs:
|
||||
|
||||
```bash
|
||||
mkdir ./workdir-client ./workdir-server
|
||||
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rosenpass \
|
||||
gen-keys --public-key=workdir/server-public --secret-key=workdir/server-secret
|
||||
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rosenpass \
|
||||
gen-keys --public-key=workdir/client-public --secret-key=workdir/client-secret
|
||||
# share the public keys between client and server
|
||||
cp workdir-client/client-public workdir-server/client-public
|
||||
cp workdir-server/server-public workdir-client/server-public
|
||||
```
|
||||
|
||||
Start the server container:
|
||||
|
||||
```bash
|
||||
docker run --name "rpserver" --network ${NET} \
|
||||
-it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rosenpass \
|
||||
exchange \
|
||||
private-key workdir/server-secret \
|
||||
public-key workdir/server-public \
|
||||
listen 0.0.0.0:9999 \
|
||||
peer public-key workdir/client-public \
|
||||
outfile workdir/server-sharedkey
|
||||
```
|
||||
|
||||
Find out the ip address of the server container:
|
||||
|
||||
```bash
|
||||
EP="rpserver"
|
||||
EP=$(docker inspect --format '{{ .NetworkSettings.Networks.rp.IPAddress }}' $EP)
|
||||
```
|
||||
|
||||
Run the client container and perform the key exchange:
|
||||
|
||||
```bash
|
||||
docker run --name "rpclient" --network ${NET} \
|
||||
-it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rosenpass \
|
||||
exchange \
|
||||
private-key workdir/client-secret \
|
||||
public-key workdir/client-public \
|
||||
peer public-key workdir/server-public endpoint ${EP}:9999 \
|
||||
outfile workdir/client-sharedkey
|
||||
```
|
||||
|
||||
Now the containers will exchange shared keys and each put them into their respective outfile.
|
||||
|
||||
Comparing the outfiles shows that these shared keys equal:
|
||||
|
||||
```bash
|
||||
cmp workdir-server/server-sharedkey workdir-client/client-sharedkey
|
||||
```
|
||||
|
||||
It is now possible to set add these keys as pre-shared keys within a wireguard interface.
|
||||
For example as the server,
|
||||
|
||||
```bash
|
||||
PREKEY=$(cat workdir-server/server-sharedkey)
|
||||
wg set <server-interface> peer <client-peer-public-key> preshared-key <(echo "$PREKEY")
|
||||
```
|
||||
|
||||
## Usage - Combined with wireguard
|
||||
|
||||
The `ghcr.io/rosenpass/rp` image can be used to build a VPN with WireGuard and Rosenpass.
|
||||
In this example, we run two containers on the same system and connect them with a bridge network within the docker overlay network.
|
||||
|
||||
Create the named docker network, to be able to connect the containers.
|
||||
|
||||
Create a docker network that is used to connect the containers:
|
||||
|
||||
```bash
|
||||
docker network create -d bridge rp
|
||||
export NET=rp
|
||||
```
|
||||
|
||||
Generate the server and client secret keys and extract public keys.
|
||||
|
||||
```bash
|
||||
mkdir -p ./workdir-server ./workdir-client
|
||||
|
||||
# server
|
||||
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rp \
|
||||
genkey workdir/server.rosenpass-secret
|
||||
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rp \
|
||||
pubkey workdir/server.rosenpass-secret workdir/server.rosenpass-public
|
||||
|
||||
# client
|
||||
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rp \
|
||||
genkey workdir/client.rosenpass-secret
|
||||
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rp \
|
||||
pubkey workdir/client.rosenpass-secret workdir/client.rosenpass-public
|
||||
|
||||
# share the public keys between client and server
|
||||
cp -r workdir-client/client.rosenpass-public workdir-server/client.rosenpass-public
|
||||
cp -r workdir-server/server.rosenpass-public workdir-client/server.rosenpass-public
|
||||
```
|
||||
|
||||
Start the server container.
|
||||
Note that the `NET_ADMIN` capability is neccessary, the rp command will create and manage wireguard interfaces.
|
||||
Also make sure the `wireguard` kernel module is loaded by the host. (`lsmod | grep wireguard`)
|
||||
|
||||
```bash
|
||||
docker run --name "rpserver" --network ${NET} -it -d --rm -v ./workdir-server:/workdir \
|
||||
--cap-add=NET_ADMIN \
|
||||
ghcr.io/rosenpass/rp \
|
||||
exchange workdir/server.rosenpass-secret dev rosenpass0 \
|
||||
listen 0.0.0.0:9999 peer workdir/client.rosenpass-public allowed-ips 10.0.0.0/8
|
||||
```
|
||||
|
||||
Now find out the ip-address of the server container and then start the client container:
|
||||
|
||||
```bash
|
||||
EP="rpserver"
|
||||
EP=$(docker inspect --format '{{ .NetworkSettings.Networks.rp.IPAddress }}' $EP)
|
||||
docker run --name "rpclient" --network ${NET} -it -d --rm -v ./workdir-client:/workdir \
|
||||
--cap-add=NET_ADMIN \
|
||||
ghcr.io/rosenpass/rp \
|
||||
exchange workdir/client.rosenpass-secret dev rosenpass1 \
|
||||
peer workdir/server.rosenpass-public endpoint ${EP}:9999 allowed-ips 10.0.0.1
|
||||
```
|
||||
|
||||
Inside the docker containers assign the IP addresses:
|
||||
|
||||
```bash
|
||||
# server
|
||||
docker exec -it rpserver ip a add 10.0.0.1/24 dev rosenpass0
|
||||
|
||||
# client
|
||||
docker exec -it rpclient ip a add 10.0.0.2/24 dev rosenpass1
|
||||
```
|
||||
|
||||
Done! The two containers should now be connected through a wireguard VPN (Port 1000) with pre-shared keys exchanged by rosenpass (Port 9999).
|
||||
|
||||
Now, test the connection by starting a shell inside the client container, and ping the server through the VPN:
|
||||
|
||||
```bash
|
||||
# client
|
||||
docker exec -it rpclient bash
|
||||
apt update; apt install iputils-ping
|
||||
ping 10.0.0.1
|
||||
```
|
||||
|
||||
The ping command should continuously show ping-logs:
|
||||
|
||||
```
|
||||
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
|
||||
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.119 ms
|
||||
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.132 ms
|
||||
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.394 ms
|
||||
...
|
||||
```
|
||||
|
||||
While the ping is running, you may stop the server container, and verify that the ping-log halts. In another terminal do:
|
||||
|
||||
```
|
||||
docker stop -t 1 rpserver
|
||||
```
|
||||
|
||||
## Building the Docker Images Locally
|
||||
|
||||
Clone the Rosenpass repository:
|
||||
|
||||
```
|
||||
git clone https://github.com/rosenpass/rosenpass
|
||||
cd rosenpass
|
||||
```
|
||||
|
||||
Build the rp image from the root of the repository as follows:
|
||||
|
||||
```
|
||||
docker build -f docker/Dockerfile -t ghcr.io/rosenpass/rp --target rp .
|
||||
```
|
||||
|
||||
Build the rosenpass image from the root of the repostiry with the following command:
|
||||
|
||||
```
|
||||
docker build -f docker/Dockerfile -t ghcr.io/rosenpass/rosenpass --target rosenpass .
|
||||
```
|
||||
105
flake.lock
generated
105
flake.lock
generated
@@ -1,36 +1,15 @@
|
||||
{
|
||||
"nodes": {
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1699770036,
|
||||
"narHash": "sha256-bZmI7ytPAYLpyFNgj5xirDkKuAniOkj1xHdv5aIJ5GM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "81ab0b4f7ae9ebb57daa0edf119c4891806e4d3a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -39,62 +18,68 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"nix-vm-test": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1698420672,
|
||||
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
|
||||
"lastModified": 1734355073,
|
||||
"narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-vm-test",
|
||||
"rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-vm-test",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1698846319,
|
||||
"narHash": "sha256-4jyW/dqFBVpWFnhl0nvP6EN4lP7/ZqPxYRjl6var0Oc=",
|
||||
"lastModified": 1728193676,
|
||||
"narHash": "sha256-PbDWAIjKJdlVg+qQRhzdSor04bAPApDqIv2DofTyynk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "34bdaaf1f0b7fb6d9091472edc968ff10a8c2857",
|
||||
"rev": "ecbc1ca8ffd6aea8372ad16be9ebbb39889e55b6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils",
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nix-vm-test": "nix-vm-test",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1699715108,
|
||||
"narHash": "sha256-yPozsobJU55gj+szgo4Lpcg1lHvGQYAT6Y4MrC80mWE=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "5fcf5289e726785d20d3aa4d13d90a43ed248e83",
|
||||
"lastModified": 1744513456,
|
||||
"narHash": "sha256-NLVluTmK8d01Iz+WyarQhwFcXpHEwU7m5hH3YQQFJS0=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "730fd8e82799219754418483fabe1844262fd1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -112,6 +97,26 @@
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1743748085,
|
||||
"narHash": "sha256-uhjnlaVTWo5iD3LXics1rp9gaKgDRQj6660+gbUU3cE=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "815e4121d6a5d504c0f96e5be2dd7f871e4fd99d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
483
flake.nix
483
flake.nix
@@ -1,343 +1,216 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
# for quicker rust builds
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
naersk.inputs.nixpkgs.follows = "nixpkgs";
|
||||
nix-vm-test.url = "github:numtide/nix-vm-test";
|
||||
nix-vm-test.inputs.nixpkgs.follows = "nixpkgs";
|
||||
nix-vm-test.inputs.flake-utils.follows = "flake-utils";
|
||||
|
||||
# for rust nightly with llvm-tools-preview
|
||||
fenix.url = "github:nix-community/fenix";
|
||||
fenix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||
rust-overlay.inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, ... }@inputs:
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
nix-vm-test,
|
||||
rust-overlay,
|
||||
treefmt-nix,
|
||||
...
|
||||
}@inputs:
|
||||
nixpkgs.lib.foldl (a: b: nixpkgs.lib.recursiveUpdate a b) { } [
|
||||
|
||||
#
|
||||
### Export the overlay.nix from this flake ###
|
||||
#
|
||||
{ overlays.default = import ./overlay.nix; }
|
||||
|
||||
#
|
||||
### Actual Rosenpass Package and Docker Container Images ###
|
||||
#
|
||||
(flake-utils.lib.eachSystem [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
(flake-utils.lib.eachSystem
|
||||
[
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
|
||||
# unsuported best-effort
|
||||
"i686-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
# "x86_64-windows"
|
||||
]
|
||||
(system:
|
||||
# unsuported best-effort
|
||||
"i686-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
# "x86_64-windows"
|
||||
]
|
||||
(
|
||||
system:
|
||||
let
|
||||
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 ./rosenpass/Cargo.toml);
|
||||
|
||||
# source files relevant for rust
|
||||
src = pkgs.lib.sources.sourceFilesBySuffices ./. [
|
||||
".lock"
|
||||
".rs"
|
||||
".toml"
|
||||
];
|
||||
|
||||
# builds a bin path for all dependencies for the `rp` shellscript
|
||||
rpBinPath = p: with p; lib.makeBinPath [
|
||||
coreutils
|
||||
findutils
|
||||
gawk
|
||||
wireguard-tools
|
||||
];
|
||||
|
||||
# a function to generate a nix derivation for rosenpass against any
|
||||
# given set of nixpkgs
|
||||
rpDerivation = 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
|
||||
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 ];
|
||||
|
||||
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"
|
||||
'');
|
||||
|
||||
preInstall = ''
|
||||
install -D ${./rp} $out/bin/rp
|
||||
wrapProgram $out/bin/rp --prefix PATH : "${ rpBinPath p }"
|
||||
'';
|
||||
};
|
||||
|
||||
# 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 docker image based of rosenpass
|
||||
rosenpassOCI = name: pkgs.dockerTools.buildImage rec {
|
||||
inherit name;
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
paths = [ self.packages.${system}.${name} ];
|
||||
pathsToLink = [ "/bin" ];
|
||||
};
|
||||
config.Cmd = [ "/bin/rosenpass" ];
|
||||
# apply our own overlay, overriding/inserting our packages as defined in ./pkgs
|
||||
overlays = [ self.overlays.default ];
|
||||
};
|
||||
in
|
||||
rec {
|
||||
packages = rec {
|
||||
default = rosenpass;
|
||||
rosenpass = rpDerivation pkgs;
|
||||
rosenpass-oci-image = rosenpassOCI "rosenpass";
|
||||
{
|
||||
packages =
|
||||
{
|
||||
default = pkgs.rosenpass;
|
||||
rosenpass = pkgs.rosenpass;
|
||||
rosenpass-oci-image = pkgs.rosenpass-oci-image;
|
||||
rp = pkgs.rp;
|
||||
|
||||
# derivation for the release
|
||||
release-package =
|
||||
let
|
||||
version = cargoToml.package.version;
|
||||
package =
|
||||
if pkgs.hostPlatform.isLinux then
|
||||
packages.rosenpass-static
|
||||
else packages.rosenpass;
|
||||
oci-image =
|
||||
if pkgs.hostPlatform.isLinux then
|
||||
packages.rosenpass-static-oci-image
|
||||
else packages.rosenpass-oci-image;
|
||||
in
|
||||
pkgs.runCommandNoCC "lace-result" { }
|
||||
''
|
||||
mkdir {bin,$out}
|
||||
cp ${./.}/rp bin/
|
||||
tar -cvf $out/rosenpass-${system}-${version}.tar bin/rp \
|
||||
-C ${package} bin/rosenpass
|
||||
cp ${oci-image} \
|
||||
$out/rosenpass-oci-image-${system}-${version}.tar.gz
|
||||
'';
|
||||
} // (if pkgs.stdenv.isLinux then rec {
|
||||
rosenpass-static = rpDerivation pkgs.pkgsStatic;
|
||||
rosenpass-static-oci-image = rosenpassOCI "rosenpass-static";
|
||||
} else { });
|
||||
release-package = pkgs.release-package;
|
||||
|
||||
# for good measure, we also offer to cross compile to Linux on Arm
|
||||
aarch64-linux-rosenpass-static = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rosenpass;
|
||||
aarch64-linux-rp-static = pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.rp;
|
||||
}
|
||||
//
|
||||
# We only offer static builds for linux, as this is not supported on OS X
|
||||
(nixpkgs.lib.attrsets.optionalAttrs pkgs.stdenv.isLinux {
|
||||
rosenpass-static = pkgs.pkgsStatic.rosenpass;
|
||||
rosenpass-static-oci-image = pkgs.pkgsStatic.rosenpass-oci-image;
|
||||
rp-static = pkgs.pkgsStatic.rp;
|
||||
});
|
||||
}
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
#
|
||||
### Linux specifics ###
|
||||
#
|
||||
(flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
packages = self.packages.${system};
|
||||
in
|
||||
{
|
||||
#
|
||||
### Whitepaper ###
|
||||
#
|
||||
packages.whitepaper =
|
||||
let
|
||||
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 unicode-math lualatex-math paralist
|
||||
gitinfo2 eso-pic biblatex biblatex-trad biblatex-software
|
||||
xkeyval xurl xifthen biber;
|
||||
});
|
||||
in
|
||||
pkgs.stdenvNoCC.mkDerivation {
|
||||
name = "whitepaper";
|
||||
src = ./papers;
|
||||
nativeBuildInputs = with pkgs; [
|
||||
ncurses # tput
|
||||
python3Packages.pygments
|
||||
tlsetup # custom tex live scheme
|
||||
which
|
||||
(flake-utils.lib.eachSystem
|
||||
[
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"i686-linux"
|
||||
]
|
||||
(
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
overlays = [
|
||||
# apply our own overlay, overriding/inserting our packages as defined in ./pkgs
|
||||
self.overlays.default
|
||||
|
||||
nix-vm-test.overlays.default
|
||||
|
||||
# apply rust-overlay to get specific versions of the rust toolchain for a MSRV check
|
||||
(import rust-overlay)
|
||||
];
|
||||
buildPhase = ''
|
||||
export HOME=$(mktemp -d)
|
||||
export OSFONTDIR="$(kpsewhich --var-value TEXMF)/fonts/{opentype/public/nunito,truetype/google/noto}"
|
||||
latexmk -r tex/CI.rc
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
mv *.pdf readme.md $out/
|
||||
'';
|
||||
};
|
||||
|
||||
treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix;
|
||||
in
|
||||
{
|
||||
packages.package-deb = pkgs.callPackage ./pkgs/package-deb.nix {
|
||||
rosenpass = pkgs.pkgsStatic.rosenpass;
|
||||
};
|
||||
packages.package-rpm = pkgs.callPackage ./pkgs/package-rpm.nix {
|
||||
rosenpass = pkgs.pkgsStatic.rosenpass;
|
||||
};
|
||||
|
||||
#
|
||||
### Proof and Proof Tools ###
|
||||
#
|
||||
packages.proverif-patched = pkgs.proverif.overrideAttrs (old: {
|
||||
postInstall = ''
|
||||
install -D -t $out/lib cryptoverif.pvl
|
||||
'';
|
||||
});
|
||||
packages.proof-proverif = pkgs.stdenv.mkDerivation {
|
||||
name = "rosenpass-proverif-proof";
|
||||
version = "unstable";
|
||||
src = pkgs.lib.sources.sourceByRegex ./. [
|
||||
"analyze.sh"
|
||||
"marzipan(/marzipan.awk)?"
|
||||
"analysis(/.*)?"
|
||||
];
|
||||
nativeBuildInputs = [ pkgs.proverif pkgs.graphviz ];
|
||||
CRYPTOVERIF_LIB = packages.proverif-patched + "/lib/cryptoverif.pvl";
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
bash analyze.sh -color -html $out
|
||||
'';
|
||||
};
|
||||
#
|
||||
### Reading materials ###
|
||||
#
|
||||
packages.whitepaper = pkgs.whitepaper;
|
||||
|
||||
#
|
||||
### Proof and Proof Tools ###
|
||||
#
|
||||
packages.proverif-patched = pkgs.proverif-patched;
|
||||
packages.proof-proverif = pkgs.proof-proverif;
|
||||
|
||||
#
|
||||
### Devshells ###
|
||||
#
|
||||
devShells.default = pkgs.mkShell {
|
||||
inherit (packages.proof-proverif) CRYPTOVERIF_LIB;
|
||||
inputsFrom = [ packages.default ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake # override the fakecmake from the main step above
|
||||
cargo-release
|
||||
clippy
|
||||
nodePackages.prettier
|
||||
rustfmt
|
||||
packages.proverif-patched
|
||||
];
|
||||
};
|
||||
devShells.coverage = pkgs.mkShell {
|
||||
inputsFrom = [ packages.default ];
|
||||
nativeBuildInputs = with pkgs; [ inputs.fenix.packages.${system}.complete.toolchain cargo-llvm-cov ];
|
||||
};
|
||||
#
|
||||
### Devshells ###
|
||||
#
|
||||
devShells.default = pkgs.mkShell {
|
||||
inherit (pkgs.proof-proverif) CRYPTOVERIF_LIB;
|
||||
inputsFrom = [ pkgs.rosenpass ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cargo-release
|
||||
clippy
|
||||
rustfmt
|
||||
nodePackages.prettier
|
||||
nushell # for the .ci/gen-workflow-files.nu script
|
||||
proverif-patched
|
||||
];
|
||||
};
|
||||
# TODO: Write this as a patched version of the default environment
|
||||
devShells.fullEnv = pkgs.mkShell {
|
||||
inherit (pkgs.proof-proverif) CRYPTOVERIF_LIB;
|
||||
inputsFrom = [ pkgs.rosenpass ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cargo-audit
|
||||
cargo-msrv
|
||||
cargo-release
|
||||
cargo-vet
|
||||
rustfmt
|
||||
nodePackages.prettier
|
||||
nushell # for the .ci/gen-workflow-files.nu script
|
||||
proverif-patched
|
||||
pkgs.cargo-llvm-cov
|
||||
pkgs.grcov
|
||||
pkgs.rust-bin.stable.latest.complete
|
||||
];
|
||||
};
|
||||
devShells.coverage = pkgs.mkShell {
|
||||
inputsFrom = [ pkgs.rosenpass ];
|
||||
nativeBuildInputs = [
|
||||
pkgs.cargo-llvm-cov
|
||||
pkgs.grcov
|
||||
pkgs.rustc.llvmPackages.llvm
|
||||
];
|
||||
env = {
|
||||
inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA;
|
||||
};
|
||||
};
|
||||
devShells.benchmarks = pkgs.mkShell {
|
||||
inputsFrom = [ pkgs.rosenpass ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cargo-release
|
||||
clippy
|
||||
rustfmt
|
||||
];
|
||||
};
|
||||
|
||||
checks =
|
||||
{
|
||||
systemd-rosenpass = pkgs.testers.runNixOSTest ./tests/systemd/rosenpass.nix;
|
||||
systemd-rp = pkgs.testers.runNixOSTest ./tests/systemd/rp.nix;
|
||||
formatting = treefmtEval.config.build.check self;
|
||||
rosenpass-msrv-check =
|
||||
let
|
||||
rosenpassCargoToml = pkgs.lib.trivial.importTOML ./rosenpass/Cargo.toml;
|
||||
|
||||
checks = {
|
||||
cargo-fmt = pkgs.runCommand "check-cargo-fmt"
|
||||
{ inherit (self.devShells.${system}.default) nativeBuildInputs buildInputs; } ''
|
||||
cargo fmt --manifest-path=${./.}/Cargo.toml --check && touch $out
|
||||
'';
|
||||
nixpkgs-fmt = pkgs.runCommand "check-nixpkgs-fmt"
|
||||
{ nativeBuildInputs = [ pkgs.nixpkgs-fmt ]; } ''
|
||||
nixpkgs-fmt --check ${./.} && touch $out
|
||||
'';
|
||||
prettier-check = pkgs.runCommand "check-with-prettier"
|
||||
{ nativeBuildInputs = [ pkgs.nodePackages.prettier ]; } ''
|
||||
cd ${./.} && prettier --check . && touch $out
|
||||
'';
|
||||
};
|
||||
rustToolchain = pkgs.rust-bin.stable.${rosenpassCargoToml.package.rust-version}.default;
|
||||
rustPlatform = pkgs.makeRustPlatform {
|
||||
cargo = rustToolchain;
|
||||
rustc = rustToolchain;
|
||||
};
|
||||
in
|
||||
pkgs.rosenpass.override { inherit rustPlatform; };
|
||||
}
|
||||
// pkgs.lib.optionalAttrs (system == "x86_64-linux") (
|
||||
import ./tests/legacy-distro-packaging.nix {
|
||||
inherit pkgs;
|
||||
rosenpass-deb = self.packages.${system}.package-deb;
|
||||
rosenpass-rpm = self.packages.${system}.package-rpm;
|
||||
}
|
||||
);
|
||||
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
}))
|
||||
# for `nix fmt`
|
||||
formatter = treefmtEval.config.build.wrapper;
|
||||
}
|
||||
)
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
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
88
fuzz/Cargo.toml
Normal file
88
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,88 @@
|
||||
[package]
|
||||
name = "rosenpass-fuzzing"
|
||||
version = "0.0.1"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[features]
|
||||
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux_all"]
|
||||
|
||||
[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
|
||||
29
fuzz/fuzz_targets/aead_enc_into.rs
Normal file
29
fuzz/fuzz_targets/aead_enc_into.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#![no_main]
|
||||
extern crate arbitrary;
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_cipher_traits::primitives::Aead as _;
|
||||
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();
|
||||
});
|
||||
23
fuzz/fuzz_targets/blake2b.rs
Normal file
23
fuzz/fuzz_targets/blake2b.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
#![no_main]
|
||||
extern crate arbitrary;
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_cipher_traits::primitives::KeyedHashTo;
|
||||
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::Blake2b::keyed_hash_to(&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::primitives::Kem;
|
||||
use rosenpass_ciphers::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);
|
||||
});
|
||||
22
fuzz/fuzz_targets/kyber_encaps.rs
Normal file
22
fuzz/fuzz_targets/kyber_encaps.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
#![no_main]
|
||||
extern crate arbitrary;
|
||||
extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass_cipher_traits::primitives::Kem;
|
||||
use rosenpass_ciphers::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::primitives::Kem;
|
||||
use rosenpass_ciphers::StaticKem;
|
||||
|
||||
fuzz_target!(|input: [u8; StaticKem::PK_LEN]| {
|
||||
let mut ciphertext = [0u8; StaticKem::CT_LEN];
|
||||
let mut shared_secret = [0u8; StaticKem::SHK_LEN];
|
||||
|
||||
// We expect errors while fuzzing therefore we do not check the result.
|
||||
let _ = StaticKem.encaps(&mut shared_secret, &mut ciphertext, &input);
|
||||
});
|
||||
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);
|
||||
});
|
||||
13
manual_tests/psk_broker/peer_a.rp.config
Normal file
13
manual_tests/psk_broker/peer_a.rp.config
Normal file
@@ -0,0 +1,13 @@
|
||||
secret_key = "peer_a.rp.sk"
|
||||
public_key = "peer_a.rp.pk"
|
||||
listen = ["[::1]:46127"]
|
||||
verbosity = "Verbose"
|
||||
|
||||
[api]
|
||||
listen_path = []
|
||||
listen_fd = []
|
||||
stream_fd = []
|
||||
|
||||
[[peers]]
|
||||
public_key = "peer_b.rp.pk"
|
||||
device = "rpPskBrkTestA"
|
||||
14
manual_tests/psk_broker/peer_b.rp.config
Normal file
14
manual_tests/psk_broker/peer_b.rp.config
Normal file
@@ -0,0 +1,14 @@
|
||||
secret_key = "peer_b.rp.sk"
|
||||
public_key = "peer_b.rp.pk"
|
||||
listen = []
|
||||
verbosity = "Verbose"
|
||||
|
||||
[api]
|
||||
listen_path = []
|
||||
listen_fd = []
|
||||
stream_fd = []
|
||||
|
||||
[[peers]]
|
||||
public_key = "peer_a.rp.pk"
|
||||
endpoint = "[::1]:46127"
|
||||
device = "rpPskBrkTestB"
|
||||
215
manual_tests/psk_broker/run_test.sh
Executable file
215
manual_tests/psk_broker/run_test.sh
Executable file
@@ -0,0 +1,215 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
enquote() {
|
||||
while (( "$#" > 1)); do
|
||||
printf "%q " "$1"
|
||||
shift
|
||||
done
|
||||
if (("$#" > 0)); then
|
||||
printf "%q" "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
CLEANUP_HOOKS=()
|
||||
hook_cleanup() {
|
||||
local hook
|
||||
set +e +o pipefail
|
||||
for hook in "${CLEANUP_HOOKS[@]}"; do
|
||||
eval "${hook}"
|
||||
done
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
CLEANUP_HOOKS=("$(enquote exc_with_ctx cleanup "$@")" "${CLEANUP_HOOKS[@]}")
|
||||
}
|
||||
|
||||
cleanup_eval() {
|
||||
cleanup eval "$*"
|
||||
}
|
||||
|
||||
stderr() {
|
||||
echo >&2 "$@"
|
||||
}
|
||||
|
||||
log() {
|
||||
local level; level="$1"; shift || fatal "USAGE: log LVL MESSAGE.."
|
||||
stderr "[${level}]" "$@"
|
||||
}
|
||||
|
||||
info() {
|
||||
log "INFO" "$@"
|
||||
}
|
||||
|
||||
debug() {
|
||||
log "DEBUG" "$@"
|
||||
}
|
||||
|
||||
fatal() {
|
||||
log "FATAL" "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
assert() {
|
||||
local msg; msg="$1"; shift || fatal "USAGE: assert_cmd MESSAGE COMMAND.."
|
||||
"$@" || fatal "${msg}"
|
||||
}
|
||||
|
||||
abs_dir() {
|
||||
local dir; dir="$1"; shift || fatal "USAGE: abs_dir DIR"
|
||||
(
|
||||
cd "${dir}"
|
||||
pwd -P
|
||||
)
|
||||
}
|
||||
|
||||
exc_with_ctx() {
|
||||
local ctx; ctx="$1"; shift || fatal "USAGE: exc_with_ctx CONTEXT COMMAND.."
|
||||
if [[ -z "${ctx}" ]]; then
|
||||
info '$' "$@"
|
||||
else
|
||||
info "${ctx}\$" "$@"
|
||||
fi
|
||||
|
||||
"$@"
|
||||
}
|
||||
|
||||
exc() {
|
||||
exc_with_ctx "" "$@"
|
||||
}
|
||||
|
||||
exc_eval() {
|
||||
exc eval "$*"
|
||||
}
|
||||
|
||||
exc_eval_with_ctx() {
|
||||
local ctx; ctx="$1"; shift || fatal "USAGE: exc_eval_with_ctx CONTEXT EVAL_COMMAND.."
|
||||
exc_with_ctx "eval:${ctx}" "$*"
|
||||
}
|
||||
|
||||
exc_as_user() {
|
||||
exc sudo -u "${SUDO_USER}" "$@"
|
||||
}
|
||||
|
||||
exc_eval_as_user() {
|
||||
exc_as_user bash -c "$*"
|
||||
}
|
||||
|
||||
fork_eval_as_user() {
|
||||
exc sudo -u "${SUDO_USER}" bash -c "$*" &
|
||||
local pid; pid="$!"
|
||||
cleanup wait "${pid}"
|
||||
cleanup pkill -2 -P "${pid}" # Reverse ordering
|
||||
}
|
||||
|
||||
info_success() {
|
||||
stderr
|
||||
stderr
|
||||
if [[ "${SUCCESS}" = 1 ]]; then
|
||||
stderr " Test was a success!"
|
||||
else
|
||||
stderr " !!! TEST WAS A FAILURE!!!"
|
||||
fi
|
||||
stderr
|
||||
}
|
||||
|
||||
main() {
|
||||
assert "Use as root with sudo" [ "$(id -u)" -eq 0 ]
|
||||
assert "Use as root with sudo" [ -n "${SUDO_UID}" ]
|
||||
assert "SUDO_UID is 0; refusing to build as root" [ "${SUDO_UID}" -ne 0 ]
|
||||
|
||||
cleanup info_success
|
||||
|
||||
trap hook_cleanup EXIT
|
||||
|
||||
SCRIPT="$0"
|
||||
CFG_TEMPLATE_DIR="$(abs_dir "$(dirname "${SCRIPT}")")"
|
||||
REPO="$(abs_dir "${CFG_TEMPLATE_DIR}/../..")"
|
||||
BINS="${REPO}/target/debug"
|
||||
|
||||
# Create temp dir
|
||||
TMP_DIR="/tmp/rosenpass-psk-broker-test-$(date +%s)-$(uuidgen)"
|
||||
cleanup rm -rf "${TMP_DIR}"
|
||||
exc_as_user mkdir -p "${TMP_DIR}"
|
||||
|
||||
# Copy config
|
||||
CFG_DIR="${TMP_DIR}/cfg"
|
||||
exc_as_user cp -R "${CFG_TEMPLATE_DIR}" "${CFG_DIR}"
|
||||
|
||||
exc umask 077
|
||||
|
||||
exc cd "${REPO}"
|
||||
local build_cmd; build_cmd=(cargo build --workspace --color=always --all-features --bins --profile dev)
|
||||
if test -e "${BINS}/rosenpass-wireguard-broker-privileged" -a -e "${BINS}/rosenpass"; then
|
||||
info "Found the binaries rosenpass-wireguard-broker-privileged and rosenpass." \
|
||||
"Run following commands as a regular user to recompile the binaries with the right options" \
|
||||
"in case of an error:" '$' "${build_cmd[@]}"
|
||||
else
|
||||
exc_as_user "${build_cmd[@]}"
|
||||
fi
|
||||
exc sudo setcap CAP_NET_ADMIN=+eip "${BINS}/rosenpass-wireguard-broker-privileged"
|
||||
|
||||
exc cd "${CFG_DIR}"
|
||||
exc_eval_as_user "wg genkey > peer_a.wg.sk"
|
||||
exc_eval_as_user "wg pubkey < peer_a.wg.sk > peer_a.wg.pk"
|
||||
exc_eval_as_user "wg genkey > peer_b.wg.sk"
|
||||
exc_eval_as_user "wg pubkey < peer_b.wg.sk > peer_b.wg.pk"
|
||||
exc_eval_as_user "wg genpsk > peer_a_invalid.psk"
|
||||
exc_eval_as_user "wg genpsk > peer_b_invalid.psk"
|
||||
exc_eval_as_user "echo $(enquote "peer = \"$(cat peer_b.wg.pk)\"") >> peer_a.rp.config"
|
||||
exc_eval_as_user "echo $(enquote "peer = \"$(cat peer_a.wg.pk)\"") >> peer_b.rp.config"
|
||||
exc_as_user "${BINS}"/rosenpass gen-keys peer_a.rp.config
|
||||
exc_as_user "${BINS}"/rosenpass gen-keys peer_b.rp.config
|
||||
|
||||
cleanup ip l del dev rpPskBrkTestA
|
||||
cleanup ip l del dev rpPskBrkTestB
|
||||
exc ip l add dev rpPskBrkTestA type wireguard
|
||||
exc ip l add dev rpPskBrkTestB type wireguard
|
||||
|
||||
exc wg set rpPskBrkTestA \
|
||||
listen-port 46125 \
|
||||
private-key peer_a.wg.sk \
|
||||
peer "$(cat peer_b.wg.pk)" \
|
||||
endpoint 'localhost:46126' \
|
||||
preshared-key peer_a_invalid.psk \
|
||||
allowed-ips fe80::2/64
|
||||
exc wg set rpPskBrkTestB \
|
||||
listen-port 46126 \
|
||||
private-key peer_b.wg.sk \
|
||||
peer "$(cat peer_a.wg.pk)" \
|
||||
endpoint 'localhost:46125' \
|
||||
preshared-key peer_b_invalid.psk \
|
||||
allowed-ips fe80::1/64
|
||||
|
||||
exc ip l set rpPskBrkTestA up
|
||||
exc ip l set rpPskBrkTestB up
|
||||
|
||||
exc ip a add fe80::1/64 dev rpPskBrkTestA
|
||||
exc ip a add fe80::2/64 dev rpPskBrkTestB
|
||||
|
||||
fork_eval_as_user "\
|
||||
RUST_LOG='info' \
|
||||
PATH=$(enquote "${REPO}/target/debug:${PATH}") \
|
||||
$(enquote "${BINS}/rosenpass") --psk-broker-spawn \
|
||||
exchange-config peer_a.rp.config"
|
||||
fork_eval_as_user "\
|
||||
RUST_LOG='info' \
|
||||
PATH=$(enquote "${REPO}/target/debug:${PATH}") \
|
||||
$(enquote "${BINS}/rosenpass-wireguard-broker-socket-handler") \
|
||||
--listen-path broker.sock"
|
||||
fork_eval_as_user "\
|
||||
RUST_LOG='info' \
|
||||
PATH=$(enquote "$PWD/target/debug:${PATH}") \
|
||||
$(enquote "${BINS}/rosenpass") --psk-broker-path broker.sock \
|
||||
exchange-config peer_b.rp.config"
|
||||
|
||||
exc_as_user ping -c 2 -w 10 fe80::1%rpPskBrkTestA
|
||||
exc_as_user ping -c 2 -w 10 fe80::2%rpPskBrkTestB
|
||||
exc_as_user ping -c 2 -w 10 fe80::2%rpPskBrkTestA
|
||||
exc_as_user ping -c 2 -w 10 fe80::1%rpPskBrkTestB
|
||||
|
||||
SUCCESS=1
|
||||
}
|
||||
|
||||
main "$@"
|
||||
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)
|
||||
21
oqs/Cargo.toml
Normal file
21
oqs/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[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"
|
||||
rust-version = "1.77.0"
|
||||
|
||||
[dependencies]
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
oqs-sys = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-constant-time = { 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.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user