From f3896fc3037f2351ca1a004dd40999f0446adf9c Mon Sep 17 00:00:00 2001 From: Sean Johnson Date: Tue, 11 Sep 2018 16:45:07 -0500 Subject: [PATCH] Fix decoder tests and do rustfmt (#4) * Update decoder tests to match new builder params * rustfmt all of the things * Provide more control over output location --- Cargo.lock | 77 +++++++++++++++++++++ Cargo.toml | 1 + src/bin/sneakercopy.rs | 65 ++++++++++++++---- src/builder.rs | 2 +- src/crypt.rs | 10 ++- src/errors.rs | 2 +- src/flate.rs | 8 +-- src/lib.rs | 144 +++++++++++++++++++++++++++++++++------ src/pack.rs | 8 ++- src/password.rs | 11 +-- src/tarbox/attributes.rs | 18 ++--- src/tarbox/decoder.rs | 47 ++++++------- src/tarbox/encoder.rs | 17 ++--- src/tarbox/errors.rs | 2 +- src/tarbox/mod.rs | 8 +-- src/tarbox/secret.rs | 17 +++-- 16 files changed, 330 insertions(+), 107 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89d3230..d39a9a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,11 @@ dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ansi_term" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ansi_term" version = "0.11.0" @@ -282,6 +287,15 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.0.2" @@ -553,6 +567,7 @@ dependencies = [ "quicli 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "sodiumoxide 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spinners 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -567,6 +582,26 @@ dependencies = [ "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "spinner" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "spinners" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "spinner 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "strum_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" version = "0.7.0" @@ -591,6 +626,20 @@ dependencies = [ "syn 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "strum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strum_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.11.11" @@ -639,6 +688,15 @@ dependencies = [ "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "term" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "1.0.1" @@ -725,6 +783,11 @@ dependencies = [ "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.3.5" @@ -734,6 +797,11 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -764,6 +832,7 @@ dependencies = [ "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c1c6d463cbe7ed28720b5b489e7c083eeb8f90d08be2a0d6bb9e1ffea9ce1afa" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum ansi_term 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "30275ad0ad84ec1c06dde3b3f7d23c6006b7d76d61a85e7060b426b747eff70d" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" @@ -794,6 +863,7 @@ dependencies = [ "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fb497c35d362b6a331cfd94956a07fc2c78a4604cdbee844a81170386b996dd3" "checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1" "checksum libflate 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7d4b4c7aff5bac19b956f693d0ea0eade8066deb092186ae954fa6ba14daab98" @@ -829,14 +899,19 @@ dependencies = [ "checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920" "checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124" "checksum sodiumoxide 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71c0682b4406fa25d621b19d2e70b5f6c8627e39b4b7ce0e24b2ef05d0fbe1ca" +"checksum spinner 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ff2afa10b6b6df2fc1a36d85da916041af4619541a0b7ab0ac9a2c713bb3825" +"checksum spinners 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf699144c201078dea174f001ea23aabee98ccf86ced267b8949d1c89fc91071" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a" "checksum structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbce8ccdc62166bd594c14396a3242bf94c337a51dbfa9be1076dd74b3db2af" +"checksum strum 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca6e4730f517e041e547ffe23d29daab8de6b73af4b6ae2a002108169f5e7da" +"checksum strum_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3384590878eb0cab3b128e844412e2d010821e7e091211b9d87324173ada7db8" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.14.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4bad7abdf6633f07c7046b90484f1d9dc055eca39f8c991177b1046ce61dba9a" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum tar 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "e8f41ca4a5689f06998f0247fcb60da6c760f1950cc9df2a10d71575ad0b062a" +"checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281" "checksum termcolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "722426c4a0539da2c4ffd9b419d90ad540b4cff4a053be9069c908d4d07e2836" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" @@ -850,7 +925,9 @@ dependencies = [ "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e84a603e7e0b1ce1aa1ee2b109c7be00155ce52df5081590d1ffb93f4f515cb2" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "773ef9dcc5f24b7d850d0ff101e542ff24c3b090a9768e03ff889fdef41f00fd" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9dc3aa9dcda98b5a16150c54619c1ead22e3d3a5d458778ae914be760aa981a" diff --git a/Cargo.toml b/Cargo.toml index b4182e1..a0beacd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ log = "0.4.0" quicli = "0.3.0" rand = "0.5.5" sodiumoxide = "0.1.0" +spinners = "1.0.0" structopt = "0.2.10" tar = "0.4.16" diff --git a/src/bin/sneakercopy.rs b/src/bin/sneakercopy.rs index ed99423..29c6c26 100644 --- a/src/bin/sneakercopy.rs +++ b/src/bin/sneakercopy.rs @@ -1,19 +1,16 @@ #![recursion_limit = "1024"] #![feature(try_from)] -#[macro_use] extern crate quicli; +#[macro_use] +extern crate quicli; extern crate sneakercopy; extern crate sodiumoxide; -#[macro_use] extern crate structopt; +extern crate structopt; use quicli::prelude::*; use std::path::PathBuf; -use sneakercopy::{ - *, - errors::*, - tarbox, -}; +use sneakercopy::{errors::*, tarbox, *}; #[derive(Debug, StructOpt)] #[structopt(raw(setting = "structopt::clap::AppSettings::ColoredHelp"))] @@ -31,6 +28,21 @@ enum Subcommand { Seal { #[structopt(help = "File/folder path to archive", parse(from_os_str))] path: PathBuf, + + #[structopt( + short = "o", + long = "output", + help = "Optional output location", + parse(from_os_str) + )] + output: Option, + + #[structopt( + short = "f", + long = "force", + help = "Force overwriting of output" + )] + force: bool, }, #[structopt(name = "unseal", about = "Unseal an encrypted archive")] @@ -41,7 +53,12 @@ enum Subcommand { #[structopt(help = "Password used for encryption")] password: String, - #[structopt(short = "C", long = "extract-to", help = "Directory to extract archive to", parse(from_os_str))] + #[structopt( + short = "C", + long = "extract-to", + help = "Directory to extract archive to", + parse(from_os_str) + )] dest: Option, }, } @@ -66,8 +83,16 @@ main!(|args: Cli, log_level: verbosity| { fn entrypoint(args: Cli) -> sneakercopy::errors::Result<()> { let action = &args.subcmd; match action { - Subcommand::Seal{ path } => seal_subcmd(&args, path)?, - Subcommand::Unseal{ path, password, dest } => unseal_subcmd(&args, path, dest, password)?, + Subcommand::Seal { + path, + output, + force, + } => seal_subcmd(&args, path, output, force)?, + Subcommand::Unseal { + path, + password, + dest, + } => unseal_subcmd(&args, path, dest, password)?, } Ok(()) @@ -84,14 +109,26 @@ fn check_path(path: &PathBuf) -> sneakercopy::errors::Result<()> { Ok(()) } -fn seal_subcmd(_args: &Cli, path: &PathBuf) -> sneakercopy::errors::Result<()> { +fn seal_subcmd( + _args: &Cli, + path: &PathBuf, + output: &Option, + force: &bool, +) -> sneakercopy::errors::Result<()> { check_path(&path)?; - let secret = seal_path(&path)?; - println!("secret: {}", secret.password()); + + let secret = seal_path(&path, &output, *force)?; + println!("\nsecret: {}", secret.password()); + Ok(()) } -fn unseal_subcmd(_args: &Cli, path: &PathBuf, dest: &Option, password: &String) -> sneakercopy::errors::Result<()> { +fn unseal_subcmd( + _args: &Cli, + path: &PathBuf, + dest: &Option, + password: &String, +) -> sneakercopy::errors::Result<()> { check_path(&path)?; let sb = tarbox::TarboxSecretBuilder::new(); diff --git a/src/builder.rs b/src/builder.rs index d3ac735..26c5699 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -57,4 +57,4 @@ macro_rules! builder { )* } }; -} \ No newline at end of file +} diff --git a/src/crypt.rs b/src/crypt.rs index e05c41e..5bb24d6 100644 --- a/src/crypt.rs +++ b/src/crypt.rs @@ -1,12 +1,16 @@ use sodiumoxide::crypto::secretbox; -use super::{ BufResult, errors, tarbox }; +use super::{errors, tarbox, BufResult}; pub fn encrypt_buffer(buf: &Vec, secret: &tarbox::TarboxSecret) -> BufResult { - Ok(secretbox::seal(buf.as_slice(), &secret.nonce(), &secret.key())) + Ok(secretbox::seal( + buf.as_slice(), + &secret.nonce(), + &secret.key(), + )) } pub fn decrypt_buffer(buf: &Vec, secret: &tarbox::TarboxSecret) -> BufResult { secretbox::open(buf.as_slice(), &secret.nonce(), &secret.key()) - .or_else(|_| { bail!(errors::ErrorKind::SecretBoxOpenFail) }) + .or_else(|_| bail!(errors::ErrorKind::SecretBoxOpenFail)) } diff --git a/src/errors.rs b/src/errors.rs index 017f2b9..9eff72f 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -24,4 +24,4 @@ error_chain! { display("could not open secretbox"), } } -} \ No newline at end of file +} diff --git a/src/flate.rs b/src/flate.rs index 5c69936..f7ad0be 100644 --- a/src/flate.rs +++ b/src/flate.rs @@ -1,8 +1,8 @@ -use libflate::gzip::{ Decoder, Encoder }; -use std::io::{ Read, Write }; +use libflate::gzip::{Decoder, Encoder}; +use std::io::{Read, Write}; use std::vec::Vec; -use super::{ BufResult }; +use super::BufResult; pub fn compress_buffer(buf: &Vec) -> BufResult { let mut compressor = Encoder::new(Vec::new())?; @@ -29,4 +29,4 @@ pub fn inflate_buffer(buf: &Vec) -> BufResult { // Finish the inflation stream Ok(outbuf) -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 64ff73c..a11abd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,33 +2,35 @@ #![feature(int_to_from_bytes)] extern crate base64; -#[macro_use] extern crate error_chain; +#[macro_use] +extern crate error_chain; extern crate libflate; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; extern crate rand; extern crate sodiumoxide; +extern crate spinners; extern crate tar; -#[macro_use] mod builder; +#[macro_use] +mod builder; pub mod crypt; pub mod errors; pub mod flate; pub mod pack; -pub mod tarbox; pub mod password; +pub mod tarbox; +use spinners::{Spinner, Spinners}; +use std::env; use std::ffi::OsStr; -use std::fs::{ DirBuilder, File, OpenOptions }; +use std::fs::{DirBuilder, File, OpenOptions}; use std::io::prelude::*; use std::path::PathBuf; pub type BufResult = errors::Result>; -/// Given a `path`, reads the resulting file or directory into a -/// `tar` archive, encrypts the resulting archive, removes the unencrypted -/// archive, and then compresses the encrypted archive, resulting in -/// a "tarbox". -pub fn seal_path(path: &PathBuf) -> errors::Result { +fn build_output_file_name(path: &PathBuf) -> PathBuf { let extension = path.extension().unwrap_or(OsStr::new("")); let mut extension = extension.to_os_string().into_string().unwrap(); @@ -43,48 +45,93 @@ pub fn seal_path(path: &PathBuf) -> errors::Result { let mut target_path = path.clone(); target_path.set_extension(extension); - let mut target_file = OpenOptions::new() - .create(true) - .write(true) - .truncate(false) - .open(target_path)?; + PathBuf::from(target_path.file_name().unwrap()) +} + +fn build_output_path(input: &PathBuf, output: &Option) -> PathBuf { + let target_file_name = build_output_file_name(input); + let output = output.clone().unwrap_or(env::current_dir().unwrap()); + if output.is_dir() { + return output.join(target_file_name); + } + + output +} + +/// Given a `path`, reads the resulting file or directory into a +/// `tar` archive, encrypts the resulting archive, removes the unencrypted +/// archive, and then compresses the encrypted archive, resulting in +/// a "tarbox". +pub fn seal_path( + path: &PathBuf, + output: &Option, + force: bool, +) -> errors::Result { + let target_path = build_output_path(path, output); + + let mut target_file = OpenOptions::new(); + target_file.create(true).write(true); + + if force { + target_file.create_new(false).truncate(true); + } else { + target_file.create_new(true).truncate(false); + } + + let mut target_file = target_file.open(target_path)?; // Make a new `BoxSecret` let password = password::generate_password(); let secret = tarbox::TarboxSecret::generate(password); + let waiter = Spinner::new(Spinners::Dots12, "Prepping...".into()); + // Pack the target files to the tar archive debug!("packing path {:?} to archive buffer", path); + waiter.message("Packing...".into()); let buf = pack::pack_archive(&path)?; debug!("compressing buf of length {}", buf.len()); + waiter.message("Compressing...".into()); let buf = flate::compress_buffer(&buf)?; debug!("encrypting compressed buf (size {})", buf.len()); + waiter.message("Encrypting...".into()); let buf = crypt::encrypt_buffer(&buf, &secret)?; debug!("finalizing tarbox (size {})", buf.len()); - // let enc = tarbox::Encoder::::new(); + waiter.message("Finishing up...".into()); let buf = tarbox::wrap_buffer(&buf, &secret)?; target_file.write_all(&buf)?; + waiter.stop(); Ok(secret) } -pub fn unseal_path(path: &PathBuf, dest: &PathBuf, sb: tarbox::TarboxSecretBuilder) -> errors::Result<()> { - DirBuilder::new() - .recursive(true) - .create(&dest)?; +pub fn unseal_path( + path: &PathBuf, + dest: &PathBuf, + sb: tarbox::TarboxSecretBuilder, +) -> errors::Result<()> { + DirBuilder::new().recursive(true).create(&dest)?; let mut source_file = File::open(path)?; let source_meta = source_file.metadata()?; - debug!("reading {} bytes from tarbox: {:?}", source_meta.len(), path); + debug!( + "reading {} bytes from tarbox: {:?}", + source_meta.len(), + path + ); + + let waiter = Spinner::new(Spinners::Dots12, "Prepping...".into()); let mut buf = Vec::new(); + waiter.message("Reading file...".into()); source_file.read_to_end(&mut buf)?; debug!("unwrapping tarbox (size {})", buf.len()); + waiter.message("Unwrapping...".into()); let (buf, attrs) = tarbox::unwrap_buffer(&buf)?; let nonce = attrs.nonce(); @@ -95,13 +142,68 @@ pub fn unseal_path(path: &PathBuf, dest: &PathBuf, sb: tarbox::TarboxSecretBuild .build()?; debug!("decrypting compressed buf (size {})", buf.len()); + waiter.message("Decrypting...".into()); let buf = crypt::decrypt_buffer(&buf, &secret)?; debug!("inflating buf of length {}", buf.len()); + waiter.message("Inflating...".into()); let buf = flate::inflate_buffer(&buf)?; debug!("unpacking archive to path: {:?}", dest); + waiter.message("Unpacking...".into()); pack::unpack_archive(&buf, &dest)?; + waiter.stop(); + Ok(()) } + +#[cfg(test)] +mod tests { + use super::{build_output_file_name, build_output_path}; + use std::path::PathBuf; + + #[test] + fn test_build_output_file_name() { + // (input, expectation) + let results = [ + ("/tmp", "tmp.tarbox"), + ("/tmp/test.txt", "test.txt.tarbox"), + ("/tmp/тест.txt", "тест.txt.tarbox"), + ]; + + for (path, result) in results.iter() { + let output_name = PathBuf::from(path); + assert_eq!(PathBuf::from(result), build_output_file_name(&output_name)); + } + } + + #[test] + fn test_build_output_path() { + // (input path, output path, expectation) + let results = [ + ("/tmp", "/", "/tmp.tarbox"), + ("/tmp/test.txt", "/", "/test.txt.tarbox"), + ("/tmp/тест.txt", "/", "/тест.txt.tarbox"), + ( + "/tmp/example.txt", + "/usr/local", + "/usr/local/example.txt.tarbox", + ), + ( + "/tmp/example.txt", + "/usr/local/example.tarbox", + "/usr/local/example.tarbox", + ), + ]; + + for (input_path, output_path, result) in results.iter() { + let input_path = PathBuf::from(input_path); + let output_path = PathBuf::from(output_path); + assert_eq!( + PathBuf::from(result), + build_output_path(&input_path, &Some(output_path)) + ); + } + } +} diff --git a/src/pack.rs b/src/pack.rs index 404664e..ec1679a 100644 --- a/src/pack.rs +++ b/src/pack.rs @@ -3,14 +3,16 @@ use std::path::PathBuf; use std::vec::Vec; use tar; -use super::{ BufResult, errors }; +use super::{errors, BufResult}; pub fn pack_archive(src: &PathBuf) -> BufResult { let mut archive = tar::Builder::new(Vec::new()); + let file_name = src.file_name().unwrap(); + if src.is_dir() { + debug!("recursively adding contents of {:?} to archive", file_name); archive.append_dir_all(".", &src)?; } else { - let file_name = src.file_name().unwrap(); let mut src_file = File::open(src.clone())?; archive.append_file(PathBuf::from(file_name), &mut src_file)?; } @@ -23,4 +25,4 @@ pub fn unpack_archive(buf: &Vec, dest: &PathBuf) -> errors::Result<()> { archive.unpack(&dest)?; Ok(()) -} \ No newline at end of file +} diff --git a/src/password.rs b/src/password.rs index 316758b..659e640 100644 --- a/src/password.rs +++ b/src/password.rs @@ -1,13 +1,14 @@ -use rand::{seq, prng, thread_rng, SeedableRng}; +use rand::{prng, seq, thread_rng, SeedableRng}; // generate a reasonable password pub fn generate_password() -> String { let mut rng = prng::chacha::ChaChaRng::from_rng(thread_rng()).unwrap(); let sample = seq::sample_iter(&mut rng, WORDS.into_iter(), 5).unwrap(); - sample.into_iter() - .map(|x| String::from(*x)) - .collect::>() - .join("-") + sample + .into_iter() + .map(|x| String::from(*x)) + .collect::>() + .join("-") } static WORDS: [&str; 2048] = [ diff --git a/src/tarbox/attributes.rs b/src/tarbox/attributes.rs index b94db01..fd698ee 100644 --- a/src/tarbox/attributes.rs +++ b/src/tarbox/attributes.rs @@ -1,16 +1,12 @@ use super::{ errors, - secret::{ - NONCEBYTES, - SALTBYTES, - TarboxSecret, - }, + secret::{TarboxSecret, NONCEBYTES, SALTBYTES}, }; pub type NonceBytes = [u8; NONCEBYTES]; pub type SaltBytes = [u8; SALTBYTES]; -pub const VERSION: u8 = 0x1; +pub const VERSION: u8 = 0x1; #[derive(Clone, Debug)] pub struct Attributes { @@ -136,9 +132,15 @@ mod tests { assert!(res.is_err()); let err = res.unwrap_err(); if let errors::Error(errors::ErrorKind::SourceNotFullyDrained(num), _) = err { - assert_eq!(2, num, "only 2 bytes were expected to be remaining (undrained)"); + assert_eq!( + 2, num, + "only 2 bytes were expected to be remaining (undrained)" + ); } else { - panic!(format!("expected `SourceNotFullyDrained` error, got: {:?}", err)); + panic!(format!( + "expected `SourceNotFullyDrained` error, got: {:?}", + err + )); } } } diff --git a/src/tarbox/decoder.rs b/src/tarbox/decoder.rs index 87fb95e..24e1d62 100644 --- a/src/tarbox/decoder.rs +++ b/src/tarbox/decoder.rs @@ -1,12 +1,8 @@ use std::cmp; use std::io; -use std::io::{ Read, Write }; +use std::io::{Read, Write}; -use super::{ - TARBOX_MAGIC, - attributes::Attributes, - errors, -}; +use super::{attributes::Attributes, errors, TARBOX_MAGIC}; #[derive(Clone, Debug)] pub struct Decoder { @@ -32,12 +28,16 @@ impl Decoder { // Get the version byte let version = inner.remove(0); if version != Attributes::version() { - bail!(errors::ErrorKind::VersionMismatch(Attributes::version(), version)); + bail!(errors::ErrorKind::VersionMismatch( + Attributes::version(), + version + )); } // Now that we have verified the version of the header, // read all bytes until a `NUL` is encountered. - let attrs_data: Vec<_> = inner.clone() + let attrs_data: Vec<_> = inner + .clone() .into_iter() .take_while(|b| *b != 0x0) .collect(); @@ -92,28 +92,20 @@ impl Read for Decoder { #[cfg(test)] mod tests { + use super::{errors, Decoder, TARBOX_MAGIC}; + use password; use std::io::Read; - use super::{ - errors, - Decoder, - TARBOX_MAGIC, - }; - use ::tarbox::secret::{ - KEYBYTES, NONCEBYTES, SALTBYTES, - Key, Nonce, Salt, - TarboxSecret, - TarboxSecretBuilder, - }; + use tarbox::secret::{Nonce, Salt, TarboxSecret, TarboxSecretBuilder, NONCEBYTES, SALTBYTES}; fn make_tarbox_secret() -> TarboxSecret { - let key = Key::from_slice(&[0xca; KEYBYTES]).unwrap(); let nonce = Nonce::from_slice(&[0xfe; NONCEBYTES]).unwrap(); let salt = Salt::from_slice(&[0xba; SALTBYTES]).unwrap(); TarboxSecretBuilder::new() - .key(key) + .password(password::generate_password()) .nonce(nonce) .salt(salt) - .build().unwrap() + .build() + .unwrap() } fn make_header(secret: &TarboxSecret) -> Vec { @@ -137,7 +129,9 @@ mod tests { // Create a decoder and read the inner data from it let mut dec = Decoder::new(payload).unwrap(); let mut data = Vec::new(); - let actual_size = dec.read_to_end(&mut data).expect("error reading data into decoder"); + let actual_size = dec + .read_to_end(&mut data) + .expect("error reading data into decoder"); let attrs = dec.attributes_into(); assert_eq!(2, actual_size); @@ -163,7 +157,10 @@ mod tests { if let errors::Error(errors::ErrorKind::SourceNotFullyDrained(num), _) = err { assert_eq!(2, num, "expected undrained data to be length of inner file"); } else { - panic!(format!("expected `SourceNotFullyDrained` error, got: {:?}", err)); + panic!(format!( + "expected `SourceNotFullyDrained` error, got: {:?}", + err + )); } } -} \ No newline at end of file +} diff --git a/src/tarbox/encoder.rs b/src/tarbox/encoder.rs index 063a794..f17a2eb 100644 --- a/src/tarbox/encoder.rs +++ b/src/tarbox/encoder.rs @@ -1,10 +1,7 @@ use std::io; -use std::io::{ Write }; +use std::io::Write; -use super::{ - Attributes, - TARBOX_MAGIC, -}; +use super::{Attributes, TARBOX_MAGIC}; #[derive(Clone, Debug)] pub struct Encoder { @@ -27,7 +24,7 @@ impl Encoder { /// Writes the tarbox header and then the wrapped content. /// The header will look like the following: - /// + /// /// ```text /// +--------------+--------+-------+-----+ /// | TARBOX MAGIC | VERS | ATTRS | NUL | @@ -69,12 +66,8 @@ impl Write for Encoder { #[cfg(test)] mod tests { + use super::{Attributes, Encoder, TARBOX_MAGIC}; use std::io::Write; - use super::{ - Attributes, - Encoder, - TARBOX_MAGIC, - }; #[test] fn test_encoder() { @@ -96,4 +89,4 @@ mod tests { assert_eq!(expected_payload, data.as_slice()); } -} \ No newline at end of file +} diff --git a/src/tarbox/errors.rs b/src/tarbox/errors.rs index 973445b..0b75a1a 100644 --- a/src/tarbox/errors.rs +++ b/src/tarbox/errors.rs @@ -30,4 +30,4 @@ error_chain! { display("tarbox header version mismatch: expected={} actual={}", expected, actual), } } -} \ No newline at end of file +} diff --git a/src/tarbox/mod.rs b/src/tarbox/mod.rs index cfb6951..4827582 100644 --- a/src/tarbox/mod.rs +++ b/src/tarbox/mod.rs @@ -1,18 +1,18 @@ //! This module defines a thin file container for //! holding tarbox metadata. -use std::io::{ Read, Write }; +use std::io::{Read, Write}; pub mod attributes; -pub mod errors; pub mod decoder; pub mod encoder; +pub mod errors; pub mod secret; pub use self::attributes::Attributes; pub use self::decoder::Decoder; pub use self::encoder::Encoder; -pub use self::secret::{ TarboxSecret, TarboxSecretBuilder }; +pub use self::secret::{TarboxSecret, TarboxSecretBuilder}; pub const TARBOX_MAGIC: [u8; 2] = [0x7a, 0xb0]; @@ -36,4 +36,4 @@ pub fn unwrap_buffer(buf: &Vec) -> errors::Result<(Vec, Attributes)> { debug!("read {} bytes into buf", read_size); Ok((buf, dec.attributes_into())) -} \ No newline at end of file +} diff --git a/src/tarbox/secret.rs b/src/tarbox/secret.rs index dd75f2b..2e77a06 100644 --- a/src/tarbox/secret.rs +++ b/src/tarbox/secret.rs @@ -1,8 +1,10 @@ use base64; use sodiumoxide::crypto::pwhash; -pub use sodiumoxide::crypto::pwhash::scryptsalsa208sha256::{ SALTBYTES, Salt, OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE }; +pub use sodiumoxide::crypto::pwhash::scryptsalsa208sha256::{ + Salt, MEMLIMIT_INTERACTIVE, OPSLIMIT_INTERACTIVE, SALTBYTES, +}; use sodiumoxide::crypto::secretbox; -pub use sodiumoxide::crypto::secretbox::xsalsa20poly1305::{ KEYBYTES, NONCEBYTES, Key, Nonce }; +pub use sodiumoxide::crypto::secretbox::xsalsa20poly1305::{Key, Nonce, KEYBYTES, NONCEBYTES}; pub fn decode_nonce(nonce: String) -> Option { let bytes = base64::decode(&nonce).unwrap(); @@ -26,7 +28,7 @@ impl TarboxSecret { TarboxSecret { password: password.clone(), nonce: secretbox::gen_nonce(), - salt: pwhash::gen_salt() + salt: pwhash::gen_salt(), } } @@ -36,8 +38,13 @@ impl TarboxSecret { { let secretbox::Key(ref mut buffer) = key; - pwhash::derive_key(buffer, self.password.as_bytes(), &self.salt, - OPSLIMIT_INTERACTIVE, MEMLIMIT_INTERACTIVE).unwrap(); + pwhash::derive_key( + buffer, + self.password.as_bytes(), + &self.salt, + OPSLIMIT_INTERACTIVE, + MEMLIMIT_INTERACTIVE, + ).unwrap(); } key