diff --git a/README.md b/README.md index fcf0415..d3615cd 100755 --- a/README.md +++ b/README.md @@ -73,11 +73,12 @@ function deriveKey(password, salt, iterations): Usage: slowkey [COMMAND] Commands: - derive Derive a key using using Scrypt, Argon2, SHA2, and SHA3 - show-checkpoint Decrypt and print a checkpoint - show-output Decrypt and print an output file - test Print test vectors - bench Run benchmarks + derive Derive a key using using Scrypt, Argon2, Balloon Hash, SHA2, and SHA3 + restore-from-checkpoint Continue derivation process from an existing checkpoint + show-checkpoint Decrypt and print a checkpoint + show-output Decrypt and print an output file + test Print test vectors + bench Run benchmarks Options: -h, --help Print help @@ -87,19 +88,11 @@ Options: ### Deriving ```sh -Derive a key using using Scrypt, Argon2, SHA2, and SHA3 - -Usage: slowkey derive [OPTIONS] - Options: -i, --iterations Number of iterations (must be greater than 1 and lesser than or equal to 4294967295) [default: 100] -l, --length Length of the derived result (must be greater than 9 and lesser than or equal to 64) [default: 32] - --base64 - Show the result in Base64 (in addition to hex) - --base58 - Show the result in Base58 (in addition to hex) --output Optional path for storing the encrypted output --scrypt-n @@ -122,8 +115,40 @@ Options: Frequency of saving encrypted checkpoints to disk, specified as the number of iterations between each save --max-checkpoints-to-keep Specifies the number of most recent checkpoints to keep, while automatically deleting older ones [default: 1] - --restore-from-checkpoint + --base64 + Show the result in Base64 (in addition to hex) + --base58 + Show the result in Base58 (in addition to hex) + --iteration-moving-window + Iteration time sampling moving window size [default: 10] + -h, --help + Print help +``` + +### Restoring from a checkpoint + +```sh +Continue derivation process from an existing checkpoint + +Usage: slowkey restore-from-checkpoint [OPTIONS] --checkpoint + +Options: + -i, --iterations + Number of iterations (must be greater than 1 and lesser than or equal to 4294967295) [default: 100] + --output + Optional path for storing the encrypted output + --checkpoint-dir + Optional directory for storing encrypted checkpoints, each appended with an iteration-specific suffix. For each iteration i, the corresponding checkpoint file is named "checkpoint.i", indicating the iteration number at which the checkpoint was created + --checkpoint-interval + Frequency of saving encrypted checkpoints to disk, specified as the number of iterations between each save + --max-checkpoints-to-keep + Specifies the number of most recent checkpoints to keep, while automatically deleting older ones [default: 1] + --checkpoint Path to an existing checkpoint from which to resume the derivation process + --base64 + Show the result in Base64 (in addition to hex) + --base58 + Show the result in Base58 (in addition to hex) --iteration-moving-window Iteration time sampling moving window size [default: 10] -h, --help @@ -399,7 +424,7 @@ The password, salt and internal data are correct Let's continue the derivation process from this checkpoint and verify that we arrive at the same final result as before. Please make sure to specify the correct number of iterations, as the checkpoint does not store the original iteration count. -> slowkey derive -i 10 --restore-from-checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 +> slowkey restore-from-checkpoint -i 10 --checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 ```sh @@ -454,7 +479,7 @@ Average iteration time: 1s 993ms In addition to the above, you can use a checkpoint while specifying a larger iteration count. For example, if you originally ran 10,000 iterations and want to continue from checkpoint 9,000, you can set a higher iteration count, such as 100,000, when restoring from this checkpoint: -> slowkey derive -i 20 --restore-from-checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 +> slowkey restore-from-checkpoint -i 20 --checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 ```sh Please input all data either in raw or hex format starting with the 0x prefix @@ -647,8 +672,6 @@ The password, salt and internal data are correct ## Test Vectors -TODO: - In order to verify the validity of SlowKey, you can run the `test` command: > slowkey test diff --git a/src/main.rs b/src/main.rs index 8a72f8b..8aa3eca 100755 --- a/src/main.rs +++ b/src/main.rs @@ -74,20 +74,6 @@ enum Commands { )] length: usize, - #[arg( - long, - action = clap::ArgAction::SetTrue, - help = "Show the result in Base64 (in addition to hex)" - )] - base64: bool, - - #[arg( - long, - action = clap::ArgAction::SetTrue, - help = "Show the result in Base58 (in addition to hex)" - )] - base58: bool, - #[arg(long, help = "Optional path for storing the encrypted output")] output: Option, @@ -138,21 +124,18 @@ enum Commands { #[arg( long, - requires = "checkpoint_interval", help = "Optional directory for storing encrypted checkpoints, each appended with an iteration-specific suffix. For each iteration i, the corresponding checkpoint file is named \"checkpoint.i\", indicating the iteration number at which the checkpoint was created" )] checkpoint_dir: Option, #[arg( long, - requires = "checkpoint_path", help = "Frequency of saving encrypted checkpoints to disk, specified as the number of iterations between each save" )] checkpoint_interval: Option, #[arg( long, - requires = "checkpoint_path", default_value = CheckpointOptions::DEFAULT_MAX_CHECKPOINTS_TO_KEEP.to_string(), help = format!("Specifies the number of most recent checkpoints to keep, while automatically deleting older ones") )] @@ -160,10 +143,73 @@ enum Commands { #[arg( long, - requires = "checkpoint_path", + action = clap::ArgAction::SetTrue, + help = "Show the result in Base64 (in addition to hex)" + )] + base64: bool, + + #[arg( + long, + action = clap::ArgAction::SetTrue, + help = "Show the result in Base58 (in addition to hex)" + )] + base58: bool, + + #[arg(long, default_value = "10", help = "Iteration time sampling moving window size")] + iteration_moving_window: u32, + }, + + #[command(about = "Continue derivation process from an existing checkpoint")] + RestoreFromCheckpoint { + #[arg( + short, + long, + default_value = SlowKeyOptions::default().iterations.to_string(), + help = format!("Number of iterations (must be greater than {} and lesser than or equal to {})", SlowKeyOptions::MIN_ITERATIONS, SlowKeyOptions::MAX_ITERATIONS) + )] + iterations: usize, + + #[arg(long, help = "Optional path for storing the encrypted output")] + output: Option, + + #[arg( + long, + help = "Optional directory for storing encrypted checkpoints, each appended with an iteration-specific suffix. For each iteration i, the corresponding checkpoint file is named \"checkpoint.i\", indicating the iteration number at which the checkpoint was created" + )] + checkpoint_dir: Option, + + #[arg( + long, + help = "Frequency of saving encrypted checkpoints to disk, specified as the number of iterations between each save" + )] + checkpoint_interval: Option, + + #[arg( + long, + default_value = CheckpointOptions::DEFAULT_MAX_CHECKPOINTS_TO_KEEP.to_string(), + help = format!("Specifies the number of most recent checkpoints to keep, while automatically deleting older ones") + )] + max_checkpoints_to_keep: usize, + + #[arg( + long, help = "Path to an existing checkpoint from which to resume the derivation process" )] - restore_from_checkpoint: Option, + checkpoint: PathBuf, + + #[arg( + long, + action = clap::ArgAction::SetTrue, + help = "Show the result in Base64 (in addition to hex)" + )] + base64: bool, + + #[arg( + long, + action = clap::ArgAction::SetTrue, + help = "Show the result in Base58 (in addition to hex)" + )] + base58: bool, #[arg(long, default_value = "10", help = "Iteration time sampling moving window size")] iteration_moving_window: u32, @@ -445,327 +491,375 @@ fn show_hint(data: &str, description: &str, hex: bool) { } } -fn main() { - better_panic::install(); - color_backtrace::install(); +struct DeriveOptions { + options: SlowKeyOptions, + checkpoint_data: Option, + output_key: Option>, + checkpoint_dir: Option, + checkpoint_interval: Option, + max_checkpoints_to_keep: usize, + output: Option, + base64: bool, + base58: bool, + iteration_moving_window: u32, +} - // Initialize libsodium - initialize(); +fn derive(derive_options: DeriveOptions) { + let options = derive_options.options; + let mut output_key = derive_options.output_key; + let mut checkpoint: Option = None; + + let mut out: Option = None; + if let Some(path) = derive_options.output { + if output_key.is_none() { + output_key = Some(get_output_key()); + } + + out = Some(Output::new(&OutputOptions { + path, + key: output_key.clone().unwrap(), + slowkey: options.clone(), + })) + } - println!("SlowKey v{VERSION}\n"); + let mut checkpointing_interval: usize = 0; - let cli = Cli::parse(); + if let Some(dir) = derive_options.checkpoint_dir { + checkpointing_interval = derive_options.checkpoint_interval.unwrap(); - match cli.command { - Some(Commands::Derive { - iterations, - length, - base64, - base58, - output, - scrypt_n, - scrypt_r, - scrypt_p, - argon2_m_cost, - argon2_t_cost, - balloon_s_cost, - balloon_t_cost, - checkpoint_interval, - checkpoint_dir, - restore_from_checkpoint, - max_checkpoints_to_keep, - iteration_moving_window, - }) => { - println!( - "Please input all data either in raw or hex format starting with the {} prefix\n", - HEX_PREFIX - ); + if output_key.is_none() { + output_key = Some(get_output_key()); + } - let slowkey_opts: SlowKeyOptions; + checkpoint = Some(Checkpoint::new(&CheckpointOptions { + iterations: options.iterations, + dir: dir.to_owned(), + key: output_key.clone().unwrap(), + max_checkpoints_to_keep: derive_options.max_checkpoints_to_keep, + slowkey: options.clone(), + })); - let mut output_key: Option> = None; - let mut checkpoint: Option = None; - let mut restore_from_checkpoint_data: Option = None; + println!( + "Checkpoint will be created every {} iterations and saved to the \"{}\" checkpoints directory\n", + checkpointing_interval.to_string().cyan(), + &dir.to_string_lossy().cyan() + ); + } - let mut offset: usize = 0; - let mut offset_data = Vec::new(); + if let Some(checkpoint_data) = &derive_options.checkpoint_data { + checkpoint_data.print(DisplayOptions::default()); + } - if let Some(path) = restore_from_checkpoint { - if output_key.is_none() { - output_key = Some(get_output_key()); - } + options.print(); - let checkpoint_data = Checkpoint::get_checkpoint(&OpenCheckpointOptions { - key: output_key.clone().unwrap(), - path: path.clone(), - }); + let salt = get_salt(); + let password = get_password(); - if iterations <= checkpoint_data.data.iteration { - panic!( - "Invalid iterations number {} for checkpoint {}", - iterations, checkpoint_data.data.iteration - ); - } + let mut offset: usize = 0; + let mut offset_data = Vec::new(); + let mut prev_data = Vec::new(); - checkpoint_data.print(DisplayOptions::default()); + if let Some(checkpoint_data) = &derive_options.checkpoint_data { + println!("Verifying the checkpoint...\n"); - let opts = &checkpoint_data.data.slowkey; - slowkey_opts = SlowKeyOptions { - iterations, - length: opts.length, - scrypt: opts.scrypt, - argon2id: opts.argon2id, - ballon_hash: opts.balloon_hash, - }; + if checkpoint_data.data.iteration > 0 { + if !checkpoint_data.verify(&salt, &password) { + panic!("The password, salt, or internal data is incorrect!"); + } - offset = checkpoint_data.data.iteration + 1; - offset_data.clone_from(&checkpoint_data.data.data); + println!("The password, salt and internal data are correct\n"); + } else { + println!("{}: Unable to verify the first checkpoint\n", "Warning".dark_yellow()); + } - restore_from_checkpoint_data = Some(checkpoint_data) - } else { - slowkey_opts = SlowKeyOptions::new( - iterations, - length, - &ScryptOptions::new(scrypt_n, scrypt_r, scrypt_p), - &Argon2idOptions::new(argon2_m_cost, argon2_t_cost), - &BalloonHashOptions::new(balloon_s_cost, balloon_t_cost), - ); - } + offset = checkpoint_data.data.iteration + 1; + offset_data.clone_from(&checkpoint_data.data.data); - let mut out: Option = None; - if let Some(path) = output { - if output_key.is_none() { - output_key = Some(get_output_key()); - } + // Since we are starting from this checkpoint, set the rolling previous data to its data + prev_data = checkpoint_data.data.data.clone(); + } - out = Some(Output::new(&OutputOptions { - path, - key: output_key.clone().unwrap(), - slowkey: slowkey_opts.clone(), - })) - } + let mb = MultiProgress::new(); - let mut checkpointing_interval: usize = 0; + // Create the main progress bar. Please note that we are using a custom message, instead of percents, since + // we want a higher resolution that the default one + let pb = mb + .add(ProgressBar::new(options.iterations as u64)) + .with_style(ProgressStyle::with_template("{bar:80.cyan/blue} {pos:>8}/{len:8} {msg}% ({eta})").unwrap()); - if let Some(dir) = checkpoint_dir { - checkpointing_interval = checkpoint_interval.unwrap(); + pb.set_position(offset as u64); + pb.reset_eta(); + pb.enable_steady_tick(Duration::from_secs(1)); - if output_key.is_none() { - output_key = Some(get_output_key()); - } + // Set the percent using a custom message + pb.set_message(format!("{}", (offset * 100) as f64 / options.iterations as f64)); - checkpoint = Some(Checkpoint::new(&CheckpointOptions { - iterations: slowkey_opts.iterations, - dir: dir.to_owned(), - key: output_key.clone().unwrap(), - max_checkpoints_to_keep, - slowkey: slowkey_opts.clone(), - })); + // Create a progress bar to track iteration times and checkpoints + let ipb = mb + .add(ProgressBar::new(options.iterations as u64)) + .with_style(ProgressStyle::with_template("{msg}").unwrap()); - println!( - "Checkpoint will be created every {} iterations and saved to the \"{}\" checkpoints directory\n", - checkpointing_interval.to_string().cyan(), - &dir.to_string_lossy().cyan() + let start_time = SystemTime::now(); + let running_time = Instant::now(); + let mut iteration_time = Instant::now(); + let mut samples: VecDeque = VecDeque::new(); + + let slowkey = SlowKey::new(&options); + + let mut checkpoint_info = String::new(); + + let prev_data_mutex = Arc::new(Mutex::new(prev_data)); + let prev_data_thread = Arc::clone(&prev_data_mutex); + + let handle = thread::spawn(move || { + let key = slowkey.derive_key_with_callback( + &salt, + &password, + &offset_data, + offset, + |current_iteration, current_data| { + // Track iteration times + let last_iteration_time = iteration_time.elapsed().as_millis(); + iteration_time = Instant::now(); + + samples.push_back(last_iteration_time); + + // If we have more than the required samples, remove the oldest one + if samples.len() > derive_options.iteration_moving_window as usize { + samples.pop_front(); + } + + // Calculate the moving average + let moving_average = samples.iter().sum::() as f64 / samples.len() as f64; + + let iteration_info = format!( + "\nIteration time moving average ({}): {}, last iteration time: {}", + derive_options.iteration_moving_window.to_string().cyan(), + format_duration(Duration::from_millis(moving_average as u64)) + .to_string() + .cyan(), + format_duration(Duration::from_millis(last_iteration_time as u64)) + .to_string() + .cyan(), ); - } - slowkey_opts.print(); + let mut prev_data = prev_data_thread.lock().unwrap(); - let salt = get_salt(); - let password = get_password(); + // Create a checkpoint if we've reached the checkpoint interval + if checkpointing_interval != 0 && (current_iteration + 1) % checkpointing_interval == 0 { + let prev_data: Option<&[u8]> = if current_iteration == 0 { None } else { Some(&prev_data) }; - let mut prev_data = Vec::new(); + if let Some(checkpoint) = &mut checkpoint { + checkpoint.create_checkpoint(current_iteration, current_data, prev_data); - if let Some(checkpoint_data) = restore_from_checkpoint_data { - println!("Verifying the checkpoint...\n"); + let hash = Checkpoint::hash_checkpoint(current_iteration, current_data, prev_data); - if checkpoint_data.data.iteration > 0 { - if !checkpoint_data.verify(&salt, &password) { - panic!("The password, salt, or internal data is incorrect!"); + checkpoint_info = format!( + "\nCreated checkpoint #{} with data hash {}", + (current_iteration + 1).to_string().cyan(), + format!("0x{}", hex::encode(hash)).cyan() + ); } + } - println!("The password, salt and internal data are correct\n"); - } else { - println!("{}: Unable to verify the first checkpoint\n", "Warning".dark_yellow()); + // Store the current data in order to store it in the checkpoint for future verification of the + // parameters + if current_iteration < options.iterations - 1 { + prev_data.clone_from(current_data); } - // Since we are starting from this checkpoint, set the rolling previous data to its data - prev_data = checkpoint_data.data.data; - } + pb.inc(1); - let mb = MultiProgress::new(); + // Set the percent using a custom message + pb.set_message(format!( + "{}", + ((current_iteration + 1) * 100) as f64 / options.iterations as f64 + )); - // Create the main progress bar. Please note that we are using a custom message, instead of percents, since - // we want a higher resolution that the default one - let pb = mb.add(ProgressBar::new(iterations as u64)).with_style( - ProgressStyle::with_template("{bar:80.cyan/blue} {pos:>8}/{len:8} {msg}% ({eta})").unwrap(), - ); + ipb.set_message(format!("{}{}", iteration_info, checkpoint_info)); + }, + ); - pb.set_position(offset as u64); - pb.reset_eta(); - pb.enable_steady_tick(Duration::from_secs(1)); - - // Set the percent using a custom message - pb.set_message(format!("{}", (offset * 100) as f64 / iterations as f64)); - - // Create a progress bar to track iteration times and checkpoints - let ipb = mb - .add(ProgressBar::new(iterations as u64)) - .with_style(ProgressStyle::with_template("{msg}").unwrap()); - - let start_time = SystemTime::now(); - let running_time = Instant::now(); - let mut iteration_time = Instant::now(); - let mut samples: VecDeque = VecDeque::new(); - - let slowkey = SlowKey::new(&slowkey_opts); - - let mut checkpoint_info = String::new(); - - let prev_data_mutex = Arc::new(Mutex::new(prev_data)); - let prev_data_thread = Arc::clone(&prev_data_mutex); - - let handle = thread::spawn(move || { - let key = slowkey.derive_key_with_callback( - &salt, - &password, - &offset_data, - offset, - |current_iteration, current_data| { - // Track iteration times - let last_iteration_time = iteration_time.elapsed().as_millis(); - iteration_time = Instant::now(); - - samples.push_back(last_iteration_time); - - // If we have more than the required samples, remove the oldest one - if samples.len() > iteration_moving_window as usize { - samples.pop_front(); - } - - // Calculate the moving average - let moving_average = samples.iter().sum::() as f64 / samples.len() as f64; - - let iteration_info = format!( - "\nIteration time moving average ({}): {}, last iteration time: {}", - iteration_moving_window.to_string().cyan(), - format_duration(Duration::from_millis(moving_average as u64)) - .to_string() - .cyan(), - format_duration(Duration::from_millis(last_iteration_time as u64)) - .to_string() - .cyan(), - ); + pb.finish(); + ipb.finish(); - let mut prev_data = prev_data_thread.lock().unwrap(); + key + }); - // Create a checkpoint if we've reached the checkpoint interval - if checkpointing_interval != 0 && (current_iteration + 1) % checkpointing_interval == 0 { - let prev_data: Option<&[u8]> = if current_iteration == 0 { None } else { Some(&prev_data) }; + mb.clear().unwrap(); - if let Some(checkpoint) = &mut checkpoint { - checkpoint.create_checkpoint(current_iteration, current_data, prev_data); + let key = handle.join().unwrap(); - let hash = Checkpoint::hash_checkpoint(current_iteration, current_data, prev_data); + println!( + "\n\nKey is (please highlight to see): {}", + format!("0x{}", hex::encode(&key)).black().on_black() + ); - checkpoint_info = format!( - "\nCreated checkpoint #{} with data hash {}", - (current_iteration + 1).to_string().cyan(), - format!("0x{}", hex::encode(hash)).cyan() - ); - } - } + if derive_options.base64 { + println!( + "Key (base64) is (please highlight to see): {}", + general_purpose::STANDARD.encode(&key).black().on_black() + ); + } - // Store the current data in order to store it in the checkpoint for future verification of the - // parameters - if current_iteration < iterations - 1 { - prev_data.clone_from(current_data); - } + if derive_options.base58 { + println!( + "Key (base58) is (please highlight to see): {}", + bs58::encode(&key).into_string().black().on_black() + ); + } - pb.inc(1); + println!(); - // Set the percent using a custom message - pb.set_message(format!( - "{}", - ((current_iteration + 1) * 100) as f64 / iterations as f64 - )); + if let Some(out) = out { + let prev_data_guard = prev_data_mutex.lock().unwrap(); + let prev_data_option: Option<&[u8]> = if prev_data_guard.is_empty() { + None + } else { + Some(&prev_data_guard[..]) + }; - ipb.set_message(format!("{}{}", iteration_info, checkpoint_info)); - }, - ); + out.save(options.iterations, &key, prev_data_option); - pb.finish(); - ipb.finish(); + println!("Saved encrypted output to \"{}\"\n", &out.path.to_str().unwrap().cyan(),); + } - key - }); + println!( + "Start time: {}", + DateTime::::from(start_time) + .format("%Y-%m-%d %H:%M:%S") + .to_string() + .cyan() + ); + println!( + "End time: {}", + DateTime::::from(SystemTime::now()) + .format("%Y-%m-%d %H:%M:%S") + .to_string() + .cyan() + ); + println!( + "Total running time: {}", + format_duration(Duration::from_secs(running_time.elapsed().as_secs())) + .to_string() + .cyan() + ); + println!( + "Average iteration time: {}\n", + format_duration(Duration::from_millis( + (running_time.elapsed().as_millis() as f64 / options.iterations as f64).round() as u64 + )) + .to_string() + .cyan() + ); +} - mb.clear().unwrap(); +fn print_input_instructions() { + println!( + "Please input all data either in raw or hex format starting with the {} prefix\n", + HEX_PREFIX + ); +} + +fn main() { + better_panic::install(); + color_backtrace::install(); - let key = handle.join().unwrap(); + // Initialize libsodium + initialize(); - println!( - "\n\nKey is (please highlight to see): {}", - format!("0x{}", hex::encode(&key)).black().on_black() - ); + println!("SlowKey v{VERSION}\n"); - if base64 { - println!( - "Key (base64) is (please highlight to see): {}", - general_purpose::STANDARD.encode(&key).black().on_black() - ); - } + let cli = Cli::parse(); - if base58 { - println!( - "Key (base58) is (please highlight to see): {}", - bs58::encode(&key).into_string().black().on_black() - ); - } + match cli.command { + Some(Commands::Derive { + iterations, + length, + output, + scrypt_n, + scrypt_r, + scrypt_p, + argon2_m_cost, + argon2_t_cost, + balloon_s_cost, + balloon_t_cost, + checkpoint_dir, + checkpoint_interval, + max_checkpoints_to_keep, + base64, + base58, + iteration_moving_window, + }) => { + print_input_instructions(); - println!(); + derive(DeriveOptions { + options: SlowKeyOptions::new( + iterations, + length, + &ScryptOptions::new(scrypt_n, scrypt_r, scrypt_p), + &Argon2idOptions::new(argon2_m_cost, argon2_t_cost), + &BalloonHashOptions::new(balloon_s_cost, balloon_t_cost), + ), + checkpoint_data: None, + output_key: None, + checkpoint_dir, + checkpoint_interval, + max_checkpoints_to_keep, + output, + base64, + base58, + iteration_moving_window, + }); + }, - if let Some(out) = out { - let prev_data_guard = prev_data_mutex.lock().unwrap(); - let prev_data_option: Option<&[u8]> = if prev_data_guard.is_empty() { - None - } else { - Some(&prev_data_guard[..]) - }; + Some(Commands::RestoreFromCheckpoint { + iterations, + output, + checkpoint_dir, + checkpoint_interval, + max_checkpoints_to_keep, + checkpoint, + base64, + base58, + iteration_moving_window, + }) => { + print_input_instructions(); - out.save(iterations, &key, prev_data_option); + let output_key = get_output_key(); + let checkpoint_data = Checkpoint::get_checkpoint(&OpenCheckpointOptions { + key: output_key.clone(), + path: checkpoint, + }); - println!("Saved encrypted output to \"{}\"\n", &out.path.to_str().unwrap().cyan(),); + if iterations <= checkpoint_data.data.iteration { + panic!( + "Invalid iterations number {} for checkpoint {}", + iterations, checkpoint_data.data.iteration + ); } - println!( - "Start time: {}", - DateTime::::from(start_time) - .format("%Y-%m-%d %H:%M:%S") - .to_string() - .cyan() - ); - println!( - "End time: {}", - DateTime::::from(SystemTime::now()) - .format("%Y-%m-%d %H:%M:%S") - .to_string() - .cyan() - ); - println!( - "Total running time: {}", - format_duration(Duration::from_secs(running_time.elapsed().as_secs())) - .to_string() - .cyan() - ); - println!( - "Average iteration time: {}\n", - format_duration(Duration::from_millis( - (running_time.elapsed().as_millis() as f64 / iterations as f64).round() as u64 - )) - .to_string() - .cyan() - ); + let opts = &checkpoint_data.data.slowkey; + let options = SlowKeyOptions { + iterations, + length: opts.length, + scrypt: opts.scrypt, + argon2id: opts.argon2id, + ballon_hash: opts.balloon_hash, + }; + + derive(DeriveOptions { + options, + checkpoint_data: Some(checkpoint_data), + output_key: Some(output_key), + checkpoint_dir, + checkpoint_interval, + max_checkpoints_to_keep, + output, + base64, + base58, + iteration_moving_window, + }); }, Some(Commands::ShowCheckpoint { @@ -774,13 +868,9 @@ fn main() { base64, base58, }) => { - println!( - "Please input all data either in raw or hex format starting with the {} prefix\n", - HEX_PREFIX - ); + print_input_instructions(); let output_key = get_output_key(); - let checkpoint_data = Checkpoint::get_checkpoint(&OpenCheckpointOptions { key: output_key, path: checkpoint, @@ -816,13 +906,9 @@ fn main() { base64, base58, }) => { - println!( - "Please input all data either in raw or hex format starting with the {} prefix\n", - HEX_PREFIX - ); + print_input_instructions(); let output_key = get_output_key(); - let output_data = Output::get(&OpenOutputOptions { key: output_key, path: output,