diff --git a/src/core/data/mod.rs b/src/core/data/mod.rs index 3cc7107..c9f3455 100644 --- a/src/core/data/mod.rs +++ b/src/core/data/mod.rs @@ -12,7 +12,7 @@ pub mod os; pub mod io; mod auth; -/// Fetches the Lockbox profiles by importing it from the file on the disk. Will return an error in +/// Fetches the Lockbox profiles by importing it from the file on the disk. Will return an error in /// case of the operation failing pub fn get_profiles() -> Result { log_debug!("Getting Lockbox profiles"); diff --git a/src/core/encryption/mod.rs b/src/core/encryption/mod.rs index 21306f0..7911d52 100644 --- a/src/core/encryption/mod.rs +++ b/src/core/encryption/mod.rs @@ -1,125 +1,5 @@ //! Contains everything which has to do with the encryption and decryption processes -use std::fs; -use std::path::{Path, PathBuf}; -use crate::{log_warn, new_err, Result}; -use crate::core::data; -use crate::options; -use crate::log_debug; -use super::data::{io, keys}; -use super::file::{header, parser}; - pub mod cipher; pub mod checksum; - -/// Encrypts the file at provided path using current profile's key. Password is required to verify -/// and get access to current profile. Additional options can be supplied to change the encryption -/// process -pub fn encrypt(password: &str, input_path: &Path, opts: &mut options::EncryptionOptions) -> Result<()> { - let mut profiles = data::get_profiles()?; - let profile = profiles.get_current_profile()?; - - if !profile.verify_password(password) { - return Err(new_err!(ProfileError: AuthenticationFailed)) - } - - if let Some(extension) = input_path.extension() { - if extension == "box" { - return Err(new_err!(InvalidData: FileAlreadyEncrypted, os input_path.file_name().unwrap())) - } - } - - let mut path_buffer = PathBuf::from(input_path); - let file_path = path_buffer.as_path(); - - // get needed data - let file_data = io::read_bytes(file_path).map_err(crate::Error::from)?; - let key = keys::get_key()?; - let nonce = cipher::generate_nonce(); - let header = header::generate_header(file_path, &file_data, &nonce)?; - - // change the file to be .box instead - fs::remove_file(file_path)?; - - if let Some(ref mut output_paths) = opts.output_paths { - log_debug!("Output paths given: {:?}", output_paths); - if let Some(output_path) = output_paths.pop_front() { - log_debug!("Writing output to: {:?}", output_path); - path_buffer = output_path; - - if path_buffer.file_name() == None { - path_buffer.set_file_name(uuid::Uuid::new_v4().to_string()); - } - } - } else if !opts.keep_original_name { - path_buffer.set_file_name(uuid::Uuid::new_v4().to_string()); - } - - path_buffer.set_extension("box"); - let file_path = path_buffer.as_path(); - - let body = cipher::encrypt(&key, &nonce, &file_data)?; - parser::write_file(file_path, header, body)?; - - Ok(()) -} - -/// Decryption the file at provided path using current profile's key. Password is required to -/// verify and get access to current profile. Additional options can be supplied to change the -/// decryption process -pub fn decrypt(password: &str, input_path: &Path, opts: &mut options::DecryptionOptions) -> Result<()> { - let mut profiles = data::get_profiles()?; - let profile = profiles.get_current_profile()?; - - if !profile.verify_password(password) { - return Err(new_err!(ProfileError: AuthenticationFailed)) - } - - if let Some(extension) = input_path.extension() { - if extension != "box" { - return Err(new_err!(InvalidData: FileNotSupported, os input_path.file_name().unwrap())) - } - } else { - return Err(new_err!(InvalidData: FileNotSupported, os input_path.file_name().unwrap())) - } - - let mut path_buffer = PathBuf::from(input_path); - let file_path = path_buffer.as_path(); - - let key = keys::get_key()?; - let box_file = parser::parse_file(file_path)?; - let header = box_file.header; - let body = cipher::decrypt(&key, &header.nonce, &box_file.body)?; - - log_debug!("Validating checksum"); - let new_checksum = checksum::generate_checksum(&body); - if new_checksum != header.checksum { - log_warn!("Checksum verification failed. Data seems to be tampered with"); - } - - fs::remove_file(file_path)?; - if let Some(ref mut output_paths) = opts.output_paths { - log_debug!("Output paths given: {:?}", output_paths); - if let Some(output_path) = output_paths.pop_front() { - log_debug!("Writing output to: {:?}", output_path); - path_buffer = output_path; - - if path_buffer.file_name() == None { - path_buffer.set_file_name(&header.original_filename); - path_buffer.set_extension(&header.original_extension); - } - - if path_buffer.extension() == None { - path_buffer.set_extension(&header.original_extension); - } - } - } else { - path_buffer.set_file_name(&header.original_filename); - path_buffer.set_extension(&header.original_extension); - } - - let file_path = path_buffer.as_path(); - io::write_bytes(&file_path, &body, true)?; - - Ok(()) -} \ No newline at end of file +pub mod boxfile; \ No newline at end of file diff --git a/src/core/mod.rs b/src/core/mod.rs index cdc9efd..741af5d 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,7 +1,226 @@ //! Contains the core functionality of the program -pub mod data; -pub mod encryption; -pub mod file; +use std::path::{Path, PathBuf}; +use std::fs; +use crate::core::data::{io, keys}; +use crate::core::encryption::{boxfile, checksum, cipher}; +use crate::{Result, log_debug, log_info, log_warn, new_err, options}; +use crate::core::data::profile::Profile; + +pub mod utils; pub mod error; -pub mod utils; \ No newline at end of file +mod data; +mod encryption; + +/// Encrypts the file at provided path using current profile's key. Password is required to verify +/// and get access to current profile. Additional options can be supplied to change the encryption +/// process +pub fn encrypt(password: &str, input_path: &Path, opts: &mut options::EncryptionOptions) -> Result<()> { + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + if let Some(extension) = input_path.extension() { + if extension == "box" { + return Err(new_err!(InvalidInput: FileAlreadyEncrypted, os input_path.file_name().unwrap())) + } + } + + let mut path_buffer = PathBuf::from(input_path); + let file_path = path_buffer.as_path(); + + // get needed data + let file_data = io::read_bytes(file_path).map_err(crate::Error::from)?; + let key = keys::get_key()?; + let nonce = cipher::generate_nonce(); + let header = boxfile::generate_header(file_path, &file_data, &nonce)?; + + // change the file to be .box instead + fs::remove_file(file_path)?; + + if let Some(ref mut output_paths) = opts.output_paths { + log_debug!("Output paths given: {:?}", output_paths); + if let Some(output_path) = output_paths.pop_front() { + log_debug!("Writing output to: {:?}", output_path); + path_buffer = output_path; + + if path_buffer.file_name() == None { + path_buffer.set_file_name(uuid::Uuid::new_v4().to_string()); + } + } + } else if !opts.keep_original_name { + path_buffer.set_file_name(uuid::Uuid::new_v4().to_string()); + } + + path_buffer.set_extension("box"); + let file_path = path_buffer.as_path(); + + let body = cipher::encrypt(&key, &nonce, &file_data)?; + boxfile::write_file(file_path, header, body)?; + + Ok(()) +} + +/// Decryption the file at provided path using current profile's key. Password is required to +/// verify and get access to current profile. Additional options can be supplied to change the +/// decryption process +pub fn decrypt(password: &str, input_path: &Path, opts: &mut options::DecryptionOptions) -> Result<()> { + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + if let Some(extension) = input_path.extension() { + if extension != "box" { + return Err(new_err!(InvalidInput: FileNotSupported, os input_path.file_name().unwrap())) + } + } else { + return Err(new_err!(InvalidInput: FileNotSupported, os input_path.file_name().unwrap())) + } + + let mut path_buffer = PathBuf::from(input_path); + let file_path = path_buffer.as_path(); + + let key = keys::get_key()?; + let box_file = boxfile::parse_file(file_path)?; + let header = box_file.header; + let body = cipher::decrypt(&key, &header.nonce, &box_file.body)?; + + log_debug!("Validating checksum"); + let new_checksum = checksum::generate_checksum(&body); + if new_checksum != header.checksum { + log_warn!("Checksum verification failed. Data seems to be tampered with"); + } + + fs::remove_file(file_path)?; + if let Some(ref mut output_paths) = opts.output_paths { + log_debug!("Output paths given: {:?}", output_paths); + if let Some(output_path) = output_paths.pop_front() { + log_debug!("Writing output to: {:?}", output_path); + path_buffer = output_path; + + if path_buffer.file_name() == None { + path_buffer.set_file_name(&header.original_filename); + path_buffer.set_extension(&header.original_extension); + } + + if path_buffer.extension() == None { + path_buffer.set_extension(&header.original_extension); + } + } + } else { + path_buffer.set_file_name(&header.original_filename); + path_buffer.set_extension(&header.original_extension); + } + + let file_path = path_buffer.as_path(); + io::write_bytes(&file_path, &body, true)?; + + Ok(()) +} + +pub fn create_profile(password: &str, profile_name: &str) -> Result<()> { + log_info!("Creating a new profile with name \"{}\"", profile_name); + let mut profiles = data::get_profiles()?; + profiles.new_profile(Profile::new(profile_name, password)?)?; + Ok(()) +} + +pub fn delete_profile(password: &str, profile_name: &str) -> Result<()> { + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + log_info!("Deleting profile \"{}\"", profile_name); + profiles.delete_profile(profile_name)?; + Ok(()) +} + +pub fn select_profile(password: &str, profile_name: &str) -> Result<()> { + let mut profiles = data::get_profiles()?; + let profile = profiles.find_profile(profile_name)?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + if let Ok(profile) = profiles.get_current_profile() { + if profile_name == profile.name { + return Err(new_err!(ProfileError: AlreadySelected, profile_name)) + } + } + + log_info!("Switching profile to \"{}\"", profile_name); + profiles.set_current(profile_name)?; + Ok(()) +} + +pub fn get_profile() -> Result { + log_info!("Getting current profile"); + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + Ok(profile.name.to_string()) +} + +pub fn get_profiles() -> Result> { + log_info!("Listing all available profiles"); + + let profiles = data::get_profiles()?; + let profile_list = profiles.get_profiles().into_iter() + .map(|p| p.name.to_string()) + .collect::>(); + Ok(profile_list) +} + +pub fn new_key(password: &str) -> Result<()> { + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + log_info!("Generating a new encryption key for current profile"); + keys::set_key(cipher::generate_key())?; + Ok(()) +} + +pub fn get_key(password: &str, opts: options::GetKeyOptions) -> Result { + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + log_info!("Retrieving the encryption key from the current profile"); + let key = keys::get_key()?; + if !opts.byte_format { + return Ok(utils::hex::key_to_hex_string(key)); + } + + Ok(format!("{:?}", key)) +} + +pub fn set_key(password: &str, new_key: &str) -> Result<()> { + let mut profiles = data::get_profiles()?; + let profile = profiles.get_current_profile()?; + + if !profile.verify_password(password) { + return Err(new_err!(ProfileError: AuthenticationFailed)) + } + + log_info!("Setting the encryption key from the current profile"); + let new_key = utils::hex::hex_string_to_key(new_key.to_string())?; + keys::set_key(new_key)?; + Ok(()) +} + diff --git a/src/core/utils/path.rs b/src/core/utils/path.rs index b685361..4701487 100644 --- a/src/core/utils/path.rs +++ b/src/core/utils/path.rs @@ -1,8 +1,8 @@ //! Contains functions for path manipulation -use std::{fs, io, ffi::OsString}; +use std::{ffi::OsString, fs, io}; use std::path::{Path, PathBuf}; -use crate::core::file::parser; +use crate::core::encryption::boxfile; use crate::{log_info, log_warn}; /// Opens and parses provided path, returning a flattened list of all found paths. Verifies if the @@ -55,7 +55,7 @@ fn search_for_original(dir_path: &Path, target_name: OsString) -> io::Result Result<()> { - // TODO: split some code - core::encryption::encrypt(password, input_path, opts)?; - Ok(()) +pub fn encrypt(password: &str, file_path: &std::path::Path, options: &mut options::EncryptionOptions) -> Result<()> { + core::encrypt(password, file_path, options) } /// Decrypts the file at the given path. Extra options can be provided to control the process. @@ -65,10 +62,8 @@ pub fn encrypt(password: &str, input_path: &std::path::Path, opts: &mut options: /// Most errors can be safely handled without an unsuccessful exit (e.g. file can just be skipped). /// Although it is better to exit on errors related with user authentication and profiles, as the /// program will simply not work without a user profile -pub fn decrypt(password: &str, input_path: &std::path::Path, opts: &mut options::DecryptionOptions) -> Result<()> { - // TODO: split some code - core::encryption::decrypt(password, input_path, opts)?; - Ok(()) +pub fn decrypt(password: &str, file_path: &std::path::Path, options: &mut options::DecryptionOptions) -> Result<()> { + core::decrypt(password, file_path, options) } /// Creates a new profile with the provided password and profile name. Will **not** automatically @@ -82,10 +77,7 @@ pub fn decrypt(password: &str, input_path: &std::path::Path, opts: &mut options: /// * `IOError` - in case of failing to access or write to a `profiles.json` file /// * `CipherError` - unsuccessful attempt to hash the password pub fn create_profile(password: &str, profile_name: &str) -> Result<()> { - log_info!("Creating a new profile with name \"{}\"", profile_name); - let mut profiles = core::data::get_profiles()?; - profiles.new_profile(Profile::new(profile_name, password)?)?; - Ok(()) + core::create_profile(password, profile_name) } /// Deletes the profile with the corresponding name. After deletion will switch back to the first @@ -100,16 +92,7 @@ pub fn create_profile(password: &str, profile_name: &str) -> Result<()> { /// * `ProfileError` - if the target profile is not found /// * `IOError` - in case of failing to access or write to a `profiles.json` file pub fn delete_profile(password: &str, profile_name: &str) -> Result<()> { - let mut profiles = core::data::get_profiles()?; - let profile = profiles.get_current_profile()?; - - if !profile.verify_password(password) { - return Err(Error::ProfileError(core::error::ProfileErrorKind::AuthenticationFailed)); - } - - log_info!("Deleting profile \"{}\"", profile_name); - profiles.delete_profile(profile_name)?; - Ok(()) + core::delete_profile(password, profile_name) } /// Select (set as the current) the profile with the corresponding name @@ -124,22 +107,7 @@ pub fn delete_profile(password: &str, profile_name: &str) -> Result<()> { /// * `ProfileError` - if the target profile is not found /// * `IOError` - in case of failing to access or write to a `profiles.json` file pub fn select_profile(password: &str, profile_name: &str) -> Result<()> { - let mut profiles = core::data::get_profiles()?; - let profile = profiles.find_profile(profile_name)?; - - if !profile.verify_password(password) { - return Err(Error::ProfileError(core::error::ProfileErrorKind::AuthenticationFailed)); - } - - if let Ok(profile) = profiles.get_current_profile() { - if profile_name == profile.name { - return Err(Error::ProfileError(core::error::ProfileErrorKind::AlreadySelected(profile_name.to_string()))); - } - } - - log_info!("Switching profile to \"{}\"", profile_name); - profiles.set_current(profile_name)?; - Ok(()) + core::select_profile(password, profile_name) } /// Returns the name of the currently selected profile @@ -152,10 +120,7 @@ pub fn select_profile(password: &str, profile_name: &str) -> Result<()> { /// * `ProfileError` - if no profile is currently selected /// * `IOError` - in case of failing to access or write to a `profiles.json` file pub fn get_profile() -> Result { - log_info!("Getting current profile"); - let mut profiles = core::data::get_profiles()?; - let profile = profiles.get_current_profile()?; - Ok(profile.name.to_string()) + core::get_profile() } /// Returns the names of all currently available profiles @@ -168,13 +133,7 @@ pub fn get_profile() -> Result { /// * `ProfileError` - if no profile data is found (no profiles exist) /// * `IOError` - in case of failing to access or write to a `profiles.json` file pub fn get_profiles() -> Result> { - log_info!("Listing all available profiles"); - - let profiles = core::data::get_profiles()?; - let profile_list = profiles.get_profiles().into_iter() - .map(|p| p.name.to_string()) - .collect::>(); - Ok(profile_list) + core::get_profiles() } /// Generates a new encryption key for the current profile @@ -191,16 +150,7 @@ pub fn get_profiles() -> Result> { /// * `ProfileError` - if there is no current profile or no profiles found in general /// * `IOError` - in case of failing to access or write to a `profiles.json` file pub fn new_key(password: &str) -> Result<()> { - let mut profiles = core::data::get_profiles()?; - let profile = profiles.get_current_profile()?; - - if !profile.verify_password(password) { - return Err(Error::ProfileError(core::error::ProfileErrorKind::AuthenticationFailed)); - } - - log_info!("Generating a new encryption key for current profile"); - core::data::keys::set_key(core::encryption::cipher::generate_key())?; - Ok(()) + core::new_key(password) } /// Returns the encryption key being used by the current profile in a hex format @@ -213,21 +163,8 @@ pub fn new_key(password: &str) -> Result<()> { /// * `AuthError` - invalid password for the current profile /// * `ProfileError` - if there is no current profile or no profiles found in general /// * `IOError` - in case of failing to access or write to a `profiles.json` file -pub fn get_key(password: &str, opts: options::GetKeyOptions) -> Result { - let mut profiles = core::data::get_profiles()?; - let profile = profiles.get_current_profile()?; - - if !profile.verify_password(password) { - return Err(Error::ProfileError(core::error::ProfileErrorKind::AuthenticationFailed)); - } - - log_info!("Retrieving the encryption key from the current profile"); - let key = core::data::keys::get_key()?; - if !opts.byte_format { - return Ok(utils::hex::key_to_hex_string(key)); - } - - Ok(format!("{:?}", key)) +pub fn get_key(password: &str, options: options::GetKeyOptions) -> Result { + core::get_key(password, options) } /// Sets a new encryption key for the current profile. The input key has to be a valid 32-byte long @@ -244,15 +181,5 @@ pub fn get_key(password: &str, opts: options::GetKeyOptions) -> Result { /// * `ProfileError` - if there is no current profile or no profiles found in general /// * `IOError` - in case of failing to access or write to a `profiles.json` file pub fn set_key(password: &str, new_key: &str) -> Result<()> { - let mut profiles = core::data::get_profiles()?; - let profile = profiles.get_current_profile()?; - - if !profile.verify_password(password) { - return Err(Error::ProfileError(core::error::ProfileErrorKind::AuthenticationFailed)); - } - - log_info!("Setting the encryption key from the current profile"); - let new_key = utils::hex::hex_string_to_key(new_key.to_string())?; - core::data::keys::set_key(new_key)?; - Ok(()) + core::set_key(password, new_key) } \ No newline at end of file