mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-18 21:34:37 +03:00
Compare commits
18 Commits
dev/add-de
...
dev/old-di
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c29b1083f3 | ||
|
|
46cbda08c4 | ||
|
|
25d7527c74 | ||
|
|
7c83e244f9 | ||
|
|
eb76179dc4 | ||
|
|
d84efa7422 | ||
|
|
61ef5b92bb | ||
|
|
4c872ec855 | ||
|
|
70d136dbd3 | ||
|
|
e793168f27 | ||
|
|
df8990f4f8 | ||
|
|
184cff0e5e | ||
|
|
9819148b6f | ||
|
|
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:
|
||||||
|
|||||||
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_ = (
|
||||||
@@ -72,6 +74,10 @@ let Oinit_conf() =
|
|||||||
0
|
0
|
||||||
#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))
|
||||||
@@ -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 */ \
|
||||||
|
|||||||
@@ -291,7 +291,6 @@
|
|||||||
];
|
];
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
export HOME=$(mktemp -d)
|
export HOME=$(mktemp -d)
|
||||||
export OSFONTDIR="$(kpsewhich --var-value TEXMF)/fonts/{opentype/public/nunito,truetype/google/noto}"
|
|
||||||
latexmk -r tex/CI.rc
|
latexmk -r tex/CI.rc
|
||||||
'';
|
'';
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
|
|||||||
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
|
||||||
@@ -87,6 +87,15 @@ pub enum Cli {
|
|||||||
force: bool,
|
force: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Deprecated - use gen-keys instead
|
||||||
|
#[allow(rustdoc::broken_intra_doc_links)]
|
||||||
|
#[allow(rustdoc::invalid_html_tags)]
|
||||||
|
Keygen {
|
||||||
|
// NOTE yes, the legacy keygen argument initially really accepted "privet-key", not "secret-key"!
|
||||||
|
/// public-key <PATH> private-key <PATH>
|
||||||
|
args: Vec<String>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Validate a configuration
|
/// Validate a configuration
|
||||||
Validate { config_files: Vec<PathBuf> },
|
Validate { config_files: Vec<PathBuf> },
|
||||||
|
|
||||||
@@ -119,6 +128,40 @@ impl Cli {
|
|||||||
config::Rosenpass::example_config().store(config_file)?;
|
config::Rosenpass::example_config().store(config_file)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated - use gen-keys instead
|
||||||
|
Keygen { args } => {
|
||||||
|
log::warn!("The 'keygen' command is deprecated. Please use the 'gen-keys' command instead.");
|
||||||
|
|
||||||
|
let mut public_key: Option<PathBuf> = None;
|
||||||
|
let mut secret_key: Option<PathBuf> = None;
|
||||||
|
|
||||||
|
// Manual arg parsing, since clap wants to prefix flags with "--"
|
||||||
|
let mut args = args.into_iter();
|
||||||
|
loop {
|
||||||
|
match (args.next().as_ref().map(String::as_str), args.next()) {
|
||||||
|
(Some("private-key"), Some(opt)) | (Some("secret-key"), Some(opt)) => {
|
||||||
|
secret_key = Some(opt.into());
|
||||||
|
}
|
||||||
|
(Some("public-key"), Some(opt)) => {
|
||||||
|
public_key = Some(opt.into());
|
||||||
|
}
|
||||||
|
(Some(flag), _) => {
|
||||||
|
bail!("Unknown option `{}`", flag);
|
||||||
|
}
|
||||||
|
(_, _) => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret_key.is_none() {
|
||||||
|
bail!("private-key is required");
|
||||||
|
}
|
||||||
|
if public_key.is_none() {
|
||||||
|
bail!("public-key is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_and_save_keypair(secret_key.unwrap(), public_key.unwrap())?;
|
||||||
|
}
|
||||||
|
|
||||||
GenKeys {
|
GenKeys {
|
||||||
config_file,
|
config_file,
|
||||||
public_key,
|
public_key,
|
||||||
@@ -160,12 +203,7 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate the keys and store them in files
|
// generate the keys and store them in files
|
||||||
let mut ssk = crate::protocol::SSk::random();
|
generate_and_save_keypair(skf, pkf)?;
|
||||||
let mut spk = crate::protocol::SPk::random();
|
|
||||||
StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
|
||||||
|
|
||||||
ssk.store_secret(skf)?;
|
|
||||||
spk.store_secret(pkf)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExchangeConfig { config_file } => {
|
ExchangeConfig { config_file } => {
|
||||||
@@ -246,3 +284,12 @@ impl Cli {
|
|||||||
srv.event_loop()
|
srv.event_loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generate secret and public keys, store in files according to the paths passed as arguments
|
||||||
|
fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
||||||
|
let mut ssk = crate::protocol::SSk::random();
|
||||||
|
let mut spk = crate::protocol::SPk::random();
|
||||||
|
StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
||||||
|
ssk.store_secret(secret_key)?;
|
||||||
|
spk.store_secret(public_key)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use std::process::exit;
|
|||||||
|
|
||||||
/// Catches errors, prints them through the logger, then exits
|
/// Catches errors, prints them through the logger, then exits
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
env_logger::init();
|
// default to displaying warning and error log messages only
|
||||||
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
||||||
|
|
||||||
let res = attempt!({
|
let res = attempt!({
|
||||||
rosenpass_sodium::init()?;
|
rosenpass_sodium::init()?;
|
||||||
|
|||||||
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