Skip to content

Commit

Permalink
add encrypt and decrypt option cli
Browse files Browse the repository at this point in the history
  • Loading branch information
robatipoor committed Feb 21, 2024
1 parent 8ba0f45 commit 1b8eeaf
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 40 deletions.
36 changes: 28 additions & 8 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;

use crate::{
parse::{parse_auth, parse_expire_time, parse_key_and_nonce},
util::crypto::KeyAndNonce,
parse::{parse_auth, parse_expire_time, parse_key_nonce},
util::crypto::KeyNonce,
};

const HELP_ENCRYPT :&str = "The encrypt format should be `key:nonce`, with the key being 32 characters in length and the nonce being 19 characters.";
Expand All @@ -13,8 +13,12 @@ const HELP_DECRYPT :&str = "The decrypt format should be `key:nonce`, with the k
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[arg(short, long)]
pub server_addr: String,
#[arg(
short,
long,
help = "The server address format should be http:// or https:// followed by the IP address and port."
)]
pub server_addr: Option<String>,
#[clap(short, long, value_parser = parse_auth, help = "The auth format should be `username:password`")]
pub auth: Option<(String, String)>,
#[clap(subcommand)]
Expand All @@ -39,8 +43,8 @@ pub enum SubCommand {
progress_bar: bool,
#[clap(short, long)]
source_file: PathBuf,
#[clap(long, value_parser = parse_key_and_nonce, help = HELP_ENCRYPT)]
encrypt: Option<KeyAndNonce>,
#[clap(long, value_parser = parse_key_nonce, help = HELP_ENCRYPT)]
key_nonce: Option<KeyNonce>,
},
Delete {
#[arg(short, long)]
Expand All @@ -57,8 +61,24 @@ pub enum SubCommand {
url_path: String,
#[clap(short, long)]
destination: PathBuf,
#[clap(long, value_parser = parse_key_and_nonce, help = HELP_DECRYPT)]
decrypt: Option<KeyAndNonce>,
#[clap(long, value_parser = parse_key_nonce, help = HELP_DECRYPT)]
key_nonce: Option<KeyNonce>,
},
Encrypt {
#[clap(short, long)]
source_file: PathBuf,
#[clap(short, long)]
destination: PathBuf,
#[clap(long, value_parser = parse_key_nonce, help = HELP_ENCRYPT)]
key_nonce: KeyNonce,
},
Decrypt {
#[clap(short, long)]
source_file: PathBuf,
#[clap(short, long)]
destination: PathBuf,
#[clap(long, value_parser = parse_key_nonce, help = HELP_DECRYPT)]
key_nonce: KeyNonce,
},
}

Expand Down
40 changes: 31 additions & 9 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ mod util;
#[tokio::main]
async fn main() {
let args = Args::parse();
let client = CommandLineClient::new(args.server_addr);
match args.cmd {
SubCommand::Ping => {
let client = CommandLineClient::new(args.server_addr.expect("Server address should be set."));
let (_, resp) = client.health_check().await.unwrap();
match resp {
ApiResponseResult::Ok(resp) => {
Expand All @@ -37,21 +37,22 @@ async fn main() {
max_download,
out,
mut source_file,
encrypt,
key_nonce,
} => {
if source_file.is_dir() {
eprintln!("The source file option should be set to the path file.");
std::process::exit(1);
}
if let Some(encrypt) = encrypt.as_ref() {
source_file = encrypt_file(encrypt, &source_file).await.unwrap();
if let Some(key_nonce) = key_nonce.as_ref() {
source_file = encrypt_file(key_nonce, &source_file).await.unwrap();
}
let query = UploadQueryParam {
max_download,
code_length,
expire_secs: expire,
delete_manually,
};
let client = CommandLineClient::new(args.server_addr.expect("Server address should be set."));
let (_, resp) = if progress_bar {
client
.upload_with_progress_bar(&source_file, &query, args.auth)
Expand Down Expand Up @@ -80,16 +81,17 @@ async fn main() {
},
ApiResponseResult::Err(err) => print_response_err(&err),
}
if encrypt.is_some() {
// tokio::fs::remove_file(source_file).await.unwrap();
if key_nonce.is_some() {
tokio::fs::remove_file(source_file).await.unwrap();
}
}
SubCommand::Download {
progress_bar,
url_path,
destination,
decrypt,
key_nonce,
} => {
let client = CommandLineClient::new(args.server_addr.expect("Server address should be set."));
let (_, resp) = if progress_bar {
client
.download_with_progress_bar(&url_path, args.auth, destination)
Expand All @@ -102,15 +104,16 @@ async fn main() {
.unwrap();
match resp {
ApiResponseResult::Ok(encrypt_source_file) => {
if let Some(decrypt) = decrypt.as_ref() {
decrypt_file(decrypt, &encrypt_source_file).await.unwrap();
if let Some(key_nonce) = key_nonce.as_ref() {
decrypt_file(key_nonce, &encrypt_source_file).await.unwrap();
}
println!("{}", serde_json::to_string(&MessageResponse::ok()).unwrap());
}
ApiResponseResult::Err(err) => print_response_err(&err),
}
}
SubCommand::Info { url_path } => {
let client = CommandLineClient::new(args.server_addr.expect("Server address should be set."));
let (_, resp) = client.info(&url_path, args.auth).await.unwrap();
match resp {
ApiResponseResult::Ok(resp) => {
Expand All @@ -120,6 +123,7 @@ async fn main() {
}
}
SubCommand::Delete { url_path } => {
let client = CommandLineClient::new(args.server_addr.expect("Server address should be set."));
let (_, resp) = client.delete(&url_path, args.auth).await.unwrap();
match resp {
ApiResponseResult::Ok(resp) => {
Expand All @@ -128,6 +132,24 @@ async fn main() {
ApiResponseResult::Err(err) => print_response_err(&err),
}
}
SubCommand::Decrypt {
source_file,
destination,
key_nonce,
} => {
crate::util::crypto::encrypt(&key_nonce, &source_file, &destination)
.await
.unwrap();
}
SubCommand::Encrypt {
source_file,
destination,
key_nonce,
} => {
crate::util::crypto::encrypt(&key_nonce, &source_file, &destination)
.await
.unwrap();
}
};
}

Expand Down
6 changes: 3 additions & 3 deletions cli/src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use anyhow::anyhow;

use crate::util::crypto::{KeyAndNonce, KeyType, NonceType};
use crate::util::crypto::{KeyNonce, KeyType, NonceType};

pub fn parse_key_and_nonce(input: &str) -> anyhow::Result<KeyAndNonce> {
pub fn parse_key_nonce(input: &str) -> anyhow::Result<KeyNonce> {
let pos = input
.find(':')
.ok_or_else(|| anyhow!("Invalid key:nonce: no `:` found in {input}"))?;
let (key_str, nonce_str) = input.split_at(pos);
let key = KeyType::new(key_str)?;
let nonce = NonceType::new(&nonce_str[1..])?;
Ok(KeyAndNonce { key, nonce })
Ok(KeyNonce { key, nonce })
}

pub fn parse_auth(input: &str) -> anyhow::Result<(String, String)> {
Expand Down
37 changes: 17 additions & 20 deletions cli/src/util/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,37 +62,34 @@ impl std::ops::Deref for NonceType {
}

#[derive(Debug, Clone, Copy)]
pub struct KeyAndNonce {
pub struct KeyNonce {
pub key: KeyType,
pub nonce: NonceType,
}

pub async fn encrypt_file(
key_and_nonce: &KeyAndNonce,
key_nonce: &KeyNonce,
plaintext_file: &PathBuf,
) -> anyhow::Result<PathBuf> {
let encrypt_file = sdk::util::file::add_extension(plaintext_file, "bin");
encrypt(key_and_nonce, plaintext_file, &encrypt_file).await?;
encrypt(key_nonce, plaintext_file, &encrypt_file).await?;
Ok(encrypt_file)
}

pub async fn decrypt_file(
key_and_nonce: &KeyAndNonce,
encrypted_file: &PathBuf,
) -> anyhow::Result<()> {
pub async fn decrypt_file(key_nonce: &KeyNonce, encrypted_file: &PathBuf) -> anyhow::Result<()> {
let decrypted_file = sdk::util::file::rm_extra_extension(&encrypted_file)?;
let destination_file =
sdk::util::file::add_parent_dir(&decrypted_file, &generate_random_string_with_prefix("tmp"))?;
tokio::fs::create_dir(&destination_file.parent().unwrap()).await?;
decrypt(key_and_nonce, encrypted_file, &destination_file).await?;
decrypt(key_nonce, encrypted_file, &destination_file).await?;
tokio::fs::remove_file(encrypted_file).await.unwrap();
tokio::fs::rename(&destination_file, decrypted_file).await?;
tokio::fs::remove_dir(destination_file.parent().unwrap()).await?;
Ok(())
}

async fn encrypt(
KeyAndNonce { key, nonce }: &KeyAndNonce,
pub async fn encrypt(
KeyNonce { key, nonce }: &KeyNonce,
input_file: &Path,
output_file: &Path,
) -> anyhow::Result<()> {
Expand All @@ -108,6 +105,8 @@ async fn encrypt(
.encrypt_next(buffer.as_slice())
.map_err(|err| anyhow!("Encrypting file failed, Error: {err}"))?;
writer.write_all(&ciphertext).await?;
} else if read_count == 0 {
break;
} else {
let ciphertext = stream_encryptor
.encrypt_last(&buffer[..read_count])
Expand All @@ -121,8 +120,8 @@ async fn encrypt(
Ok(())
}

async fn decrypt(
KeyAndNonce { key, nonce }: &KeyAndNonce,
pub async fn decrypt(
KeyNonce { key, nonce }: &KeyNonce,
input_file: &Path,
output_file: &Path,
) -> anyhow::Result<()> {
Expand Down Expand Up @@ -166,20 +165,18 @@ mod tests {
#[test_context(FileTestContext)]
#[tokio::test]
pub async fn test_encrypt_and_decrypt_file(ctx: &mut FileTestContext) {
let key_and_nonce = KeyAndNonce {
let key_nonce = KeyNonce {
key: KeyType::new("01234567890123456789012345678912").unwrap(),
nonce: NonceType::new("1234567891213141516").unwrap(),
};
let contents: String = Faker.fake::<String>();
let plaintext_file = ctx.temp_path.join("file.txt");
tokio::fs::write(&plaintext_file, &contents).await.unwrap();
let ciphertext_file = encrypt_file(&key_and_nonce, &plaintext_file).await.unwrap();
let ciphertext_file = encrypt_file(&key_nonce, &plaintext_file).await.unwrap();
tokio::fs::remove_file(&plaintext_file).await.unwrap();
let exist = tokio::fs::try_exists(&ciphertext_file).await.unwrap();
assert!(exist, "ciphertext file {ciphertext_file:?} should be exist");
decrypt_file(&key_and_nonce, &ciphertext_file)
.await
.unwrap();
decrypt_file(&key_nonce, &ciphertext_file).await.unwrap();
let exist = tokio::fs::try_exists(&ciphertext_file).await.unwrap();
assert!(!exist, "ciphertext file should not be exist");
let actual_contents = tokio::fs::read_to_string(plaintext_file).await.unwrap();
Expand All @@ -189,7 +186,7 @@ mod tests {
#[test_context(FileTestContext)]
#[tokio::test]
pub async fn test_encrypt_and_decrypt(ctx: &mut FileTestContext) {
let key_and_nonce = KeyAndNonce {
let key_nonce = KeyNonce {
key: KeyType::new("01234567890123456789012345678912").unwrap(),
nonce: NonceType::new("1234567891213141516").unwrap(),
};
Expand All @@ -198,11 +195,11 @@ mod tests {
let plaintext_file = ctx.temp_path.join("file.txt");
tokio::fs::write(&plaintext_file, &contents).await.unwrap();
let ciphertext_file = ctx.temp_path.join("file.bin");
encrypt(&key_and_nonce, &plaintext_file, &ciphertext_file)
encrypt(&key_nonce, &plaintext_file, &ciphertext_file)
.await
.unwrap();
let result_file = ctx.temp_path.join("result_file.txt");
decrypt(&key_and_nonce, &ciphertext_file, &result_file)
decrypt(&key_nonce, &ciphertext_file, &result_file)
.await
.unwrap();
let actual_contents = tokio::fs::read_to_string(result_file).await.unwrap();
Expand Down

0 comments on commit 1b8eeaf

Please sign in to comment.