Compare commits

...

18 Commits

Author SHA1 Message Date
James Brownlee
c29b1083f3 Merge branch 'main' of https://github.com/digital-phoenix/rosenpass into main
* 'main' of https://github.com/digital-phoenix/rosenpass:
  fix: fix Rust code in markdown files
  feat: add format_rustcode.sh script
  fix: add deprecated keygen command
2024-01-02 20:02:57 -05:00
James Brownlee
46cbda08c4 added a comment explaining the process and tradeoffs 2024-01-02 20:02:28 -05:00
James Brownlee
25d7527c74 Merge branch 'rosenpass:main' into main 2024-01-02 18:27:38 -05:00
wucke13
7c83e244f9 fix: fix Rust code in markdown files
This applies the novel format_rustcode.sh script to the markdown files in the
repo, to maintain a consistent style across code examples.
2023-12-22 17:57:32 +01:00
alankritdabral_2
eb76179dc4 feat: add format_rustcode.sh script
This script makes it possible to check formatting of rust code found in the various markdown files in the repo. It is also added as a job to the QC CI workflow.
2023-12-22 17:57:32 +01:00
wucke13
d84efa7422 Merge pull request #197 from guhitb/main
Add backwards compatibility for keygen command
2023-12-21 11:28:25 +01:00
user
61ef5b92bb fix: add deprecated keygen command
This allows users to use the old keygen command, while being informed
about its deprecation.
2023-12-20 16:03:47 +01:00
James Brownlee
4c872ec855 Merge branch 'main' of https://github.com/digital-phoenix/rosenpass into main
* 'main' of https://github.com/digital-phoenix/rosenpass: (46 commits)
  fix: remove OSFONTDIR var from whitepaper build
  feat: Add fuzzing for libsodium allocator
  fix: Guaranteed results typo
  feat: Move lenses into library
  chore: Move kems out of rosenpass crate
  feat: Add an internal library for guaranteed results
  fix output of authorlist to support unlimited authors
  chore: Upgrade dependencies
  feat: Use the rand crate for random values instead of sodium
  feat: Move prftree into ciphers crate
  chore: Shorten fuzzing runtime to make sure the CI finishes quickly
  feat: Wrap sodium_malloc as a custom allocator
  feat: Use the zeroize crate for zeroization
  chore: Move rest of coloring.rs into secret-memory crate
  chore: Move Public and debug_crypto_array into secret-memory crate
  Added example for additional PSK
  #172 removed exchange_command
  Added indications that file paths are used
  Added indication that exchange_command is not used
  added WireGuard config example to gen-config
  ...
2023-12-15 10:56:33 -05:00
James Brownlee
70d136dbd3 added INITIATOR_TEST and RESPONDER_TEST macros to the identity hiding mpv file that can be used to selectively test the anonymity of the initiator or the responder 2023-12-15 10:55:16 -05:00
James Brownlee
e793168f27 Merge branch 'rosenpass:main' into main 2023-12-14 21:03:44 -05:00
James Brownlee
df8990f4f8 fixed issues with identity hiding proverif code 2023-12-14 21:02:39 -05:00
wucke13
184cff0e5e Merge pull request #196 from rosenpass/dev/fix-65
fix: remove OSFONTDIR var from whitepaper build
2023-12-03 14:01:25 +01:00
wucke13
9819148b6f fix: remove OSFONTDIR var from whitepaper build
Fixes #65. I checked with `pdffonts` that the whitepaper still has all fonts embedded.
2023-12-03 13:27:47 +01:00
James Brownlee
2c4ab16eb7 fixed small bug 2023-11-25 20:50:43 -05:00
James Brownlee
0cdd06031b Changed identity hiding test to work as a two stage process where participants with fresh secure secret keys communicate with each other and other compromised participants. Then the attacker is asked to identify the difference between two of the secure participants as on of them acts as a responder. 2023-11-25 20:49:32 -05:00
James Brownlee
8027ccbf38 removing commented out code 2023-11-22 20:18:33 -05:00
James Brownlee
d8033968fd Merge branch 'main' of https://github.com/digital-phoenix/rosenpass
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2023-11-22 20:16:09 -05:00
James Brownlee
a7439aedbb adding inital identity hiding code 2023-11-22 20:11:04 -05:00
9 changed files with 435 additions and 75 deletions

View File

@@ -25,6 +25,14 @@ jobs:
- name: Run ShellCheck
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:
runs-on: ubuntu-latest
steps:

View 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.

View File

@@ -47,14 +47,16 @@ CK_EV( event OskOinit_conf(key, key). )
MTX_EV( event ICRjct(InitConf_t, key, kem_sk, kem_pk). )
SES_EV( event ResponderSession(InitConf_t, key). )
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
new call:Atom;
#else
call <- Cinit_conf(Ssskm, Spsk, Sspkt, ic);
#endif
SETUP_HANDSHAKE_STATE()
eski <- kem_sk0;
epki <- kem_pk0;
let try_ = (
@@ -72,6 +74,10 @@ let Oinit_conf() =
0
#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;
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 ICSent(RespHello_t, InitConf_t, key, kem_sk, kem_pk). )
SES_EV( event InitiatorSession(RespHello_t, key). )
let Oresp_hello(HS_DECL_ARGS) =
in(C, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
let Oresp_hello(HS_DECL_ARGS, C_in:channel) =
in(C_in, Cresp_hello(RespHello(sidr, =sidi, ecti, scti, biscuit, auth)));
rh <- RespHello(sidr, sidi, ecti, scti, biscuit, auth);
/* try */ let ic = (
ck_ini <- ck;
@@ -98,7 +104,7 @@ let Oresp_hello(HS_DECL_ARGS) =
SES_EV( event InitiatorSession(rh, osk); )
ic
/* success */ ) in (
out(C, ic)
out(C_in, Envelope(create_mac(spkt, IC2b(ic)), IC2b(ic)))
/* fail */ ) else (
#if MESSAGE_TRANSMISSION_EVENTS
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). )
event ConsumeSidr(SessionId, 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
new call:Atom;
#else
@@ -125,14 +131,19 @@ let Oinit_hello() =
#endif
// TODO: This is ugly
let InitHello(sidi, epki, sctr, pidiC, auth) = ih in
SETUP_HANDSHAKE_STATE()
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 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(Spti, setup_seed(Sspti), call);
let rh = (
INITHELLO_CONSUME()
ck_ini <- ck;
@@ -141,7 +152,8 @@ let Oinit_hello() =
MTX_EV( event RHSent(ih, rh, psk, sskr, spki); )
rh
/* success */ ) in (
out(C, rh)
out(C_out, Envelope(create_mac(spkt, RH2b(rh)), RH2b(rh)))
/* fail */ ) else (
#if MESSAGE_TRANSMISSION_EVENTS
event IHRjct(ih, psk, sskr, spki)
@@ -150,6 +162,10 @@ let Oinit_hello() =
#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;
event(ConsumeSidr(sid, ad1)) && event(ConsumeSidr(sid, ad2))
==> ad1 = ad2.
@@ -167,26 +183,34 @@ CK_EV( event OskOinitiator_ck(key). )
CK_EV( event OskOinitiator(key, key, kem_sk, kem_pk, key). )
MTX_EV( event IHSent(InitHello_t, key, kem_sk, kem_pk). )
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() =
in(C, Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr));
#if RANDOMIZED_CALL_IDS
new call:Atom;
#else
call <- Cinitiator(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr);
#endif
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).
Oinitiator_inner(sidi, Ssskm, Spsk, Sspkt, Seski, Ssptr, C).
restriction sid:SessionId, ad1:Atom, ad2:Atom;
event(ConsumeSidi(sid, ad1)) && event(ConsumeSidi(sid, ad2))

View File

@@ -2,6 +2,12 @@
#include "crypto/kem.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.
fun InitHello(
SessionId, // sidi
@@ -11,6 +17,8 @@ fun InitHello(
bits // auth
) : InitHello_t [data].
fun IH2b(InitHello_t) : bitstring [typeConverter].
#define INITHELLO_PRODUCE() \
ck <- lprf1(CK_INIT, kem_pk2b(spkr)); /* IHI1 */ \
/* not handled here */ /* IHI2 */ \
@@ -41,7 +49,9 @@ fun RespHello(
bits // auth
) : RespHello_t [data].
#define RESPHELLO_PRODUCE() \
fun RH2b(RespHello_t) : bitstring [typeConverter].
#define RESPHELLO_PRODUCE() \
/* not handled here */ /* RHR1 */ \
MIX2(sid2b(sidr), sid2b(sidi)) /* RHR3 */ \
ENCAPS_AND_MIX(ecti, epki, epti) /* RHR4 */ \
@@ -67,6 +77,8 @@ fun InitConf(
bits // auth
) : InitConf_t [data].
fun IC2b(InitConf_t) : bitstring [typeConverter].
#define INITCONF_PRODUCE() \
MIX2(sid2b(sidi), sid2b(sidr)) /* ICI3 */ \
ENCRYPT_AND_MIX(auth, empty) /* ICI4 */ \

View File

@@ -291,7 +291,6 @@
];
buildPhase = ''
export HOME=$(mktemp -d)
export OSFONTDIR="$(kpsewhich --var-value TEXMF)/fonts/{opentype/public/nunito,truetype/google/noto}"
latexmk -r tex/CI.rc
'';
installPhase = ''

115
format_rust_code.sh Executable file
View 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

View File

@@ -87,6 +87,15 @@ pub enum Cli {
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 { config_files: Vec<PathBuf> },
@@ -119,6 +128,40 @@ impl Cli {
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 {
config_file,
public_key,
@@ -160,12 +203,7 @@ impl Cli {
}
// generate the keys and store them in files
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(skf)?;
spk.store_secret(pkf)?;
generate_and_save_keypair(skf, pkf)?;
}
ExchangeConfig { config_file } => {
@@ -246,3 +284,12 @@ impl Cli {
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)
}

View File

@@ -5,7 +5,8 @@ use std::process::exit;
/// Catches errors, prints them through the logger, then exits
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!({
rosenpass_sodium::init()?;

View File

@@ -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.
```rust
use std::ops::BitXorAssign;
use rosenpass_to::{To, to, with_destination};
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.
// Unfortunately dealing with lifetimes is a bit more finicky than it would#
// be without destination parameters
fn xor_slice<'a, T>(src: &'a[T]) -> impl To<[T], ()> + 'a
where T: BitXorAssign + Clone {
fn xor_slice<'a, T>(src: &'a [T]) -> impl To<[T], ()> + 'a
where
T: BitXorAssign + Clone,
{
// Custom implementations of the to trait can be created, but the easiest
with_destination(move |dst: &mut [T]| {
assert!(src.len() == dst.len());
@@ -65,7 +67,7 @@ assert_eq!(&dst[..], &flip01[..]);
// The builtin function copy_array supports to_value() since its
// destination parameter is a fixed size array, which can be allocated
// using default()
let dst : [u8; 4] = copy_array(flip01).to_value();
let dst: [u8; 4] = copy_array(flip01).to_value();
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;
fn xor_slice<T>(dst: &mut [T], src: &[T])
where T: BitXorAssign + Clone {
where
T: BitXorAssign + Clone,
{
assert!(src.len() == dst.len());
for (d, s) in dst.iter_mut().zip(src.iter()) {
*d ^= s.clone();
@@ -114,8 +118,8 @@ assert_eq!(&dst[..], &flip01[..]);
There are a couple of ways to use a function with destination:
```rust
use rosenpass_to::{to, To};
use rosenpass_to::ops::{copy_array, copy_slice_least};
use rosenpass_to::{to, To};
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");
// 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");
// 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.
```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::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();
@@ -161,18 +169,33 @@ to(&mut dst[4..], copy_slice_least_src(b"!!!"));
assert_eq!(&dst[..], b"Hell!!!orld");
// 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");
// 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!(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");
// 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!(None, to(&mut dst[4..], try_copy_slice_least_src(b"-------------------------")));
assert_eq!(
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");
// 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.
```rust
use rosenpass_to::{To, to, with_destination};
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
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>| {
dst.clear();
dst.extend_from_slice(src);
@@ -217,7 +242,9 @@ The same pattern can be implemented without `to`, at the cost of being slightly
```rust
/// 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])
where T: Clone {
where
T: Clone,
{
dst.clear();
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.
```rust
use std::cmp::{min, max};
use rosenpass_to::{To, to, with_destination, Beside};
use rosenpass_to::{to, with_destination, Beside, To};
use std::cmp::{max, min};
/// 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]| {
assert!(src.len() == dst.len());
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.
```rust
use rosenpass_to::Beside;
use std::result::Result;
use rosenpass_to::{Beside};
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:
```rust
use rosenpass_to::ops::try_copy_slice;
use rosenpass_to::To;
use rosenpass_to::ops::try_copy_slice;;
let tmp = try_copy_slice(b"Hello World").collect::<[u8; 11]>();
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:
```rust
use rosenpass_to::{to, with_destination, To};
use std::result::Result;
use rosenpass_to::{to, To, with_destination};
#[derive(PartialEq, Eq, Debug, Default)]
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.
```rust
use rosenpass_to::{To, with_destination, Beside, CondenseBeside};
use rosenpass_to::ops::copy_slice;
use rosenpass_to::{with_destination, Beside, CondenseBeside, To};
#[derive(PartialEq, Eq, Debug, Default)]
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
where T: Copy, U: 'a {
where
T: Copy,
U: 'a,
{
with_destination(move |dst: &mut [T]| {
copy_slice(src).to(dst);
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.
```rust
use rosenpass_to::{to, To, with_destination};
use rosenpass_to::{to, with_destination, To};
struct TryCopySliceSource<'a, T: Copy> {
src: &'a [T],
@@ -425,17 +455,20 @@ struct TryCopySliceSource<'a, T: Copy> {
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
fn to(self, dst: &mut [T]) -> Option<()> {
(self.src.len() == dst.len())
.then(|| dst.copy_from_slice(self.src))
(self.src.len() == dst.len()).then(|| dst.copy_from_slice(self.src))
}
}
fn try_copy_slice<'a, T>(src: &'a [T]) -> TryCopySliceSource<'a, T>
where T: Copy {
where
T: Copy,
{
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!(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.
```rust
use rosenpass_to::{to, with_destination, To};
use std::borrow::Borrow;
use rosenpass_to::{to, To, with_destination};
struct TryCopySliceSource<'a, T: Copy> {
src: &'a [T],
@@ -454,24 +487,24 @@ struct TryCopySliceSource<'a, T: Copy> {
impl<'a, T: Copy> To<[T], Option<()>> for TryCopySliceSource<'a, T> {
fn to(self, dst: &mut [T]) -> Option<()> {
(self.src.len() == dst.len())
.then(|| dst.copy_from_slice(self.src))
(self.src.len() == dst.len()).then(|| dst.copy_from_slice(self.src))
}
}
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 {
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> {
TryCopySliceSource {
src: self.borrow()
}
fn try_copy_slice(&'a self) -> TryCopySliceSource<'a, T> {
TryCopySliceSource { 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!(None, to(&mut dst[..], b"---".try_copy_slice()));
```