Compare commits

..

1 Commits

Author SHA1 Message Date
Karolin Varner
64c2cf60fe chore: DOS protection and identity hiding symbolic models
Starting points for actually integrating these models in a
comprehensive, reliable way.
2024-06-07 21:04:14 +02:00
40 changed files with 556 additions and 1306 deletions

View File

@@ -1,33 +0,0 @@
#!/bin/bash
iterations=$1
sleep_time=$2
PWD=$(pwd)
EXEC=$PWD/target/release/rosenpass
LOGS=$PWD/output/logs
mkdir -p output/logs
run_command() {
local file=$1
local log_file="$2"
($EXEC exchange-config $file 2>&1 | sed "s/^/[$2] /" | tee -a $log_file) &
echo $!
}
pids=()
(cd output/dut && run_command "configs/dut-$iterations.toml" "dut.log") & piddut=$!
for (( x=0; x<$iterations; x++ )); do
(cd output/ate && run_command "configs/ate-$x.toml" "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

View File

@@ -176,12 +176,8 @@ 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_malloc -- -max_total_time=5 cargo fuzz run fuzz_box_secret_alloc -- -max_total_time=5
cargo fuzz run fuzz_box_secret_alloc_memfdsec -- -max_total_time=5 cargo fuzz run fuzz_vec_secret_alloc -- -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

View File

@@ -1,19 +0,0 @@
name: QC
on:
pull_request:
push:
branches: [main]
permissions:
checks: write
contents: read
jobs:
multi-peer:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: cargo build --bin rosenpass --release
- run: python misc/generate_configs.py
- run: chmod +x .ci/run-regression.sh
- run: .ci/run-regression.sh 100 20

2
.gitignore vendored
View File

@@ -20,5 +20,3 @@ _markdown_*
**/result **/result
**/result-* **/result-*
.direnv .direnv
/output

487
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,10 +12,14 @@ members = [
"fuzz", "fuzz",
"secret-memory", "secret-memory",
"rp", "rp",
"wireguard-broker", "wireguard-broker"
] ]
default-members = ["rosenpass", "rp", "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`
@@ -41,24 +45,18 @@ 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 = { git="https://github.com/rosenpass/memsec.git" ,rev="aceb9baee8aec6844125bd6612f92e9a281373df", features = [ "alloc_ext", ] } memsec = "0.6.3"
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.7", features = ["derive"] } clap = { version = "4.5.6", 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 = [ oqs-sys = { version = "0.9.1", default-features = false, features = ['classic_mceliece', 'kyber'] }
'classic_mceliece',
'kyber',
] }
blake2 = "0.10.6" blake2 = "0.10.6"
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ chacha20poly1305 = { version = "0.10.1", default-features = false, features = [ "std", "heapless" ] }
"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"
@@ -67,14 +65,12 @@ postcard= {version = "1.0.8", features = ["alloc"]}
#Dev dependencies #Dev dependencies
serial_test = "3.1.1" serial_test = "3.1.1"
tempfile = "3" tempfile="3"
stacker = "0.1.15" stacker = "0.1.15"
libfuzzer-sys = "0.4" 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 = "3.0.0"

View File

@@ -3,12 +3,33 @@
#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,6 +10,26 @@
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,6 +88,18 @@ 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,23 +41,32 @@ 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). )
event ConsumeBiscuit(Atom, kem_sk, kem_pk, Atom). KEM_EV(event Oinit_conf_KemUse(SessionId, SessionId, Atom).)
let Oinit_conf() = #ifdef KEM_EVENTS
in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic)); restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
#if RANDOMIZED_CALL_IDS event(Oinit_conf_KemUse(sidi, sidr, ad1)) && event(Oinit_conf_KemUse(sidi, sidr, ad2))
new call:Atom; ==> ad1 = ad2.
#else
call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic);
#endif #endif
event ConsumeBiscuit(Atom, kem_sk, kem_pk, Atom).
fun Ccookie(key, bits) : Atom[data].
let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t, call:Atom) =
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); )
@@ -73,10 +82,20 @@ let Oinit_conf() =
#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"
@@ -85,27 +104,56 @@ 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) =
in(C, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth))); KEM_EV(event Oresp_hello_KemUse(SessionId, SessionId, Atom).)
rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth); #ifdef KEM_EVENTS
/* try */ let ic = ( restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
ck_ini <- ck; event(Oresp_hello_KemUse(sidi, sidr, ad1)) && event(Oresp_hello_KemUse(sidi, sidr, ad2))
RESPHELLO_CONSUME() ==> ad1 = ad2.
ck_ih <- ck; #endif
INITCONF_PRODUCE()
CK_EV (event OskOresp_hello(ck_ini, ck_ih, osk); ) // TODO: Queries testing that there is no duplication #ifdef COOKIE_EVENTS
MTX_EV( event ICSent(rh, ic, psk, sski, spkr); ) COOKIE_EVENTS(Oresp_hello)
SES_EV( event InitiatorSession(rh, osk); ) #endif
ic let Oresp_hello(HS_DECL_ARGS, C_in:channel, call:Atom) =
/* success */ ) in ( in(C_in, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
out(C, ic) in(C_in, mac2_key:key);
/* fail */ ) else ( rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
#if MESSAGE_TRANSMISSION_EVENTS #ifdef COOKIE_EVENTS
event RHRjct(rh, psk, sski, spkr) msg <- RH2b(rh);
#else
0 COOKIE_PROCESS(Oresp_hello,
#endif
/* try */ let ic = (
ck_ini <- ck;
KEM_EV(event Oresp_hello_KemUse(sidi, sidr, call);)
RESPHELLO_CONSUME()
ck_ih <- ck;
INITCONF_PRODUCE()
CK_EV (event OskOresp_hello(ck_ini, ck_ih, osk); ) // TODO: Queries testing that there is no duplication
MTX_EV( event ICSent(rh, ic, psk, sski, spkr); )
SES_EV( event InitiatorSession(rh, osk); )
ic
/* success */ ) in (
icbits <- IC2b(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 (
#if MESSAGE_TRANSMISSION_EVENTS
event RHRjct(rh, psk, sski, spkr)
#else
0
#endif
)
#ifdef COOKIE_EVENTS
)
#else
.
#endif #endif
).
// TODO: Restriction: Biscuit no invalidation // TODO: Restriction: Biscuit no invalidation
@@ -116,24 +164,33 @@ 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).
let Oinit_hello() = KEM_EV(event Oinit_hello_KemUse(SessionId, SessionId, Atom).)
in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih));
#if RANDOMIZED_CALL_IDS #ifdef KEM_EVENTS
new call:Atom; restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
#else event(Oinit_hello_KemUse(sidi, sidr, ad1)) && event(Oinit_hello_KemUse(sidi, sidr, ad2))
call <- Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih); ==> ad1 = ad2.
#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;
epti <- rng_key(setup_seed(Septi)); // RHR4
spti <- rng_key(setup_seed(Sspti)); // RHR5
event ConsumeBn(biscuit_no, sskm, spkt, call); event ConsumeBn(biscuit_no, sskm, spkt, call);
event ConsumeSidr(sidr, call); event ConsumeSidr(sidr, call);
epti <- rng_key(setup_seed(Septi)); // RHR4
spti <- rng_key(setup_seed(Sspti)); // RHR5
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()
@@ -141,7 +198,14 @@ let Oinit_hello() =
MTX_EV( event RHSent(ih, rh, psk, sskr, spki); ) MTX_EV( event RHSent(ih, rh, psk, sskr, spki); )
rh rh
/* success */ ) in ( /* success */ ) in (
out(C, rh) rhbits <- RH2b(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)
@@ -150,6 +214,18 @@ let Oinit_hello() =
#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.
@@ -166,27 +242,55 @@ 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). )
event ConsumeSidi(SessionId, Atom). KEM_EV(event Oinitiator_inner_KemUse(SessionId, SessionId, Atom).)
let Oinitiator() =
in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr)); #ifdef KEM_EVENTS
#if RANDOMIZED_CALL_IDS restriction sidi:SessionId, sidr:SessionId, ad1:Atom, ad2:Atom;
new call:Atom; event(Oinitiator_inner_KemUse(sidi, sidr, ad1)) && event(Oinitiator_inner_KemUse(sidi, sidr, ad2))
#else ==> ad1 = ad2.
call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr);
#endif #endif
SETUP_HANDSHAKE_STATE() event ConsumeSidi(SessionId, Atom).
RNG_KEM_PAIR(eski, epki, Seski) // IHI3
sidr <- sid0; 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) =
sptr <- rng_key(setup_seed(Ssptr)); // IHI5
event ConsumeSidi(sidi, call); SETUP_HANDSHAKE_STATE()
event ConsumeSeed(Sptr, setup_seed(Ssptr), call); sidr <- sid0;
event ConsumeSeed(Eski, setup_seed(Seski), call);
INITHELLO_PRODUCE() KEM_EV(event Oinitiator_inner_KemUse(sidi, sidr, call);)
CK_EV( event OskOinitiator_ck(ck); )
CK_EV( event OskOinitiator(ck, psk, sski, spkr, sptr); ) RNG_KEM_PAIR(eski, epki, Seski) // IHI3
MTX_EV( event IHSent(ih, psk, sski, spkr); ) sptr <- rng_key(setup_seed(Ssptr)); // IHI5
out(C, ih); event ConsumeSidi(sidi, call);
Oresp_hello(HS_PASS_ARGS). 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() =
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);
Oinitiator_inner(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr, last_cookie, C, call).
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))
@@ -207,21 +311,3 @@ 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,6 +2,26 @@
#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
@@ -11,6 +31,8 @@ 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 */ \
@@ -41,7 +63,9 @@ fun RespHello(
bits // auth bits // auth
) : RespHello_t [data]. ) : RespHello_t [data].
#define RESPHELLO_PRODUCE() \ fun RH2b(RespHello_t) : bitstring [typeConverter].
#define RESPHELLO_PRODUCE() \
/* not handled here */ /* RHR1 */ \ /* not handled here */ /* RHR1 */ \
MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \ MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \
ENCAPS_AND_MIX(ecti, epki, epti) /* RHR4 */ \ ENCAPS_AND_MIX(ecti, epki, epti) /* RHR4 */ \
@@ -67,13 +91,14 @@ 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,37 +48,13 @@ test = false
doc = false doc = false
[[bin]] [[bin]]
name = "fuzz_box_secret_alloc_malloc" name = "fuzz_box_secret_alloc"
path = "fuzz_targets/box_secret_alloc_malloc.rs" path = "fuzz_targets/box_secret_alloc.rs"
test = false test = false
doc = false doc = false
[[bin]] [[bin]]
name = "fuzz_vec_secret_alloc_malloc" name = "fuzz_vec_secret_alloc"
path = "fuzz_targets/vec_secret_alloc_malloc.rs" path = "fuzz_targets/vec_secret_alloc.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

@@ -2,12 +2,7 @@
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

@@ -1,12 +0,0 @@
#![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

@@ -1,13 +0,0 @@
#![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

@@ -6,13 +6,9 @@ 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

@@ -1,15 +1,9 @@
#![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);
}); });

View File

@@ -1,15 +0,0 @@
#![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

@@ -1,15 +0,0 @@
#![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,40 +0,0 @@
# 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.

View File

@@ -1,105 +0,0 @@
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)

View File

@@ -49,8 +49,6 @@ 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,7 +5,6 @@ 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,
@@ -57,7 +56,6 @@ 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,9 +3,6 @@ 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,
@@ -157,13 +154,6 @@ 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,16 +19,12 @@
//! [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());
@@ -2149,7 +2145,6 @@ 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>);
@@ -2171,21 +2166,7 @@ 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.
/// ///
@@ -2201,8 +2182,6 @@ 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>;
@@ -2273,10 +2252,7 @@ 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();
@@ -2320,7 +2296,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)
@@ -2334,10 +2310,7 @@ 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();
@@ -2382,7 +2355,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)
@@ -2395,7 +2368,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)
@@ -2409,10 +2382,7 @@ 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();
@@ -2506,10 +2476,7 @@ 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::Parser; use clap::{builder::Str, 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,7 +275,6 @@ 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,6 +37,3 @@ 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,7 +102,6 @@ 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;
@@ -111,8 +110,7 @@ mod tests {
use crate::key::{genkey, pubkey, WG_B64_LEN}; use crate::key::{genkey, pubkey, WG_B64_LEN};
#[test] #[test]
fn test_key_loopback() { fn it_works() {
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,7 +3,6 @@ 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;
@@ -11,11 +10,6 @@ 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,4 +23,3 @@ 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,41 +4,37 @@ 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 MallocAllocatorContents; struct MemsecAllocatorContents;
/// Memory allocation using using the memsec crate /// Memory allocation using using the memsec crate
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default)]
pub struct MallocAllocator { pub struct MemsecAllocator {
_dummy_private_data: MallocAllocatorContents, _dummy_private_data: MemsecAllocatorContents,
} }
/// A box backed by the memsec allocator /// A box backed by the memsec allocator
pub type MallocBox<T> = allocator_api2::boxed::Box<T, MallocAllocator>; pub type MemsecBox<T> = allocator_api2::boxed::Box<T, MemsecAllocator>;
/// A vector backed by the memsec allocator /// A vector backed by the memsec allocator
pub type MallocVec<T> = allocator_api2::vec::Vec<T, MallocAllocator>; pub type MemsecVec<T> = allocator_api2::vec::Vec<T, MemsecAllocator>;
pub fn malloc_box_try<T>(x: T) -> Result<MallocBox<T>, AllocError> { pub fn memsec_box<T>(x: T) -> MemsecBox<T> {
MallocBox::<T>::try_new_in(x, MallocAllocator::new()) MemsecBox::<T>::new_in(x, MemsecAllocator::new())
} }
pub fn malloc_box<T>(x: T) -> MallocBox<T> { pub fn memsec_vec<T>() -> MemsecVec<T> {
MallocBox::<T>::new_in(x, MallocAllocator::new()) MemsecVec::<T>::new_in(MemsecAllocator::new())
} }
pub fn malloc_vec<T>() -> MallocVec<T> { impl MemsecAllocator {
MallocVec::<T>::new_in(MallocAllocator::new())
}
impl MallocAllocator {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
_dummy_private_data: MallocAllocatorContents, _dummy_private_data: MemsecAllocatorContents,
} }
} }
} }
unsafe impl Allocator for MallocAllocator { unsafe impl Allocator for MemsecAllocator {
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()) };
@@ -52,8 +48,8 @@ unsafe impl Allocator for MallocAllocator {
// 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 malloc-based memsec returned allocation \ log::error!("Allocation {layout:?} was requested but memsec returned allocation \
with offset {off} from the requested alignment. Malloc always allocates values \ with offset {off} from the requested alignment. Memsec 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) };
@@ -70,7 +66,7 @@ unsafe impl Allocator for MallocAllocator {
} }
} }
impl fmt::Debug for MallocAllocator { impl fmt::Debug for MemsecAllocator {
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>")
} }
@@ -82,21 +78,21 @@ mod test {
use super::*; use super::*;
make_test! { test_sizes(MallocAllocator::new()) } make_test! { test_sizes(MemsecAllocator::new()) }
make_test! { test_vec(MallocAllocator::new()) } make_test! { test_vec(MemsecAllocator::new()) }
make_test! { test_many_boxes(MallocAllocator::new()) } make_test! { test_many_boxes(MemsecAllocator::new()) }
#[test] #[test]
fn malloc_allocation() { fn memsec_allocation() {
let alloc = MallocAllocator::new(); let alloc = MemsecAllocator::new();
malloc_allocation_impl::<0>(&alloc); memsec_allocation_impl::<0>(&alloc);
malloc_allocation_impl::<7>(&alloc); memsec_allocation_impl::<7>(&alloc);
malloc_allocation_impl::<8>(&alloc); memsec_allocation_impl::<8>(&alloc);
malloc_allocation_impl::<64>(&alloc); memsec_allocation_impl::<64>(&alloc);
malloc_allocation_impl::<999>(&alloc); memsec_allocation_impl::<999>(&alloc);
} }
fn malloc_allocation_impl<const N: usize>(alloc: &MallocAllocator) { fn memsec_allocation_impl<const N: usize>(alloc: &MemsecAllocator) {
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

@@ -1,112 +0,0 @@
#![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

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

View File

@@ -1,86 +1,6 @@
pub mod memsec; pub mod memsec;
use std::sync::OnceLock; pub use crate::alloc::memsec::{
memsec_box as secret_box, memsec_vec as secret_vec, MemsecAllocator as SecretAllocator,
use allocator_api2::alloc::{AllocError, Allocator}; MemsecBox as SecretBox, MemsecVec as SecretVec,
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,6 +9,3 @@ 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

@@ -1,82 +0,0 @@
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,147 +321,133 @@ 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(); let old_secret_ptr = secret.as_ref().as_ptr();
let old_secret_ptr = secret.as_ref().as_ptr();
secret.as_mut()[0] = 0x13; secret.as_mut()[0] = 0x13;
pool.release(secret); pool.release(secret);
// now check that we get the same ptr // now check that we get the same ptr
let new_secret: ZeroizingSecretBox<[u8; N]> = pool.take(); let new_secret: ZeroizingSecretBox<[u8; N]> = pool.take();
assert_eq!(old_secret_ptr, new_secret.as_ref().as_ptr()); assert_eq!(old_secret_ptr, new_secret.as_ref().as_ptr());
// 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
let original_bytes: [u8; N] = [rand::random(); N]; let original_bytes: [u8; N] = [rand::random(); N];
// Create a temporary directory // Create a temporary directory
let temp_dir = tempdir().unwrap(); let temp_dir = tempdir().unwrap();
// Store the original secret to an example file in the temporary directory // Store the original secret to an example file in the temporary directory
let example_file = temp_dir.path().join("example_file"); let example_file = temp_dir.path().join("example_file");
std::fs::write(example_file.clone(), &original_bytes).unwrap(); std::fs::write(example_file.clone(), &original_bytes).unwrap();
// Load the secret from the example file // Load the secret from the example file
let loaded_secret = Secret::load(&example_file).unwrap(); let loaded_secret = Secret::load(&example_file).unwrap();
// Check that the loaded secret matches the original bytes // Check that the loaded secret matches the original bytes
assert_eq!(loaded_secret.secret(), &original_bytes); assert_eq!(loaded_secret.secret(), &original_bytes);
// Store the loaded secret to a different file in the temporary directory // Store the loaded secret to a different file in the temporary directory
let new_file = temp_dir.path().join("new_file"); let new_file = temp_dir.path().join("new_file");
loaded_secret.store(&new_file).unwrap(); loaded_secret.store(&new_file).unwrap();
// Read the contents of the new file // Read the contents of the new file
let new_file_contents = fs::read(&new_file).unwrap(); let new_file_contents = fs::read(&new_file).unwrap();
// Read the contents of the original file // Read the contents of the original file
let original_file_contents = fs::read(&example_file).unwrap(); let original_file_contents = fs::read(&example_file).unwrap();
// 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]; // Create a temporary directory
// Create a temporary directory let temp_dir = tempdir().unwrap();
let temp_dir = tempdir().unwrap(); let example_file = temp_dir.path().join("example_file");
let example_file = temp_dir.path().join("example_file"); let mut encoded_secret = [0u8; N * 2];
let mut encoded_secret = [0u8; N * 2]; let encoded_secret = b64_encode(&original_bytes, &mut encoded_secret).unwrap();
let encoded_secret = b64_encode(&original_bytes, &mut encoded_secret).unwrap();
std::fs::write(&example_file, encoded_secret).unwrap(); std::fs::write(&example_file, encoded_secret).unwrap();
// Load the secret from the example file // Load the secret from the example file
let loaded_secret = Secret::load_b64::<{ N * 2 }, _>(&example_file).unwrap(); let loaded_secret = Secret::load_b64::<{ N * 2 }, _>(&example_file).unwrap();
// Check that the loaded secret matches the original bytes // Check that the loaded secret matches the original bytes
assert_eq!(loaded_secret.secret(), &original_bytes); assert_eq!(loaded_secret.secret(), &original_bytes);
// Store the loaded secret to a different file in the temporary directory // Store the loaded secret to a different file in the temporary directory
let new_file = temp_dir.path().join("new_file"); let new_file = temp_dir.path().join("new_file");
loaded_secret.store_b64::<{ N * 2 }, _>(&new_file).unwrap(); loaded_secret.store_b64::<{ N * 2 }, _>(&new_file).unwrap();
// Read the contents of the new file // Read the contents of the new file
let new_file_contents = fs::read(&new_file).unwrap(); let new_file_contents = fs::read(&new_file).unwrap();
// Read the contents of the original file // Read the contents of the original file
let original_file_contents = fs::read(&example_file).unwrap(); let original_file_contents = fs::read(&example_file).unwrap();
// 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);
//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);
// Store the loaded secret to a different file in the temporary directory for a second time // Store the loaded secret to a different file in the temporary directory for a second time
let new_file = temp_dir.path().join("new_file_writer"); let new_file = temp_dir.path().join("new_file_writer");
let new_file_writer = fopen_w(new_file.clone(), Visibility::Secret).unwrap(); let new_file_writer = fopen_w(new_file.clone(), Visibility::Secret).unwrap();
loaded_secret loaded_secret
.store_b64_writer::<{ N * 2 }, _>(&new_file_writer) .store_b64_writer::<{ N * 2 }, _>(&new_file_writer)
.unwrap(); .unwrap();
// Read the contents of the new file // Read the contents of the new file
let new_file_contents = fs::read(&new_file).unwrap(); let new_file_contents = fs::read(&new_file).unwrap();
// Read the contents of the original file // Read the contents of the original file
let original_file_contents = fs::read(&example_file).unwrap(); let original_file_contents = fs::read(&example_file).unwrap();
// 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);
//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,7 +33,6 @@ 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=[]

View File

@@ -77,7 +77,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,88 +53,82 @@ 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; let server_broker_inner = Arc::new(Mutex::new(MockServerBrokerInner::default()));
// Create a mock BrokerServer
let server_broker = MockServerBroker::new(server_broker_inner.clone());
test_spawn_process_provided_policies!({ let mut server = BrokerServer::<SetPskError, MockServerBroker>::new(server_broker);
let server_broker_inner = Arc::new(Mutex::new(MockServerBrokerInner::default()));
// Create a mock BrokerServer
let server_broker = MockServerBroker::new(server_broker_inner.clone());
let mut server = BrokerServer::<SetPskError, MockServerBroker>::new(server_broker); let (client_socket, mut server_socket) = mio::net::UnixStream::pair().unwrap();
let (client_socket, mut server_socket) = mio::net::UnixStream::pair().unwrap();
// Spawn a new thread to connect to the unix socket
let handle = std::thread::spawn(move || {
for _ in 0..TEST_RUNS {
// Wait for 8 bytes of length to come in
let mut length_buffer = [0; 8];
while let Err(_err) = server_socket.read_exact(&mut length_buffer) {}
let length = u64::from_le_bytes(length_buffer) as usize;
// Read the amount of length bytes into a buffer
let mut data_buffer = [0; REQUEST_MSG_BUFFER_SIZE];
while let Err(_err) = server_socket.read_exact(&mut data_buffer[0..length]) {}
let mut response = [0; RESPONSE_MSG_BUFFER_SIZE];
server.handle_message(&data_buffer[0..length], &mut response)?;
}
Ok::<(), BrokerServerError>(())
});
// Create a MioBrokerClient and send a psk
let mut client = MioBrokerClient::new(client_socket);
// Spawn a new thread to connect to the unix socket
let handle = std::thread::spawn(move || {
for _ in 0..TEST_RUNS { for _ in 0..TEST_RUNS {
//Create psk of random 32 bytes // Wait for 8 bytes of length to come in
let psk = Secret::random(); let mut length_buffer = [0; 8];
let peer_id = Public::random();
let interface = "test";
let config = SerializedBrokerConfig {
psk: &psk,
peer_id: &peer_id,
interface: interface.as_bytes(),
additional_params: &[],
};
client.set_psk(config).unwrap();
//Sleep for a while to allow the server to process the message while let Err(_err) = server_socket.read_exact(&mut length_buffer) {}
std::thread::sleep(std::time::Duration::from_millis(
rand::thread_rng().gen_range(100..500),
));
let psk = psk.secret().to_owned(); let length = u64::from_le_bytes(length_buffer) as usize;
loop { // Read the amount of length bytes into a buffer
let mut lock = server_broker_inner.try_lock(); let mut data_buffer = [0; REQUEST_MSG_BUFFER_SIZE];
while let Err(_err) = server_socket.read_exact(&mut data_buffer[0..length]) {}
if let Ok(ref mut inner) = lock { let mut response = [0; RESPONSE_MSG_BUFFER_SIZE];
// Check if the psk is received by the server server.handle_message(&data_buffer[0..length], &mut response)?;
let received_psk = &inner.psk; }
assert_eq!( Ok::<(), BrokerServerError>(())
received_psk.as_ref().map(|psk| psk.secret().to_owned()), });
Some(psk)
);
let recieved_peer_id = inner.peer_id; // Create a MioBrokerClient and send a psk
assert_eq!(recieved_peer_id, Some(peer_id)); let mut client = MioBrokerClient::new(client_socket);
let target_interface = &inner.interface; for _ in 0..TEST_RUNS {
assert_eq!(target_interface.as_deref(), Some(interface)); //Create psk of random 32 bytes
let psk = Secret::random();
let peer_id = Public::random();
let interface = "test";
let config = SerializedBrokerConfig {
psk: &psk,
peer_id: &peer_id,
interface: interface.as_bytes(),
additional_params: &[],
};
client.set_psk(config).unwrap();
break; //Sleep for a while to allow the server to process the message
} std::thread::sleep(std::time::Duration::from_millis(
rand::thread_rng().gen_range(100..500),
));
let psk = psk.secret().to_owned();
loop {
let mut lock = server_broker_inner.try_lock();
if let Ok(ref mut inner) = lock {
// Check if the psk is received by the server
let received_psk = &inner.psk;
assert_eq!(
received_psk.as_ref().map(|psk| psk.secret().to_owned()),
Some(psk)
);
let recieved_peer_id = inner.peer_id;
assert_eq!(recieved_peer_id, Some(peer_id));
let target_interface = &inner.interface;
assert_eq!(target_interface.as_deref(), Some(interface));
break;
} }
} }
handle.join().unwrap().unwrap(); }
}); handle.join().unwrap().unwrap();
} }
} }