Compare commits

..

20 Commits

Author SHA1 Message Date
Alice Bowman
60a411e363 removed m1 cargo test 2024-06-17 15:39:43 +02:00
Alice Bowman
caf8801004 trialing cargo m1 test 2024-06-13 15:42:02 +02:00
Alice Bowman
858c0904ad refactored different cargo os tests into an array 2024-06-13 15:19:20 +02:00
Alice Bowman
e9838722b0 changed qc arm test to mac-14 to avoid bug 2024-06-13 15:12:34 +02:00
Alice Bowman
69043fbae7 added Mac ARM cargo test 2024-06-13 15:01:20 +02:00
Prabhpreet Dua
a70b71d509 Include uncommited changes 2024-06-13 17:35:10 +05:30
Prabhpreet Dua
96bed38ad0 Enable privileged only on linux 2024-06-13 17:34:48 +05:30
Prabhpreet Dua
a75d7a53e0 Add cfg flag to bin .rs files 2024-06-13 17:19:57 +05:30
Prabhpreet Dua
c4314c0eff Limit wireguard broker privledged/socket handler to linux 2024-06-13 17:13:00 +05:30
Alice Bowman
511a59fe1b Alter target OS for mod.rs 2024-06-13 13:18:23 +02:00
Alice Bowman
5e4a7c3b7f netlink.rs and cargo.toml features re: xplatform 2024-06-13 13:07:51 +02:00
Alice Bowman
139e62d1fb fixed indentation 2024-06-13 12:34:06 +02:00
Alice Bowman
c417fafe2a fixed qc-yaml test name 2024-06-13 12:30:56 +02:00
Alice Bowman
8f4141a159 added cargo-test runner for macos 86-64 2024-06-13 12:27:58 +02:00
Paul Spooren
7c54a37618 misc: add generate_configs.py script
The script can be used to simulate setups of different sizes. A short
description is added to the `misc/` folder for further information.

This can be used for both benchmarking but also hunting down bugs which
may occur with larger setups.

Signed-off-by: Paul Spooren <mail@aparcar.org>
2024-06-13 11:11:53 +02:00
Prabhpreet Dua
7a4f700186 feat: Improved memfd-secret allocation (#347)
Improve memfd-secret guard page allocation by using combination of mmap to map allocation area, and nest memfd-secret mapping and meta information with different permissions within the area

Implemented in quininer/memsec#18 

Co-authored-by: Prabhpreet Dua <615318+prabhpreet@users.noreply.github.com>
Co-authored-by: Karolin Varner <karo@cupdev.net>
2024-06-13 10:04:35 +05:30
Prabhpreet Dua
f535a31cd7 Feature flag for memfd_secret alloc (#343)
* feature flag for memfd_secret alloc

* Cargo fmt
2024-06-11 14:53:30 +05:30
Karolin Varner
ac2aaa5fbd Merge pull request #336 from rosenpass/dev/karo/rollback-proofs
chore: Rollback symbolic models to original state
2024-06-11 09:57:36 +02:00
dependabot[bot]
e472fa1fcd build(deps): bump clap from 4.5.6 to 4.5.7 (#340)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.6 to 4.5.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.6...v4.5.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-11 08:26:40 +05:30
Prabhpreet Dua
526c930119 Secret memory with memfd_secret (#321)
Implements:
- An additional allocator to use memfd_secret(2) and guard pages using mmap(2), implemented in quininer/memsec#16
- An allocator that abstracts away underlying allocators, and uses specified allocator set by rosenpass_secret_memory::policy functions (or a function that sets rosenpass_secret_memory::alloc::ALLOC_INIT
- Updates to tests- integration, fuzz, bench: some tests use procspawn to spawn multiple processes with different allocator policies
2024-06-10 13:12:44 +05:30
39 changed files with 1330 additions and 600 deletions

View File

@@ -110,7 +110,12 @@ jobs:
- run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items - run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items
cargo-test: cargo-test:
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-13]
# - ubuntu is x86-64
# - macos-13 is also x86-64 architecture
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/cache@v3 - uses: actions/cache@v3
@@ -176,8 +181,12 @@ jobs:
cargo fuzz run fuzz_handle_msg -- -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 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_mceliece_encaps -- -max_total_time=5
cargo fuzz run fuzz_box_secret_alloc -- -max_total_time=5 cargo fuzz run fuzz_box_secret_alloc_malloc -- -max_total_time=5
cargo fuzz run fuzz_vec_secret_alloc -- -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: codecov:
runs-on: ubuntu-latest runs-on: ubuntu-latest

501
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,15 +12,11 @@ members = [
"fuzz", "fuzz",
"secret-memory", "secret-memory",
"rp", "rp",
"wireguard-broker"
]
default-members = [
"rosenpass",
"rp",
"wireguard-broker", "wireguard-broker",
] ]
default-members = ["rosenpass", "rp", "wireguard-broker"]
[workspace.metadata.release] [workspace.metadata.release]
# ensure that adding `--package` as argument to `cargo release` still creates version tags in the form of `vx.y.z` # ensure that adding `--package` as argument to `cargo release` still creates version tags in the form of `vx.y.z`
tag-prefix = "" tag-prefix = ""
@@ -45,18 +41,24 @@ env_logger = "0.10.2"
toml = "0.7.8" toml = "0.7.8"
static_assertions = "1.1.0" static_assertions = "1.1.0"
allocator-api2 = "0.2.14" allocator-api2 = "0.2.14"
memsec = "0.6.3" memsec = { git="https://github.com/rosenpass/memsec.git" ,rev="aceb9baee8aec6844125bd6612f92e9a281373df", features = [ "alloc_ext", ] }
rand = "0.8.5" rand = "0.8.5"
typenum = "1.17.0" typenum = "1.17.0"
log = { version = "0.4.21" } log = { version = "0.4.21" }
clap = { version = "4.5.6", features = ["derive"] } clap = { version = "4.5.7", features = ["derive"] }
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.203", features = ["derive"] }
arbitrary = { version = "1.3.2", features = ["derive"] } arbitrary = { version = "1.3.2", features = ["derive"] }
anyhow = { version = "1.0.86", features = ["backtrace", "std"] } anyhow = { version = "1.0.86", features = ["backtrace", "std"] }
mio = { version = "0.8.11", features = ["net", "os-poll"] } mio = { version = "0.8.11", features = ["net", "os-poll"] }
oqs-sys = { version = "0.9.1", default-features = false, features = ['classic_mceliece', 'kyber'] } oqs-sys = { version = "0.9.1", default-features = false, features = [
'classic_mceliece',
'kyber',
] }
blake2 = "0.10.6" blake2 = "0.10.6"
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] } chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
"std",
"heapless",
] }
zerocopy = { version = "0.7.34", features = ["derive"] } zerocopy = { version = "0.7.34", features = ["derive"] }
home = "0.5.9" home = "0.5.9"
derive_builder = "0.20.0" derive_builder = "0.20.0"
@@ -71,8 +73,10 @@ libfuzzer-sys = "0.4"
test_bin = "0.4.0" test_bin = "0.4.0"
criterion = "0.4.0" criterion = "0.4.0"
allocator-api2-tests = "0.2.15" allocator-api2-tests = "0.2.15"
procspawn = {version = "1.0.0", features= ["test-support"]}
#Broker dependencies (might need cleanup or changes) #Broker dependencies (might need cleanup or changes)
wireguard-uapi = "3.0.0" wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
command-fds = "0.2.3" command-fds = "0.2.3"
rustix = { version = "0.38.27", features = ["net"] } rustix = { version = "0.38.27", features = ["net"] }

View File

@@ -3,33 +3,12 @@
#define SESSION_START_EVENTS 0 #define SESSION_START_EVENTS 0
#define RANDOMIZED_CALL_IDS 0 #define RANDOMIZED_CALL_IDS 0
#include "config.mpv" #include "config.mpv"
#include "prelude/basic.mpv" #include "prelude/basic.mpv"
#include "crypto/key.mpv" #include "crypto/key.mpv"
#include "crypto/kem.mpv" #include "crypto/kem.mpv"
#include "rosenpass/oracles.mpv" #include "rosenpass/oracles.mpv"
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].
let main = rosenpass_main. let main = rosenpass_main.
@lemma "state coherence, initiator: Initiator accepting a RespHello message implies they also generated the associated InitHello message" @lemma "state coherence, initiator: Initiator accepting a RespHello message implies they also generated the associated InitHello message"

View File

@@ -10,26 +10,6 @@
let main = rosenpass_main. let main = rosenpass_main.
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].
@lemma "non-interruptability: Adv cannot prevent a genuine InitHello message from being accepted" @lemma "non-interruptability: Adv cannot prevent a genuine InitHello message from being accepted"
lemma ih:InitHello_t, psk:key, sski:kem_sk, sskr:kem_sk; lemma ih:InitHello_t, psk:key, sski:kem_sk, sskr:kem_sk;
event(IHRjct(ih, psk, sskr, kem_pub(sski))) event(IHRjct(ih, psk, sskr, kem_pub(sski)))

View File

@@ -88,18 +88,6 @@ set verboseCompleted=VERBOSE.
#define SES_EV(...) #define SES_EV(...)
#endif #endif
#if COOKIE_EVENTS
#define COOKIE_EV(...) __VA_ARGS__
#else
#define COOKIE_EV(...)
#endif
#if KEM_EVENTS
#define KEM_EV(...) __VA_ARGS__
#else
#define KEM_EV(...)
#endif
(* TODO: Authentication timing properties *) (* TODO: Authentication timing properties *)
(* TODO: Proof that every adversary submitted package is equivalent to one generated by the proper algorithm using different coins. This probably requires introducing an oracle that extracts the coins used and explicitly adding the notion of coins used for Packet->Packet steps and an inductive RNG notion. *) (* TODO: Proof that every adversary submitted package is equivalent to one generated by the proper algorithm using different coins. This probably requires introducing an oracle that extracts the coins used and explicitly adding the notion of coins used for Packet->Packet steps and an inductive RNG notion. *)

View File

@@ -41,32 +41,23 @@ restriction s:seed, p1:Atom, p2:Atom, ad1:Atom, ad2:Atom;
event(ConsumeSeed(p1, s, ad1)) && event(ConsumeSeed(p2, s, ad2)) event(ConsumeSeed(p1, s, ad1)) && event(ConsumeSeed(p2, s, ad2))
==> p1 = p2 && ad1 = ad2. ==> p1 = p2 && ad1 = ad2.
letfun create_mac2(k:key, msg:bits) = prf(k,msg).
#include "rosenpass/responder.macro" #include "rosenpass/responder.macro"
fun Cinit_conf(kem_sk_tmpl, key_tmpl, kem_pk_tmpl, InitConf_t) : Atom [data]. fun Cinit_conf(kem_sk_tmpl, key_tmpl, kem_pk_tmpl, InitConf_t) : Atom [data].
CK_EV( event OskOinit_conf(key, key). ) CK_EV( event OskOinit_conf(key, key). )
MTX_EV( event ICRjct(InitConf_t, key, kem_sk, kem_pk). ) MTX_EV( event ICRjct(InitConf_t, key, kem_sk, kem_pk). )
SES_EV( event ResponderSession(InitConf_t, key). ) SES_EV( event ResponderSession(InitConf_t, key). )
KEM_EV(event Oinit_conf_KemUse(SessionId, SessionId, Atom).)
#ifdef KEM_EVENTS
restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
event(Oinit_conf_KemUse(sidi, sidr, ad1)) && event(Oinit_conf_KemUse(sidi, sidr, ad2))
==> ad1 = ad2.
#endif
event ConsumeBiscuit(Atom, kem_sk, kem_pk, Atom). event ConsumeBiscuit(Atom, kem_sk, kem_pk, Atom).
let Oinit_conf() =
fun Ccookie(key, bits) : Atom[data]. in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic));
#if RANDOMIZED_CALL_IDS
let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t, call:Atom) = new call:Atom;
#else
call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic);
#endif
SETUP_HANDSHAKE_STATE() SETUP_HANDSHAKE_STATE()
eski <- kem_sk0; eski <- kem_sk0;
epki <- kem_pk0; epki <- kem_pk0;
let try_ = ( let try_ = (
let InitConf(sidi, sidr, biscuit, auth) = ic in
KEM_EV(event Oinit_conf_KemUse(sidi, sidr, call);)
INITCONF_CONSUME() INITCONF_CONSUME()
event ConsumeBiscuit(biscuit_no, sskm, spkt, call); event ConsumeBiscuit(biscuit_no, sskm, spkt, call);
CK_EV( event OskOinit_conf(ck_rh, osk); ) CK_EV( event OskOinit_conf(ck_rh, osk); )
@@ -82,20 +73,10 @@ let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:Ini
#endif #endif
). ).
let Oinit_conf() =
in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic));
#if RANDOMIZED_CALL_IDS
new call:Atom;
#else
call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic);
#endif
Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic, call).
restriction biscuit_no:Atom, sskm:kem_sk, spkr:kem_pk, ad1:Atom, ad2:Atom; restriction biscuit_no:Atom, sskm:kem_sk, spkr:kem_pk, ad1:Atom, ad2:Atom;
event(ConsumeBiscuit(biscuit_no, sskm, spkr, ad1)) && event(ConsumeBiscuit(biscuit_no, sskm, spkr, ad2)) event(ConsumeBiscuit(biscuit_no, sskm, spkr, ad1)) && event(ConsumeBiscuit(biscuit_no, sskm, spkr, ad2))
==> ad1 = ad2. ==> ad1 = ad2.
// TODO: Restriction biscuit no invalidation // TODO: Restriction biscuit no invalidation
#include "rosenpass/initiator.macro" #include "rosenpass/initiator.macro"
@@ -104,29 +85,11 @@ CK_EV( event OskOresp_hello(key, key, key). )
MTX_EV( event RHRjct(RespHello_t, key, kem_sk, kem_pk). ) MTX_EV( event RHRjct(RespHello_t, key, kem_sk, kem_pk). )
MTX_EV( event ICSent(RespHello_t, InitConf_t, key, kem_sk, kem_pk). ) MTX_EV( event ICSent(RespHello_t, InitConf_t, key, kem_sk, kem_pk). )
SES_EV( event InitiatorSession(RespHello_t, key). ) SES_EV( event InitiatorSession(RespHello_t, key). )
let Oresp_hello(HS_DECL_ARGS) =
KEM_EV(event Oresp_hello_KemUse(SessionId, SessionId, Atom).) in(C, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
#ifdef KEM_EVENTS
restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
event(Oresp_hello_KemUse(sidi, sidr, ad1)) && event(Oresp_hello_KemUse(sidi, sidr, ad2))
==> ad1 = ad2.
#endif
#ifdef COOKIE_EVENTS
COOKIE_EVENTS(Oresp_hello)
#endif
let Oresp_hello(HS_DECL_ARGS, C_in:channel, call:Atom) =
in(C_in, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
in(C_in, mac2_key:key);
rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth); rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
#ifdef COOKIE_EVENTS
msg <- RH2b(rh);
COOKIE_PROCESS(Oresp_hello,
#endif
/* try */ let ic = ( /* try */ let ic = (
ck_ini <- ck; ck_ini <- ck;
KEM_EV(event Oresp_hello_KemUse(sidi, sidr, call);)
RESPHELLO_CONSUME() RESPHELLO_CONSUME()
ck_ih <- ck; ck_ih <- ck;
INITCONF_PRODUCE() INITCONF_PRODUCE()
@@ -135,25 +98,14 @@ let Oresp_hello(HS_DECL_ARGS, C_in:channel, call:Atom) =
SES_EV( event InitiatorSession(rh, osk); ) SES_EV( event InitiatorSession(rh, osk); )
ic ic
/* success */ ) in ( /* success */ ) in (
icbits <- IC2b(ic); out(C, ic)
mac <- create_mac(spkt, icbits);
mac2 <- create_mac2(mac2_key, mac_envelope2b(mac));
out(C_in, ic);
out(C_in, mac);
out(C_in, mac2)
/* fail */ ) else ( /* fail */ ) else (
#if MESSAGE_TRANSMISSION_EVENTS #if MESSAGE_TRANSMISSION_EVENTS
event RHRjct(rh, psk, sski, spkr) event RHRjct(rh, psk, sski, spkr)
#else #else
0 0
#endif #endif
) ).
#ifdef COOKIE_EVENTS
)
#else
.
#endif
// TODO: Restriction: Biscuit no invalidation // TODO: Restriction: Biscuit no invalidation
@@ -164,33 +116,24 @@ MTX_EV( event IHRjct(InitHello_t, key, kem_sk, kem_pk). )
MTX_EV( event RHSent(InitHello_t, RespHello_t, key, kem_sk, kem_pk). ) MTX_EV( event RHSent(InitHello_t, RespHello_t, key, kem_sk, kem_pk). )
event ConsumeSidr(SessionId, Atom). event ConsumeSidr(SessionId, Atom).
event ConsumeBn(Atom, kem_sk, kem_pk, Atom). event ConsumeBn(Atom, kem_sk, kem_pk, Atom).
KEM_EV(event Oinit_hello_KemUse(SessionId, SessionId, Atom).) let Oinit_hello() =
in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih));
#ifdef KEM_EVENTS #if RANDOMIZED_CALL_IDS
restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom; new call:Atom;
event(Oinit_hello_KemUse(sidi, sidr, ad1)) && event(Oinit_hello_KemUse(sidi, sidr, ad2)) #else
==> ad1 = ad2. call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih);
#endif #endif
let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt: kem_sk_tmpl, Septi: seed_tmpl, Sspti: seed_tmpl, ih: InitHello_t, mac2_key:key, C_out:channel, call:Atom) =
// TODO: This is ugly // TODO: This is ugly
let InitHello(sidi, epki, sctr, pidiC, auth) = ih in let InitHello(sidi, epki, sctr, pidiC, auth) = ih in
SETUP_HANDSHAKE_STATE() SETUP_HANDSHAKE_STATE()
eski <- kem_sk0; eski <- kem_sk0;
event ConsumeBn(biscuit_no, sskm, spkt, call);
event ConsumeSidr(sidr, call);
epti <- rng_key(setup_seed(Septi)); // RHR4 epti <- rng_key(setup_seed(Septi)); // RHR4
spti <- rng_key(setup_seed(Sspti)); // RHR5 spti <- rng_key(setup_seed(Sspti)); // RHR5
event ConsumeBn(biscuit_no, sskm, spkt, call);
event ConsumeSidr(sidr, call);
event ConsumeSeed(Epti, setup_seed(Septi), call); event ConsumeSeed(Epti, setup_seed(Septi), call);
event ConsumeSeed(Spti, setup_seed(Sspti), call); event ConsumeSeed(Spti, setup_seed(Sspti), call);
// out(C_out, spkt);
let rh = ( let rh = (
KEM_EV(event Oinit_hello_KemUse(sidi, sidr, call);)
INITHELLO_CONSUME() INITHELLO_CONSUME()
ck_ini <- ck; ck_ini <- ck;
RESPHELLO_PRODUCE() RESPHELLO_PRODUCE()
@@ -198,14 +141,7 @@ let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:k
MTX_EV( event RHSent(ih, rh, psk, sskr, spki); ) MTX_EV( event RHSent(ih, rh, psk, sskr, spki); )
rh rh
/* success */ ) in ( /* success */ ) in (
rhbits <- RH2b(rh); out(C, rh)
mac <- create_mac(spkt, rhbits);
out(C_out, rh);
out(C_out, mac);
mac2 <- create_mac2(mac2_key, mac_envelope2b(mac));
out(C_out, mac2)
/* fail */ ) else ( /* fail */ ) else (
#if MESSAGE_TRANSMISSION_EVENTS #if MESSAGE_TRANSMISSION_EVENTS
event IHRjct(ih, psk, sskr, spki) event IHRjct(ih, psk, sskr, spki)
@@ -214,18 +150,6 @@ let Oinit_hello_inner(sidm:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:k
#endif #endif
). ).
let Oinit_hello() =
in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih));
in(C, mac2_key:key);
#if RANDOMIZED_CALL_IDS
new call:Atom;
#else
call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih);
#endif
Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, mac2_key, C, call).
restriction sid:SessionId, ad1:Atom, ad2:Atom; restriction sid:SessionId, ad1:Atom, ad2:Atom;
event(ConsumeSidr(sid, ad1)) && event(ConsumeSidr(sid, ad2)) event(ConsumeSidr(sid, ad1)) && event(ConsumeSidr(sid, ad2))
==> ad1 = ad2. ==> ad1 = ad2.
@@ -242,55 +166,27 @@ fun Cinitiator(SessionId, kem_sk_tmpl, key_tmpl, kem_pk_tmpl, seed_tmpl, seed_tm
CK_EV( event OskOinitiator_ck(key). ) CK_EV( event OskOinitiator_ck(key). )
CK_EV( event OskOinitiator(key, key, kem_sk, kem_pk, key). ) CK_EV( event OskOinitiator(key, key, kem_sk, kem_pk, key). )
MTX_EV( event IHSent(InitHello_t, key, kem_sk, kem_pk). ) MTX_EV( event IHSent(InitHello_t, key, kem_sk, kem_pk). )
KEM_EV(event Oinitiator_inner_KemUse(SessionId, SessionId, Atom).)
#ifdef KEM_EVENTS
restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
event(Oinitiator_inner_KemUse(sidi, sidr, ad1)) && event(Oinitiator_inner_KemUse(sidi, sidr, ad2))
==> ad1 = ad2.
#endif
event ConsumeSidi(SessionId, Atom). event ConsumeSidi(SessionId, Atom).
let Oinitiator_inner(sidi: SessionId, Ssskm: kem_sk_tmpl, Spsk: key_tmpl, Sspkt: kem_sk_tmpl, Seski: seed_tmpl, Ssptr: seed_tmpl, last_cookie:key, C_out:channel, call:Atom) =
SETUP_HANDSHAKE_STATE()
sidr <- sid0;
KEM_EV(event Oinitiator_inner_KemUse(sidi, sidr, call);)
RNG_KEM_PAIR(eski, epki, Seski) // IHI3
sptr <- rng_key(setup_seed(Ssptr)); // IHI5
event ConsumeSidi(sidi, call);
event ConsumeSeed(Sptr, setup_seed(Ssptr), call);
event ConsumeSeed(Eski, setup_seed(Seski), call);
INITHELLO_PRODUCE()
CK_EV( event OskOinitiator_ck(ck); )
CK_EV( event OskOinitiator(ck, psk, sski, spkr, sptr); )
MTX_EV( event IHSent(ih, psk, sski, spkr); )
out(C_out, ih);
ihbits <- IH2b(ih);
mac <- create_mac(spkt, ihbits);
out(C_out, mac);
mac2 <- create_mac2(last_cookie, mac_envelope2b(mac));
out(C_out, mac2);
Oresp_hello(HS_PASS_ARGS, C_out, call).
let Oinitiator() = let Oinitiator() =
in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr)); in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr));
#if RANDOMIZED_CALL_IDS #if RANDOMIZED_CALL_IDS
new call:Atom; new call:Atom;
#else #else
call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr); call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr);
#endif #endif
SETUP_HANDSHAKE_STATE()
in(C, last_cookie:key); RNG_KEM_PAIR(eski, epki, Seski) // IHI3
Oinitiator_inner(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr, last_cookie, C, call). sidr <- sid0;
sptr <- rng_key(setup_seed(Ssptr)); // IHI5
event ConsumeSidi(sidi, call);
event ConsumeSeed(Sptr, setup_seed(Ssptr), call);
event ConsumeSeed(Eski, setup_seed(Seski), call);
INITHELLO_PRODUCE()
CK_EV( event OskOinitiator_ck(ck); )
CK_EV( event OskOinitiator(ck, psk, sski, spkr, sptr); )
MTX_EV( event IHSent(ih, psk, sski, spkr); )
out(C, ih);
Oresp_hello(HS_PASS_ARGS).
restriction sid:SessionId, ad1:Atom, ad2:Atom; restriction sid:SessionId, ad1:Atom, ad2:Atom;
event(ConsumeSidi(sid, ad1)) && event(ConsumeSidi(sid, ad2)) event(ConsumeSidi(sid, ad1)) && event(ConsumeSidi(sid, ad2))
@@ -311,3 +207,21 @@ let rosenpass_main() = 0
| REP(RESPONDER_BOUND, Oinit_hello) | REP(RESPONDER_BOUND, Oinit_hello)
| REP(RESPONDER_BOUND, Oinit_conf). | REP(RESPONDER_BOUND, Oinit_conf).
nounif v:seed_prec; attacker(prepare_seed(trusted_seed( v )))/6217[hypothesis].
nounif v:seed; attacker(prepare_seed( v ))/6216[hypothesis].
nounif v:seed; attacker(rng_kem_sk( v ))/6215[hypothesis].
nounif v:seed; attacker(rng_key( v ))/6214[hypothesis].
nounif v:key_prec; attacker(prepare_key(trusted_key( v )))/6213[hypothesis].
nounif v:kem_sk_prec; attacker(prepare_kem_sk(trusted_kem_sk( v )))/6212[hypothesis].
nounif v:key; attacker(prepare_key( v ))/6211[hypothesis].
nounif v:kem_sk; attacker(prepare_kem_sk( v ))/6210[hypothesis].
nounif Spk:kem_sk_tmpl;
attacker(Creveal_kem_pk(Spk))/6110[conclusion].
nounif sid:SessionId, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Seski:seed_tmpl, Ssptr:seed_tmpl;
attacker(Cinitiator( *sid, *Ssskm, *Spsk, *Sspkt, *Seski, *Ssptr ))/6109[conclusion].
nounif sid:SessionId, biscuit_no:Atom, Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, Septi:seed_tmpl, Sspti:seed_tmpl, ih:InitHello_t;
attacker(Cinit_hello( *sid, *biscuit_no, *Ssskm, *Spsk, *Sspkt, *Septi, *Sspti, *ih ))/6108[conclusion].
nounif rh:RespHello_t;
attacker(Cresp_hello( *rh ))/6107[conclusion].
nounif Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t;
attacker(Cinit_conf( *Ssskm, *Spsk, *Sspkt, *ic ))/6106[conclusion].

View File

@@ -2,26 +2,6 @@
#include "crypto/kem.mpv" #include "crypto/kem.mpv"
#include "rosenpass/handshake_state.mpv" #include "rosenpass/handshake_state.mpv"
fun Envelope(
key,
bits
): bits [data].
type mac_envelope_t.
fun mac_envelope(
key,
bits
) : mac_envelope_t.
fun mac_envelope2b(mac_envelope_t) : bits [typeConverter].
letfun create_mac(pk:kem_pk, payload:bits) = mac_envelope(lprf2(MAC, kem_pk2b(pk), payload), payload).
fun mac_envelope_pk_test(mac_envelope_t, kem_pk) : bool
reduc forall pk:kem_pk, b:bits;
mac_envelope_pk_test(mac_envelope(prf(prf(prf(prf(key0,PROTOCOL),MAC),kem_pk2b(pk)),
b), b), pk) = true.
type InitHello_t. type InitHello_t.
fun InitHello( fun InitHello(
SessionId, // sidi SessionId, // sidi
@@ -31,8 +11,6 @@ fun InitHello(
bits // auth bits // auth
) : InitHello_t [data]. ) : InitHello_t [data].
fun IH2b(InitHello_t) : bitstring [typeConverter].
#define INITHELLO_PRODUCE() \ #define INITHELLO_PRODUCE() \
ck <- lprf1(CK_INIT, kem_pk2b(spkr)); /* IHI1 */ \ ck <- lprf1(CK_INIT, kem_pk2b(spkr)); /* IHI1 */ \
/* not handled here */ /* IHI2 */ \ /* not handled here */ /* IHI2 */ \
@@ -63,8 +41,6 @@ fun RespHello(
bits // auth bits // auth
) : RespHello_t [data]. ) : RespHello_t [data].
fun RH2b(RespHello_t) : bitstring [typeConverter].
#define RESPHELLO_PRODUCE() \ #define RESPHELLO_PRODUCE() \
/* not handled here */ /* RHR1 */ \ /* not handled here */ /* RHR1 */ \
MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \ MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \
@@ -91,14 +67,13 @@ fun InitConf(
bits // auth bits // auth
) : InitConf_t [data]. ) : InitConf_t [data].
fun IC2b(InitConf_t) : bitstring [typeConverter].
#define INITCONF_PRODUCE() \ #define INITCONF_PRODUCE() \
MIX2(sid2b(sidi), sid2b(sidr)) /* ICI3 */ \ MIX2(sid2b(sidi), sid2b(sidr)) /* ICI3 */ \
ENCRYPT_AND_MIX(auth, empty) /* ICI4 */ \ ENCRYPT_AND_MIX(auth, empty) /* ICI4 */ \
ic <- InitConf(sidi, sidr, biscuit, auth); ic <- InitConf(sidi, sidr, biscuit, auth);
#define INITCONF_CONSUME() \ #define INITCONF_CONSUME() \
let InitConf(sidi, sidr, biscuit, auth) = ic in \
LOAD_BISCUIT(biscuit_no, biscuit) /* ICR1 */ \ LOAD_BISCUIT(biscuit_no, biscuit) /* ICR1 */ \
ENCRYPT_AND_MIX(rh_auth, empty) /* ICIR */ \ ENCRYPT_AND_MIX(rh_auth, empty) /* ICIR */ \
ck_rh <- ck; /* ---- */ /* TODO: Move into oracles.mpv */ \ ck_rh <- ck; /* ---- */ /* TODO: Move into oracles.mpv */ \

View File

@@ -48,13 +48,37 @@ test = false
doc = false doc = false
[[bin]] [[bin]]
name = "fuzz_box_secret_alloc" name = "fuzz_box_secret_alloc_malloc"
path = "fuzz_targets/box_secret_alloc.rs" path = "fuzz_targets/box_secret_alloc_malloc.rs"
test = false test = false
doc = false doc = false
[[bin]] [[bin]]
name = "fuzz_vec_secret_alloc" name = "fuzz_vec_secret_alloc_malloc"
path = "fuzz_targets/vec_secret_alloc.rs" 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 test = false
doc = false doc = false

View 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);
});

View 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);
});

View File

@@ -2,7 +2,12 @@
use libfuzzer_sys::fuzz_target; use libfuzzer_sys::fuzz_target;
use rosenpass_secret_memory::alloc::secret_box; 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]| { fuzz_target!(|data: &[u8]| {
ONCE.call_once(secret_policy_try_use_memfd_secrets);
let _ = secret_box(data); let _ = secret_box(data);
}); });

View File

@@ -6,9 +6,13 @@ use libfuzzer_sys::fuzz_target;
use rosenpass::protocol::CryptoServer; use rosenpass::protocol::CryptoServer;
use rosenpass_cipher_traits::Kem; use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem; use rosenpass_ciphers::kem::StaticKem;
use rosenpass_secret_memory::policy::*;
use rosenpass_secret_memory::Secret; use rosenpass_secret_memory::Secret;
use std::sync::Once;
static ONCE: Once = Once::new();
fuzz_target!(|rx_buf: &[u8]| { fuzz_target!(|rx_buf: &[u8]| {
ONCE.call_once(secret_policy_use_only_malloc_secrets);
let sk = Secret::from_slice(&[0; StaticKem::SK_LEN]); let sk = Secret::from_slice(&[0; StaticKem::SK_LEN]);
let pk = Secret::from_slice(&[0; StaticKem::PK_LEN]); let pk = Secret::from_slice(&[0; StaticKem::PK_LEN]);

View 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);
});

View 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);
});

View File

@@ -1,9 +1,15 @@
#![no_main] #![no_main]
use std::sync::Once;
use libfuzzer_sys::fuzz_target; use libfuzzer_sys::fuzz_target;
use rosenpass_secret_memory::alloc::secret_vec; use rosenpass_secret_memory::alloc::secret_vec;
use rosenpass_secret_memory::policy::*;
static ONCE: Once = Once::new();
fuzz_target!(|data: &[u8]| { fuzz_target!(|data: &[u8]| {
ONCE.call_once(secret_policy_try_use_memfd_secrets);
let mut vec = secret_vec(); let mut vec = secret_vec();
vec.extend_from_slice(data); vec.extend_from_slice(data);
}); });

40
misc/README.md Normal file
View 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
View File

@@ -0,0 +1,105 @@
from pathlib import Path
from subprocess import run
config = dict(
peer_counts=[1, 5, 10, 50, 100, 500],
peer_count_max=100,
ate_ip="192.168.2.1",
dut_ip="192.168.2.4",
dut_port=9999,
path_to_rosenpass_bin="/Users/user/src/rosenppass/rosenpass/target/debug/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)

View File

@@ -49,6 +49,8 @@ criterion = { workspace = true }
test_bin = { workspace = true } test_bin = { workspace = true }
stacker = { workspace = true } stacker = { workspace = true }
serial_test = {workspace = true} serial_test = {workspace = true}
procspawn = {workspace = true}
[features] [features]
enable_broker_api = ["rosenpass-wireguard-broker/enable_broker_api"] enable_broker_api = ["rosenpass-wireguard-broker/enable_broker_api"]
enable_memfd_alloc = []

View File

@@ -5,6 +5,7 @@ use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem; use rosenpass_ciphers::kem::StaticKem;
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
fn handle( fn handle(
tx: &mut CryptoServer, tx: &mut CryptoServer,
@@ -56,6 +57,7 @@ fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> {
} }
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
secret_policy_try_use_memfd_secrets();
let (mut a, mut b) = make_server_pair().unwrap(); let (mut a, mut b) = make_server_pair().unwrap();
c.bench_function("cca_secret_alloc", |bench| { c.bench_function("cca_secret_alloc", |bench| {
bench.iter(|| { bench.iter(|| {

View File

@@ -3,6 +3,9 @@ use clap::{Parser, Subcommand};
use rosenpass_cipher_traits::Kem; use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem; use rosenpass_ciphers::kem::StaticKem;
use rosenpass_secret_memory::file::StoreSecret; use rosenpass_secret_memory::file::StoreSecret;
use rosenpass_secret_memory::{
secret_policy_try_use_memfd_secrets, secret_policy_use_only_malloc_secrets,
};
use rosenpass_util::file::{LoadValue, LoadValueB64}; use rosenpass_util::file::{LoadValue, LoadValueB64};
use rosenpass_wireguard_broker::brokers::native_unix::{ use rosenpass_wireguard_broker::brokers::native_unix::{
NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError, NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError,
@@ -154,6 +157,13 @@ impl CliCommand {
/// ## TODO /// ## TODO
/// - This method consumes the [`CliCommand`] value. It might be wise to use a reference... /// - This method consumes the [`CliCommand`] value. It might be wise to use a reference...
pub fn run(self, test_helpers: Option<AppServerTest>) -> anyhow::Result<()> { pub fn run(self, test_helpers: Option<AppServerTest>) -> anyhow::Result<()> {
//Specify secret policy
#[cfg(feature = "enable_memfd_alloc")]
secret_policy_try_use_memfd_secrets();
#[cfg(not(feature = "enable_memfd_alloc"))]
secret_policy_use_only_malloc_secrets();
use CliCommand::*; use CliCommand::*;
match self { match self {
Man => { Man => {

View File

@@ -19,12 +19,16 @@
//! [CryptoServer]. //! [CryptoServer].
//! //!
//! ``` //! ```
//! use rosenpass_secret_memory::policy::*;
//! use rosenpass_cipher_traits::Kem; //! use rosenpass_cipher_traits::Kem;
//! use rosenpass_ciphers::kem::StaticKem; //! use rosenpass_ciphers::kem::StaticKem;
//! use rosenpass::{ //! use rosenpass::{
//! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey}, //! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey},
//! }; //! };
//! # fn main() -> anyhow::Result<()> { //! # fn main() -> anyhow::Result<()> {
//! // Set security policy for storing secrets
//!
//! secret_policy_try_use_memfd_secrets();
//! //!
//! // initialize secret and public key for peer a ... //! // initialize secret and public key for peer a ...
//! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero()); //! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero());
@@ -2145,6 +2149,7 @@ mod test {
use std::{net::SocketAddrV4, thread::sleep, time::Duration}; use std::{net::SocketAddrV4, thread::sleep, time::Duration};
use super::*; use super::*;
use serial_test::serial;
struct VecHostIdentifier(Vec<u8>); struct VecHostIdentifier(Vec<u8>);
@@ -2166,7 +2171,21 @@ mod test {
} }
} }
fn setup_logging() {
use std::io::Write;
let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults)
log_builder.filter_level(log::LevelFilter::Info);
log_builder.format_timestamp_nanos();
log_builder.format(|buf, record| {
let ts_format = buf.timestamp_nanos().to_string();
writeln!(buf, "{}: {}", &ts_format[14..], record.args())
});
let _ = log_builder.try_init();
}
#[test] #[test]
#[serial]
/// Ensure that the protocol implementation can deal with truncated /// Ensure that the protocol implementation can deal with truncated
/// messages and with overlong messages. /// messages and with overlong messages.
/// ///
@@ -2182,6 +2201,8 @@ mod test {
/// Through all this, the handshake should still successfully terminate; /// Through all this, the handshake should still successfully terminate;
/// i.e. an exchanged key must be produced in both servers. /// i.e. an exchanged key must be produced in both servers.
fn handles_incorrect_size_messages() { fn handles_incorrect_size_messages() {
setup_logging();
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
stacker::grow(8 * 1024 * 1024, || { stacker::grow(8 * 1024 * 1024, || {
const OVERSIZED_MESSAGE: usize = ((MAX_MESSAGE_LEN as f32) * 1.2) as usize; const OVERSIZED_MESSAGE: usize = ((MAX_MESSAGE_LEN as f32) * 1.2) as usize;
type MsgBufPlus = Public<OVERSIZED_MESSAGE>; type MsgBufPlus = Public<OVERSIZED_MESSAGE>;
@@ -2252,7 +2273,10 @@ mod test {
} }
#[test] #[test]
#[serial]
fn test_regular_exchange() { fn test_regular_exchange() {
setup_logging();
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
stacker::grow(8 * 1024 * 1024, || { stacker::grow(8 * 1024 * 1024, || {
type MsgBufPlus = Public<MAX_MESSAGE_LEN>; type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
let (mut a, mut b) = make_server_pair().unwrap(); let (mut a, mut b) = make_server_pair().unwrap();
@@ -2296,7 +2320,7 @@ mod test {
//B handles InitConf, sends EmptyData //B handles InitConf, sends EmptyData
let HandleMsgResult { let HandleMsgResult {
resp, resp: _,
exchanged_with, exchanged_with,
} = b } = b
.handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf)
@@ -2310,7 +2334,10 @@ mod test {
} }
#[test] #[test]
#[serial]
fn test_regular_init_conf_retransmit() { fn test_regular_init_conf_retransmit() {
setup_logging();
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
stacker::grow(8 * 1024 * 1024, || { stacker::grow(8 * 1024 * 1024, || {
type MsgBufPlus = Public<MAX_MESSAGE_LEN>; type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
let (mut a, mut b) = make_server_pair().unwrap(); let (mut a, mut b) = make_server_pair().unwrap();
@@ -2355,7 +2382,7 @@ mod test {
//B handles InitConf, sends EmptyData //B handles InitConf, sends EmptyData
let HandleMsgResult { let HandleMsgResult {
resp, resp: _,
exchanged_with, exchanged_with,
} = b } = b
.handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf)
@@ -2368,7 +2395,7 @@ mod test {
//B handles InitConf again, sends EmptyData //B handles InitConf again, sends EmptyData
let HandleMsgResult { let HandleMsgResult {
resp, resp: _,
exchanged_with, exchanged_with,
} = b } = b
.handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf) .handle_msg(&a_to_b_buf.as_slice()[..init_conf_len], &mut *b_to_a_buf)
@@ -2382,7 +2409,10 @@ mod test {
} }
#[test] #[test]
#[serial]
fn cookie_reply_mechanism_responder_under_load() { fn cookie_reply_mechanism_responder_under_load() {
setup_logging();
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
stacker::grow(8 * 1024 * 1024, || { stacker::grow(8 * 1024 * 1024, || {
type MsgBufPlus = Public<MAX_MESSAGE_LEN>; type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
let (mut a, mut b) = make_server_pair().unwrap(); let (mut a, mut b) = make_server_pair().unwrap();
@@ -2476,7 +2506,10 @@ mod test {
} }
#[test] #[test]
#[serial]
fn cookie_reply_mechanism_initiator_bails_on_message_under_load() { fn cookie_reply_mechanism_initiator_bails_on_message_under_load() {
setup_logging();
rosenpass_secret_memory::secret_policy_try_use_memfd_secrets();
stacker::grow(8 * 1024 * 1024, || { stacker::grow(8 * 1024 * 1024, || {
type MsgBufPlus = Public<MAX_MESSAGE_LEN>; type MsgBufPlus = Public<MAX_MESSAGE_LEN>;
let (mut a, mut b) = make_server_pair().unwrap(); let (mut a, mut b) = make_server_pair().unwrap();

View File

@@ -6,7 +6,7 @@ use std::{
time::Duration, time::Duration,
}; };
use clap::{builder::Str, Parser}; use clap::Parser;
use rosenpass::{app_server::AppServerTestBuilder, cli::CliArgs}; use rosenpass::{app_server::AppServerTestBuilder, cli::CliArgs};
use rosenpass_secret_memory::{Public, Secret}; use rosenpass_secret_memory::{Public, Secret};
use rosenpass_wireguard_broker::{WireguardBrokerMio, WG_KEY_LEN, WG_PEER_LEN}; use rosenpass_wireguard_broker::{WireguardBrokerMio, WG_KEY_LEN, WG_PEER_LEN};
@@ -275,6 +275,7 @@ fn check_exchange_under_dos() {
fs::remove_dir_all(&tmpdir).unwrap(); fs::remove_dir_all(&tmpdir).unwrap();
} }
#[allow(dead_code)]
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct MockBrokerInner { struct MockBrokerInner {
psk: Option<Secret<WG_KEY_LEN>>, psk: Option<Secret<WG_KEY_LEN>>,

View File

@@ -37,3 +37,6 @@ netlink-packet-wireguard = "0.2"
[dev-dependencies] [dev-dependencies]
tempfile = {workspace = true} tempfile = {workspace = true}
stacker = {workspace = true} stacker = {workspace = true}
[features]
enable_memfd_alloc = []

View File

@@ -102,6 +102,7 @@ mod tests {
use std::fs; use std::fs;
use rosenpass::protocol::{SPk, SSk}; use rosenpass::protocol::{SPk, SSk};
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
use rosenpass_secret_memory::Secret; use rosenpass_secret_memory::Secret;
use rosenpass_util::file::LoadValue; use rosenpass_util::file::LoadValue;
use rosenpass_util::file::LoadValueB64; use rosenpass_util::file::LoadValueB64;
@@ -110,7 +111,8 @@ mod tests {
use crate::key::{genkey, pubkey, WG_B64_LEN}; use crate::key::{genkey, pubkey, WG_B64_LEN};
#[test] #[test]
fn it_works() { fn test_key_loopback() {
secret_policy_try_use_memfd_secrets();
let private_keys_dir = tempdir().unwrap(); let private_keys_dir = tempdir().unwrap();
fs::remove_dir(private_keys_dir.path()).unwrap(); fs::remove_dir(private_keys_dir.path()).unwrap();

View File

@@ -3,6 +3,7 @@ use std::process::exit;
use cli::{Cli, Command}; use cli::{Cli, Command};
use exchange::exchange; use exchange::exchange;
use key::{genkey, pubkey}; use key::{genkey, pubkey};
use rosenpass_secret_memory::policy;
mod cli; mod cli;
mod exchange; mod exchange;
@@ -10,6 +11,11 @@ mod key;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
#[cfg(feature = "enable_memfd_alloc")]
policy::secret_policy_try_use_memfd_secrets();
#[cfg(not(feature = "enable_memfd_alloc"))]
policy::secret_policy_use_only_malloc_secrets();
let cli = match Cli::parse(std::env::args().peekable()) { let cli = match Cli::parse(std::env::args().peekable()) {
Ok(cli) => cli, Ok(cli) => cli,
Err(err) => { Err(err) => {

View File

@@ -23,3 +23,4 @@ log = { workspace = true }
allocator-api2-tests = { workspace = true } allocator-api2-tests = { workspace = true }
tempfile = {workspace = true} tempfile = {workspace = true}
base64ct = {workspace = true} base64ct = {workspace = true}
procspawn = {workspace = true}

View File

@@ -4,37 +4,41 @@ use std::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator, Layout}; use allocator_api2::alloc::{AllocError, Allocator, Layout};
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
struct MemsecAllocatorContents; struct MallocAllocatorContents;
/// Memory allocation using using the memsec crate /// Memory allocation using using the memsec crate
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
pub struct MemsecAllocator { pub struct MallocAllocator {
_dummy_private_data: MemsecAllocatorContents, _dummy_private_data: MallocAllocatorContents,
} }
/// A box backed by the memsec allocator /// A box backed by the memsec allocator
pub type MemsecBox<T> = allocator_api2::boxed::Box<T, MemsecAllocator>; pub type MallocBox<T> = allocator_api2::boxed::Box<T, MallocAllocator>;
/// A vector backed by the memsec allocator /// A vector backed by the memsec allocator
pub type MemsecVec<T> = allocator_api2::vec::Vec<T, MemsecAllocator>; pub type MallocVec<T> = allocator_api2::vec::Vec<T, MallocAllocator>;
pub fn memsec_box<T>(x: T) -> MemsecBox<T> { pub fn malloc_box_try<T>(x: T) -> Result<MallocBox<T>, AllocError> {
MemsecBox::<T>::new_in(x, MemsecAllocator::new()) MallocBox::<T>::try_new_in(x, MallocAllocator::new())
} }
pub fn memsec_vec<T>() -> MemsecVec<T> { pub fn malloc_box<T>(x: T) -> MallocBox<T> {
MemsecVec::<T>::new_in(MemsecAllocator::new()) MallocBox::<T>::new_in(x, MallocAllocator::new())
} }
impl MemsecAllocator { pub fn malloc_vec<T>() -> MallocVec<T> {
MallocVec::<T>::new_in(MallocAllocator::new())
}
impl MallocAllocator {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
_dummy_private_data: MemsecAllocatorContents, _dummy_private_data: MallocAllocatorContents,
} }
} }
} }
unsafe impl Allocator for MemsecAllocator { unsafe impl Allocator for MallocAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
// Call memsec allocator // Call memsec allocator
let mem: Option<NonNull<[u8]>> = unsafe { memsec::malloc_sized(layout.size()) }; let mem: Option<NonNull<[u8]>> = unsafe { memsec::malloc_sized(layout.size()) };
@@ -48,8 +52,8 @@ unsafe impl Allocator for MemsecAllocator {
// Ensure the right alignment is used // Ensure the right alignment is used
let off = (mem.as_ptr() as *const u8).align_offset(layout.align()); let off = (mem.as_ptr() as *const u8).align_offset(layout.align());
if off != 0 { if off != 0 {
log::error!("Allocation {layout:?} was requested but memsec returned allocation \ log::error!("Allocation {layout:?} was requested but malloc-based memsec returned allocation \
with offset {off} from the requested alignment. Memsec always allocates values \ with offset {off} from the requested alignment. Malloc always allocates values \
at the end of a memory page for security reasons, custom alignments are not supported. \ at the end of a memory page for security reasons, custom alignments are not supported. \
You could try allocating an oversized value."); You could try allocating an oversized value.");
unsafe { memsec::free(mem) }; unsafe { memsec::free(mem) };
@@ -66,7 +70,7 @@ unsafe impl Allocator for MemsecAllocator {
} }
} }
impl fmt::Debug for MemsecAllocator { impl fmt::Debug for MallocAllocator {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str("<memsec based Rust allocator>") fmt.write_str("<memsec based Rust allocator>")
} }
@@ -78,21 +82,21 @@ mod test {
use super::*; use super::*;
make_test! { test_sizes(MemsecAllocator::new()) } make_test! { test_sizes(MallocAllocator::new()) }
make_test! { test_vec(MemsecAllocator::new()) } make_test! { test_vec(MallocAllocator::new()) }
make_test! { test_many_boxes(MemsecAllocator::new()) } make_test! { test_many_boxes(MallocAllocator::new()) }
#[test] #[test]
fn memsec_allocation() { fn malloc_allocation() {
let alloc = MemsecAllocator::new(); let alloc = MallocAllocator::new();
memsec_allocation_impl::<0>(&alloc); malloc_allocation_impl::<0>(&alloc);
memsec_allocation_impl::<7>(&alloc); malloc_allocation_impl::<7>(&alloc);
memsec_allocation_impl::<8>(&alloc); malloc_allocation_impl::<8>(&alloc);
memsec_allocation_impl::<64>(&alloc); malloc_allocation_impl::<64>(&alloc);
memsec_allocation_impl::<999>(&alloc); malloc_allocation_impl::<999>(&alloc);
} }
fn memsec_allocation_impl<const N: usize>(alloc: &MemsecAllocator) { fn malloc_allocation_impl<const N: usize>(alloc: &MallocAllocator) {
let layout = Layout::new::<[u8; N]>(); let layout = Layout::new::<[u8; N]>();
let mem = alloc.allocate(layout).unwrap(); let mem = alloc.allocate(layout).unwrap();

View File

@@ -0,0 +1,112 @@
#![cfg(target_os = "linux")]
use std::fmt;
use std::ptr::NonNull;
use allocator_api2::alloc::{AllocError, Allocator, Layout};
#[derive(Copy, Clone, Default)]
struct MemfdSecAllocatorContents;
/// Memory allocation using using the memsec crate
#[derive(Copy, Clone, Default)]
pub struct MemfdSecAllocator {
_dummy_private_data: MemfdSecAllocatorContents,
}
/// A box backed by the memsec allocator
pub type MemfdSecBox<T> = allocator_api2::boxed::Box<T, MemfdSecAllocator>;
/// A vector backed by the memsec allocator
pub type MemfdSecVec<T> = allocator_api2::vec::Vec<T, MemfdSecAllocator>;
pub fn memfdsec_box_try<T>(x: T) -> Result<MemfdSecBox<T>, AllocError> {
MemfdSecBox::<T>::try_new_in(x, MemfdSecAllocator::new())
}
pub fn memfdsec_box<T>(x: T) -> MemfdSecBox<T> {
MemfdSecBox::<T>::new_in(x, MemfdSecAllocator::new())
}
pub fn memfdsec_vec<T>() -> MemfdSecVec<T> {
MemfdSecVec::<T>::new_in(MemfdSecAllocator::new())
}
impl MemfdSecAllocator {
pub fn new() -> Self {
Self {
_dummy_private_data: MemfdSecAllocatorContents,
}
}
}
unsafe impl Allocator for MemfdSecAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
// Call memsec allocator
let mem: Option<NonNull<[u8]>> = unsafe { memsec::memfd_secret_sized(layout.size()) };
// Unwrap the option
let Some(mem) = mem else {
log::error!("Allocation {layout:?} was requested but memfd-based memsec returned a null pointer");
return Err(AllocError);
};
// Ensure the right alignment is used
let off = (mem.as_ptr() as *const u8).align_offset(layout.align());
if off != 0 {
log::error!("Allocation {layout:?} was requested but memfd-based memsec returned allocation \
with offset {off} from the requested alignment. Memfd always allocates values \
at the end of a memory page for security reasons, custom alignments are not supported. \
You could try allocating an oversized value.");
unsafe { memsec::free_memfd_secret(mem) };
return Err(AllocError);
};
Ok(mem)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
unsafe {
memsec::free_memfd_secret(ptr);
}
}
}
impl fmt::Debug for MemfdSecAllocator {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str("<memsec based Rust allocator>")
}
}
#[cfg(test)]
mod test {
use allocator_api2_tests::make_test;
use super::*;
make_test! { test_sizes(MemfdSecAllocator::new()) }
make_test! { test_vec(MemfdSecAllocator::new()) }
make_test! { test_many_boxes(MemfdSecAllocator::new()) }
#[test]
fn memfdsec_allocation() {
let alloc = MemfdSecAllocator::new();
memfdsec_allocation_impl::<0>(&alloc);
memfdsec_allocation_impl::<7>(&alloc);
memfdsec_allocation_impl::<8>(&alloc);
memfdsec_allocation_impl::<64>(&alloc);
memfdsec_allocation_impl::<999>(&alloc);
}
fn memfdsec_allocation_impl<const N: usize>(alloc: &MemfdSecAllocator) {
let layout = Layout::new::<[u8; N]>();
let mem = alloc.allocate(layout).unwrap();
// https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations
// promises us that allocated memory is initialized with the magic byte 0xDB
// and memsec promises to provide a reimplementation of the libsodium mechanism;
// it uses the magic value 0xD0 though
assert_eq!(unsafe { mem.as_ref() }, &[0xD0u8; N]);
let mem = NonNull::new(mem.as_ptr() as *mut u8).unwrap();
unsafe { alloc.deallocate(mem, layout) };
}
}

View File

@@ -0,0 +1,2 @@
pub mod malloc;
pub mod memfdsec;

View File

@@ -1,6 +1,86 @@
pub mod memsec; pub mod memsec;
pub use crate::alloc::memsec::{ use std::sync::OnceLock;
memsec_box as secret_box, memsec_vec as secret_vec, MemsecAllocator as SecretAllocator,
MemsecBox as SecretBox, MemsecVec as SecretVec, use allocator_api2::alloc::{AllocError, Allocator};
}; use memsec::malloc::MallocAllocator;
#[cfg(target_os = "linux")]
use memsec::memfdsec::MemfdSecAllocator;
static ALLOC_TYPE: OnceLock<SecretAllocType> = OnceLock::new();
/// Sets the secret allocation type to use.
/// Intended usage at startup before secret allocation
/// takes place
pub fn set_secret_alloc_type(alloc_type: SecretAllocType) {
ALLOC_TYPE.set(alloc_type).unwrap();
}
pub fn get_or_init_secret_alloc_type(alloc_type: SecretAllocType) -> SecretAllocType {
*ALLOC_TYPE.get_or_init(|| alloc_type)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SecretAllocType {
MemsecMalloc,
#[cfg(target_os = "linux")]
MemsecMemfdSec,
}
pub struct SecretAlloc {
alloc_type: SecretAllocType,
}
impl Default for SecretAlloc {
fn default() -> Self {
Self {
alloc_type: *ALLOC_TYPE.get().expect(
"Secret security policy not specified. \
Run the specifying policy function in \
rosenpass_secret_memory::policy or set a \
custom policy by initializing \
rosenpass_secret_memory::alloc::ALLOC_TYPE \
before using secrets",
),
}
}
}
unsafe impl Allocator for SecretAlloc {
fn allocate(
&self,
layout: std::alloc::Layout,
) -> Result<std::ptr::NonNull<[u8]>, allocator_api2::alloc::AllocError> {
match self.alloc_type {
SecretAllocType::MemsecMalloc => MallocAllocator::default().allocate(layout),
#[cfg(target_os = "linux")]
SecretAllocType::MemsecMemfdSec => MemfdSecAllocator::default().allocate(layout),
}
}
unsafe fn deallocate(&self, ptr: std::ptr::NonNull<u8>, layout: std::alloc::Layout) {
match self.alloc_type {
SecretAllocType::MemsecMalloc => MallocAllocator::default().deallocate(ptr, layout),
#[cfg(target_os = "linux")]
SecretAllocType::MemsecMemfdSec => MemfdSecAllocator::default().deallocate(ptr, layout),
}
}
}
pub type SecretBox<T> = allocator_api2::boxed::Box<T, SecretAlloc>;
/// A vector backed by the memsec allocator
pub type SecretVec<T> = allocator_api2::vec::Vec<T, SecretAlloc>;
pub fn secret_box_try<T>(x: T) -> Result<SecretBox<T>, AllocError> {
SecretBox::<T>::try_new_in(x, SecretAlloc::default())
}
pub fn secret_box<T>(x: T) -> SecretBox<T> {
SecretBox::<T>::new_in(x, SecretAlloc::default())
}
pub fn secret_vec<T>() -> SecretVec<T> {
SecretVec::<T>::new_in(SecretAlloc::default())
}

View File

@@ -9,3 +9,6 @@ pub use crate::public::Public;
mod secret; mod secret;
pub use crate::secret::Secret; pub use crate::secret::Secret;
pub mod policy;
pub use crate::policy::*;

View File

@@ -0,0 +1,82 @@
pub fn secret_policy_try_use_memfd_secrets() {
let alloc_type = {
#[cfg(target_os = "linux")]
{
if crate::alloc::memsec::memfdsec::memfdsec_box_try(0u8).is_ok() {
crate::alloc::SecretAllocType::MemsecMemfdSec
} else {
crate::alloc::SecretAllocType::MemsecMalloc
}
}
#[cfg(not(target_os = "linux"))]
{
crate::alloc::SecretAllocType::MemsecMalloc
}
};
assert_eq!(
alloc_type,
crate::alloc::get_or_init_secret_alloc_type(alloc_type)
);
log::info!("Secrets will be allocated using {:?}", alloc_type);
}
#[cfg(target_os = "linux")]
pub fn secret_policy_use_only_memfd_secrets() {
let alloc_type = crate::alloc::SecretAllocType::MemsecMemfdSec;
assert_eq!(
alloc_type,
crate::alloc::get_or_init_secret_alloc_type(alloc_type)
);
log::info!("Secrets will be allocated using {:?}", alloc_type);
}
pub fn secret_policy_use_only_malloc_secrets() {
let alloc_type = crate::alloc::SecretAllocType::MemsecMalloc;
assert_eq!(
alloc_type,
crate::alloc::get_or_init_secret_alloc_type(alloc_type)
);
log::info!("Secrets will be allocated using {:?}", alloc_type);
}
pub mod test {
#[macro_export]
macro_rules! test_spawn_process_with_policies {
($body:block, $($f: expr),*) => {
$(
let handle = procspawn::spawn((), |_| {
$f();
$body
});
handle.join().unwrap();
)*
};
}
#[macro_export]
macro_rules! test_spawn_process_provided_policies {
($body: block) => {
$crate::test_spawn_process_with_policies!(
$body,
$crate::policy::secret_policy_try_use_memfd_secrets,
$crate::secret_policy_use_only_malloc_secrets
);
#[cfg(target_os = "linux")]
{
$crate::test_spawn_process_with_policies!(
$body,
$crate::policy::secret_policy_use_only_memfd_secrets
);
}
};
}
}

View File

@@ -321,32 +321,41 @@ impl<const N: usize> StoreSecret for Secret<N> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::test_spawn_process_provided_policies;
use super::*; use super::*;
use std::{fs, os::unix::fs::PermissionsExt}; use std::{fs, os::unix::fs::PermissionsExt};
use tempfile::tempdir; use tempfile::tempdir;
procspawn::enable_test_support!();
/// check that we can alloc using the magic pool /// check that we can alloc using the magic pool
#[test] #[test]
fn secret_memory_pool_take() { fn secret_memory_pool_take() {
test_spawn_process_provided_policies!({
const N: usize = 0x100; const N: usize = 0x100;
let mut pool = SecretMemoryPool::new(); let mut pool = SecretMemoryPool::new();
let secret: ZeroizingSecretBox<[u8; N]> = pool.take(); let secret: ZeroizingSecretBox<[u8; N]> = pool.take();
assert_eq!(secret.as_ref(), &[0; N]); assert_eq!(secret.as_ref(), &[0; N]);
});
} }
/// check that a secret lives, even if its [SecretMemoryPool] is deleted /// check that a secret lives, even if its [SecretMemoryPool] is deleted
#[test] #[test]
fn secret_memory_pool_drop() { fn secret_memory_pool_drop() {
test_spawn_process_provided_policies!({
const N: usize = 0x100; const N: usize = 0x100;
let mut pool = SecretMemoryPool::new(); let mut pool = SecretMemoryPool::new();
let secret: ZeroizingSecretBox<[u8; N]> = pool.take(); let secret: ZeroizingSecretBox<[u8; N]> = pool.take();
std::mem::drop(pool); std::mem::drop(pool);
assert_eq!(secret.as_ref(), &[0; N]); assert_eq!(secret.as_ref(), &[0; N]);
});
} }
/// check that a secret can be reborn, freshly initialized with zero /// check that a secret can be reborn, freshly initialized with zero
#[test] #[test]
fn secret_memory_pool_release() { fn secret_memory_pool_release() {
test_spawn_process_provided_policies!({
const N: usize = 1; const N: usize = 1;
let mut pool = SecretMemoryPool::new(); let mut pool = SecretMemoryPool::new();
let mut secret: ZeroizingSecretBox<[u8; N]> = pool.take(); let mut secret: ZeroizingSecretBox<[u8; N]> = pool.take();
@@ -361,11 +370,13 @@ mod test {
// and that the secret was zeroized // and that the secret was zeroized
assert_eq!(new_secret.as_ref(), &[0; N]); assert_eq!(new_secret.as_ref(), &[0; N]);
});
} }
/// test loading a secret from an example file, and then storing it again in a different file /// test loading a secret from an example file, and then storing it again in a different file
#[test] #[test]
fn test_secret_load_store() { fn test_secret_load_store() {
test_spawn_process_provided_policies!({
const N: usize = 100; const N: usize = 100;
// Generate original random bytes // Generate original random bytes
@@ -396,11 +407,13 @@ mod test {
// Check that the contents of the new file match the original file // Check that the contents of the new file match the original file
assert_eq!(new_file_contents, original_file_contents); assert_eq!(new_file_contents, original_file_contents);
});
} }
/// test loading a base64 encoded secret from an example file, and then storing it again in a different file /// test loading a base64 encoded secret from an example file, and then storing it again in a different file
#[test] #[test]
fn test_secret_load_store_base64() { fn test_secret_load_store_base64() {
test_spawn_process_provided_policies!({
const N: usize = 100; const N: usize = 100;
// Generate original random bytes // Generate original random bytes
let original_bytes: [u8; N] = [rand::random(); N]; let original_bytes: [u8; N] = [rand::random(); N];
@@ -449,5 +462,6 @@ mod test {
//Check new file permissions are secret //Check new file permissions are secret
let metadata = fs::metadata(&new_file).unwrap(); let metadata = fs::metadata(&new_file).unwrap();
assert_eq!(metadata.permissions().mode() & 0o000777, 0o600); assert_eq!(metadata.permissions().mode() & 0o000777, 0o600);
});
} }
} }

View File

@@ -33,6 +33,7 @@ rosenpass-util = { workspace = true }
[dev-dependencies] [dev-dependencies]
rand = {workspace = true} rand = {workspace = true}
procspawn = {workspace = true}
[features] [features]
enable_broker_api=[] enable_broker_api=[]
@@ -43,6 +44,7 @@ path = "src/bin/priviledged.rs"
test = false test = false
doc = false doc = false
required-features=["enable_broker_api"] required-features=["enable_broker_api"]
cfg = { target_os = "linux" }
[[bin]] [[bin]]
name = "rosenpass-wireguard-broker-socket-handler" name = "rosenpass-wireguard-broker-socket-handler"
@@ -50,3 +52,4 @@ test = false
path = "src/bin/socket_handler.rs" path = "src/bin/socket_handler.rs"
doc = false doc = false
required-features=["enable_broker_api"] required-features=["enable_broker_api"]
cfg = { target_os = "linux" }

View File

@@ -1,3 +1,13 @@
fn main() {
#[cfg(target_os = "linux")]
linux::main().unwrap();
#[cfg(not(target_os = "linux"))]
panic!("This binary is only supported on Linux");
}
#[cfg(target_os = "linux")]
pub mod linux {
use std::io::{stdin, stdout, Read, Write}; use std::io::{stdin, stdout, Read, Write};
use std::result::Result; use std::result::Result;
@@ -17,7 +27,7 @@ pub enum BrokerAppError {
OversizedMessage(u64), OversizedMessage(u64),
} }
fn main() -> Result<(), BrokerAppError> { pub fn main() -> Result<(), BrokerAppError> {
let mut broker = BrokerServer::new(wg::NetlinkWireGuardBroker::new()?); let mut broker = BrokerServer::new(wg::NetlinkWireGuardBroker::new()?);
let mut stdin = stdin().lock(); let mut stdin = stdin().lock();
@@ -54,3 +64,4 @@ fn main() -> Result<(), BrokerAppError> {
stdout.flush()?; stdout.flush()?;
} }
} }
}

View File

@@ -1,6 +1,6 @@
#[cfg(feature = "enable_broker_api")] #[cfg(feature = "enable_broker_api")]
pub mod mio_client; pub mod mio_client;
#[cfg(feature = "enable_broker_api")] #[cfg(all(feature = "enable_broker_api", target_os = "linux"))]
pub mod netlink; pub mod netlink;
pub mod native_unix; pub mod native_unix;

View File

@@ -1,3 +1,5 @@
#![cfg(target_os = "linux")]
use std::fmt::Debug; use std::fmt::Debug;
use wireguard_uapi::linux as wg; use wireguard_uapi::linux as wg;
@@ -77,7 +79,7 @@ impl WireGuardBroker for NetlinkWireGuardBroker {
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> { fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
let config: NetworkBrokerConfig = config let config: NetworkBrokerConfig = config
.try_into() .try_into()
.map_err(|e| SetPskError::NoSuchInterface)?; .map_err(|_e| SetPskError::NoSuchInterface)?;
// Ensure that the peer exists by querying the device configuration // Ensure that the peer exists by querying the device configuration
// TODO: Use InvalidInterfaceError // TODO: Use InvalidInterfaceError

View File

@@ -53,10 +53,15 @@ mod integration_tests {
} }
} }
procspawn::enable_test_support!();
#[test] #[test]
fn test_psk_exchanges() { fn test_psk_exchanges() {
const TEST_RUNS: usize = 100; const TEST_RUNS: usize = 100;
use rosenpass_secret_memory::test_spawn_process_provided_policies;
test_spawn_process_provided_policies!({
let server_broker_inner = Arc::new(Mutex::new(MockServerBrokerInner::default())); let server_broker_inner = Arc::new(Mutex::new(MockServerBrokerInner::default()));
// Create a mock BrokerServer // Create a mock BrokerServer
let server_broker = MockServerBroker::new(server_broker_inner.clone()); let server_broker = MockServerBroker::new(server_broker_inner.clone());
@@ -130,5 +135,6 @@ mod integration_tests {
} }
} }
handle.join().unwrap().unwrap(); handle.join().unwrap().unwrap();
});
} }
} }