Skip to content

Commit

Permalink
move crypto to sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
robatipoor committed Mar 2, 2024
1 parent a2fb4cd commit 298bcf0
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 195 deletions.
1 change: 0 additions & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@ url = { workspace = true }
tracing-subscriber = { workspace = true }
base64 = { workspace = true }
cuid2 = { workspace = true }
chacha20poly1305 = { workspace = true }
indicatif = { workspace = true }
11 changes: 4 additions & 7 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use clap::{Parser, Subcommand, ValueEnum};
use sdk::dto::FileUrlPath;
use sdk::{dto::FileUrlPath, util::crypto::KeyNonce};

use std::path::PathBuf;

use crate::{
parse::{
parse_auth, parse_destination, parse_expire_time, parse_file_url_path, parse_key_nonce,
parse_source_file,
},
util::crypto::KeyNonce,
use crate::parse::{
parse_auth, parse_destination, parse_expire_time, parse_file_url_path, parse_key_nonce,
parse_source_file,
};

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 Down
8 changes: 4 additions & 4 deletions cli/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ use sdk::{
response::{ApiResponseResult, BodyResponseError, MessageResponse, UploadResponse},
FileUrlPath,
},
util::file::{add_extension, rm_extra_extension},
util::{crypto::KeyNonce, file::{add_extension, rm_extra_extension}},
};
use std::path::{Path, PathBuf};
use url::Url;

use crate::{args::UploadOutput, client::CommandLineClient, util::crypto::KeyNonce};
use crate::{args::UploadOutput, client::CommandLineClient};

#[derive(Debug)]
pub struct UploadArguments {
Expand Down Expand Up @@ -180,7 +180,7 @@ pub async fn encrypt_file(key_nonce: &KeyNonce, source_file: &Path, mut destinat
"bin",
));
}
crate::util::crypto::encrypt_file(key_nonce, source_file, destination)
sdk::util::crypto::encrypt_file(key_nonce, source_file, destination)
.await
.unwrap();
}
Expand All @@ -193,7 +193,7 @@ pub async fn decrypt_file(key_nonce: &KeyNonce, source_file: &Path, mut destinat
if source_file == destination {
panic!("Please specify the valid destination file path.")
}
crate::util::crypto::decrypt_file(key_nonce, source_file, destination)
sdk::util::crypto::decrypt_file(key_nonce, source_file, destination)
.await
.unwrap();
}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{path::PathBuf, str::FromStr};
use anyhow::anyhow;
use sdk::dto::FileUrlPath;

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

pub fn parse_key_nonce(input: &str) -> anyhow::Result<KeyNonce> {
let pos = input
Expand Down
186 changes: 5 additions & 181 deletions cli/src/util/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,9 @@
use anyhow::anyhow;
use chacha20poly1305::{
aead::{
generic_array::GenericArray,
stream::{DecryptorBE32, EncryptorBE32},
KeyInit,
},
consts::U32,
XChaCha20Poly1305,
};
use sdk::util::{
crypto::{decrypt_file, encrypt_file, KeyNonce},
file::{add_extension, add_parent_dir, rm_extra_extension},
random::generate_random_string_with_prefix,
};
use std::path::{Path, PathBuf};
use tokio::{
fs::File,
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
};

#[derive(Debug, Clone, Copy)]
pub struct KeyType(GenericArray<u8, U32>);

impl KeyType {
pub fn new(key: &str) -> anyhow::Result<Self> {
let key: [u8; 32] = key
.as_bytes()
.try_into()
.map_err(|_e| anyhow::anyhow!("The key length should be 32 characters."))?;

Ok(Self(GenericArray::from_iter(key)))
}
}

impl std::ops::Deref for KeyType {
type Target = GenericArray<u8, U32>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

#[derive(Debug, Clone, Copy)]
pub struct NonceType([u8; 19]);

impl NonceType {
pub fn new(nonce: &str) -> anyhow::Result<Self> {
let nonce: [u8; 19] = nonce
.as_bytes()
.try_into()
.map_err(|_e| anyhow::anyhow!("The nonce length should be 19 characters."))?;

Ok(Self(nonce))
}
}

impl std::ops::Deref for NonceType {
type Target = [u8; 19];

fn deref(&self) -> &Self::Target {
&self.0
}
}

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

pub async fn encrypt_upload_file(
key_nonce: &KeyNonce,
Expand Down Expand Up @@ -100,106 +37,16 @@ pub async fn decrypt_download_file(
Ok(())
}

pub async fn encrypt_file(
key_nonce: &KeyNonce,
plaintext_file: impl AsRef<Path>,
destination_file: impl AsRef<Path>,
) -> anyhow::Result<()> {
let reader = File::open(plaintext_file).await?;
let writer = File::create(destination_file).await?;
encrypt(key_nonce, reader, writer).await?;
Ok(())
}

pub async fn decrypt_file(
key_nonce: &KeyNonce,
encrypted_file: impl AsRef<Path>,
destination_file: impl AsRef<Path>,
) -> anyhow::Result<()> {
let reader = File::open(encrypted_file).await?;
let writer = File::create(destination_file).await?;
decrypt(key_nonce, reader, writer).await?;
Ok(())
}

pub async fn encrypt<R, W>(
KeyNonce { key, nonce }: &KeyNonce,
mut reader: R,
mut writer: W,
) -> anyhow::Result<()>
where
R: AsyncRead + Unpin,
W: AsyncWrite + Unpin,
{
const BUFFER_LEN: usize = 500;
let mut buffer = [0u8; BUFFER_LEN];
let mut stream_encryptor =
EncryptorBE32::from_aead(XChaCha20Poly1305::new(key), (*nonce).as_ref().into());
loop {
let read_count = reader.read(&mut buffer).await?;
if read_count == BUFFER_LEN {
let ciphertext = stream_encryptor
.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])
.map_err(|err| anyhow!("Encrypting file failed, Error: {err}"))?;
writer.write_all(&ciphertext).await?;
break;
}
}
writer.flush().await?;

Ok(())
}

pub async fn decrypt<R, W>(
KeyNonce { key, nonce }: &KeyNonce,
mut reader: R,
mut writer: W,
) -> anyhow::Result<()>
where
R: AsyncRead + Unpin,
W: AsyncWrite + Unpin,
{
const BUFFER_LEN: usize = 500 + 16;
let mut buffer = [0u8; BUFFER_LEN];
let mut stream_decryptor =
DecryptorBE32::from_aead(XChaCha20Poly1305::new(key), nonce.as_ref().into());

loop {
let read_count = reader.read(&mut buffer).await?;
if read_count == BUFFER_LEN {
let plaintext = stream_decryptor
.decrypt_next(buffer.as_slice())
.map_err(|err| anyhow!("Decrypting file failed, Error: {err}"))?;
writer.write_all(&plaintext).await?;
} else if read_count == 0 {
break;
} else {
let plaintext = stream_decryptor
.decrypt_last(&buffer[..read_count])
.map_err(|err| anyhow!("Decrypting file failed, Error: {err}"))?;
writer.write_all(&plaintext).await?;
break;
}
}
writer.flush().await?;

Ok(())
}

#[cfg(test)]
mod tests {

use fake::{Fake, Faker};
use test_context::test_context;

use crate::util::test::FileTestContext;
use sdk::util::{
crypto::{KeyType, NonceType},
test::FileTestContext,
};

use super::*;

Expand Down Expand Up @@ -227,27 +74,4 @@ mod tests {
let actual_contents = tokio::fs::read_to_string(plaintext_file).await.unwrap();
assert_eq!(contents, actual_contents)
}

#[test_context(FileTestContext)]
#[tokio::test]
pub async fn test_encrypt_file_and_decrypt_file(ctx: &mut FileTestContext) {
let key_nonce = KeyNonce {
key: KeyType::new("01234567890123456789012345678912").unwrap(),
nonce: NonceType::new("1234567891213141516").unwrap(),
};
let contents = Faker.fake::<String>();

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_file(&key_nonce, &plaintext_file, &ciphertext_file)
.await
.unwrap();
let result_file = ctx.temp_path.join("result_file.txt");
decrypt_file(&key_nonce, &ciphertext_file, &result_file)
.await
.unwrap();
let actual_contents = tokio::fs::read_to_string(result_file).await.unwrap();
assert_eq!(contents, actual_contents)
}
}
1 change: 0 additions & 1 deletion cli/src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
pub mod crypto;
pub mod progress;
pub mod test;
2 changes: 2 additions & 0 deletions sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ mime_guess = { workspace = true }
url = { workspace = true }
qrcode = { workspace = true }
image = { workspace = true }
test-context = { workspace = true }
chacha20poly1305 = { workspace = true }
Loading

0 comments on commit 298bcf0

Please sign in to comment.