Overwrite by default
This commit is contained in:
parent
2c8ab7e8ad
commit
311677d195
77
src/cli.rs
77
src/cli.rs
@ -1,11 +1,6 @@
|
|||||||
use std::{
|
use std::{fs::File, io::{self, Read, stdin, stdout}, path::Path, str::FromStr};
|
||||||
path::Path,
|
|
||||||
fs::File,
|
|
||||||
str::FromStr,
|
|
||||||
io::{stdin, stdout, Read},
|
|
||||||
};
|
|
||||||
use clap::{crate_name, crate_version, App, Arg, AppSettings};
|
use clap::{crate_name, crate_version, App, Arg, AppSettings};
|
||||||
use crate::{LazyWriter, WrappedPassword, crypto::CipherAlgorithm};
|
use crate::{WrappedWriter, WrappedPassword, crypto::CipherAlgorithm};
|
||||||
|
|
||||||
cpufeatures::new!(aes_ni, "aes");
|
cpufeatures::new!(aes_ni, "aes");
|
||||||
|
|
||||||
@ -16,10 +11,27 @@ pub struct CliArgs {
|
|||||||
pub cipher: CipherAlgorithm,
|
pub cipher: CipherAlgorithm,
|
||||||
pub block_size: usize,
|
pub block_size: usize,
|
||||||
pub reader: Box<dyn Read>,
|
pub reader: Box<dyn Read>,
|
||||||
pub writer: LazyWriter<String>,
|
pub writer: WrappedWriter<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse() -> Option<CliArgs> {
|
pub struct ParseResult {
|
||||||
|
pub error: bool,
|
||||||
|
pub cli_args: Option<CliArgs>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseResult {
|
||||||
|
fn exited() -> Self {
|
||||||
|
Self { error: false, cli_args: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CliArgs> for ParseResult {
|
||||||
|
fn from(args: CliArgs) -> Self {
|
||||||
|
ParseResult { error: false, cli_args: Some(args) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse() -> Option<ParseResult> {
|
||||||
let app = App::new(crate_name!())
|
let app = App::new(crate_name!())
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.setting(AppSettings::ColoredHelp)
|
.setting(AppSettings::ColoredHelp)
|
||||||
@ -27,11 +39,17 @@ pub fn parse() -> Option<CliArgs> {
|
|||||||
.arg(Arg::with_name("INPUT").help("<PATH> | \"-\" or empty for stdin"))
|
.arg(Arg::with_name("INPUT").help("<PATH> | \"-\" or empty for stdin"))
|
||||||
.arg(Arg::with_name("OUTPUT").help("<PATH> | \"-\" or empty for stdout"))
|
.arg(Arg::with_name("OUTPUT").help("<PATH> | \"-\" or empty for stdout"))
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("force-encrypt")
|
Arg::with_name("1_force_encrypt")
|
||||||
.short("f")
|
.short("f")
|
||||||
.long("force-encrypt")
|
.long("force-encrypt")
|
||||||
.help(&format!("Encrypt even if {} format is recognized", crate_name!()))
|
.help(&format!("Encrypt even if {} format is recognized", crate_name!()))
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("2_interactive")
|
||||||
|
.short("i")
|
||||||
|
.long("interactive")
|
||||||
|
.help("Prompt before overwriting files")
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("1_password")
|
Arg::with_name("1_password")
|
||||||
.long("password")
|
.long("password")
|
||||||
@ -42,7 +60,7 @@ pub fn parse() -> Option<CliArgs> {
|
|||||||
Arg::with_name("2_t_cost")
|
Arg::with_name("2_t_cost")
|
||||||
.short("t")
|
.short("t")
|
||||||
.long("time-cost")
|
.long("time-cost")
|
||||||
.value_name("number of iterations")
|
.value_name("iterations")
|
||||||
.help("Argon2 time cost")
|
.help("Argon2 time cost")
|
||||||
.default_value("10")
|
.default_value("10")
|
||||||
)
|
)
|
||||||
@ -50,7 +68,7 @@ pub fn parse() -> Option<CliArgs> {
|
|||||||
Arg::with_name("3_m_cost")
|
Arg::with_name("3_m_cost")
|
||||||
.short("m")
|
.short("m")
|
||||||
.long("memory-cost")
|
.long("memory-cost")
|
||||||
.value_name("memory cost")
|
.value_name("memory size")
|
||||||
.help("Argon2 memory cost (in kilobytes)")
|
.help("Argon2 memory cost (in kilobytes)")
|
||||||
.default_value("4096")
|
.default_value("4096")
|
||||||
)
|
)
|
||||||
@ -58,7 +76,7 @@ pub fn parse() -> Option<CliArgs> {
|
|||||||
Arg::with_name("4_p_cost")
|
Arg::with_name("4_p_cost")
|
||||||
.short("p")
|
.short("p")
|
||||||
.long("parallelism")
|
.long("parallelism")
|
||||||
.value_name("degree of parallelism")
|
.value_name("threads")
|
||||||
.help("Argon2 parallelism cost")
|
.help("Argon2 parallelism cost")
|
||||||
.default_value("4")
|
.default_value("4")
|
||||||
)
|
)
|
||||||
@ -126,28 +144,37 @@ pub fn parse() -> Option<CliArgs> {
|
|||||||
None => Box::new(stdin())
|
None => Box::new(stdin())
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = app
|
let wrapped_writer = match app
|
||||||
.value_of("OUTPUT")
|
.value_of("OUTPUT")
|
||||||
.and_then(|s| if s == "-" { None } else { Some(s) })
|
.and_then(|s| if s == "-" { None } else { Some(s) }) {
|
||||||
.map(|s| {
|
Some(path) => {
|
||||||
if Path::new(s).exists() {
|
if {
|
||||||
eprintln!("WARNING: {} already exists", s);
|
if app.is_present("2_interactive") && Path::new(path).exists() {
|
||||||
None
|
eprint!("Warning: {} already exists. Overwrite [y/N]? ", path);
|
||||||
|
let mut c = String::with_capacity(2);
|
||||||
|
io::stdin().read_line(&mut c).unwrap();
|
||||||
|
!c.is_empty() && c.chars().nth(0).unwrap() == 'y'
|
||||||
} else {
|
} else {
|
||||||
Some(LazyWriter::from_path(s.to_owned()))
|
true
|
||||||
}
|
}
|
||||||
})
|
} {
|
||||||
.unwrap_or_else(|| Some(LazyWriter::from_writer(stdout())))?;
|
WrappedWriter::from_path(path.to_string())
|
||||||
|
} else {
|
||||||
|
return Some(ParseResult::exited())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => WrappedWriter::from_writer(stdout())
|
||||||
|
};
|
||||||
|
|
||||||
Some(CliArgs {
|
Some(CliArgs {
|
||||||
password: app.value_of("1_password").into(),
|
password: app.value_of("1_password").into(),
|
||||||
force_encrypt: app.is_present("force-encrypt"),
|
force_encrypt: app.is_present("1_force_encrypt"),
|
||||||
argon2_params: params,
|
argon2_params: params,
|
||||||
cipher,
|
cipher,
|
||||||
block_size,
|
block_size,
|
||||||
reader: input,
|
reader: input,
|
||||||
writer: output,
|
writer: wrapped_writer,
|
||||||
})
|
}.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number<T: FromStr>(val: &str) -> Option<T> {
|
fn number<T: FromStr>(val: &str) -> Option<T> {
|
||||||
|
46
src/lib.rs
46
src/lib.rs
@ -1,7 +1,7 @@
|
|||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
|
|
||||||
use std::{fs::File, path::Path, io::{self, Read, Write}};
|
use std::{fmt::Display, fs::OpenOptions, io::{self, BufWriter, Read, Write}, path::Path};
|
||||||
use crypto::{DobyCipher, EncryptionParams};
|
use crypto::{DobyCipher, EncryptionParams};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
@ -36,37 +36,33 @@ impl From<Option<&str>> for WrappedPassword {
|
|||||||
Self(s.map(String::from))
|
Self(s.map(String::from))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub enum WrappedWriter<P: AsRef<Path>> {
|
||||||
pub struct LazyWriter<P: AsRef<Path>> {
|
PATH {
|
||||||
path: Option<P>,
|
path: P
|
||||||
writer: Option<Box<dyn Write>>,
|
},
|
||||||
|
WRITER {
|
||||||
|
writer: Box<dyn Write>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: AsRef<Path>> LazyWriter<P> {
|
impl<P: AsRef<Path> + Display> WrappedWriter<P> {
|
||||||
fn from_path(path: P) -> Self {
|
fn from_path(path: P) -> Self {
|
||||||
Self {
|
Self::PATH { path }
|
||||||
path: Some(path),
|
|
||||||
writer: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_writer<T: 'static + Write>(writer: T) -> Self {
|
fn from_writer<T: 'static + Write>(writer: T) -> Self {
|
||||||
Self {
|
Self::WRITER { writer: Box::new(writer) }
|
||||||
path: None,
|
|
||||||
writer: Some(Box::new(writer)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: AsRef<Path>> Write for LazyWriter<P> {
|
pub fn into_buf_writer(self) -> Option<BufWriter<Box<dyn Write>>> {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
Some(BufWriter::new(match self {
|
||||||
if self.writer.is_none() {
|
Self::PATH { path } => Box::new(
|
||||||
self.writer = Some(Box::new(File::create(self.path.as_ref().unwrap()).unwrap()));
|
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
|
||||||
}
|
.map_err(|e| eprintln!("{}: {}", path, e))
|
||||||
self.writer.as_mut().unwrap().write(buf)
|
.ok()?
|
||||||
}
|
) as Box<dyn Write>,
|
||||||
|
Self::WRITER { writer } => writer,
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
}))
|
||||||
self.writer.as_mut().unwrap().flush()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
src/main.rs
26
src/main.rs
@ -1,4 +1,4 @@
|
|||||||
use std::{process, io::{BufWriter, BufReader, Read}};
|
use std::{process, io::{BufReader, Read}};
|
||||||
use doby::{
|
use doby::{
|
||||||
cli,
|
cli,
|
||||||
crypto::{EncryptionParams, DobyCipher},
|
crypto::{EncryptionParams, DobyCipher},
|
||||||
@ -10,9 +10,9 @@ use zeroize::Zeroize;
|
|||||||
|
|
||||||
fn run() -> bool {
|
fn run() -> bool {
|
||||||
let mut success = false;
|
let mut success = false;
|
||||||
if let Some(cli_args) = cli::parse() {
|
if let Some(result) = cli::parse() {
|
||||||
let mut reader = BufReader::with_capacity(cli_args.block_size, cli_args.reader);
|
if let Some(cli_args) = result.cli_args {
|
||||||
let mut writer = BufWriter::with_capacity(cli_args.block_size, cli_args.writer);
|
let mut reader = BufReader::new(cli_args.reader);
|
||||||
|
|
||||||
let mut magic_bytes = vec![0; MAGIC_BYTES.len()];
|
let mut magic_bytes = vec![0; MAGIC_BYTES.len()];
|
||||||
match reader.read(&mut magic_bytes) {
|
match reader.read(&mut magic_bytes) {
|
||||||
@ -20,9 +20,9 @@ fn run() -> bool {
|
|||||||
if magic_bytes == MAGIC_BYTES && !cli_args.force_encrypt { //we probably want to decrypt
|
if magic_bytes == MAGIC_BYTES && !cli_args.force_encrypt { //we probably want to decrypt
|
||||||
match EncryptionParams::read(&mut reader) {
|
match EncryptionParams::read(&mut reader) {
|
||||||
Ok(params) => {
|
Ok(params) => {
|
||||||
match params {
|
if let Some(params) = params {
|
||||||
Some(params) => {
|
|
||||||
if let Some(mut password) = cli_args.password.get(false) {
|
if let Some(mut password) = cli_args.password.get(false) {
|
||||||
|
if let Some(mut writer) = cli_args.writer.into_buf_writer() {
|
||||||
let cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
let cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
||||||
password.zeroize();
|
password.zeroize();
|
||||||
match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) {
|
match decrypt(&mut reader, &mut writer, cipher, cli_args.block_size) {
|
||||||
@ -30,14 +30,17 @@ fn run() -> bool {
|
|||||||
if verified {
|
if verified {
|
||||||
success = true
|
success = true
|
||||||
} else {
|
} else {
|
||||||
eprintln!("WARNING: HMAC verification failed !\nEither your password is incorrect or the ciphertext has been corrupted.\nBe careful, the data could have been altered by an attacker.");
|
eprintln!("Warning: HMAC verification failed !\nEither your password is incorrect or the ciphertext has been corrupted.\nBe careful, the data could have been altered by an attacker.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("I/O error while decrypting: {}", e)
|
Err(e) => eprintln!("I/O error while decrypting: {}", e)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
password.zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => eprintln!("Invalid parameters")
|
} else {
|
||||||
|
eprintln!("Invalid parameters")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("I/O error while reading headers: {}", e)
|
Err(e) => eprintln!("I/O error while reading headers: {}", e)
|
||||||
@ -45,6 +48,7 @@ fn run() -> bool {
|
|||||||
} else { //otherwise, encrypt
|
} else { //otherwise, encrypt
|
||||||
let params = EncryptionParams::new(cli_args.argon2_params, cli_args.cipher);
|
let params = EncryptionParams::new(cli_args.argon2_params, cli_args.cipher);
|
||||||
if let Some(mut password) = cli_args.password.get(true) {
|
if let Some(mut password) = cli_args.password.get(true) {
|
||||||
|
if let Some(mut writer) = cli_args.writer.into_buf_writer() {
|
||||||
let cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
let cipher = DobyCipher::new(password.as_bytes(), ¶ms);
|
||||||
password.zeroize();
|
password.zeroize();
|
||||||
match encrypt(
|
match encrypt(
|
||||||
@ -58,11 +62,17 @@ fn run() -> bool {
|
|||||||
Ok(_) => success = true,
|
Ok(_) => success = true,
|
||||||
Err(e) => eprintln!("I/O error while encrypting: {}", e)
|
Err(e) => eprintln!("I/O error while encrypting: {}", e)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
password.zeroize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("I/O error while reading magic bytes: {}", e),
|
Err(e) => eprintln!("I/O error while reading magic bytes: {}", e),
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
success = !result.error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user