mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 06:23:08 -08:00
Compare commits
14 Commits
dev/improv
...
dev/old-di
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c29b1083f3 | ||
|
|
46cbda08c4 | ||
|
|
25d7527c74 | ||
|
|
7c83e244f9 | ||
|
|
eb76179dc4 | ||
|
|
4c872ec855 | ||
|
|
70d136dbd3 | ||
|
|
e793168f27 | ||
|
|
df8990f4f8 | ||
|
|
2c4ab16eb7 | ||
|
|
0cdd06031b | ||
|
|
8027ccbf38 | ||
|
|
d8033968fd | ||
|
|
a7439aedbb |
8
.github/workflows/qc.yaml
vendored
8
.github/workflows/qc.yaml
vendored
@@ -25,6 +25,14 @@ jobs:
|
|||||||
- name: Run ShellCheck
|
- name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
|
|
||||||
|
rustfmt:
|
||||||
|
name: Rust Format
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Run Rust Formatting Script
|
||||||
|
run: bash format_rust_code.sh --mode check
|
||||||
|
|
||||||
cargo-audit:
|
cargo-audit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -1176,13 +1176,6 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rosenpass-log"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rosenpass-oqs"
|
name = "rosenpass-oqs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ members = [
|
|||||||
"fuzz",
|
"fuzz",
|
||||||
"secret-memory",
|
"secret-memory",
|
||||||
"lenses",
|
"lenses",
|
||||||
"rosenpass-log",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = [
|
default-members = [
|
||||||
|
|||||||
121
analysis/03_identity_hiding.entry.mpv
Normal file
121
analysis/03_identity_hiding.entry.mpv
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
This identity hiding process tests whether the rosenpass protocol is able to protect the identity of an initiator or responder.
|
||||||
|
The participants in the test are trusted initiators, trusted responders and compromised initiators and responders.
|
||||||
|
The test consists of two phases. In the first phase all of the participants can communicate with each other using the rosenpass protocol.
|
||||||
|
An attacker observes the first phase and is able to intercept and modify messages and choose participants to communicate with each other
|
||||||
|
|
||||||
|
In the second phase if the anonymity of an initiator is being tested then one of two trusted initiators is chosen.
|
||||||
|
The chosen initiator communicates directly with a trusted responder.
|
||||||
|
If an attacker can determine which initiator was chosen then the anonymity of the initiator has been compromised.
|
||||||
|
Otherwise the protocol has successfully protected the initiators’ identity.
|
||||||
|
|
||||||
|
If the anonymity of a responder is being tested then one of two trusted responders is chosen instead.
|
||||||
|
Then an initiator communicates directly with the chosen responder.
|
||||||
|
If an attacker can determine which responder was chosen then the anonymity of the responder is compromised.
|
||||||
|
Otherwise the protocol successfully protects the identity of a responder.
|
||||||
|
|
||||||
|
The Proverif code treats the public key as synonymous with identity.
|
||||||
|
In the above test when a responder or initiator is chosen what is actually chosen is the public/private key pair to use for communication.
|
||||||
|
Traditionally when a responder or initiator is chosen they would be chosen randomly.
|
||||||
|
The way Proverif makes a "choice" is by simulating multiple processes, one process per choice
|
||||||
|
Then the processes are compared and if an association between a public key and a process can be made the test fails.
|
||||||
|
As the choice is at least as bad as choosing the worst possible option the credibility of the test is maintained.
|
||||||
|
The drawback is that Proverif is only able to tell if the identity can be brute forced but misses any probabilistic associations.
|
||||||
|
As usual Proverif also assumes perfect encryption and in particular assumes encryption cannot be linked to identity.
|
||||||
|
|
||||||
|
One of the tradeoffs made here is that the choice function in Proverif is slow but this is in favour of being able to write more precise tests.
|
||||||
|
Another issue is the choice function does not work with queries so a test needs to be run for each set of assumptions.
|
||||||
|
In this case the test uses secure rng and a fresh secure biscuit key.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.mpv"
|
||||||
|
|
||||||
|
#define CHAINING_KEY_EVENTS 1
|
||||||
|
#define MESSAGE_TRANSMISSION_EVENTS 1
|
||||||
|
#define SESSION_START_EVENTS 0
|
||||||
|
#define RANDOMIZED_CALL_IDS 0
|
||||||
|
#undef FULL_MODEL
|
||||||
|
#undef SIMPLE_MODEL
|
||||||
|
#define SIMPLE_MODEL 1
|
||||||
|
|
||||||
|
#include "prelude/basic.mpv"
|
||||||
|
#include "crypto/key.mpv"
|
||||||
|
#include "rosenpass/oracles.mpv"
|
||||||
|
#include "crypto/kem.mpv"
|
||||||
|
|
||||||
|
#define INITIATOR_TEST
|
||||||
|
#define NEW_TRUSTED_SEED(name) \
|
||||||
|
new MCAT(name, _secret_seed):seed_prec; \
|
||||||
|
name <- make_trusted_seed(MCAT(name, _secret_seed)); \
|
||||||
|
|
||||||
|
free D:channel [private].
|
||||||
|
free secure_biscuit_no:Atom [private].
|
||||||
|
free secure_sidi,secure_sidr:SessionId [private].
|
||||||
|
free secure_psk:key [private].
|
||||||
|
free initiator1, initiator2:kem_sk_prec.
|
||||||
|
free responder1, responder2:kem_sk_prec.
|
||||||
|
|
||||||
|
let secure_init_hello(initiator: kem_sk_tmpl, sidi : SessionId, psk: key_tmpl, responder: kem_sk_tmpl) =
|
||||||
|
NEW_TRUSTED_SEED(seski_trusted_seed)
|
||||||
|
NEW_TRUSTED_SEED(ssptr_trusted_seed)
|
||||||
|
Oinitiator_inner(sidi, initiator, psk, responder, seski_trusted_seed, ssptr_trusted_seed, D).
|
||||||
|
|
||||||
|
let secure_resp_hello(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, sidr:SessionId, sidi:SessionId, biscuit_no:Atom, psk:key_tmpl) =
|
||||||
|
in(D, Envelope(k, IH2b(InitHello(=sidi, epki, sctr, pidiC, auth))));
|
||||||
|
ih <- InitHello(sidi, epki, sctr, pidiC, auth);
|
||||||
|
NEW_TRUSTED_SEED(septi_trusted_seed)
|
||||||
|
NEW_TRUSTED_SEED(sspti_trusted_seed)
|
||||||
|
Oinit_hello_inner(sidr, biscuit_no, responder, psk, initiator, septi_trusted_seed, sspti_trusted_seed, ih, D).
|
||||||
|
|
||||||
|
let secure_init_conf(initiator: kem_sk_tmpl, responder: kem_sk_tmpl, psk:key_tmpl, sidi:SessionId, sidr:SessionId) =
|
||||||
|
in(D, Envelope(k3, IC2b(InitConf(=sidi, =sidr, biscuit, auth3))));
|
||||||
|
ic <- InitConf(sidi,sidr,biscuit, auth3);
|
||||||
|
NEW_TRUSTED_SEED(seski_trusted_seed)
|
||||||
|
NEW_TRUSTED_SEED(ssptr_trusted_seed)
|
||||||
|
Oinit_conf_inner(initiator, psk, responder, ic).
|
||||||
|
|
||||||
|
let secure_communication(initiator: kem_sk_tmpl, responder:kem_sk_tmpl) =
|
||||||
|
secure_key <- prepare_key(secure_psk);
|
||||||
|
(!secure_init_hello(initiator, secure_sidi, secure_key, responder))
|
||||||
|
| !secure_resp_hello(initiator, responder, secure_sidr, secure_sidi, secure_biscuit_no, secure_key)
|
||||||
|
| !(secure_init_conf(initiator, responder, secure_key, secure_sidi, secure_sidr)).
|
||||||
|
|
||||||
|
let pipeChannel(D:channel, C:channel) =
|
||||||
|
in(D, b:bits);
|
||||||
|
out(C, b).
|
||||||
|
|
||||||
|
fun kem_private(kem_pk): kem_sk
|
||||||
|
reduc forall sk_tmpl:kem_sk;
|
||||||
|
kem_private(kem_pub(sk_tmpl)) = sk_tmpl[private].
|
||||||
|
|
||||||
|
let secretCommunication() =
|
||||||
|
#ifdef INITIATOR_TEST
|
||||||
|
initiator_pk <- choice[setup_kem_pk(make_trusted_kem_sk(initiator1)), setup_kem_pk(make_trusted_kem_sk(initiator2))];
|
||||||
|
initiator_seed <- prepare_kem_sk(kem_private(initiator_pk));
|
||||||
|
#else
|
||||||
|
initiator_seed <- prepare_kem_sk(trusted_kem_sk(initiator1));
|
||||||
|
#endif
|
||||||
|
#ifdef RESPONDER_TEST
|
||||||
|
responder_pk <- choice[setup_kem_pk(make_trusted_kem_sk(responder1)), setup_kem_pk(make_trusted_kem_sk(responder2))];
|
||||||
|
responder_seed <- prepare_kem_sk(kem_private(responder_pk));
|
||||||
|
#else
|
||||||
|
responder_seed <- prepare_kem_sk(trusted_kem_sk(responder1));
|
||||||
|
#endif
|
||||||
|
secure_communication(initiator_seed, responder_seed) | !pipeChannel(D, C).
|
||||||
|
|
||||||
|
let reveal_pks() =
|
||||||
|
out(C, setup_kem_pk(make_trusted_kem_sk(responder1)));
|
||||||
|
out(C, setup_kem_pk(make_trusted_kem_sk(responder2)));
|
||||||
|
out(C, setup_kem_pk(make_trusted_kem_sk(initiator1)));
|
||||||
|
out(C, setup_kem_pk(make_trusted_kem_sk(initiator2))).
|
||||||
|
|
||||||
|
let rosenpass_main2() =
|
||||||
|
REP(INITIATOR_BOUND, Oinitiator)
|
||||||
|
| REP(RESPONDER_BOUND, Oinit_hello)
|
||||||
|
| REP(RESPONDER_BOUND, Oinit_conf).
|
||||||
|
|
||||||
|
let identity_hiding_main() =
|
||||||
|
0 | reveal_pks() | rosenpass_main2() | phase 1; secretCommunication().
|
||||||
|
|
||||||
|
let main = identity_hiding_main.
|
||||||
@@ -47,14 +47,16 @@ 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).
|
event ConsumeBiscuit(Atom, kem_sk, kem_pk, Atom).
|
||||||
let Oinit_conf() =
|
|
||||||
in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic));
|
let Oinit_conf_inner(Ssskm:kem_sk_tmpl, Spsk:key_tmpl, Sspkt:kem_sk_tmpl, ic:InitConf_t) =
|
||||||
#if RANDOMIZED_CALL_IDS
|
#if RANDOMIZED_CALL_IDS
|
||||||
new call:Atom;
|
new call:Atom;
|
||||||
#else
|
#else
|
||||||
call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic);
|
call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SETUP_HANDSHAKE_STATE()
|
SETUP_HANDSHAKE_STATE()
|
||||||
|
|
||||||
eski <- kem_sk0;
|
eski <- kem_sk0;
|
||||||
epki <- kem_pk0;
|
epki <- kem_pk0;
|
||||||
let try_ = (
|
let try_ = (
|
||||||
@@ -73,6 +75,10 @@ let Oinit_conf() =
|
|||||||
#endif
|
#endif
|
||||||
).
|
).
|
||||||
|
|
||||||
|
let Oinit_conf() =
|
||||||
|
in(C, Cinit_conf(Ssskm, Spsk, Sspkt, ic));
|
||||||
|
Oinit_conf_inner(Ssskm, Spsk, Sspkt, ic).
|
||||||
|
|
||||||
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.
|
||||||
@@ -85,8 +91,8 @@ 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) =
|
let Oresp_hello(HS_DECL_ARGS, C_in:channel) =
|
||||||
in(C, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
|
in(C_in, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
|
||||||
rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
|
rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
|
||||||
/* try */ let ic = (
|
/* try */ let ic = (
|
||||||
ck_ini <- ck;
|
ck_ini <- ck;
|
||||||
@@ -98,7 +104,7 @@ let Oresp_hello(HS_DECL_ARGS) =
|
|||||||
SES_EV( event InitiatorSession(rh, osk); )
|
SES_EV( event InitiatorSession(rh, osk); )
|
||||||
ic
|
ic
|
||||||
/* success */ ) in (
|
/* success */ ) in (
|
||||||
out(C, ic)
|
out(C_in, Envelope(create_mac(spkt, IC2b(ic)), IC2b(ic)))
|
||||||
/* fail */ ) else (
|
/* fail */ ) else (
|
||||||
#if MESSAGE_TRANSMISSION_EVENTS
|
#if MESSAGE_TRANSMISSION_EVENTS
|
||||||
event RHRjct(rh, psk, sski, spkr)
|
event RHRjct(rh, psk, sski, spkr)
|
||||||
@@ -116,8 +122,8 @@ 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() =
|
|
||||||
in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih));
|
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, C_out:channel) =
|
||||||
#if RANDOMIZED_CALL_IDS
|
#if RANDOMIZED_CALL_IDS
|
||||||
new call:Atom;
|
new call:Atom;
|
||||||
#else
|
#else
|
||||||
@@ -125,14 +131,19 @@ let Oinit_hello() =
|
|||||||
#endif
|
#endif
|
||||||
// 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);
|
||||||
|
|
||||||
let rh = (
|
let rh = (
|
||||||
INITHELLO_CONSUME()
|
INITHELLO_CONSUME()
|
||||||
ck_ini <- ck;
|
ck_ini <- ck;
|
||||||
@@ -141,7 +152,8 @@ 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)
|
out(C_out, Envelope(create_mac(spkt, RH2b(rh)), RH2b(rh)))
|
||||||
|
|
||||||
/* 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 +162,10 @@ let Oinit_hello() =
|
|||||||
#endif
|
#endif
|
||||||
).
|
).
|
||||||
|
|
||||||
|
let Oinit_hello() =
|
||||||
|
in(C, Cinit_hello(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih));
|
||||||
|
Oinit_hello_inner(sidr, biscuit_no, Ssskm, Spsk, Sspkt, Septi, Sspti, ih, C).
|
||||||
|
|
||||||
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.
|
||||||
@@ -167,26 +183,34 @@ 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).
|
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, C_out:channel) =
|
||||||
|
#if RANDOMIZED_CALL_IDS
|
||||||
|
new call:Atom;
|
||||||
|
#else
|
||||||
|
call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SETUP_HANDSHAKE_STATE()
|
||||||
|
|
||||||
|
sidr <- sid0;
|
||||||
|
|
||||||
|
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, Envelope(create_mac(spkt, IH2b(ih)), IH2b(ih)));
|
||||||
|
Oresp_hello(HS_PASS_ARGS, C_out).
|
||||||
|
|
||||||
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
|
Oinitiator_inner(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr, C).
|
||||||
new call:Atom;
|
|
||||||
#else
|
|
||||||
call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr);
|
|
||||||
#endif
|
|
||||||
SETUP_HANDSHAKE_STATE()
|
|
||||||
RNG_KEM_PAIR(eski, epki, Seski) // IHI3
|
|
||||||
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))
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
#include "crypto/kem.mpv"
|
#include "crypto/kem.mpv"
|
||||||
#include "rosenpass/handshake_state.mpv"
|
#include "rosenpass/handshake_state.mpv"
|
||||||
|
|
||||||
|
fun Envelope(
|
||||||
|
key,
|
||||||
|
bits
|
||||||
|
): bits [data].
|
||||||
|
letfun create_mac(pk:kem_pk, payload:bits) = lprf2(MAC, kem_pk2b(pk), payload).
|
||||||
|
|
||||||
type InitHello_t.
|
type InitHello_t.
|
||||||
fun InitHello(
|
fun InitHello(
|
||||||
SessionId, // sidi
|
SessionId, // sidi
|
||||||
@@ -11,6 +17,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 +49,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,6 +77,8 @@ 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 */ \
|
||||||
|
|||||||
115
format_rust_code.sh
Executable file
115
format_rust_code.sh
Executable file
@@ -0,0 +1,115 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Parse command line options
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--mode)
|
||||||
|
mode="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if mode is specified
|
||||||
|
if [ -z "$mode" ]; then
|
||||||
|
echo "Please specify the mode using --mode option. Valid modes are 'check' and 'fix'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find all Markdown files in the current directory and its subdirectories
|
||||||
|
mapfile -t md_files < <(find . -type f -name "*.md")
|
||||||
|
|
||||||
|
count=0
|
||||||
|
# Iterate through each Markdown file
|
||||||
|
for file in "${md_files[@]}"; do
|
||||||
|
# Use awk to extract Rust code blocks enclosed within triple backticks
|
||||||
|
rust_code_blocks=$(awk '/```rust/{flag=1; next}/```/{flag=0} flag' "$file")
|
||||||
|
|
||||||
|
# Count the number of Rust code blocks
|
||||||
|
num_fences=$(awk '/```rust/{f=1} f{if(/```/){f=0; count++}} END{print count}' "$file")
|
||||||
|
|
||||||
|
if [ -n "$rust_code_blocks" ]; then
|
||||||
|
echo "Processing Rust code in $file"
|
||||||
|
# Iterate through each Rust code block
|
||||||
|
for ((i=1; i <= num_fences ; i++)); do
|
||||||
|
# Extract individual Rust code block using awk
|
||||||
|
current_rust_block=$(awk -v i="$i" '/```rust/{f=1; if (++count == i) next} f&&/```/{f=0;next} f' "$file")
|
||||||
|
# Variable to check if we have added the main function
|
||||||
|
add_main=0
|
||||||
|
# Check if the Rust code block is already inside a function
|
||||||
|
if ! echo "$current_rust_block" | grep -q "fn main()"; then
|
||||||
|
# If not, wrap it in a main function
|
||||||
|
current_rust_block=$'fn main() {\n'"$current_rust_block"$'\n}'
|
||||||
|
add_main=1
|
||||||
|
fi
|
||||||
|
if [ "$mode" == "check" ]; then
|
||||||
|
# Apply changes to the Rust code block
|
||||||
|
formatted_rust_code=$(echo "$current_rust_block" | rustfmt)
|
||||||
|
# Use rustfmt to format the Rust code block, remove first and last lines, and remove the first 4 spaces if added main function
|
||||||
|
if [ "$add_main" == 1 ]; then
|
||||||
|
formatted_rust_code=$(echo "$formatted_rust_code" | sed '1d;$d' | sed 's/^ //')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '1d;')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '$d')
|
||||||
|
fi
|
||||||
|
if [ "$formatted_rust_code" == "$current_rust_block" ]; then
|
||||||
|
echo "No changes needed in Rust code block $i in $file"
|
||||||
|
else
|
||||||
|
echo -e "\nChanges needed in Rust code block $i in $file:\n"
|
||||||
|
echo "$formatted_rust_code"
|
||||||
|
count=+1
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [ "$mode" == "fix" ]; then
|
||||||
|
# Replace current_rust_block with formatted_rust_code in the file
|
||||||
|
formatted_rust_code=$(echo "$current_rust_block" | rustfmt)
|
||||||
|
# Use rustfmt to format the Rust code block, remove first and last lines, and remove the first 4 spaces if added main function
|
||||||
|
if [ "$add_main" == 1 ]; then
|
||||||
|
formatted_rust_code=$(echo "$formatted_rust_code" | sed '1d;$d' | sed 's/^ //')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '1d;')
|
||||||
|
current_rust_block=$(echo "$current_rust_block" | sed '$d')
|
||||||
|
fi
|
||||||
|
# Check if the formatted code is the same as the current Rust code block
|
||||||
|
if [ "$formatted_rust_code" == "$current_rust_block" ]; then
|
||||||
|
echo "No changes needed in Rust code block $i in $file"
|
||||||
|
else
|
||||||
|
echo "Formatting Rust code block $i in $file"
|
||||||
|
# Replace current_rust_block with formatted_rust_code in the file
|
||||||
|
# Use awk to find the line number of the pattern
|
||||||
|
|
||||||
|
start_line=$(grep -n "^\`\`\`rust" "$file" | sed -n "${i}p" | cut -d: -f1)
|
||||||
|
end_line=$(grep -n "^\`\`\`" "$file" | awk -F: -v start_line="$start_line" '$1 > start_line {print $1; exit;}')
|
||||||
|
|
||||||
|
if [ -n "$start_line" ] && [ -n "$end_line" ]; then
|
||||||
|
# Print lines before the Rust code block
|
||||||
|
head -n "$((start_line - 1))" "$file"
|
||||||
|
|
||||||
|
# Print the formatted Rust code block
|
||||||
|
echo "\`\`\`rust"
|
||||||
|
echo "$formatted_rust_code"
|
||||||
|
echo "\`\`\`"
|
||||||
|
|
||||||
|
# Print lines after the Rust code block
|
||||||
|
tail -n +"$((end_line + 1))" "$file"
|
||||||
|
else
|
||||||
|
# Rust code block not found or end line not found
|
||||||
|
cat "$file"
|
||||||
|
fi > tmpfile && mv tmpfile "$file"
|
||||||
|
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Unknown mode: $mode. Valid modes are 'check' and 'fix'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# CI failure if changes are needed
|
||||||
|
if [ $count -gt 0 ]; then
|
||||||
|
echo "CI failed: Changes needed in Rust code blocks."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "rosenpass-log"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log.workspace = true
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
#![allow(unused_macros)]
|
|
||||||
/// Whenever a log event occurs, the cause of the event must be decided on. This cause will then
|
|
||||||
/// be used to decide, if an actual log event is to be cause. The goal is to prevent especially
|
|
||||||
/// external, unautherized entities from causing excessive loggin, which otherwise might open the
|
|
||||||
/// door to MITM attacks
|
|
||||||
pub enum Cause {
|
|
||||||
/// An unauthorized entitiy triggered this event via Network
|
|
||||||
///
|
|
||||||
/// Example: a InitHello message in the rosenpass protocol
|
|
||||||
UnauthorizedNetwork,
|
|
||||||
|
|
||||||
/// An authorized entitity triggered this event via Network
|
|
||||||
///
|
|
||||||
/// Example: a handshake was succesful (which asserts the peer is authorized)
|
|
||||||
AuthorizedNetwork,
|
|
||||||
|
|
||||||
/// A local entity like rosenpassctl triggered this event
|
|
||||||
///
|
|
||||||
/// Example: the broker adds a new peer
|
|
||||||
LocalNetwork,
|
|
||||||
|
|
||||||
/// The user caused this event
|
|
||||||
///
|
|
||||||
/// Examples:
|
|
||||||
/// - The process was started
|
|
||||||
/// - Ctrl+C was used to send sig SIGINT
|
|
||||||
User,
|
|
||||||
|
|
||||||
/// The developer wanted this in the log!
|
|
||||||
Developer,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rational: All events are to be displayed if trace level debugging is configured
|
|
||||||
macro_rules! trace {
|
|
||||||
($cause:expr, $($tail:tt)* ) => {{
|
|
||||||
use crate::Cause::*;
|
|
||||||
match $cause {
|
|
||||||
UnauthorizedNetwork | AuthorizedNetwork | LocalNetwork | User | Developer => {
|
|
||||||
::log::trace!($($tail)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rational: All events are to be displayed if debug level debugging is configured
|
|
||||||
macro_rules! debug {
|
|
||||||
($cause:expr, $($tail:tt)* ) => {{
|
|
||||||
use crate::Cause::*;
|
|
||||||
match $cause {
|
|
||||||
UnauthorizedNetwork | AuthorizedNetwork | LocalNetwork | User | Developer => {
|
|
||||||
::log::debug!($($tail)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rational: Only authorized causes shall be able to emit info messages
|
|
||||||
macro_rules! info {
|
|
||||||
($cause:expr, $($tail:tt)* ) => {{
|
|
||||||
use crate::Cause::*;
|
|
||||||
match $cause {
|
|
||||||
UnauthorizedNetwork => {},
|
|
||||||
AuthorizedNetwork | LocalNetwork | User | Developer => {
|
|
||||||
::log::info!($($tail)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rational: Only authorized causes shall be able to emit info messages
|
|
||||||
macro_rules! warn {
|
|
||||||
($cause:expr, $($tail:tt)* ) => {{
|
|
||||||
use crate::Cause::*;
|
|
||||||
match $cause {
|
|
||||||
UnauthorizedNetwork => {},
|
|
||||||
AuthorizedNetwork | LocalNetwork | User | Developer =>{
|
|
||||||
::log::warn!($($tail)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rational: Only local sources shall be able to cause errors to be displayed
|
|
||||||
macro_rules! error {
|
|
||||||
($cause:expr, $($tail:tt)* ) => {{
|
|
||||||
use crate::Cause::*;
|
|
||||||
match $cause {
|
|
||||||
UnauthorizedNetwork | AuthorizedNetwork => {},
|
|
||||||
LocalNetwork | User | Developer => {
|
|
||||||
::log::error!($($tail)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn expand_all_macros() {
|
|
||||||
use Cause::*;
|
|
||||||
|
|
||||||
trace!(UnauthorizedNetwork, "beep");
|
|
||||||
debug!(UnauthorizedNetwork, "boop");
|
|
||||||
info!(LocalNetwork, "tock");
|
|
||||||
warn!(LocalNetwork, "möp");
|
|
||||||
error!(User, "knirsch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
109
to/README.md
109
to/README.md
@@ -12,15 +12,17 @@ The crate provides chained functions to simplify allocating the destination para
|
|||||||
For now this crate is experimental; patch releases are guaranteed not to contain any breaking changes, but minor releases may.
|
For now this crate is experimental; patch releases are guaranteed not to contain any breaking changes, but minor releases may.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::ops::BitXorAssign;
|
|
||||||
use rosenpass_to::{To, to, with_destination};
|
|
||||||
use rosenpass_to::ops::copy_array;
|
use rosenpass_to::ops::copy_array;
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
|
use std::ops::BitXorAssign;
|
||||||
|
|
||||||
// Destination functions return some value that implements the To trait.
|
// Destination functions return some value that implements the To trait.
|
||||||
// Unfortunately dealing with lifetimes is a bit more finicky than it would#
|
// Unfortunately dealing with lifetimes is a bit more finicky than it would#
|
||||||
// be without destination parameters
|
// be without destination parameters
|
||||||
fn xor_slice<'a, T>(src: &'a[T]) -> impl To<[T], ()> + 'a
|
fn xor_slice<'a, T>(src: &'a [T]) -> impl To<[T], ()> + 'a
|
||||||
where T: BitXorAssign + Clone {
|
where
|
||||||
|
T: BitXorAssign + Clone,
|
||||||
|
{
|
||||||
// Custom implementations of the to trait can be created, but the easiest
|
// Custom implementations of the to trait can be created, but the easiest
|
||||||
with_destination(move |dst: &mut [T]| {
|
with_destination(move |dst: &mut [T]| {
|
||||||
assert!(src.len() == dst.len());
|
assert!(src.len() == dst.len());
|
||||||
@@ -65,7 +67,7 @@ assert_eq!(&dst[..], &flip01[..]);
|
|||||||
// The builtin function copy_array supports to_value() since its
|
// The builtin function copy_array supports to_value() since its
|
||||||
// destination parameter is a fixed size array, which can be allocated
|
// destination parameter is a fixed size array, which can be allocated
|
||||||
// using default()
|
// using default()
|
||||||
let dst : [u8; 4] = copy_array(flip01).to_value();
|
let dst: [u8; 4] = copy_array(flip01).to_value();
|
||||||
assert_eq!(&dst, flip01);
|
assert_eq!(&dst, flip01);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,7 +86,9 @@ Functions declared like this are more cumbersome to use when the destination par
|
|||||||
use std::ops::BitXorAssign;
|
use std::ops::BitXorAssign;
|
||||||
|
|
||||||
fn xor_slice<T>(dst: &mut [T], src: &[T])
|
fn xor_slice<T>(dst: &mut [T], src: &[T])
|
||||||
where T: BitXorAssign + Clone {
|
where
|
||||||
|
T: BitXorAssign + Clone,
|
||||||
|
{
|
||||||
assert!(src.len() == dst.len());
|
assert!(src.len() == dst.len());
|
||||||
for (d, s) in dst.iter_mut().zip(src.iter()) {
|
for (d, s) in dst.iter_mut().zip(src.iter()) {
|
||||||
*d ^= s.clone();
|
*d ^= s.clone();
|
||||||
@@ -114,8 +118,8 @@ assert_eq!(&dst[..], &flip01[..]);
|
|||||||
There are a couple of ways to use a function with destination:
|
There are a couple of ways to use a function with destination:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{to, To};
|
|
||||||
use rosenpass_to::ops::{copy_array, copy_slice_least};
|
use rosenpass_to::ops::{copy_array, copy_slice_least};
|
||||||
|
use rosenpass_to::{to, To};
|
||||||
|
|
||||||
let mut dst = b" ".to_vec();
|
let mut dst = b" ".to_vec();
|
||||||
|
|
||||||
@@ -129,7 +133,8 @@ copy_slice_least(b"This is fin").to(&mut dst[..]);
|
|||||||
assert_eq!(&dst[..], b"This is fin");
|
assert_eq!(&dst[..], b"This is fin");
|
||||||
|
|
||||||
// You can allocate the destination variable on the fly using `.to_this(...)`
|
// You can allocate the destination variable on the fly using `.to_this(...)`
|
||||||
let tmp = copy_slice_least(b"This is new---").to_this(|| b"This will be overwritten".to_owned());
|
let tmp =
|
||||||
|
copy_slice_least(b"This is new---").to_this(|| b"This will be overwritten".to_owned());
|
||||||
assert_eq!(&tmp[..], b"This is new---verwritten");
|
assert_eq!(&tmp[..], b"This is new---verwritten");
|
||||||
|
|
||||||
// You can allocate the destination variable on the fly `.collect(..)` if it implements default
|
// You can allocate the destination variable on the fly `.collect(..)` if it implements default
|
||||||
@@ -147,8 +152,11 @@ assert_eq!(&tmp[..], b"Fixed");
|
|||||||
The to crate provides basic functions with destination for copying data between slices and arrays.
|
The to crate provides basic functions with destination for copying data between slices and arrays.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::ops::{
|
||||||
|
copy_array, copy_slice, copy_slice_least, copy_slice_least_src, try_copy_slice,
|
||||||
|
try_copy_slice_least_src,
|
||||||
|
};
|
||||||
use rosenpass_to::{to, To};
|
use rosenpass_to::{to, To};
|
||||||
use rosenpass_to::ops::{copy_array, copy_slice, copy_slice_least, copy_slice_least_src, try_copy_slice, try_copy_slice_least_src};
|
|
||||||
|
|
||||||
let mut dst = b" ".to_vec();
|
let mut dst = b" ".to_vec();
|
||||||
|
|
||||||
@@ -161,18 +169,33 @@ to(&mut dst[4..], copy_slice_least_src(b"!!!"));
|
|||||||
assert_eq!(&dst[..], b"Hell!!!orld");
|
assert_eq!(&dst[..], b"Hell!!!orld");
|
||||||
|
|
||||||
// Copy a slice, copying as many bytes as possible
|
// Copy a slice, copying as many bytes as possible
|
||||||
to(&mut dst[6..], copy_slice_least(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
|
to(
|
||||||
|
&mut dst[6..],
|
||||||
|
copy_slice_least(b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
|
||||||
|
);
|
||||||
assert_eq!(&dst[..], b"Hell!!xxxxx");
|
assert_eq!(&dst[..], b"Hell!!xxxxx");
|
||||||
|
|
||||||
// Copy a slice, will return None and abort if the sizes do not much
|
// Copy a slice, will return None and abort if the sizes do not much
|
||||||
assert_eq!(Some(()), to(&mut dst[..], try_copy_slice(b"Hello World")));
|
assert_eq!(Some(()), to(&mut dst[..], try_copy_slice(b"Hello World")));
|
||||||
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
||||||
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---------------------")));
|
assert_eq!(
|
||||||
|
None,
|
||||||
|
to(&mut dst[..], try_copy_slice(b"---------------------"))
|
||||||
|
);
|
||||||
assert_eq!(&dst[..], b"Hello World");
|
assert_eq!(&dst[..], b"Hello World");
|
||||||
|
|
||||||
// Copy a slice, will return None and abort if source is longer than destination
|
// Copy a slice, will return None and abort if source is longer than destination
|
||||||
assert_eq!(Some(()), to(&mut dst[4..], try_copy_slice_least_src(b"!!!")));
|
assert_eq!(
|
||||||
assert_eq!(None, to(&mut dst[4..], try_copy_slice_least_src(b"-------------------------")));
|
Some(()),
|
||||||
|
to(&mut dst[4..], try_copy_slice_least_src(b"!!!"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
None,
|
||||||
|
to(
|
||||||
|
&mut dst[4..],
|
||||||
|
try_copy_slice_least_src(b"-------------------------")
|
||||||
|
)
|
||||||
|
);
|
||||||
assert_eq!(&dst[..], b"Hell!!!orld");
|
assert_eq!(&dst[..], b"Hell!!!orld");
|
||||||
|
|
||||||
// Copy fixed size arrays all at once
|
// Copy fixed size arrays all at once
|
||||||
@@ -186,12 +209,14 @@ assert_eq!(&dst, b"Hello");
|
|||||||
The easiest way to declare a function with destination is to use the with_destination function.
|
The easiest way to declare a function with destination is to use the with_destination function.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{To, to, with_destination};
|
|
||||||
use rosenpass_to::ops::copy_array;
|
use rosenpass_to::ops::copy_array;
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
|
|
||||||
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
||||||
fn copy_to_vec<'a, T>(src: &'a [T]) -> impl To<Vec<T>, ()> + 'a
|
fn copy_to_vec<'a, T>(src: &'a [T]) -> impl To<Vec<T>, ()> + 'a
|
||||||
where T: Clone {
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
with_destination(move |dst: &mut Vec<T>| {
|
with_destination(move |dst: &mut Vec<T>| {
|
||||||
dst.clear();
|
dst.clear();
|
||||||
dst.extend_from_slice(src);
|
dst.extend_from_slice(src);
|
||||||
@@ -217,7 +242,9 @@ The same pattern can be implemented without `to`, at the cost of being slightly
|
|||||||
```rust
|
```rust
|
||||||
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
/// Copy the given slice to the start of a vector, reusing its memory if possible
|
||||||
fn copy_to_vec<T>(dst: &mut Vec<T>, src: &[T])
|
fn copy_to_vec<T>(dst: &mut Vec<T>, src: &[T])
|
||||||
where T: Clone {
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
dst.clear();
|
dst.clear();
|
||||||
dst.extend_from_slice(src);
|
dst.extend_from_slice(src);
|
||||||
}
|
}
|
||||||
@@ -240,11 +267,11 @@ Alternative functions are returned, that return a `to::Beside` value, containing
|
|||||||
destination variable and the return value.
|
destination variable and the return value.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::cmp::{min, max};
|
use rosenpass_to::{to, with_destination, Beside, To};
|
||||||
use rosenpass_to::{To, to, with_destination, Beside};
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
/// Copy an array of floats and calculate the average
|
/// Copy an array of floats and calculate the average
|
||||||
pub fn copy_and_average<'a>(src: &'a[f64]) -> impl To<[f64], f64> + 'a {
|
pub fn copy_and_average<'a>(src: &'a [f64]) -> impl To<[f64], f64> + 'a {
|
||||||
with_destination(move |dst: &mut [f64]| {
|
with_destination(move |dst: &mut [f64]| {
|
||||||
assert!(src.len() == dst.len());
|
assert!(src.len() == dst.len());
|
||||||
let mut sum = 0f64;
|
let mut sum = 0f64;
|
||||||
@@ -300,8 +327,8 @@ assert_eq!(tmp, Beside([42f64; 3], 42f64));
|
|||||||
When Beside values contain a `()`, `Option<()>`, or `Result<(), Error>` return value, they expose a special method called `.condense()`; this method consumes the Beside value and condenses destination and return value into one value.
|
When Beside values contain a `()`, `Option<()>`, or `Result<(), Error>` return value, they expose a special method called `.condense()`; this method consumes the Beside value and condenses destination and return value into one value.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::Beside;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use rosenpass_to::{Beside};
|
|
||||||
|
|
||||||
assert_eq!((), Beside((), ()).condense());
|
assert_eq!((), Beside((), ()).condense());
|
||||||
|
|
||||||
@@ -318,8 +345,8 @@ assert_eq!(Err(()), Beside(42, err_unit).condense());
|
|||||||
When condense is implemented for a type, `.to_this(|| ...)`, `.to_value()`, and `.collect::<...>()` on the `To` trait can be used even with a return value:
|
When condense is implemented for a type, `.to_this(|| ...)`, `.to_value()`, and `.collect::<...>()` on the `To` trait can be used even with a return value:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::ops::try_copy_slice;
|
||||||
use rosenpass_to::To;
|
use rosenpass_to::To;
|
||||||
use rosenpass_to::ops::try_copy_slice;;
|
|
||||||
|
|
||||||
let tmp = try_copy_slice(b"Hello World").collect::<[u8; 11]>();
|
let tmp = try_copy_slice(b"Hello World").collect::<[u8; 11]>();
|
||||||
assert_eq!(tmp, Some(*b"Hello World"));
|
assert_eq!(tmp, Some(*b"Hello World"));
|
||||||
@@ -337,8 +364,8 @@ assert_eq!(tmp, None);
|
|||||||
The same naturally also works for Results, but the example is a bit harder to motivate:
|
The same naturally also works for Results, but the example is a bit harder to motivate:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use rosenpass_to::{to, To, with_destination};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
struct InvalidFloat;
|
struct InvalidFloat;
|
||||||
@@ -380,8 +407,8 @@ Condensation is implemented through a trait called CondenseBeside ([local](Conde
|
|||||||
If you can not implement this trait because its for an external type (see [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type)), this crate welcomes contributions of new Condensation rules.
|
If you can not implement this trait because its for an external type (see [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type)), this crate welcomes contributions of new Condensation rules.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{To, with_destination, Beside, CondenseBeside};
|
|
||||||
use rosenpass_to::ops::copy_slice;
|
use rosenpass_to::ops::copy_slice;
|
||||||
|
use rosenpass_to::{with_destination, Beside, CondenseBeside, To};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Default)]
|
#[derive(PartialEq, Eq, Debug, Default)]
|
||||||
struct MyTuple<Left, Right>(Left, Right);
|
struct MyTuple<Left, Right>(Left, Right);
|
||||||
@@ -396,7 +423,10 @@ impl<Val, Right> CondenseBeside<Val> for MyTuple<(), Right> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn copy_slice_and_return_something<'a, T, U>(src: &'a [T], something: U) -> impl To<[T], U> + 'a
|
fn copy_slice_and_return_something<'a, T, U>(src: &'a [T], something: U) -> impl To<[T], U> + 'a
|
||||||
where T: Copy, U: 'a {
|
where
|
||||||
|
T: Copy,
|
||||||
|
U: 'a,
|
||||||
|
{
|
||||||
with_destination(move |dst: &mut [T]| {
|
with_destination(move |dst: &mut [T]| {
|
||||||
copy_slice(src).to(dst);
|
copy_slice(src).to(dst);
|
||||||
something
|
something
|
||||||
@@ -417,7 +447,7 @@ Using `with_destination(...)` is convenient, but since it uses closures it resul
|
|||||||
Implementing the ToTrait manual is the right choice for library use cases.
|
Implementing the ToTrait manual is the right choice for library use cases.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosenpass_to::{to, To, with_destination};
|
use rosenpass_to::{to, with_destination, To};
|
||||||
|
|
||||||
struct TryCopySliceSource<'a, T: Copy> {
|
struct TryCopySliceSource<'a, T: Copy> {
|
||||||
src: &'a [T],
|
src: &'a [T],
|
||||||
@@ -425,17 +455,20 @@ struct TryCopySliceSource<'a, T: Copy> {
|
|||||||
|
|
||||||
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
||||||
fn to(self, dst: &mut [T]) -> Option<()> {
|
fn to(self, dst: &mut [T]) -> Option<()> {
|
||||||
(self.src.len() == dst.len())
|
(self.src.len() == dst.len()).then(|| dst.copy_from_slice(self.src))
|
||||||
.then(|| dst.copy_from_slice(self.src))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_copy_slice<'a, T>(src: &'a [T]) -> TryCopySliceSource<'a, T>
|
fn try_copy_slice<'a, T>(src: &'a [T]) -> TryCopySliceSource<'a, T>
|
||||||
where T: Copy {
|
where
|
||||||
|
T: Copy,
|
||||||
|
{
|
||||||
TryCopySliceSource { src }
|
TryCopySliceSource { src }
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dst = try_copy_slice(b"Hello World").collect::<[u8; 11]>().unwrap();
|
let mut dst = try_copy_slice(b"Hello World")
|
||||||
|
.collect::<[u8; 11]>()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(&dst[..], b"Hello World");
|
assert_eq!(&dst[..], b"Hello World");
|
||||||
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
||||||
```
|
```
|
||||||
@@ -445,8 +478,8 @@ assert_eq!(None, to(&mut dst[..], try_copy_slice(b"---")));
|
|||||||
Destinations can also be used with methods. This example demonstrates using destinations in an extension trait for everything that implements `Borrow<[T]>` for any `T` and a concrete `To` trait implementation.
|
Destinations can also be used with methods. This example demonstrates using destinations in an extension trait for everything that implements `Borrow<[T]>` for any `T` and a concrete `To` trait implementation.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
use rosenpass_to::{to, with_destination, To};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use rosenpass_to::{to, To, with_destination};
|
|
||||||
|
|
||||||
struct TryCopySliceSource<'a, T: Copy> {
|
struct TryCopySliceSource<'a, T: Copy> {
|
||||||
src: &'a [T],
|
src: &'a [T],
|
||||||
@@ -454,24 +487,24 @@ struct TryCopySliceSource<'a, T: Copy> {
|
|||||||
|
|
||||||
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
|
||||||
fn to(self, dst: &mut [T]) -> Option<()> {
|
fn to(self, dst: &mut [T]) -> Option<()> {
|
||||||
(self.src.len() == dst.len())
|
(self.src.len() == dst.len()).then(|| dst.copy_from_slice(self.src))
|
||||||
.then(|| dst.copy_from_slice(self.src))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TryCopySliceExt<'a, T: Copy> {
|
trait TryCopySliceExt<'a, T: Copy> {
|
||||||
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T>;
|
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: 'a + Copy, Ref: 'a + Borrow<[T]>> TryCopySliceExt<'a, T> for Ref {
|
impl<'a, T: 'a + Copy, Ref: 'a + Borrow<[T]>> TryCopySliceExt<'a, T> for Ref {
|
||||||
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> {
|
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> {
|
||||||
TryCopySliceSource {
|
TryCopySliceSource { src: self.borrow() }
|
||||||
src: self.borrow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dst = b"Hello World".try_copy_slice().collect::<[u8; 11]>().unwrap();
|
let mut dst = b"Hello World"
|
||||||
|
.try_copy_slice()
|
||||||
|
.collect::<[u8; 11]>()
|
||||||
|
.unwrap();
|
||||||
assert_eq!(&dst[..], b"Hello World");
|
assert_eq!(&dst[..], b"Hello World");
|
||||||
assert_eq!(None, to(&mut dst[..], b"---".try_copy_slice()));
|
assert_eq!(None, to(&mut dst[..], b"---".try_copy_slice()));
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user