mirror of
https://github.com/002Hub/IPass.git
synced 2025-04-20 14:21:22 +02:00
Compare commits
No commits in common. "master" and "v0.4.0-pre" have entirely different histories.
master
...
v0.4.0-pre
@ -1,7 +0,0 @@
|
|||||||
version = 1
|
|
||||||
|
|
||||||
[[analyzers]]
|
|
||||||
name = "rust"
|
|
||||||
|
|
||||||
[analyzers.meta]
|
|
||||||
msrv = "stable"
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,4 +4,3 @@ comp.sh
|
|||||||
report.json
|
report.json
|
||||||
*.ipass
|
*.ipass
|
||||||
*.ipassx
|
*.ipassx
|
||||||
library/*
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "library"]
|
|
||||||
path = library
|
|
||||||
url = https://github.com/002Hub/IPass-library
|
|
1076
Cargo.lock
generated
1076
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ipass"
|
name = "ipass"
|
||||||
version = "0.4.3"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
home = "0.5.4"
|
||||||
rpassword = "7.2"
|
rpassword = "7.2"
|
||||||
ip_lib = { path = "library/" }
|
rand = { features = ["std"], default-features = false, version = "0.8.5" }
|
||||||
|
aes-gcm = { features = ["alloc", "aes"], default-features = false, version = "0.10.1" }
|
||||||
|
sha2 = { default-features = false, version = "0.10.6" }
|
||||||
|
hex = "0.4.3"
|
||||||
|
brotli = { features = ["std"], default-features = false, version = "3.3.4" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 'z' # Optimize for size
|
opt-level = 'z' # Optimize for size
|
||||||
|
2
build.rs
2
build.rs
@ -1,5 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
if std::env::consts::OS == "windows" {
|
|
||||||
println!("cargo:rustc-link-arg=.res");
|
println!("cargo:rustc-link-arg=.res");
|
||||||
}
|
}
|
||||||
}
|
|
1
library
1
library
@ -1 +0,0 @@
|
|||||||
Subproject commit 29f6a59b1fed936be05c4c512a4c4fc20d73429d
|
|
113
src/main.rs
113
src/main.rs
@ -3,16 +3,17 @@
|
|||||||
|
|
||||||
//! IPass Password Manager
|
//! IPass Password Manager
|
||||||
|
|
||||||
extern crate ip_lib;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use rand::RngCore;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("x.x.x");
|
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("x.x.x");
|
||||||
println!("IPass v{}\n", version);
|
println!("IPass v{}\n", version);
|
||||||
|
|
||||||
let args = ip_lib::get_args();
|
let args = utils::get_args();
|
||||||
|
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
help_message(&args);
|
help_message(&args);
|
||||||
@ -21,16 +22,15 @@ fn main() {
|
|||||||
|
|
||||||
let mode: &str = &args[1].trim().to_lowercase().to_owned();
|
let mode: &str = &args[1].trim().to_lowercase().to_owned();
|
||||||
|
|
||||||
let sync_file_loc = ip_lib::get_home_folder_str()+"/.sync.ipass";
|
let sync_file_loc = utils::get_home_folder_str()+"/.sync.ipass";
|
||||||
let sync_file_path = std::path::Path::new(&sync_file_loc);
|
let sync_file_path = std::path::Path::new(&sync_file_loc);
|
||||||
|
|
||||||
let sync_enabled = sync_file_path.clone().exists();
|
let sync_enabled = sync_file_path.clone().exists();
|
||||||
let sync_loc: String;
|
let sync_loc: String;
|
||||||
|
|
||||||
if sync_enabled {
|
if sync_enabled {
|
||||||
// println!("Sync enabled, syncing...");
|
|
||||||
sync_loc = std::fs::read_to_string(sync_file_path).expect("Should have been able to read the sync file");
|
sync_loc = std::fs::read_to_string(sync_file_path).expect("Should have been able to read the sync file");
|
||||||
ip_lib::import_file(&sync_loc);
|
utils::import_file(&sync_loc);
|
||||||
} else {
|
} else {
|
||||||
sync_loc = "".to_string(); //sync is disabled, no location needed
|
sync_loc = "".to_string(); //sync is disabled, no location needed
|
||||||
}
|
}
|
||||||
@ -52,17 +52,12 @@ fn main() {
|
|||||||
_ => help_message(&args),
|
_ => help_message(&args),
|
||||||
}
|
}
|
||||||
if sync_enabled {
|
if sync_enabled {
|
||||||
ip_lib::export_file(&sync_loc);
|
utils::export_file(&sync_loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ask_for_pw() -> String {
|
|
||||||
let output = rpassword::prompt_password("Please enter the master password: ").unwrap();
|
|
||||||
return output.replace(|c| matches!(c, '\n' | '\r'), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn version_help(version: &str) {
|
fn version_help(version: &str) {
|
||||||
let mut data = version.split('.');
|
let mut data = version.split(".");
|
||||||
print!("Major {} ", data.next().unwrap());
|
print!("Major {} ", data.next().unwrap());
|
||||||
print!("Sub {} ", data.next().unwrap());
|
print!("Sub {} ", data.next().unwrap());
|
||||||
println!("Bugfix {}", data.next().unwrap());
|
println!("Bugfix {}", data.next().unwrap());
|
||||||
@ -135,21 +130,17 @@ fn help_message(args: &Vec<String>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if help_messages.contains_key(&args[2]) {
|
|
||||||
println!("{} {}", &args[2], &help_messages[&args[2]])
|
println!("{} {}", &args[2], &help_messages[&args[2]])
|
||||||
} else {
|
|
||||||
println!("Unknown option {}",&args[2])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list() {
|
fn list() {
|
||||||
let mut paths = ip_lib::get_entries();
|
let mut paths: std::fs::ReadDir = std::fs::read_dir(utils::get_ipass_folder()).unwrap();
|
||||||
|
|
||||||
let mut has_entry:bool = false;
|
let mut has_entry:bool = false;
|
||||||
|
|
||||||
println!("Total entries: {}\n", paths.count());
|
println!("Total entries: {}\n", paths.count());
|
||||||
|
|
||||||
paths = ip_lib::get_entries();
|
paths = std::fs::read_dir(utils::get_ipass_folder()).unwrap();
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
has_entry = true;
|
has_entry = true;
|
||||||
@ -176,7 +167,17 @@ fn add(args: &Vec<String>) {
|
|||||||
if args.len() > 4 {
|
if args.len() > 4 {
|
||||||
pw = username+";"+args[4].trim();
|
pw = username+";"+args[4].trim();
|
||||||
} else {
|
} else {
|
||||||
let chars = ip_lib::random_password();
|
let alphabet: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"$%&/()=?{[]}\\,.-;:_><|+*#'";
|
||||||
|
let alph_len: usize = alphabet.chars().count();
|
||||||
|
let char_set:Vec<char> = alphabet.chars().collect();
|
||||||
|
let mut chars_index: Vec<u8> = vec![0;20];
|
||||||
|
OsRng.fill_bytes(&mut chars_index);
|
||||||
|
let mut chars: String = String::new();
|
||||||
|
|
||||||
|
for index in chars_index {
|
||||||
|
// println!("{} - {} - {}",index,(index as usize)%(alph_len-1),alph_len);
|
||||||
|
chars += &char_set[(index as usize)%(alph_len-1)].to_string();
|
||||||
|
}
|
||||||
pw = username+";"+chars.as_str();
|
pw = username+";"+chars.as_str();
|
||||||
|
|
||||||
println!("Using auto generated password");
|
println!("Using auto generated password");
|
||||||
@ -185,7 +186,7 @@ fn add(args: &Vec<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("Adding password for {}",args[2]);
|
println!("Adding password for {}",args[2]);
|
||||||
let succeeded: bool = ip_lib::create_entry(&args[2], pw, ask_for_pw());
|
let succeeded: bool = utils::create_entry(&args[2], pw);
|
||||||
if !succeeded {
|
if !succeeded {
|
||||||
println!("You already have an entry with that name! Did you mean to use \"edit\"?");
|
println!("You already have an entry with that name! Did you mean to use \"edit\"?");
|
||||||
return;
|
return;
|
||||||
@ -200,11 +201,11 @@ fn get(args: &Vec<String>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let name: &String = &args[2];
|
let name: &String = &args[2];
|
||||||
let filepath = &(ip_lib::get_ipass_folder()+name+".ipass");
|
let filepath = &(utils::get_ipass_folder()+name+".ipass");
|
||||||
if std::path::Path::new(filepath).exists() {
|
if std::path::Path::new(filepath).exists() {
|
||||||
println!("Getting entry");
|
println!("Getting entry");
|
||||||
let entry: String = ip_lib::get_entry(name, ask_for_pw()).expect("Failed to get entry");
|
let entry: String = utils::get_entry(name);
|
||||||
let mut data = entry.split(';');
|
let mut data = entry.split(";");
|
||||||
println!("Username: '{}' Password: '{}'",data.next().unwrap(),data.next().unwrap());
|
println!("Username: '{}' Password: '{}'",data.next().unwrap(),data.next().unwrap());
|
||||||
} else {
|
} else {
|
||||||
println!("No such entry!");
|
println!("No such entry!");
|
||||||
@ -218,16 +219,16 @@ fn changepw(args: &Vec<String>) {
|
|||||||
println!("Invalid usage of \"changepw\"");
|
println!("Invalid usage of \"changepw\"");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let filepath = &(ip_lib::get_ipass_folder()+&args[2]+".ipass");
|
let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass");
|
||||||
if std::path::Path::new(filepath).exists() {
|
if std::path::Path::new(filepath).exists() {
|
||||||
let mut output: String = String::default();
|
let output: String;
|
||||||
if args.len() != 4 {
|
if args.len() != 4 {
|
||||||
output = rpassword::prompt_password("Please enter the new password: ").unwrap();
|
output = rpassword::prompt_password("Please enter the new password: ").unwrap();
|
||||||
} else {
|
} else {
|
||||||
output.clone_from(&args[3]);
|
output = args[3].clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
ip_lib::edit_password(&args[2], output, ask_for_pw());
|
utils::edit_password(&args[2], output);
|
||||||
|
|
||||||
println!("Changed Password of {}!", args[2]);
|
println!("Changed Password of {}!", args[2]);
|
||||||
} else {
|
} else {
|
||||||
@ -241,16 +242,16 @@ fn changeuser(args: &Vec<String>) {
|
|||||||
println!("Invalid usage of \"changeuser\"");
|
println!("Invalid usage of \"changeuser\"");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let filepath = &(ip_lib::get_ipass_folder()+&args[2]+".ipass");
|
let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass");
|
||||||
if std::path::Path::new(filepath).exists() {
|
if std::path::Path::new(filepath).exists() {
|
||||||
let mut output: String = String::default();
|
let output: String;
|
||||||
if args.len() != 4 {
|
if args.len() != 4 {
|
||||||
output = ip_lib::prompt_answer("Enter new Username: ".to_string());
|
output = utils::prompt_answer("Enter new Username: ".to_string());
|
||||||
} else {
|
} else {
|
||||||
output.clone_from(&args[3]);
|
output = args[3].clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
ip_lib::edit_username(&args[2], output, ask_for_pw());
|
utils::edit_username(&args[2], output);
|
||||||
|
|
||||||
println!("Changed Username of {}!", args[2]);
|
println!("Changed Username of {}!", args[2]);
|
||||||
} else {
|
} else {
|
||||||
@ -264,9 +265,9 @@ fn rename(args: &Vec<String>) {
|
|||||||
println!("Invalid usage of \"rename\"");
|
println!("Invalid usage of \"rename\"");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let filepath = &(ip_lib::get_ipass_folder()+&args[2]+".ipass");
|
let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass");
|
||||||
if std::path::Path::new(filepath).exists() {
|
if std::path::Path::new(filepath).exists() {
|
||||||
ip_lib::rename(&args[2],&args[3], ask_for_pw());
|
utils::rename(&args[2],&args[3]);
|
||||||
println!("Renamed {} to {}", args[2], args[3]);
|
println!("Renamed {} to {}", args[2], args[3]);
|
||||||
} else {
|
} else {
|
||||||
println!("No such file");
|
println!("No such file");
|
||||||
@ -280,9 +281,9 @@ fn remove(args: &Vec<String>) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let name = &args[2];
|
let name = &args[2];
|
||||||
let filepath = &(ip_lib::get_ipass_folder()+name+".ipass");
|
let filepath = &(utils::get_ipass_folder()+name+".ipass");
|
||||||
if std::path::Path::new(filepath).exists() {
|
if std::path::Path::new(filepath).exists() {
|
||||||
if ip_lib::prompt_answer(format!("Are you sure you want to delete {}? [y/N] ", name)) == "y" {
|
if utils::prompt_answer(format!("Are you sure you want to delete {}? [y/N] ", name)) == "y" {
|
||||||
std::fs::remove_file(filepath).unwrap();
|
std::fs::remove_file(filepath).unwrap();
|
||||||
println!("Removed entry \"{}\"", name);
|
println!("Removed entry \"{}\"", name);
|
||||||
} else {
|
} else {
|
||||||
@ -296,16 +297,16 @@ fn remove(args: &Vec<String>) {
|
|||||||
|
|
||||||
fn import(args: &Vec<String>) {
|
fn import(args: &Vec<String>) {
|
||||||
//! arguments: program import {location}
|
//! arguments: program import {location}
|
||||||
let mut location = ip_lib::get_home_folder_str();
|
let mut location = utils::get_home_folder_str();
|
||||||
if args.len() == 3 {
|
if args.len() == 3 {
|
||||||
location.clone_from(&args[2]);
|
location = args[2].clone();
|
||||||
} else {
|
} else {
|
||||||
if ip_lib::prompt_answer(format!("No location specified, defaulting to {} continue? [Y/n] ", location.clone())) == "n" {
|
if utils::prompt_answer(format!("No location specified, defaulting to {} continue? [Y/n] ", location.clone())) == "n" {
|
||||||
println!("Operation cancelled");
|
println!("Operation cancelled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ip_lib::import_file(&location) {
|
if utils::import_file(&location) {
|
||||||
println!("Imported all entries!");
|
println!("Imported all entries!");
|
||||||
} else {
|
} else {
|
||||||
println!("No such file found!");
|
println!("No such file found!");
|
||||||
@ -315,16 +316,16 @@ fn import(args: &Vec<String>) {
|
|||||||
|
|
||||||
fn export(args: &Vec<String>) {
|
fn export(args: &Vec<String>) {
|
||||||
//! arguments: program export {location}
|
//! arguments: program export {location}
|
||||||
let mut location = ip_lib::get_home_folder_str();
|
let mut location = utils::get_home_folder_str();
|
||||||
if args.len() == 3 {
|
if args.len() == 3 {
|
||||||
location.clone_from(&args[2]);
|
location = args[2].clone();
|
||||||
} else {
|
} else {
|
||||||
if ip_lib::prompt_answer(format!("No location specified, defaulting to {} continue? [Y/n] ", location.clone())) == "n" {
|
if utils::prompt_answer(format!("No location specified, defaulting to {} continue? [Y/n] ", location.clone())) == "n" {
|
||||||
println!("Operation cancelled");
|
println!("Operation cancelled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ip_lib::export_file(&location) {
|
if utils::export_file(&location) {
|
||||||
println!("Saved at: '{}/export.ipassx'", location);
|
println!("Saved at: '{}/export.ipassx'", location);
|
||||||
} else {
|
} else {
|
||||||
println!("Failed saving at '{}/export.ipassx' does it exist?",location);
|
println!("Failed saving at '{}/export.ipassx' does it exist?",location);
|
||||||
@ -332,16 +333,16 @@ fn export(args: &Vec<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clear() {
|
fn clear() {
|
||||||
if ip_lib::prompt_answer("Are you sure you want to clear everything? [y/N] ".to_string()) != "y" {
|
if utils::prompt_answer("Are you sure you want to clear everything? [y/N] ".to_string()) != "y" {
|
||||||
println!("operation cancelled!");
|
println!("operation cancelled!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let paths = std::fs::read_dir(ip_lib::get_ipass_folder()).unwrap();
|
let paths = std::fs::read_dir(utils::get_ipass_folder()).unwrap();
|
||||||
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
if let Ok(p) = path {
|
if let Ok(p) = path {
|
||||||
std::fs::remove_file(ip_lib::get_ipass_folder()+"/"+p.file_name().into_string().unwrap().as_str()).unwrap();
|
std::fs::remove_file(utils::get_ipass_folder()+"/"+p.file_name().into_string().unwrap().as_str()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Cleared all entries!");
|
println!("Cleared all entries!");
|
||||||
@ -359,22 +360,22 @@ fn sync(args: &Vec<String>) {
|
|||||||
|
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"on" => {
|
"on" => {
|
||||||
let mut location: String = String::default();
|
let location: String;
|
||||||
|
|
||||||
if args.len() < 4 {
|
if args.len() < 4 {
|
||||||
location = ip_lib::prompt_answer_nolower("No location specified, please provide the location of the file you want to sync: ".to_string());
|
location = utils::prompt_answer("No location specified, please provide the location of the file you want to sync: ".to_string());
|
||||||
} else {
|
} else {
|
||||||
location.clone_from(&args[3]);
|
location = args[3].clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sync_file = std::fs::File::create(ip_lib::get_home_folder_str()+"/.sync.ipass").expect("could not open sync file");
|
let mut sync_file = std::fs::File::create(utils::get_home_folder_str()+"/.sync.ipass").expect("could not open sync file");
|
||||||
sync_file.write(location.as_bytes()).expect("could not write to sync file");
|
sync_file.write(location.as_bytes()).expect("could not write to sync file");
|
||||||
|
|
||||||
if !ip_lib::export_file(&location) {
|
if !utils::export_file(&location) {
|
||||||
eprintln!("Test sync error, make sure you specified a valid folder!");
|
eprintln!("Test sync error, make sure you specified a valid folder!");
|
||||||
} else {
|
} else {
|
||||||
println!("Sync is now Enabled!");
|
println!("Sync is now Enabled!");
|
||||||
println!("Sync file: {}\\export.ipassx",location);
|
println!("Sync file: {}",location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -383,14 +384,14 @@ fn sync(args: &Vec<String>) {
|
|||||||
},
|
},
|
||||||
"off" => {
|
"off" => {
|
||||||
|
|
||||||
let sync_file_loc = ip_lib::get_home_folder_str()+"/.sync.ipass";
|
let sync_file_loc = utils::get_home_folder_str()+"/.sync.ipass";
|
||||||
let sync_file_path = std::path::Path::new(&sync_file_loc);
|
let sync_file_path = std::path::Path::new(&sync_file_loc);
|
||||||
|
|
||||||
let sync_enabled = sync_file_path.clone().exists();
|
let sync_enabled = sync_file_path.clone().exists();
|
||||||
|
|
||||||
if sync_enabled {
|
if sync_enabled {
|
||||||
|
|
||||||
std::fs::remove_file(ip_lib::get_home_folder_str()+"/.sync.ipass").expect("could not disable sync, is it already disabled?");
|
std::fs::remove_file(utils::get_home_folder_str()+"/.sync.ipass").expect("could not disable sync, is it already disabled?");
|
||||||
|
|
||||||
println!("Sync is now Disabled!");
|
println!("Sync is now Disabled!");
|
||||||
} else {
|
} else {
|
||||||
|
257
src/utils.rs
Normal file
257
src/utils.rs
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
use std::io::{Read, Write};
|
||||||
|
use aes_gcm::{
|
||||||
|
aead::{Aead, KeyInit},
|
||||||
|
Aes256Gcm, Nonce
|
||||||
|
};
|
||||||
|
use sha2::{Sha256, Digest};
|
||||||
|
|
||||||
|
pub fn get_args() -> Vec<String> {
|
||||||
|
std::env::args().collect() // [0] = file path; [n>0] = argument
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_file(location:&String) -> bool {
|
||||||
|
if std::path::Path::new(&(location.clone()+"/export.ipassx")).exists() {
|
||||||
|
let mut reader = brotli::Decompressor::new(
|
||||||
|
std::fs::File::open(location.clone()+"/export.ipassx").unwrap(),
|
||||||
|
4096, // buffer size
|
||||||
|
);
|
||||||
|
let mut content: String = String::new();
|
||||||
|
let mut buf = [0u8; 4096];
|
||||||
|
loop {
|
||||||
|
match reader.read(&mut buf[..]) {
|
||||||
|
Err(e) => {
|
||||||
|
if let std::io::ErrorKind::Interrupted = e.kind() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
panic!("{}", e);
|
||||||
|
}
|
||||||
|
Ok(size) => {
|
||||||
|
if size == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
content += &std::str::from_utf8(&buf[..size]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lines = content.lines();
|
||||||
|
let mut name = "";
|
||||||
|
for i in lines {
|
||||||
|
if name == "" {
|
||||||
|
name = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file = std::fs::File::create(format!("{}/{}.ipass",get_ipass_folder(), name)).unwrap();
|
||||||
|
file.write_all(i.as_bytes()).unwrap();
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_file(location:&String) -> bool {
|
||||||
|
let mut collected_data: String = String::new();
|
||||||
|
|
||||||
|
let paths = std::fs::read_dir(get_ipass_folder()).unwrap();
|
||||||
|
|
||||||
|
for p in paths {
|
||||||
|
if let Ok(path) = p {
|
||||||
|
let content = &mut std::fs::read_to_string(get_ipass_folder()+&path.file_name().into_string().unwrap()).expect("Should have been able to read the file");
|
||||||
|
collected_data += format!("{}\n{}\n", path.file_name().into_string().unwrap().replace(".ipass", ""),content).as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(file) = std::fs::File::create(location.clone()+"/export.ipassx") {
|
||||||
|
let mut writer = brotli::CompressorWriter::new(
|
||||||
|
file,
|
||||||
|
4096,
|
||||||
|
11,
|
||||||
|
22);
|
||||||
|
|
||||||
|
match writer.write_all(collected_data.as_bytes()) {
|
||||||
|
Err(e) => panic!("{}", e),
|
||||||
|
Ok(_) => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vecu8_to_string(vec: Vec<u8>) -> String {
|
||||||
|
let mut do_print_warning = false;
|
||||||
|
let mut out: String = String::new();
|
||||||
|
for ind in vec {
|
||||||
|
if let Ok(a) = std::str::from_utf8(&[ind]) {
|
||||||
|
out += a;
|
||||||
|
} else {
|
||||||
|
do_print_warning = true;
|
||||||
|
eprintln!("[WARNING] malformed character {}",ind);
|
||||||
|
let mut temp_vec: Vec<u8> = Vec::new();
|
||||||
|
temp_vec.insert(0,ind%128);
|
||||||
|
out += vecu8_to_string(temp_vec).as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if do_print_warning {
|
||||||
|
println!("[WARNING] Output may be corrupt");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt_pass(nonce_arg:String, pass: String,mpw: String) -> String {
|
||||||
|
let mut nonce_argument = String::new();
|
||||||
|
if nonce_arg.len() < 12 {
|
||||||
|
nonce_argument = nonce_arg.clone() + &" ".repeat(12-nonce_arg.len());
|
||||||
|
}
|
||||||
|
if nonce_arg.len() > 12 {
|
||||||
|
nonce_argument = nonce_arg[0..12].to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nonce_hasher = Sha256::new();
|
||||||
|
nonce_hasher.update(nonce_argument.as_bytes());
|
||||||
|
|
||||||
|
let nonce_final = &nonce_hasher.finalize()[0..12];
|
||||||
|
|
||||||
|
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(mpw.as_bytes());
|
||||||
|
|
||||||
|
let master_pw = &hasher.finalize();
|
||||||
|
|
||||||
|
let cipher = Aes256Gcm::new(master_pw);
|
||||||
|
let nonce = Nonce::from_slice(nonce_final); // 96-bits; unique per message
|
||||||
|
let ciphertext = cipher.encrypt(nonce, pass.as_ref()).unwrap();
|
||||||
|
return hex::encode(ciphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt_pass(nonce_arg:String, pass: Vec<u8>,mpw: String) -> String {
|
||||||
|
let mut nonce_argument = String::new();
|
||||||
|
if nonce_arg.len() < 12 {
|
||||||
|
nonce_argument = nonce_arg.clone() + &" ".repeat(12-nonce_arg.len());
|
||||||
|
}
|
||||||
|
if nonce_arg.len() > 12 {
|
||||||
|
nonce_argument = nonce_arg[0..12].to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nonce_hasher = Sha256::new();
|
||||||
|
nonce_hasher.update(nonce_argument.as_bytes());
|
||||||
|
|
||||||
|
let nonce_final = &nonce_hasher.finalize()[0..12];
|
||||||
|
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(mpw.as_bytes());
|
||||||
|
|
||||||
|
let master_pw = &hasher.finalize();
|
||||||
|
let cipher = Aes256Gcm::new(master_pw);
|
||||||
|
let nonce = Nonce::from_slice(nonce_final); // 96-bits; unique per message
|
||||||
|
|
||||||
|
let plaintext = cipher.decrypt(nonce, pass.as_ref());
|
||||||
|
match plaintext {
|
||||||
|
Ok(res) => {
|
||||||
|
return vecu8_to_string(res);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("[ERROR] Error decrypting data, check your master password");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_home_folder_str() -> String {
|
||||||
|
match home::home_dir() {
|
||||||
|
Some(path) => {
|
||||||
|
let p = path.to_str();
|
||||||
|
match p {
|
||||||
|
Some(pa) => return pa.to_owned(),
|
||||||
|
None => return "".to_owned(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return "".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ipass_folder() -> String {
|
||||||
|
let path = get_home_folder_str()+"/.IPass/";
|
||||||
|
std::fs::create_dir_all(&path).unwrap();
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_entry(name: &String, pw: String) -> bool {
|
||||||
|
if std::path::Path::new(&(get_ipass_folder()+name+".ipass")).exists() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
// println!("{pw}");
|
||||||
|
let pw = encrypt_pass(name.to_owned(), pw,mpw);
|
||||||
|
let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap();
|
||||||
|
file.write_all(pw.as_bytes()).unwrap();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_entry(name:&String,mpw:String) -> String {
|
||||||
|
let content = &mut std::fs::read_to_string(get_ipass_folder()+name+".ipass").expect("Should have been able to read the file");
|
||||||
|
return decrypt_pass(name.to_owned(),hex::decode(content).unwrap(),mpw).to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entry(name:&String) -> String {
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
return read_entry(name,mpw);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edit_password(name:&String, password:String) {
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
let entry = read_entry(name, mpw.clone());
|
||||||
|
// println!("entry: {entry}");
|
||||||
|
let mut parts = entry.split(";");
|
||||||
|
let username = parts.next().unwrap().to_string();
|
||||||
|
let _old_password = parts.next().unwrap();
|
||||||
|
let data = encrypt_pass(name.to_owned(), username+";"+password.as_str(),mpw);
|
||||||
|
let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap();
|
||||||
|
file.write_all(data.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edit_username(name:&String, username: String) {
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
let entry = read_entry(name, mpw.clone());
|
||||||
|
// println!("entry: {entry}");
|
||||||
|
let mut parts = entry.split(";");
|
||||||
|
let _old_username = parts.next().unwrap();
|
||||||
|
let password = parts.next().unwrap();
|
||||||
|
let data = encrypt_pass(name.to_owned(), username+";"+password,mpw);
|
||||||
|
let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap();
|
||||||
|
file.write_all(data.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ask_for_pw() -> String {
|
||||||
|
let output = rpassword::prompt_password("Please enter the master password: ").unwrap();
|
||||||
|
return output.replace("\n", "").replace("\r","");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt_answer(toprint: String) -> String {
|
||||||
|
print!("{toprint}");
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
let mut choice = String::new();
|
||||||
|
std::io::stdin().read_line(&mut choice).expect("Failed to read choice");
|
||||||
|
|
||||||
|
return choice.trim().to_lowercase();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename(name: &String, new_name: &String) {
|
||||||
|
if !std::path::Path::new(&(get_ipass_folder()+name+".ipass")).exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if std::path::Path::new(&(get_ipass_folder()+new_name+".ipass")).exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let content = &mut std::fs::read_to_string(get_ipass_folder()+name+".ipass").expect("Should have been able to read the file");
|
||||||
|
let mpw = ask_for_pw();
|
||||||
|
let mut pw = decrypt_pass(name.to_owned(),hex::decode(content).unwrap(),mpw.clone()).to_owned();
|
||||||
|
|
||||||
|
pw = encrypt_pass(new_name.to_owned(), pw,mpw);
|
||||||
|
let mut file = std::fs::File::create(get_ipass_folder()+new_name+".ipass").unwrap();
|
||||||
|
file.write_all(pw.as_bytes()).unwrap();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user