feat: Encrypt the hole vault file #26

Merged
awiteb merged 4 commits from awiteb/fix-5 into master 2024-05-03 07:51:41 +02:00 AGit
13 changed files with 48 additions and 101 deletions
Showing only changes of commit 7b4079e22c - Show all commits

View file

@ -17,7 +17,7 @@
use clap::Args; use clap::Args;
use crate::{ use crate::{
vault::{vault_state::*, Vault, Vaults}, vault::{Vault, Vaults},
LprsCommand, LprsError, LprsResult, LprsCommand, LprsError, LprsResult,
}; };
@ -25,14 +25,14 @@ use crate::{
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Add { pub struct Add {
#[command(flatten)] #[command(flatten)]
vault_info: Vault<Plain>, vault_info: Vault,
/// The password, if there is no value for it you will prompt it /// The password, if there is no value for it you will prompt it
#[arg(short, long)] #[arg(short, long)]
password: Option<Option<String>>, password: Option<Option<String>>,
} }
impl LprsCommand for Add { impl LprsCommand for Add {
fn run(mut self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(mut self, mut vault_manager: Vaults) -> LprsResult<()> {
match self.password { match self.password {
Some(Some(password)) => { Some(Some(password)) => {
log::debug!("User provided a password"); log::debug!("User provided a password");

View file

@ -18,17 +18,14 @@ use std::fs;
use clap::Args; use clap::Args;
use crate::{ use crate::{vault::Vaults, LprsCommand, LprsError, LprsResult};
vault::{vault_state::*, Vaults},
LprsCommand, LprsError, LprsResult,
};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Clean {} pub struct Clean {}
impl LprsCommand for Clean { impl LprsCommand for Clean {
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, vault_manager: Vaults) -> LprsResult<()> {
log::info!( log::info!(
"Cleaning the vaults file: {:?}", "Cleaning the vaults file: {:?}",
vault_manager.vaults_file.display() vault_manager.vaults_file.display()

View file

@ -19,7 +19,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use crate::{ use crate::{
vault::{vault_state::*, Vault, Vaults}, vault::{Vault, Vaults},
LprsCommand, LprsError, LprsResult, LprsCommand, LprsError, LprsResult,
}; };
@ -47,7 +47,7 @@ pub struct Edit {
} }
impl LprsCommand for Edit { impl LprsCommand for Edit {
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, mut vault_manager: Vaults) -> LprsResult<()> {
let index = self.index.get() as usize; let index = self.index.get() as usize;
log::debug!("Editing vault at index: {index}"); log::debug!("Editing vault at index: {index}");
@ -73,7 +73,7 @@ impl LprsCommand for Edit {
}; };
log::info!("Applying the new values to the vault"); log::info!("Applying the new values to the vault");
*vault = Vault::<Plain>::new( *vault = Vault::new(
self.name.as_ref().unwrap_or(&vault.name), self.name.as_ref().unwrap_or(&vault.name),
self.username.as_ref().or(vault.username.as_ref()), self.username.as_ref().or(vault.username.as_ref()),
password.as_ref().or(vault.password.as_ref()), password.as_ref().or(vault.password.as_ref()),

View file

@ -19,7 +19,7 @@ use std::{fs, io::Error as IoError, io::ErrorKind as IoErrorKind, path::PathBuf}
use clap::Args; use clap::Args;
use crate::{ use crate::{
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults}, vault::{BitWardenPasswords, Format, Vault, Vaults},
LprsCommand, LprsError, LprsResult, LprsCommand, LprsError, LprsResult,
}; };
@ -34,7 +34,7 @@ pub struct Export {
} }
impl LprsCommand for Export { impl LprsCommand for Export {
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, vault_manager: Vaults) -> LprsResult<()> {
log::debug!( log::debug!(
"Exporting vault {} to: {} with format: {}", "Exporting vault {} to: {} with format: {}",
vault_manager.vaults_file.display(), vault_manager.vaults_file.display(),
@ -42,9 +42,7 @@ impl LprsCommand for Export {
self.format self.format
); );
let exported_data = match self.format { let exported_data = match self.format {
Format::Lprs => { Format::Lprs => serde_json::to_string::<Vec<Vault>>(&vault_manager.encrypt_vaults()?),
serde_json::to_string::<Vec<Vault<Encrypted>>>(&vault_manager.encrypt_vaults()?)
}
Format::BitWarden => serde_json::to_string(&BitWardenPasswords::from(vault_manager)), Format::BitWarden => serde_json::to_string(&BitWardenPasswords::from(vault_manager)),
}?; }?;

View file

@ -18,10 +18,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use crate::{ use crate::{vault::Vaults, LprsCommand, LprsError, LprsResult};
vault::{vault_state::*, Vaults},
LprsCommand, LprsError, LprsResult,
};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -45,7 +42,7 @@ pub struct Gen {
} }
impl LprsCommand for Gen { impl LprsCommand for Gen {
fn run(self, _vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, _vault_manager: Vaults) -> LprsResult<()> {
println!( println!(
"{}", "{}",
passwords::PasswordGenerator::new() passwords::PasswordGenerator::new()

View file

@ -19,7 +19,7 @@ use std::{fs::File, io::Error as IoError, io::ErrorKind as IoErrorKind, path::Pa
use clap::Args; use clap::Args;
use crate::{ use crate::{
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults}, vault::{BitWardenPasswords, Format, Vault, Vaults},
LprsCommand, LprsError, LprsResult, LprsCommand, LprsError, LprsResult,
}; };
@ -35,7 +35,7 @@ pub struct Import {
} }
impl LprsCommand for Import { impl LprsCommand for Import {
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, mut vault_manager: Vaults) -> LprsResult<()> {
log::debug!( log::debug!(
"Importing vaults from: {} with format: {} to the vault: {}", "Importing vaults from: {} with format: {} to the vault: {}",
self.path.display(), self.path.display(),

View file

@ -19,10 +19,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use inquire::Select; use inquire::Select;
use crate::{ use crate::{vault::Vaults, LprsCommand, LprsError, LprsResult};
vault::{vault_state::*, Vaults},
LprsCommand, LprsError, LprsResult,
};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -39,7 +36,7 @@ pub struct List {
} }
impl LprsCommand for List { impl LprsCommand for List {
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, vault_manager: Vaults) -> LprsResult<()> {
if vault_manager.vaults.is_empty() { if vault_manager.vaults.is_empty() {
return Err(LprsError::Other( return Err(LprsError::Other(
"Looks like there is no vaults to list".to_owned(), "Looks like there is no vaults to list".to_owned(),

View file

@ -18,10 +18,7 @@ use std::num::NonZeroU64;
use clap::Args; use clap::Args;
use crate::{ use crate::{vault::Vaults, LprsCommand, LprsError, LprsResult};
vault::{vault_state::*, Vaults},
LprsCommand, LprsError, LprsResult,
};
#[derive(Debug, Args)] #[derive(Debug, Args)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -35,7 +32,7 @@ pub struct Remove {
} }
impl LprsCommand for Remove { impl LprsCommand for Remove {
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> { fn run(self, mut vault_manager: Vaults) -> LprsResult<()> {
let index = (self.index.get() - 1) as usize; let index = (self.index.get() - 1) as usize;
log::debug!("Removing vault at index: {index}"); log::debug!("Removing vault at index: {index}");

View file

@ -53,7 +53,7 @@ macro_rules! impl_commands {
($enum_name: ident, $($varint: ident)+) => { ($enum_name: ident, $($varint: ident)+) => {
#[automatically_derived] #[automatically_derived]
impl $crate::LprsCommand for $enum_name{ impl $crate::LprsCommand for $enum_name{
fn run(self, vault_manager: $crate::vault::Vaults<$crate::vault::vault_state::Plain>) -> $crate::LprsResult<()> { fn run(self, vault_manager: $crate::vault::Vaults) -> $crate::LprsResult<()> {
match self { match self {
$( $(
Self::$varint(command) => command.run(vault_manager), Self::$varint(command) => command.run(vault_manager),

View file

@ -14,15 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>. // along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
use crate::{ use crate::{vault::Vaults, LprsResult};
vault::{vault_state::*, Vaults},
LprsResult,
};
/// Trait to work with the commands /// Trait to work with the commands
pub trait LprsCommand { pub trait LprsCommand {
/// Run the command, should do all the logic, even the export /// Run the command, should do all the logic, even the export
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()>; fn run(self, vault_manager: Vaults) -> LprsResult<()>;
/// Validate the gaiven args from the user. /// Validate the gaiven args from the user.
fn validate_args(&self) -> LprsResult<()> { fn validate_args(&self) -> LprsResult<()> {

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{vault_state::*, Vault, Vaults}; use super::{Vault, Vaults};
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct BitWardenLoginData { pub struct BitWardenLoginData {
@ -39,7 +39,7 @@ pub struct BitWardenPasswords {
pub items: Vec<BitWardenPassword>, pub items: Vec<BitWardenPassword>,
} }
impl From<BitWardenPassword> for Vault<Plain> { impl From<BitWardenPassword> for Vault {
fn from(value: BitWardenPassword) -> Self { fn from(value: BitWardenPassword) -> Self {
Self::new( Self::new(
value.name, value.name,
@ -55,8 +55,8 @@ impl From<BitWardenPassword> for Vault<Plain> {
} }
} }
impl From<Vault<Plain>> for BitWardenPassword { impl From<Vault> for BitWardenPassword {
fn from(value: Vault<Plain>) -> Self { fn from(value: Vault) -> Self {
Self { Self {
ty: 1, ty: 1,
name: value.name, name: value.name,
@ -72,8 +72,8 @@ impl From<Vault<Plain>> for BitWardenPassword {
} }
} }
impl From<Vaults<Plain>> for BitWardenPasswords { impl From<Vaults> for BitWardenPasswords {
fn from(value: Vaults<Plain>) -> Self { fn from(value: Vaults) -> Self {
Self { Self {
encrypted: false, encrypted: false,
folders: Vec::new(), folders: Vec::new(),

View file

@ -14,13 +14,12 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>. // along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
use std::{fs, marker::PhantomData, path::PathBuf}; use std::{fs, path::PathBuf};
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{LprsError, LprsResult}; use crate::{LprsError, LprsResult};
use vault_state::*;
pub mod cipher; pub mod cipher;
@ -36,23 +35,10 @@ pub enum Format {
BitWarden, BitWarden,
} }
/// The states of the vaults
pub mod vault_state {
/// Means the vault is encrypted
#[derive(Clone, Debug, Default)]
pub struct Encrypted;
/// Means the vault is not encrypted
#[derive(Clone, Debug, Default)]
pub struct Plain;
}
/// The vault struct /// The vault struct
#[serde_with_macros::skip_serializing_none] #[serde_with_macros::skip_serializing_none]
#[derive(Clone, Debug, Deserialize, Serialize, Parser)] #[derive(Clone, Debug, Deserialize, Serialize, Parser)]
pub struct Vault<T> pub struct Vault {
where
T: std::fmt::Debug + Clone,
{
/// The name of the vault /// The name of the vault
#[arg(short, long)] #[arg(short, long)]
pub name: String, pub name: String,
@ -68,31 +54,20 @@ where
/// Add a note to the vault /// Add a note to the vault
#[arg(short = 'o', long)] #[arg(short = 'o', long)]
pub note: Option<String>, pub note: Option<String>,
/// State phantom
#[serde(skip)]
#[arg(skip)]
phantom: PhantomData<T>,
} }
/// The vaults manager /// The vaults manager
#[derive(Default)] #[derive(Default)]
pub struct Vaults<T> pub struct Vaults {
where
T: std::fmt::Debug + Clone,
{
/// Hash of the master password /// Hash of the master password
pub master_password: Vec<u8>, pub master_password: Vec<u8>,
/// The json vaults file /// The json vaults file
pub vaults_file: PathBuf, pub vaults_file: PathBuf,
/// The vaults /// The vaults
pub vaults: Vec<Vault<T>>, pub vaults: Vec<Vault>,
} }
impl<T> Vault<T> impl Vault {
where
T: std::fmt::Debug + Clone,
{
/// Create new [`Vault`] instance /// Create new [`Vault`] instance
pub fn new( pub fn new(
name: impl Into<String>, name: impl Into<String>,
@ -107,15 +82,12 @@ where
password: password.map(Into::into), password: password.map(Into::into),
service: service.map(Into::into), service: service.map(Into::into),
note: note.map(Into::into), note: note.map(Into::into),
phantom: std::marker::PhantomData,
} }
} }
}
impl Vault<Encrypted> {
/// Decrypt the vault /// Decrypt the vault
pub fn decrypt(&self, master_password: &[u8]) -> LprsResult<Vault<Plain>> { pub fn decrypt(&self, master_password: &[u8]) -> LprsResult<Vault> {
Ok(Vault::<Plain>::new( Ok(Vault::new(
cipher::decrypt(master_password, &self.name)?, cipher::decrypt(master_password, &self.name)?,
cipher::decrypt_some(master_password, self.username.as_ref())?, cipher::decrypt_some(master_password, self.username.as_ref())?,
cipher::decrypt_some(master_password, self.password.as_ref())?, cipher::decrypt_some(master_password, self.password.as_ref())?,
@ -123,12 +95,10 @@ impl Vault<Encrypted> {
cipher::decrypt_some(master_password, self.note.as_ref())?, cipher::decrypt_some(master_password, self.note.as_ref())?,
)) ))
} }
}
impl Vault<Plain> {
/// Encrypt the vault /// Encrypt the vault
pub fn encrypt(&self, master_password: &[u8]) -> LprsResult<Vault<Encrypted>> { pub fn encrypt(&self, master_password: &[u8]) -> LprsResult<Vault> {
Ok(Vault::<Encrypted>::new( Ok(Vault::new(
cipher::encrypt(master_password, &self.name)?, cipher::encrypt(master_password, &self.name)?,
cipher::encrypt_some(master_password, self.username.as_ref())?, cipher::encrypt_some(master_password, self.username.as_ref())?,
cipher::encrypt_some(master_password, self.password.as_ref())?, cipher::encrypt_some(master_password, self.password.as_ref())?,
@ -154,23 +124,18 @@ impl Vault<Plain> {
} }
} }
impl<T> Vaults<T> impl Vaults {
where
T: std::fmt::Debug + Clone,
{
/// Create new [`Vaults`] instnce /// Create new [`Vaults`] instnce
pub fn new(master_password: Vec<u8>, vaults_file: PathBuf, vaults: Vec<Vault<T>>) -> Self { pub fn new(master_password: Vec<u8>, vaults_file: PathBuf, vaults: Vec<Vault>) -> Self {
Self { Self {
master_password, master_password,
vaults_file, vaults_file,
vaults, vaults,
} }
} }
}
impl Vaults<Plain> {
/// Encrypt the vaults /// Encrypt the vaults
pub fn encrypt_vaults(&self) -> LprsResult<Vec<Vault<Encrypted>>> { pub fn encrypt_vaults(&self) -> LprsResult<Vec<Vault>> {
self.vaults self.vaults
.iter() .iter()
.map(|p| p.encrypt(&self.master_password)) .map(|p| p.encrypt(&self.master_password))
@ -179,11 +144,10 @@ impl Vaults<Plain> {
/// Reload the vaults from the file then decrypt it /// Reload the vaults from the file then decrypt it
pub fn try_reload(vaults_file: PathBuf, master_password: Vec<u8>) -> LprsResult<Self> { pub fn try_reload(vaults_file: PathBuf, master_password: Vec<u8>) -> LprsResult<Self> {
let vaults = let vaults = serde_json::from_str::<Vec<Vault>>(&fs::read_to_string(&vaults_file)?)?
serde_json::from_str::<Vec<Vault<Encrypted>>>(&fs::read_to_string(&vaults_file)?)?
.into_iter() .into_iter()
.map(|p| p.decrypt(master_password.as_slice())) .map(|p| p.decrypt(master_password.as_slice()))
.collect::<LprsResult<Vec<Vault<Plain>>>>()?; .collect::<LprsResult<Vec<Vault>>>()?;
Ok(Self::new(master_password, vaults_file, vaults)) Ok(Self::new(master_password, vaults_file, vaults))
} }
@ -202,7 +166,7 @@ impl Vaults<Plain> {
} }
/// Add new vault /// Add new vault
pub fn add_vault(&mut self, vault: Vault<Plain>) { pub fn add_vault(&mut self, vault: Vault) {
self.vaults.push(vault) self.vaults.push(vault)
} }
} }
@ -219,7 +183,7 @@ impl std::fmt::Display for Format {
} }
} }
impl<T: std::fmt::Debug + Clone> std::fmt::Display for Vault<T> { impl std::fmt::Display for Vault {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Name: {}", self.name)?; write!(f, "Name: {}", self.name)?;
if let Some(ref username) = self.username { if let Some(ref username) = self.username {

View file

@ -18,7 +18,7 @@ use std::{fs, path::Path};
use crate::LprsResult; use crate::LprsResult;
use super::{vault_state::*, Vault}; use super::Vault;
/// Return if the vaults file new file or not /// Return if the vaults file new file or not
pub fn is_new_vaults_file(path: &Path) -> LprsResult<bool> { pub fn is_new_vaults_file(path: &Path) -> LprsResult<bool> {
@ -26,7 +26,7 @@ pub fn is_new_vaults_file(path: &Path) -> LprsResult<bool> {
let file_content = fs::read_to_string(path)?; let file_content = fs::read_to_string(path)?;
if !file_content.is_empty() if !file_content.is_empty()
&& file_content.trim() != "[]" && file_content.trim() != "[]"
&& serde_json::from_str::<Vec<Vault<Encrypted>>>(&file_content).is_ok() && serde_json::from_str::<Vec<Vault>>(&file_content).is_ok()
{ {
return Ok(false); return Ok(false);
} }