diff --git a/Cargo.lock b/Cargo.lock index da1657c..9718481 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,6 +272,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "data-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993a608597367c6377b258c25d7120740f00ed23a2252b729b1932dd7866f908" + [[package]] name = "debug-helper" version = "0.3.10" @@ -616,6 +622,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -819,6 +831,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "ring" +version = "0.16.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rpassword" version = "5.0.0" @@ -957,6 +984,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "ssh2" version = "0.9.0" @@ -1002,6 +1035,7 @@ dependencies = [ "chrono", "content_inspector", "crossterm", + "data-encoding", "dirs", "edit", "ftp4", @@ -1011,6 +1045,7 @@ dependencies = [ "magic-crypt", "rand", "regex", + "ring", "rpassword", "serde", "ssh2", @@ -1110,6 +1145,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "users" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index e33b1c0..1d3601a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,8 @@ toml = "0.5.7" tui = { version = "0.13.0", features = ["crossterm"], default-features = false } unicode-width = "0.1.7" whoami = "1.0.0" +ring = "0.16.19" +data-encoding = "2.3.1" [target.'cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))'.dependencies] users = "0.11.0" diff --git a/src/utils.rs b/src/utils.rs index fe1b0c4..6160266 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -25,6 +25,8 @@ // Dependencies extern crate chrono; +extern crate data_encoding; +extern crate ring; extern crate textwrap; extern crate whoami; @@ -32,6 +34,11 @@ use crate::filetransfer::FileTransferProtocol; use chrono::format::ParseError; use chrono::prelude::*; +use data_encoding::HEXLOWER; +use ring::digest::{Context, Digest, SHA256}; +use std::fs::File; +use std::io::Read; +use std::path::Path; use std::time::{Duration, SystemTime}; /// ### parse_remote_opt @@ -100,7 +107,10 @@ pub fn parse_remote_opt( _ => return Err(String::from("Bad syntax")), // Too many tokens... } // Set username to default if sftp or scp - if matches!(protocol, FileTransferProtocol::Sftp | FileTransferProtocol::Scp) { + if matches!( + protocol, + FileTransferProtocol::Sftp | FileTransferProtocol::Scp + ) { // Set username to current username username = Some(whoami::username()); } @@ -204,7 +214,7 @@ pub fn time_to_str(time: SystemTime, fmt: &str) -> String { } /// ### fmt_millis -/// +/// /// Format duration as {secs}.{millis} pub fn fmt_millis(duration: Duration) -> String { let seconds: u128 = duration.as_millis() / 1000; @@ -268,11 +278,33 @@ pub fn align_text_center(text: &str, width: u16) -> String { .to_string() } +/// ### hash_sha256_file +/// +/// Get SHA256 of provided path +pub fn hash_sha256_file(file: &Path) -> Result { + // Open file + let mut reader: File = File::open(file)?; + let mut context = Context::new(&SHA256); + let mut buffer = [0; 8192]; + loop { + let count = reader.read(&mut buffer)?; + if count == 0 { + break; + } + context.update(&buffer[..count]); + } + // Finish context + let digest: Digest = context.finish(); + Ok(HEXLOWER.encode(digest.as_ref())) +} + #[cfg(test)] mod tests { use super::*; + use std::io::Write; + #[test] fn test_utils_parse_remote_opt() { // Base case @@ -440,11 +472,33 @@ mod tests { String::from("hello world!") ); } - #[test] fn test_utils_fmt_millis() { - assert_eq!(fmt_millis(Duration::from_millis(2048)), String::from("2.048")); - assert_eq!(fmt_millis(Duration::from_millis(8192)), String::from("8.192")); - assert_eq!(fmt_millis(Duration::from_millis(18192)), String::from("18.192")); + assert_eq!( + fmt_millis(Duration::from_millis(2048)), + String::from("2.048") + ); + assert_eq!( + fmt_millis(Duration::from_millis(8192)), + String::from("8.192") + ); + assert_eq!( + fmt_millis(Duration::from_millis(18192)), + String::from("18.192") + ); + } + + #[test] + fn test_utils_hash_sha256() { + let tmp: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap(); + // Write + let mut fhnd: File = File::create(tmp.path()).unwrap(); + assert!(fhnd.write_all(b"Hello world!\n").is_ok()); + assert_eq!( + *hash_sha256_file(tmp.path()).ok().as_ref().unwrap(), + String::from("0ba904eae8773b70c75333db4de2f3ac45a8ad4ddba1b242f0b3cfc199391dd8") + ); + // Bad file + assert!(hash_sha256_file(Path::new("/tmp/oiojjt5ig/aiehgoiwg")).is_err()); } }