fcry - [f]ile[cry]pt - initial commit (alpha 0.9.0)
A file en-/decryption tool for easy use. Currently `fcry` uses `ChaCha20Poly1305` ([RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439)) as [AEAD](https://en.wikipedia.org/wiki/Authenticated_encryption) cipher provided by the [chacha20poly1305](https://docs.rs/chacha20poly1305/latest/chacha20poly1305/) crate.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use chacha20poly1305::{
|
||||
aead::{stream, NewAead},
|
||||
ChaCha20Poly1305,
|
||||
};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
|
||||
use crate::error::*;
|
||||
use crate::utils::*;
|
||||
|
||||
const BUFSIZE: usize = 64 * 1024; // 64 KiB
|
||||
|
||||
fn new_random_nonce() -> Result<[u8; 7], FcryError> {
|
||||
let mut nonce = [0u8; 7];
|
||||
OsRng.try_fill_bytes(&mut nonce)?;
|
||||
Ok(nonce)
|
||||
}
|
||||
|
||||
pub fn encrypt<S: AsRef<str>>(
|
||||
input_file: Option<S>,
|
||||
output_file: Option<S>,
|
||||
key: [u8; 32],
|
||||
) -> Result<(), FcryError> {
|
||||
let mut f_plain = read_from_file_or_stdin(input_file);
|
||||
let mut f_encrypted = write_to_file_or_stdout(output_file);
|
||||
|
||||
let nonce = new_random_nonce()?;
|
||||
f_encrypted.write_all(&nonce)?;
|
||||
|
||||
let aead = ChaCha20Poly1305::new(&key.into());
|
||||
let mut stream_encryptor = stream::EncryptorBE32::from_aead(aead, &nonce.into());
|
||||
|
||||
let mut buf = vec![0; BUFSIZE];
|
||||
let mut read_bytes;
|
||||
|
||||
loop {
|
||||
read_bytes = f_plain.read(&mut buf)?;
|
||||
|
||||
if read_bytes < BUFSIZE {
|
||||
break;
|
||||
}
|
||||
|
||||
stream_encryptor.encrypt_next_in_place(&[], &mut buf)?;
|
||||
f_encrypted.write_all(&buf)?;
|
||||
buf.truncate(BUFSIZE);
|
||||
}
|
||||
|
||||
buf.truncate(read_bytes);
|
||||
stream_encryptor.encrypt_last_in_place(&[], &mut buf)?;
|
||||
f_encrypted.write_all(&buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decrypt<S: AsRef<str>>(
|
||||
input_file: Option<S>,
|
||||
output_file: Option<S>,
|
||||
key: [u8; 32],
|
||||
) -> Result<(), FcryError> {
|
||||
let mut f_encrypted = read_from_file_or_stdin(input_file);
|
||||
let mut f_plain = write_to_file_or_stdout(output_file);
|
||||
|
||||
let mut nonce = [0u8; 7];
|
||||
f_encrypted.read_exact(&mut nonce)?;
|
||||
|
||||
let aead = ChaCha20Poly1305::new(&key.into());
|
||||
let mut stream_decryptor = stream::DecryptorBE32::from_aead(aead, &nonce.into());
|
||||
|
||||
let mut buf = vec![0; BUFSIZE + 16];
|
||||
let mut read_bytes;
|
||||
|
||||
loop {
|
||||
read_bytes = f_encrypted.read(&mut buf)?;
|
||||
|
||||
if read_bytes < BUFSIZE + 16 {
|
||||
break;
|
||||
}
|
||||
|
||||
stream_decryptor.decrypt_next_in_place(&[], &mut buf)?;
|
||||
f_plain.write_all(&buf)?;
|
||||
buf.resize(BUFSIZE + 16, 0);
|
||||
}
|
||||
|
||||
buf.truncate(read_bytes);
|
||||
stream_decryptor.decrypt_last_in_place(&[], &mut buf)?;
|
||||
f_plain.write_all(&buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use chacha20poly1305::aead;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FcryError {
|
||||
Io(io::Error),
|
||||
Crypto(aead::Error),
|
||||
Rng(rand::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for FcryError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
FcryError::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<aead::Error> for FcryError {
|
||||
fn from(e: aead::Error) -> Self {
|
||||
FcryError::Crypto(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rand::Error> for FcryError {
|
||||
fn from(e: rand::Error) -> Self {
|
||||
FcryError::Rng(e)
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
mod crypto;
|
||||
mod error;
|
||||
mod utils;
|
||||
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
|
||||
use crypto::*;
|
||||
use error::FcryError;
|
||||
|
||||
fn build_cmdline_args() -> ArgMatches {
|
||||
Command::new("fcry - [f]ile[cry]pt").version(env!("CARGO_PKG_VERSION"))
|
||||
.about("A file en-/decryption tool for easy use.")
|
||||
.arg(Arg::new("decrypt")
|
||||
.short('d')
|
||||
.long("decrypt")
|
||||
.help("Decrypt instead of encrypt. Encrypting is the default.")
|
||||
.takes_value(false))
|
||||
.arg(Arg::new("input_file")
|
||||
.short('i')
|
||||
.long("input-file")
|
||||
.help("The input file to en-/decrypt")
|
||||
.takes_value(true))
|
||||
.arg(Arg::new("output_file")
|
||||
.short('o')
|
||||
.long("output-file")
|
||||
.help("The output file. If not specified, en-/decrypted bytes will be sent to stdout.")
|
||||
.takes_value(true))
|
||||
.arg(Arg::new("raw-key")
|
||||
.required(true)
|
||||
.long("raw-key")
|
||||
.help("The raw bytes of the crypto key. Has to be exactly 32 bytes.\n*** DANGEROUS, use for testing purposes only! ***")
|
||||
.takes_value(true))
|
||||
.get_matches()
|
||||
}
|
||||
|
||||
fn run(args: ArgMatches) -> Result<(), FcryError> {
|
||||
let input_file = args.value_of("input_file");
|
||||
let output_file = args.value_of("output_file");
|
||||
|
||||
let mut key = [0u8; 32];
|
||||
key.clone_from_slice(args.value_of("raw-key").unwrap().as_bytes());
|
||||
|
||||
if args.is_present("decrypt") {
|
||||
decrypt(input_file, output_file, key)?
|
||||
} else {
|
||||
encrypt(input_file, output_file, key)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = run(build_cmdline_args()) {
|
||||
println!("Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, Read, Write},
|
||||
};
|
||||
|
||||
pub(crate) fn read_from_file_or_stdin<S: AsRef<str>>(input_file: Option<S>) -> Box<dyn Read> {
|
||||
match input_file {
|
||||
Some(f) => Box::new(File::open(f.as_ref()).unwrap()),
|
||||
None => Box::new(io::stdin()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write_to_file_or_stdout<S: AsRef<str>>(output_file: Option<S>) -> Box<dyn Write> {
|
||||
match output_file {
|
||||
Some(f) => Box::new(File::create(f.as_ref()).unwrap()),
|
||||
None => Box::new(io::stdout()),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user