diff --git a/README.md b/README.md index 70e8d8d..db0ac2b 100755 --- a/README.md +++ b/README.md @@ -169,8 +169,9 @@ Checkpoint operations Usage: slowkey checkpoint Commands: - show Print a checkpoint - restore Continue derivation process from an existing checkpoint + show Print a checkpoint + restore Continue derivation process from an existing checkpoint + reencrypt Reencrypt a checkpoint Options: -h, --help Print help @@ -181,14 +182,14 @@ Options: ```sh Print a checkpoint -Usage: slowkey checkpoint show [OPTIONS] --checkpoint +Usage: slowkey checkpoint show [OPTIONS] --path Options: - --checkpoint Path to an existing checkpoint - --verify Verify that the password and salt match the checkpoint - --base64 Show the result in Base64 (in addition to hex) - --base58 Show the result in Base58 (in addition to hex) - -h, --help Print help + --path Path to an existing checkpoint + --verify Verify that the password and salt match the checkpoint + --base64 Show the result in Base64 (in addition to hex) + --base58 Show the result in Base58 (in addition to hex) + -h, --help Print help ``` #### Restoring from a Checkpoint @@ -209,7 +210,7 @@ Options: Frequency of saving encrypted checkpoints to disk, specified as the number of iterations between each save [default: 1] --max-checkpoints-to-keep Specifies the number of most recent checkpoints to keep, while automatically deleting older ones [default: 1] - --checkpoint + --path 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) @@ -225,6 +226,19 @@ Options: Print help ``` +#### Reencrypting a Checkpoint + +```sh +Reencrypt a checkpoint + +Usage: slowkey checkpoint reencrypt --input --output + +Options: + --input Path to an existing checkpoint + --output Path to the new checkpoint + -h, --help Print help +``` + ### Outputs ```sh @@ -233,7 +247,8 @@ Output operations Usage: slowkey output Commands: - show Print an output file + show Print an output file + reencrypt Reencrypt an output file Options: -h, --help Print help @@ -244,13 +259,26 @@ Options: ```sh Print an output file -Usage: slowkey output show [OPTIONS] --output +Usage: slowkey output show [OPTIONS] --path + +Options: + --path Path to an existing output + --verify Verify that the password and salt match the output + --base64 Show the result in Base64 (in addition to hex) + --base58 Show the result in Base58 (in addition to hex) + -h, --help Print help +``` + +#### Reencrypting an Output + +```sh +Reencrypt an output file + +Usage: slowkey output reencrypt --input --output Options: - --output Path to an existing output - --verify Verify that the password and salt match the output - --base64 Show the result in Base64 (in addition to hex) - --base58 Show the result in Base58 (in addition to hex) + --input Path to an existing output + --output Path to the new checkpoint -h, --help Print help ``` @@ -458,7 +486,7 @@ We can see that the `checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4 Let's use the `checkpoint show` command to decrypt its contents and verify the parameters: -> slowkey checkpoint show --checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 +> slowkey checkpoint show --path ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 ```sh Please input all data either in raw or hex format starting with the 0x prefix @@ -480,7 +508,7 @@ SlowKey Parameters: We can also verify that the password and salt match the checkpoint by passing the optional `--verify` flag: -> slowkey checkpoint show --checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 --verify +> slowkey checkpoint show --path ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 --verify ```sh Please input all data either in raw or hex format starting with the 0x prefix @@ -514,7 +542,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 checkpoint restore -i 10 --checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 +> slowkey checkpoint restore -i 10 --path ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 ```sh Please input all data either in raw or hex format starting with the 0x prefix @@ -570,7 +598,7 @@ Average iteration time: 2s 40ms 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 checkpoint restore -i 20 --checkpoint ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 +> slowkey checkpoint restore -i 20 --path ~/checkpoints/checkpoint.05.c33f06fe6bdaac774ab473181aa4fe46a3baadee4b8f4dc02be2248dea5308c0 ```sh Please input all data either in raw or hex format starting with the 0x prefix @@ -771,7 +799,7 @@ Average iteration time: 2s 717ms Let's use the `output show` command to decrypt its contents: -> slowkey output show --output ~/output.enc +> slowkey output show --path ~/output.enc ```sh Output: @@ -791,7 +819,7 @@ Fingerprint: E5E61F417790448A The output file checkpoint, except for the one that coincides with the first iteration, also includes the output of the previous iteration. This allows the system to verify that the password and salt match the output by attempting to derive the output's data from the previous iteration's data. This verification is optional and requires the `--verify` flag: -> slowkey output show --output ~/output.enc --verify +> slowkey output show --path ~/output.enc --verify ```sh Please input all data either in raw or hex format starting with the 0x prefix diff --git a/src/main.rs b/src/main.rs index 6494551..14143c6 100755 --- a/src/main.rs +++ b/src/main.rs @@ -205,7 +205,7 @@ enum CheckpointCommands { #[command(about = "Print a checkpoint", arg_required_else_help = true)] Show { #[arg(long, help = "Path to an existing checkpoint")] - checkpoint: PathBuf, + path: PathBuf, #[arg(long, help = "Verify that the password and salt match the checkpoint")] verify: bool, @@ -266,7 +266,7 @@ enum CheckpointCommands { long, help = "Path to an existing checkpoint from which to resume the derivation process" )] - checkpoint: Option, + path: Option, #[arg( long, @@ -303,7 +303,7 @@ enum CheckpointCommands { #[command(about = "Reencrypt a checkpoint", arg_required_else_help = true)] Reencrypt { #[arg(long, help = "Path to an existing checkpoint")] - checkpoint: PathBuf, + input: PathBuf, #[arg(long, help = "Path to the new checkpoint")] output: PathBuf, @@ -315,7 +315,7 @@ enum OutputCommands { #[command(about = "Print an output file", arg_required_else_help = true)] Show { #[arg(long, help = "Path to an existing output")] - output: PathBuf, + path: PathBuf, #[arg(long, help = "Verify that the password and salt match the output")] verify: bool, @@ -334,6 +334,15 @@ enum OutputCommands { )] base58: bool, }, + + #[command(about = "Reencrypt an output file", arg_required_else_help = true)] + Reencrypt { + #[arg(long, help = "Path to an existing output")] + input: PathBuf, + + #[arg(long, help = "Path to the new checkpoint")] + output: PathBuf, + }, } const BENCHMARKS_DIRECTORY: &str = "benchmarks"; @@ -1033,7 +1042,7 @@ fn main() { Commands::Checkpoint(cmd) => match cmd { CheckpointCommands::Show { - checkpoint, + path, verify, base64, base58, @@ -1041,10 +1050,7 @@ fn main() { print_input_instructions(); let file_key = get_file_key(); - let checkpoint_data = Checkpoint::open(&OpenCheckpointOptions { - key: file_key, - path: checkpoint, - }); + let checkpoint_data = Checkpoint::open(&OpenCheckpointOptions { key: file_key, path }); checkpoint_data.print(DisplayOptions { base64, @@ -1072,7 +1078,7 @@ fn main() { checkpoint_dir, checkpoint_interval, max_checkpoints_to_keep, - checkpoint, + path, interactive, base64, base58, @@ -1083,7 +1089,7 @@ fn main() { let mut file_key: Option> = None; - let checkpoint_data = match checkpoint { + let checkpoint_data = match path { Some(path) => { let key = get_file_key(); file_key = Some(key.clone()); @@ -1127,7 +1133,7 @@ fn main() { }); }, - CheckpointCommands::Reencrypt { checkpoint, output } => { + CheckpointCommands::Reencrypt { input, output } => { print_input_instructions(); let key = get_file_key(); @@ -1136,19 +1142,15 @@ fn main() { let new_key = get_file_key(); - Checkpoint::reencrypt(&checkpoint, key, &output, new_key); + Checkpoint::reencrypt(&input, key, &output, new_key); - println!( - "Reencrypted checkpoint at \"{}\" and saved at \"{}\"", - checkpoint.to_string_lossy(), - output.to_string_lossy() - ); + println!("Saved new checkpoint at \"{}\"", output.to_string_lossy()); }, }, Commands::Output(cmd) => match cmd { OutputCommands::Show { - output, + path, verify, base64, base58, @@ -1156,10 +1158,7 @@ fn main() { print_input_instructions(); let file_key = get_file_key(); - let output_data = Output::get(&OpenOutputOptions { - key: file_key, - path: output, - }); + let output_data = Output::open(&OpenOutputOptions { key: file_key, path }); output_data.print(DisplayOptions { base64, @@ -1180,6 +1179,20 @@ fn main() { println!("The password, salt and internal data are correct\n"); } }, + + OutputCommands::Reencrypt { input, output } => { + print_input_instructions(); + + let key = get_file_key(); + + println!("Please provide the new file encryption key:\n"); + + let new_key = get_file_key(); + + Output::reencrypt(&input, key, &output, new_key); + + println!("Saved new output at \"{}\"", output.to_string_lossy()); + }, }, Commands::Bench {} => { diff --git a/src/utils/checkpoints/checkpoint.rs b/src/utils/checkpoints/checkpoint.rs index f337334..2081744 100644 --- a/src/utils/checkpoints/checkpoint.rs +++ b/src/utils/checkpoints/checkpoint.rs @@ -253,7 +253,7 @@ impl Checkpoint { }, }; - self.store(&checkpoint_path, &checkpoint); + self.save(&checkpoint_path, &checkpoint); self.data = CheckpointData { version: Version::V2, @@ -305,7 +305,7 @@ impl Checkpoint { } } - fn store(&mut self, checkpoint_path: &Path, checkpoint: &CheckpointData) { + fn save(&mut self, checkpoint_path: &Path, checkpoint: &CheckpointData) { let file = File::create(checkpoint_path).unwrap(); let mut writer = BufWriter::new(file); @@ -328,29 +328,20 @@ impl Checkpoint { self.checkpoint_paths.push_back(checkpoint_path.to_path_buf()); } - pub fn reencrypt(checkpoint_path: &Path, key: Vec, output_path: &Path, new_key: Vec) { - if !checkpoint_path.exists() { - panic!( - "Input checkpoint path \"{}\" does not exist", - checkpoint_path.to_string_lossy() - ); + pub fn reencrypt(input_path: &Path, key: Vec, output_path: &Path, new_key: Vec) { + if !input_path.exists() { + panic!("Input path \"{}\" does not exist", input_path.to_string_lossy()); } - if checkpoint_path.is_dir() { - panic!( - "Input checkpoint path \"{}\" is a directory", - checkpoint_path.to_string_lossy() - ); + if input_path.is_dir() { + panic!("Input path \"{}\" is a directory", input_path.to_string_lossy()); } if output_path.exists() { - panic!( - "Output checkpoint path \"{}\" already exists", - output_path.to_string_lossy() - ); + panic!("Output path \"{}\" already exists", output_path.to_string_lossy()); } - let checkpoint_file = File::open(checkpoint_path).unwrap(); + let checkpoint_file = File::open(input_path).unwrap(); let mut reader = BufReader::new(checkpoint_file); // Read the first byte (version) diff --git a/src/utils/outputs/output.rs b/src/utils/outputs/output.rs index a5d938f..1fd7651 100644 --- a/src/utils/outputs/output.rs +++ b/src/utils/outputs/output.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use std::{ fs::File, io::{BufReader, BufWriter, Read, Write}, - path::PathBuf, + path::{Path, PathBuf}, }; #[derive(PartialEq, Debug, Clone)] @@ -128,7 +128,7 @@ impl Output { } } - pub fn get(opts: &OpenOutputOptions) -> OutputData { + pub fn open(opts: &OpenOutputOptions) -> OutputData { if !opts.path.exists() { panic!("Output file \"{}\" does not exist", opts.path.to_str().unwrap()); } @@ -182,4 +182,47 @@ impl Output { writer.write_all(hex::encode(encrypted_data).as_bytes()).unwrap(); writer.flush().unwrap(); } + + pub fn reencrypt(input_path: &Path, key: Vec, output_path: &Path, new_key: Vec) { + if !input_path.exists() { + panic!("Input path \"{}\" does not exist", input_path.to_string_lossy()); + } + + if input_path.is_dir() { + panic!("Input path \"{}\" is a directory", input_path.to_string_lossy()); + } + + if output_path.exists() { + panic!("Output path \"{}\" already exists", output_path.to_string_lossy()); + } + + let output_file = File::open(input_path).unwrap(); + let mut reader = BufReader::new(output_file); + + // Read the first byte (version) + let mut version_byte = [0u8; 1]; + reader.read_exact(&mut version_byte).unwrap(); + + // Read the rest of the data + let mut encrypted_data = Vec::new(); + reader.read_to_end(&mut encrypted_data).unwrap(); + + // Decrypt tje data + let cipher = ChaCha20Poly1305::new(&key); + let data: SlowKeyData = cipher.decrypt(&hex::decode(encrypted_data).unwrap()); + + // Reencrypt the data + let new_cipher = ChaCha20Poly1305::new(&new_key); + let reencrypted_data = new_cipher.encrypt(Nonce::Random, &data); + + let output_file = File::create(output_path).unwrap(); + let mut writer = BufWriter::new(output_file); + + // Write the first byte (version) + writer.write_all(&version_byte).unwrap(); + + // Write the rest of the data + writer.write_all(hex::encode(reencrypted_data).as_bytes()).unwrap(); + writer.flush().unwrap(); + } }