Skip to content

Commit

Permalink
Add interactive mode
Browse files Browse the repository at this point in the history
  • Loading branch information
lbeder committed Dec 10, 2024
1 parent 4cc2f90 commit ddfcb1c
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 14 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Leonid Beder <[email protected]>"]
edition = "2021"
name = "slowkey"
version = "2.0.0"
version = "2.1.0"

[dependencies]
balloon-hash = "0.4.0"
Expand Down
73 changes: 70 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ Options:
```sh
Continue derivation process from an existing checkpoint

Usage: slowkey restore-from-checkpoint [OPTIONS] --checkpoint <CHECKPOINT>
Usage: slowkey restore-from-checkpoint [OPTIONS]

Options:
-i, --iterations <ITERATIONS>
Expand All @@ -145,6 +145,8 @@ Options:
Specifies the number of most recent checkpoints to keep, while automatically deleting older ones [default: 1]
--checkpoint <CHECKPOINT>
Path to an existing checkpoint from which to resume the derivation process
--interactive
Input checkpoint data interactively (instead of providing the path to an existing checkpoint)
--base64
Show the result in Base64 (in addition to hex)
--base58
Expand Down Expand Up @@ -433,7 +435,7 @@ Please input all data either in raw or hex format starting with the 0x prefix
✔ Enter your checkpoint/output encryption key · ********
Checkpoint:
Version: 1:
Version: 2:
Iterations: 5:
Data (please highlight to see): 0x7ce6792307959432459050b666260a72c7105d18e66c31cc59d3044fb827f482
Previous Iteration's Data (please highlight to see): 0xf131df94fd3c0294685d19097f9c331bd41abafdcc972695cce89d0d21707ec2
Expand Down Expand Up @@ -487,7 +489,7 @@ Please input all data either in raw or hex format starting with the 0x prefix
✔ Enter your checkpoint/output encryption key · ********
Checkpoint:
Version: 1:
Version: 2:
Iterations: 5:
Data (please highlight to see): 0x7ce6792307959432459050b666260a72c7105d18e66c31cc59d3044fb827f482
Previous Iteration's Data (please highlight to see): 0xf131df94fd3c0294685d19097f9c331bd41abafdcc972695cce89d0d21707ec2
Expand Down Expand Up @@ -529,6 +531,71 @@ Total running time: 39s
Average iteration time: 1s 993ms
```
You can also provide checkpoint data in an interactive way by specifying the `--interactive` flag:
> slowkey restore-from-checkpoint -i 10 --interactive
```sh
Please input all data either in raw or hex format starting with the 0x prefix
✔ Enter your checkpoint/output encryption key · ********
Please enter the checkpoint data manually:
Version: 2
Length: 32
Iteration: 5
Data: 0x7ce6792307959432459050b666260a72c7105d18e66c31cc59d3044fb827f482
Previous data: 0xf131df94fd3c0294685d19097f9c331bd41abafdcc972695cce89d0d21707ec2
Scrypt n: 1048576
Scrypt r: 8
Scrypt p: 1
Argon2id m_cost: 2097152
Argon2id t_cost: 2
Balloon Hash s_cost: 131072
Balloon Hash t_cost: 1
Checkpoint:
Version: 2:
Iterations: 5:
Data (please highlight to see): 0x7ce6792307959432459050b666260a72c7105d18e66c31cc59d3044fb827f482
Previous Iteration's Data (please highlight to see): 0xf131df94fd3c0294685d19097f9c331bd41abafdcc972695cce89d0d21707ec2
SlowKey Parameters:
Iterations: 10
Length: 32
Scrypt: (n: 1048576, r: 8, p: 1)
Argon2id: (version: 19, m_cost: 2097152, t_cost: 2)
Balloon Hash: (hash: SHA512, s_cost: 131072, t_cost: 1)
✔ Enter your salt · ********
Salt is: s...t
✔ Enter your password · ********
Password is: p...d
Verifying the checkpoint...
The password, salt and internal data are correct
████████████████████████████████████████████████████████████████████████████████ 10/10 100% (0s)
Iteration time moving average (10): 2s 610ms, last iteration time: 2s 625ms
Key is (please highlight to see): 0xda158bedf00e5abba900e0c027c249912e3ad5ce54304fdb54f1939ddb14232a
Start time: 2024-12-10 08:47:27
End time: 2024-12-10 08:47:40
Total running time: 13s
Average iteration time: 1s 305ms
```
### Outputs
By default, the tool outputs they key in a hexadecimal format, but the tool also supports both [Base64](https://en.wikipedia.org/wiki/Base64) and [Base58](https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58) formats optionally:
Expand Down
130 changes: 123 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use base64::{engine::general_purpose, Engine as _};
use chrono::{DateTime, Utc};
use clap::{Parser, Subcommand};
use crossterm::style::Stylize;
use dialoguer::{theme::ColorfulTheme, Confirm, Password};
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Password};
use humantime::format_duration;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use mimalloc::MiMalloc;
Expand All @@ -38,7 +38,12 @@ use std::{
use utils::{
algorithms::{argon2id::Argon2idOptions, balloon_hash::BalloonHashOptions, scrypt::ScryptOptions},
chacha20poly1305::ChaCha20Poly1305,
checkpoints::checkpoint::{Checkpoint, CheckpointData, CheckpointOptions, OpenCheckpointOptions},
checkpoints::{
checkpoint::{
Checkpoint, CheckpointData, CheckpointOptions, CheckpointSlowKeyOptions, OpenCheckpointOptions, SlowKeyData,
},
version::Version,
},
outputs::output::{OpenOutputOptions, Output, OutputOptions},
};

Expand Down Expand Up @@ -195,7 +200,14 @@ enum Commands {
long,
help = "Path to an existing checkpoint from which to resume the derivation process"
)]
checkpoint: PathBuf,
checkpoint: Option<PathBuf>,

#[arg(
long,
action = clap::ArgAction::SetTrue,
help = "Input checkpoint data interactively (instead of providing the path to an existing checkpoint)"
)]
interactive: bool,

#[arg(
long,
Expand Down Expand Up @@ -475,6 +487,102 @@ fn get_output_key() -> Vec<u8> {
key
}

fn get_checkpoint_data() -> CheckpointData {
println!("Please enter the checkpoint data manually:\n");

let version: u8 = Input::new().with_prompt("Version").interact_text().unwrap();
let version = Version::from(version);

println!();

let length: usize = Input::new().with_prompt("Length").interact_text().unwrap();
if length < SlowKeyOptions::MIN_KEY_SIZE {
panic!(
"length {} is shorter than the min value of {}",
SlowKeyOptions::MIN_KEY_SIZE,
length
);
} else if length > SlowKeyOptions::MAX_KEY_SIZE {
panic!(
"length {} is greater than the max value of {}",
SlowKeyOptions::MAX_KEY_SIZE,
length
);
}

let iteration: usize = Input::new().with_prompt("Iteration").interact_text().unwrap();
let iteration = iteration - 1;
if iteration < SlowKeyOptions::MIN_ITERATIONS {
panic!(
"iteration {} is shorter than the min value of {}",
SlowKeyOptions::MIN_ITERATIONS,
iteration
);
} else if iteration > SlowKeyOptions::MAX_ITERATIONS {
panic!(
"iteration {} is greater than the max value of {}",
SlowKeyOptions::MAX_ITERATIONS,
iteration
);
}

let data: String = Input::new().with_prompt("Data").interact_text().unwrap();
let data = if data.starts_with(HEX_PREFIX) {
hex::decode(data.strip_prefix(HEX_PREFIX).unwrap()).unwrap()
} else {
data.as_bytes().to_vec()
};

let prev_data = if iteration > 1 {
let prev_data: String = Input::new().with_prompt("Previous data").interact_text().unwrap();
let prev_data = if prev_data.starts_with(HEX_PREFIX) {
hex::decode(prev_data.strip_prefix(HEX_PREFIX).unwrap()).unwrap()
} else {
prev_data.as_bytes().to_vec()
};

Some(prev_data)
} else {
None
};

println!();

let scrypt_n: u64 = Input::new().with_prompt("Scrypt n").interact_text().unwrap();
let scrypt_r: u32 = Input::new().with_prompt("Scrypt r").interact_text().unwrap();
let scrypt_p: u32 = Input::new().with_prompt("Scrypt p").interact_text().unwrap();
let scrypt = ScryptOptions::new(scrypt_n, scrypt_r, scrypt_p);

println!();

let argon2id_m_cost: u32 = Input::new().with_prompt("Argon2id m_cost").interact_text().unwrap();
let argon2id_t_cost: u32 = Input::new().with_prompt("Argon2id t_cost").interact_text().unwrap();
let argon2id = Argon2idOptions::new(argon2id_m_cost, argon2id_t_cost);

println!();

let balloon_s_cost: u32 = Input::new().with_prompt("Balloon Hash s_cost").interact_text().unwrap();
let balloon_t_cost: u32 = Input::new().with_prompt("Balloon Hash t_cost").interact_text().unwrap();
let balloon_hash = BalloonHashOptions::new(balloon_s_cost, balloon_t_cost);

println!();

CheckpointData {
version,
data: SlowKeyData {
iteration,
data,
prev_data,
slowkey: CheckpointSlowKeyOptions {
length,
scrypt,
argon2id,
balloon_hash,
},
},
}
}

fn show_hint(data: &str, description: &str, hex: bool) {
let len = data.len();

Expand Down Expand Up @@ -820,17 +928,25 @@ fn main() {
checkpoint_interval,
max_checkpoints_to_keep,
checkpoint,
interactive,
base64,
base58,
iteration_moving_window,
}) => {
print_input_instructions();

let output_key = get_output_key();
let checkpoint_data = Checkpoint::get_checkpoint(&OpenCheckpointOptions {
key: output_key.clone(),
path: checkpoint,
});

let checkpoint_data = match checkpoint {
Some(path) => Checkpoint::get_checkpoint(&OpenCheckpointOptions {
key: output_key.clone(),
path,
}),
None => match interactive {
true => get_checkpoint_data(),
false => panic!("Missing checkpoint path"),
},
};

if iterations <= checkpoint_data.data.iteration {
panic!(
Expand Down
6 changes: 5 additions & 1 deletion src/utils/algorithms/scrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ impl ScryptOptions {
pub const DEFAULT_P: u32 = 1;

pub fn new(n: u64, r: u32, p: u32) -> Self {
// Note that there is no need to check if either n, r or p are in bounds, since both are bound by the maximum
if n == 0 || (n & (n - 1)) != 0 {
panic!("Invalid n");
}

// Note that there is no need to check if either r or p are in bounds, since both are bound by the maximum
// and the minimum values for this type

Self { n, r, p }
Expand Down

0 comments on commit ddfcb1c

Please sign in to comment.