Compare commits
No commits in common. "015b4229440aaa384b431d5af860e5ebfa1f185a" and "979c717cc0ecc3f9a0c26ce868dafc2b20c3328c" have entirely different histories.
015b422944
...
979c717cc0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
/target
|
/target
|
||||||
mutants.out*
|
|
652
Cargo.lock
generated
652
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -6,13 +6,13 @@ 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"
|
home = "0.5.5"
|
||||||
rand = { features = ["std"], default-features = false, version = "0.8" }
|
rand = { features = ["std"], default-features = false, version = "0.8.5" }
|
||||||
aes-gcm = { features = ["alloc", "aes"], default-features = false, version = "0.10" }
|
aes-gcm = { features = ["alloc", "aes"], default-features = false, version = "0.10.1" }
|
||||||
sha2 = { default-features = false, version = "0.10.6" }
|
sha2 = { default-features = false, version = "0.10.6" }
|
||||||
hex = "0.4"
|
hex = "0.4.3"
|
||||||
brotli = { features = ["std"], default-features = false, version = "3.3" }
|
brotli = { features = ["std"], default-features = false, version = "3.3.4" }
|
||||||
reqwest = { features = ["json"], version = "0.11"}
|
reqwest = { features = ["json"], version = "0.11.17"}
|
||||||
serde = { features = ["serde_derive"], version = "1.0"}
|
serde = { features = ["serde_derive"], version = "1.0"}
|
||||||
serde_json="1.0"
|
serde_json="1.0"
|
||||||
|
|
||||||
|
361
src/lib.rs
361
src/lib.rs
@ -1,12 +1,19 @@
|
|||||||
|
use std::io::{Read, Write};
|
||||||
use aes_gcm::{
|
use aes_gcm::{
|
||||||
aead::{Aead, KeyInit},
|
aead::{Aead, KeyInit},
|
||||||
Aes256Gcm, Nonce,
|
Aes256Gcm, Nonce
|
||||||
|
};
|
||||||
|
use sha2::{Sha256, Digest};
|
||||||
|
use std::fs::{
|
||||||
|
File,
|
||||||
|
read_dir,
|
||||||
|
read_to_string
|
||||||
|
};
|
||||||
|
use rand::{
|
||||||
|
rngs::OsRng,
|
||||||
|
RngCore
|
||||||
};
|
};
|
||||||
use rand::{rngs::OsRng, RngCore};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use std::fs::{read_dir, read_to_string, File};
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
pub fn get_args() -> Vec<String> {
|
pub fn get_args() -> Vec<String> {
|
||||||
//! [0] = file path; [n>0] = argument
|
//! [0] = file path; [n>0] = argument
|
||||||
@ -18,19 +25,16 @@ struct Saved {
|
|||||||
success: bool,
|
success: bool,
|
||||||
status: u16,
|
status: u16,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
errorcode: u64,
|
errorcode: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_token() -> Option<String> {
|
fn get_token() -> Option<String> {
|
||||||
let mut token: String = String::new();
|
let mut token: String = String::default();
|
||||||
//check if file exists
|
//check if file exists
|
||||||
if !std::path::Path::new(&(get_ipass_folder() + "token.ipasst")).exists() {
|
if !std::path::Path::new(&(get_ipass_folder()+"token.ipasst")).exists() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
File::open(get_ipass_folder() + "token.ipasst")
|
File::open(get_ipass_folder()+"token.ipasst").unwrap().read_to_string(&mut token).unwrap();
|
||||||
.unwrap()
|
|
||||||
.read_to_string(&mut token)
|
|
||||||
.unwrap();
|
|
||||||
Some(token)
|
Some(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +43,7 @@ struct HashRes {
|
|||||||
success: bool,
|
success: bool,
|
||||||
hash: String,
|
hash: String,
|
||||||
status: u16,
|
status: u16,
|
||||||
errorcode: u64,
|
errorcode: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sha256hexhash(data: Vec<u8>) -> String {
|
fn sha256hexhash(data: Vec<u8>) -> String {
|
||||||
@ -61,8 +65,7 @@ pub async fn isync_compare_hashes() -> bool {
|
|||||||
match token {
|
match token {
|
||||||
Some(token) => {
|
Some(token) => {
|
||||||
let client = reqwest::Client::builder().https_only(true).build().unwrap();
|
let client = reqwest::Client::builder().https_only(true).build().unwrap();
|
||||||
let req = client
|
let req = client.get("https://ipass.ipost.rocks/hash")
|
||||||
.get("https://ipass.ipost.rocks/hash")
|
|
||||||
.header("ipass-auth-token", token)
|
.header("ipass-auth-token", token)
|
||||||
.timeout(std::time::Duration::from_secs(3))
|
.timeout(std::time::Duration::from_secs(3))
|
||||||
.build();
|
.build();
|
||||||
@ -90,7 +93,7 @@ pub async fn isync_compare_hashes() -> bool {
|
|||||||
eprintln!("Error: {}", req.err().unwrap());
|
eprintln!("Error: {}", req.err().unwrap());
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
eprintln!("No token found!");
|
eprintln!("No token found!");
|
||||||
true
|
true
|
||||||
@ -104,8 +107,7 @@ pub async fn isync_get() -> bool {
|
|||||||
match token {
|
match token {
|
||||||
Some(token) => {
|
Some(token) => {
|
||||||
let client = reqwest::Client::builder().https_only(true).build().unwrap();
|
let client = reqwest::Client::builder().https_only(true).build().unwrap();
|
||||||
let req = client
|
let req = client.get("https://ipass.ipost.rocks/saved")
|
||||||
.get("https://ipass.ipost.rocks/saved")
|
|
||||||
.header("ipass-auth-token", token)
|
.header("ipass-auth-token", token)
|
||||||
.timeout(std::time::Duration::from_secs(3))
|
.timeout(std::time::Duration::from_secs(3))
|
||||||
.build();
|
.build();
|
||||||
@ -115,13 +117,10 @@ pub async fn isync_get() -> bool {
|
|||||||
let body = res.json::<Saved>().await;
|
let body = res.json::<Saved>().await;
|
||||||
if let Ok(body) = body {
|
if let Ok(body) = body {
|
||||||
if body.success {
|
if body.success {
|
||||||
println!("new hash: {}", sha256hexhash(body.data.clone()));
|
println!("new hash: {}",sha256hexhash(body.data.clone()));
|
||||||
File::create(get_ipass_folder() + "temp.ipassx")
|
File::create(get_ipass_folder()+"temp.ipassx").unwrap().write_all(&body.data).unwrap();
|
||||||
.unwrap()
|
import_file(&(get_ipass_folder()+"temp.ipassx"));
|
||||||
.write_all(&body.data)
|
std::fs::remove_file(get_ipass_folder()+"temp.ipassx").unwrap();
|
||||||
.unwrap();
|
|
||||||
import_file(&(get_ipass_folder() + "temp.ipassx"));
|
|
||||||
std::fs::remove_file(get_ipass_folder() + "temp.ipassx").unwrap();
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if body.status == 200 {
|
if body.status == 200 {
|
||||||
@ -142,7 +141,7 @@ pub async fn isync_get() -> bool {
|
|||||||
eprintln!("Error: {}", req.err().unwrap());
|
eprintln!("Error: {}", req.err().unwrap());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
eprintln!("No token found!");
|
eprintln!("No token found!");
|
||||||
return false;
|
return false;
|
||||||
@ -156,14 +155,14 @@ pub async fn isync_get() -> bool {
|
|||||||
struct Save {
|
struct Save {
|
||||||
success: bool,
|
success: bool,
|
||||||
status: u16,
|
status: u16,
|
||||||
errorcode: u64,
|
errorcode: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
struct SavedData {
|
struct SavedData {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
amount: i32,
|
amount: i32,
|
||||||
token: String,
|
token: String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn isync_save() -> bool {
|
pub async fn isync_save() -> bool {
|
||||||
@ -176,14 +175,13 @@ pub async fn isync_save() -> bool {
|
|||||||
let saveddata = SavedData {
|
let saveddata = SavedData {
|
||||||
data,
|
data,
|
||||||
amount: read_dir(get_ipass_folder()).unwrap().count() as i32,
|
amount: read_dir(get_ipass_folder()).unwrap().count() as i32,
|
||||||
token,
|
token
|
||||||
};
|
};
|
||||||
|
|
||||||
let requestbody = serde_json::to_string(&saveddata).unwrap();
|
let requestbody = serde_json::to_string(&saveddata).unwrap();
|
||||||
|
|
||||||
let client = reqwest::Client::builder().https_only(true).build().unwrap();
|
let client = reqwest::Client::builder().https_only(true).build().unwrap();
|
||||||
let req = client
|
let req = client.post("https://ipass.ipost.rocks/save")
|
||||||
.post("https://ipass.ipost.rocks/save")
|
|
||||||
.body(requestbody)
|
.body(requestbody)
|
||||||
.header("content-type", "application/json")
|
.header("content-type", "application/json")
|
||||||
.timeout(std::time::Duration::from_secs(5))
|
.timeout(std::time::Duration::from_secs(5))
|
||||||
@ -200,21 +198,25 @@ pub async fn isync_save() -> bool {
|
|||||||
eprintln!("Error: {}", response.errorcode);
|
eprintln!("Error: {}", response.errorcode);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => false,
|
None => {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => false,
|
},
|
||||||
|
None => {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_data<R: Read>(mut reader: brotli::Decompressor<R>) {
|
pub fn import_data<R: Read>(mut reader: brotli::Decompressor<R>) {
|
||||||
let mut content: String = String::new();
|
let mut content: String = String::default();
|
||||||
let mut buf = [0u8; 4096];
|
let mut buf = [0u8; 4096];
|
||||||
loop {
|
loop {
|
||||||
match reader.read(&mut buf[..]) {
|
match reader.read(&mut buf[..]) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let std::io::ErrorKind::Interrupted = e.kind() {
|
if e.kind() == std::io::ErrorKind::Interrupted {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
panic!("{}", e);
|
panic!("{}", e);
|
||||||
@ -230,9 +232,7 @@ pub fn import_data<R: Read>(mut reader: brotli::Decompressor<R>) {
|
|||||||
|
|
||||||
let entries = get_entries().flatten();
|
let entries = get_entries().flatten();
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
if entry.file_name().to_str().unwrap().ends_with(".ipasst")
|
if entry.file_name().to_str().unwrap().ends_with(".ipasst") || entry.file_name().to_str().unwrap().ends_with(".ipassx") {
|
||||||
|| entry.file_name().to_str().unwrap().ends_with(".ipassx")
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::fs::remove_file(entry.path()).unwrap();
|
std::fs::remove_file(entry.path()).unwrap();
|
||||||
@ -248,39 +248,35 @@ pub fn import_data<R: Read>(mut reader: brotli::Decompressor<R>) {
|
|||||||
|
|
||||||
println!("importing {}...", name);
|
println!("importing {}...", name);
|
||||||
|
|
||||||
let mut file = File::create(format!("{}/{}.ipass", get_ipass_folder(), name)).unwrap();
|
let mut file = File::create(format!("{}/{}.ipass",get_ipass_folder(), name)).unwrap();
|
||||||
file.write_all(i.as_bytes()).unwrap();
|
file.write_all(i.as_bytes()).unwrap();
|
||||||
name = "";
|
name = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_file(file_path: &String) -> bool {
|
pub fn import_file(file_path:&String) -> bool {
|
||||||
if std::path::Path::new(file_path).exists() {
|
let file_exists = std::path::Path::new(file_path).exists();
|
||||||
|
if file_exists {
|
||||||
let reader = brotli::Decompressor::new(
|
let reader = brotli::Decompressor::new(
|
||||||
File::open(file_path).unwrap(),
|
File::open(file_path).unwrap(),
|
||||||
4096, // buffer size
|
4096, // buffer size
|
||||||
);
|
);
|
||||||
import_data(reader);
|
import_data(reader);
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_exists
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_data() -> Option<Vec<u8>> {
|
pub fn export_data() -> Option<Vec<u8>> {
|
||||||
let mut collected_data = String::new();
|
let mut collected_data = String::default();
|
||||||
let paths = std::fs::read_dir(get_ipass_folder()).ok()?;
|
let paths = std::fs::read_dir(get_ipass_folder()).ok()?;
|
||||||
|
|
||||||
for path in paths.flatten() {
|
for path in paths.flatten() {
|
||||||
if path.file_name().into_string().ok()?.ends_with(".ipasst")
|
if path.file_name().into_string().ok()?.ends_with(".ipasst") || path.file_name().into_string().ok()?.ends_with(".ipassx") {
|
||||||
|| path.file_name().into_string().ok()?.ends_with(".ipassx")
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let file_name = path.file_name().into_string().ok()?.replace(".ipass", "");
|
let file_name = path.file_name().into_string().ok()?.replace(".ipass", "");
|
||||||
let content =
|
let content = std::fs::read_to_string(get_ipass_folder() + &path.file_name().to_string_lossy()).ok()?;
|
||||||
std::fs::read_to_string(get_ipass_folder() + &path.file_name().to_string_lossy())
|
|
||||||
.ok()?;
|
|
||||||
collected_data += format!("{}\n{}\n", file_name, content).as_str();
|
collected_data += format!("{}\n{}\n", file_name, content).as_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +290,8 @@ pub fn export_data() -> Option<Vec<u8>> {
|
|||||||
Some(compressed_data)
|
Some(compressed_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn export_file(file_path: &String) -> bool {
|
pub fn export_file(file_path: &String) -> bool {
|
||||||
match export_data() {
|
match export_data() {
|
||||||
Some(compressed_data) => {
|
Some(compressed_data) => {
|
||||||
@ -317,15 +315,15 @@ pub fn export_file(file_path: &String) -> bool {
|
|||||||
|
|
||||||
fn vecu8_to_string(vec: Vec<u8>) -> String {
|
fn vecu8_to_string(vec: Vec<u8>) -> String {
|
||||||
let mut do_print_warning = false;
|
let mut do_print_warning = false;
|
||||||
let mut out: String = String::new();
|
let mut out: String = String::default();
|
||||||
for ind in vec {
|
for ind in vec {
|
||||||
if let Ok(a) = std::str::from_utf8(&[ind]) {
|
if let Ok(a) = std::str::from_utf8(&[ind]) {
|
||||||
out += a;
|
out += a;
|
||||||
} else {
|
} else {
|
||||||
do_print_warning = true;
|
do_print_warning = true;
|
||||||
eprintln!("[WARNING] malformed character {}", ind);
|
eprintln!("[WARNING] malformed character {}",ind);
|
||||||
let mut temp_vec: Vec<u8> = Vec::new();
|
let mut temp_vec: Vec<u8> = Vec::new();
|
||||||
temp_vec.insert(0, ind % 128);
|
temp_vec.insert(0,ind%128);
|
||||||
out += vecu8_to_string(temp_vec).as_str();
|
out += vecu8_to_string(temp_vec).as_str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,25 +333,21 @@ fn vecu8_to_string(vec: Vec<u8>) -> String {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_nonce(nonce_arg: &str) -> String {
|
fn encrypt_pass(nonce_arg:String, pass: String,mpw: String) -> String {
|
||||||
const NONCE_LEN: usize = 12;
|
let mut nonce_argument = String::default();
|
||||||
match nonce_arg.len().cmp(&NONCE_LEN) {
|
if nonce_arg.len() < 12 {
|
||||||
std::cmp::Ordering::Less => {
|
nonce_argument = nonce_arg.clone() + &" ".repeat(12-nonce_arg.len());
|
||||||
nonce_arg.to_string() + &" ".repeat(NONCE_LEN - nonce_arg.len())
|
|
||||||
}
|
}
|
||||||
std::cmp::Ordering::Greater => nonce_arg[0..NONCE_LEN].to_string(),
|
if nonce_arg.len() > 12 {
|
||||||
std::cmp::Ordering::Equal => nonce_arg.to_string(),
|
nonce_argument = nonce_arg[0..12].to_string();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn encrypt_pass(nonce_arg: &str, pass: &str, mpw: &str) -> String {
|
|
||||||
let nonce_argument = generate_nonce(nonce_arg);
|
|
||||||
|
|
||||||
let mut nonce_hasher = Sha256::new();
|
let mut nonce_hasher = Sha256::new();
|
||||||
nonce_hasher.update(nonce_argument.as_bytes());
|
nonce_hasher.update(nonce_argument.as_bytes());
|
||||||
|
|
||||||
let nonce_final = &nonce_hasher.finalize()[0..12];
|
let nonce_final = &nonce_hasher.finalize()[0..12];
|
||||||
|
|
||||||
|
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(mpw.as_bytes());
|
hasher.update(mpw.as_bytes());
|
||||||
|
|
||||||
@ -365,8 +359,16 @@ fn encrypt_pass(nonce_arg: &str, pass: &str, mpw: &str) -> String {
|
|||||||
hex::encode(ciphertext)
|
hex::encode(ciphertext)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_pass(nonce_arg: &str, pass: Vec<u8>, mpw: &str) -> Result<String, String> {
|
|
||||||
let nonce_argument = generate_nonce(nonce_arg);
|
|
||||||
|
fn decrypt_pass(nonce_arg:String, pass: Vec<u8>,mpw: String) -> Result<String,String> {
|
||||||
|
let mut nonce_argument = String::default();
|
||||||
|
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();
|
let mut nonce_hasher = Sha256::new();
|
||||||
nonce_hasher.update(nonce_argument.as_bytes());
|
nonce_hasher.update(nonce_argument.as_bytes());
|
||||||
@ -382,132 +384,123 @@ fn decrypt_pass(nonce_arg: &str, pass: Vec<u8>, mpw: &str) -> Result<String, Str
|
|||||||
|
|
||||||
let plaintext = cipher.decrypt(nonce, pass.as_ref());
|
let plaintext = cipher.decrypt(nonce, pass.as_ref());
|
||||||
match plaintext {
|
match plaintext {
|
||||||
Ok(res) => Ok(vecu8_to_string(res)),
|
Ok(res) => {
|
||||||
Err(_) => Err("[ERROR] Error decrypting data, check your master password".to_string()),
|
Ok(vecu8_to_string(res))
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
Err("[ERROR] Error decrypting data, check your master password".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_home_folder_str() -> String {
|
pub fn get_home_folder_str() -> String {
|
||||||
const HOME_MESSAGE: &str = "Could not get home folder, set the IPASS_HOME environment variable for the parent-folder of where you want to store your passwords";
|
|
||||||
match home::home_dir() {
|
match home::home_dir() {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
let p = path.to_str();
|
let p = path.to_str();
|
||||||
match p {
|
match p {
|
||||||
Some(pa) => pa.to_owned(),
|
Some(pa) => pa.to_owned(),
|
||||||
None => panic!("{HOME_MESSAGE}"),
|
None => "".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => panic!("{HOME_MESSAGE}"),
|
None => "".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ipass_folder() -> String {
|
pub fn get_ipass_folder() -> String {
|
||||||
let path = get_home_folder_str() + "/.IPass/";
|
let path = get_home_folder_str()+"/.IPass/";
|
||||||
std::fs::create_dir_all(&path).unwrap();
|
std::fs::create_dir_all(&path).unwrap();
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_entry(name: &str, pw: &str, mpw: &str) -> bool {
|
pub fn create_entry(name: &str, pw: String, mpw: String) -> bool {
|
||||||
let mut entry_name = String::new();
|
let mut entry_name = String::default();
|
||||||
for c in name.chars() {
|
for c in name.chars() {
|
||||||
match c {
|
match c {
|
||||||
':' | '$' | '<' | '>' | '|' | '?' | '*' | '/' | '\\' => {}
|
':' | '$' | '<' | '>' | '|' | '?' | '*' | '/' | '\\' => {},
|
||||||
_ => entry_name.push(c),
|
_ => entry_name.push(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if std::path::Path::new(&(get_ipass_folder() + entry_name.as_str() + ".ipass")).exists() {
|
if std::path::Path::new(&(get_ipass_folder()+entry_name.as_str()+".ipass")).exists() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let pw = encrypt_pass(&entry_name, pw, mpw);
|
// println!("{pw}");
|
||||||
let mut file = File::create(get_ipass_folder() + entry_name.as_str() + ".ipass").unwrap();
|
let pw = encrypt_pass(entry_name.to_owned(), pw,mpw);
|
||||||
|
let mut file = File::create(get_ipass_folder()+entry_name.as_str()+".ipass").unwrap();
|
||||||
file.write_all(pw.as_bytes()).unwrap();
|
file.write_all(pw.as_bytes()).unwrap();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_entry(name: &str, mpw: &str) -> Result<String, String> {
|
fn read_entry(name:&String,mpw:String) -> Result<String,String> {
|
||||||
let path = get_ipass_folder() + name + ".ipass";
|
let path = get_ipass_folder()+name+".ipass";
|
||||||
|
|
||||||
//check if entry exists
|
//check if entry exists
|
||||||
if !std::path::Path::new(&path).exists() {
|
if !std::path::Path::new(&path).exists() {
|
||||||
return Err(format!("Entry {} does not exist", name));
|
return Err(format!("Entry {} does not exist",name));
|
||||||
}
|
}
|
||||||
|
|
||||||
let err_msg = format!("Should have been able to read the file {}", path);
|
let err_msg = format!("Should have been able to read the file {}",path);
|
||||||
let content = &mut read_to_string(path).unwrap_or_else(|_| panic!("{}", err_msg));
|
let content = &mut read_to_string(path).unwrap_or_else(|_| { panic!("{}", err_msg) });
|
||||||
decrypt_pass(name, hex::decode(content).unwrap(), mpw)
|
decrypt_pass(name.to_owned(),hex::decode(content).unwrap(),mpw)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_entry(name: &str, mpw: &str) -> Result<String, String> {
|
pub fn get_entry(name:&String, mpw: String) -> Result<String,String> {
|
||||||
read_entry(name, mpw)
|
read_entry(name,mpw)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_password(name: &str, password: &str, mpw: &str) -> bool {
|
pub fn edit_password(name:&String, password:String, mpw: String) -> bool {
|
||||||
let entry_result = read_entry(name, mpw);
|
let entry_result = read_entry(name, mpw.clone());
|
||||||
if let Ok(entry) = entry_result {
|
|
||||||
let mut parts = entry.split(';');
|
|
||||||
let username = parts.next().unwrap().to_string();
|
|
||||||
parts
|
|
||||||
.next()
|
|
||||||
.expect("Expected to be able to get old password");
|
|
||||||
|
|
||||||
let username_pw_combo = username + ";" + password;
|
|
||||||
let data = encrypt_pass(name, &username_pw_combo, mpw);
|
|
||||||
let mut file = File::create(get_ipass_folder() + name + ".ipass").unwrap();
|
|
||||||
file.write_all(data.as_bytes()).unwrap();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn edit_username(name: &str, username: &str, mpw: &str) -> bool {
|
|
||||||
let entry_result = read_entry(name, mpw);
|
|
||||||
if let Ok(entry) = entry_result {
|
if let Ok(entry) = entry_result {
|
||||||
// println!("entry: {entry}");
|
// println!("entry: {entry}");
|
||||||
let mut parts = entry.split(';');
|
let mut parts = entry.split(';');
|
||||||
|
let username = parts.next().unwrap().to_string();
|
||||||
parts
|
let _old_password = parts.next().unwrap();
|
||||||
.next()
|
let data = encrypt_pass(name.to_owned(), username+";"+password.as_str(),mpw);
|
||||||
.expect("Expected to be able to get old username");
|
let mut file = File::create(get_ipass_folder()+name+".ipass").unwrap();
|
||||||
|
|
||||||
let password = parts.next().unwrap();
|
|
||||||
|
|
||||||
let username_pw_combo = username.to_string() + ";" + password;
|
|
||||||
|
|
||||||
let data = encrypt_pass(name, &username_pw_combo, mpw);
|
|
||||||
let mut file = File::create(get_ipass_folder() + name + ".ipass").unwrap();
|
|
||||||
file.write_all(data.as_bytes()).unwrap();
|
file.write_all(data.as_bytes()).unwrap();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prompt_answer(toprint: &str) -> String {
|
pub fn edit_username(name:&String, username: String, mpw: String) -> bool {
|
||||||
|
let entry_result = read_entry(name, mpw.clone());
|
||||||
|
if let Ok(entry) = entry_result {
|
||||||
|
// 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 = File::create(get_ipass_folder()+name+".ipass").unwrap();
|
||||||
|
file.write_all(data.as_bytes()).unwrap();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prompt_answer(toprint: String) -> String {
|
||||||
prompt_answer_nolower(toprint).to_lowercase()
|
prompt_answer_nolower(toprint).to_lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prompt_answer_nolower(toprint: &str) -> String {
|
pub fn prompt_answer_nolower(toprint: String) -> String {
|
||||||
print!("{toprint}");
|
print!("{toprint}");
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
let mut choice = String::new();
|
let mut choice = String::default();
|
||||||
std::io::stdin()
|
std::io::stdin().read_line(&mut choice).expect("Failed to read choice");
|
||||||
.read_line(&mut choice)
|
|
||||||
.expect("Failed to read choice");
|
|
||||||
|
|
||||||
choice.trim().to_string()
|
return choice.trim().to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rename(name: &str, new_name: &str, mpw: &str) -> bool {
|
pub fn rename(name: &String, new_name: &String, mpw: String) -> bool {
|
||||||
if !std::path::Path::new(&(get_ipass_folder() + name + ".ipass")).exists() {
|
if !std::path::Path::new(&(get_ipass_folder()+name+".ipass")).exists() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if std::path::Path::new(&(get_ipass_folder() + new_name + ".ipass")).exists() {
|
if std::path::Path::new(&(get_ipass_folder()+new_name+".ipass")).exists() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let content = &mut read_to_string(get_ipass_folder() + name + ".ipass")
|
let content = &mut read_to_string(get_ipass_folder()+name+".ipass").expect("Should have been able to read the file");
|
||||||
.expect("Should have been able to read the file");
|
if let Ok(mut data) = decrypt_pass(name.to_owned(),hex::decode(content).unwrap(),mpw.clone()) {
|
||||||
if let Ok(mut data) = decrypt_pass(name, hex::decode(content).unwrap(), mpw) {
|
data = encrypt_pass(new_name.to_owned(), data,mpw);
|
||||||
data = encrypt_pass(new_name, &data, mpw);
|
let mut file = File::create(get_ipass_folder()+new_name+".ipass").unwrap();
|
||||||
let mut file = File::create(get_ipass_folder() + new_name + ".ipass").unwrap();
|
|
||||||
file.write_all(data.as_bytes()).unwrap();
|
file.write_all(data.as_bytes()).unwrap();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -519,94 +512,56 @@ pub fn get_entries() -> std::fs::ReadDir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_password() -> String {
|
pub fn random_password() -> String {
|
||||||
const ALPHABET: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"$%&/()=?{[]}\\,.-;:_><|+*#'";
|
let alphabet: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"$%&/()=?{[]}\\,.-;:_><|+*#'";
|
||||||
let alph_len: usize = ALPHABET.len();
|
let alph_len: usize = alphabet.chars().count();
|
||||||
let char_set: Vec<char> = ALPHABET.chars().collect();
|
let char_set:Vec<char> = alphabet.chars().collect();
|
||||||
let mut chars_index: Vec<u8> = vec![0; 20];
|
let mut chars_index: Vec<u8> = vec![0;20];
|
||||||
OsRng.fill_bytes(&mut chars_index);
|
OsRng.fill_bytes(&mut chars_index);
|
||||||
chars_index
|
let mut chars: String = String::default();
|
||||||
.iter()
|
|
||||||
.map(|index| char_set[(*index as usize) % alph_len].to_string())
|
for index in chars_index {
|
||||||
.collect()
|
// println!("{} - {} - {}",index,(index as usize)%(alph_len-1),alph_len);
|
||||||
|
chars += &char_set[(index as usize)%(alph_len-1)].to_string();
|
||||||
|
}
|
||||||
|
chars
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn encrypt_decrypt() {
|
fn encrypt_decrypt() {
|
||||||
const NAME: &str = "test";
|
let name = "test".to_string();
|
||||||
const PASSWORD: &str = "test";
|
let password = "test".to_string();
|
||||||
const MASTER_PASSWORD: &str = "test";
|
let mpw = "test".to_string();
|
||||||
let encrypted = hex::decode(super::encrypt_pass(NAME, PASSWORD, MASTER_PASSWORD)).unwrap();
|
let encrypted = hex::decode(super::encrypt_pass(name.clone(), password.clone(), mpw.clone())).unwrap();
|
||||||
let decrypted = super::decrypt_pass(NAME, encrypted, MASTER_PASSWORD).unwrap();
|
let decrypted = super::decrypt_pass(name, encrypted, mpw).unwrap();
|
||||||
assert_eq!(decrypted, PASSWORD);
|
assert_eq!(decrypted, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encrypt_decrypt_error() {
|
fn encrypt_decrypt_error() {
|
||||||
const NAME: &str = "test";
|
let name = "test".to_string();
|
||||||
const PASSWORD: &str = "test";
|
let password = "test".to_string();
|
||||||
const MASTER_PASSWORD: &str = "test";
|
let mpw = "test".to_string();
|
||||||
let encrypted = hex::decode(super::encrypt_pass(NAME, PASSWORD, MASTER_PASSWORD)).unwrap();
|
let encrypted = hex::decode(super::encrypt_pass(name.clone(), password.clone(), mpw.clone())).unwrap();
|
||||||
let decrypted = super::decrypt_pass(NAME, encrypted, "test2");
|
let decrypted = super::decrypt_pass(name, encrypted, "test2".to_string());
|
||||||
assert!(decrypted.is_err());
|
assert!(decrypted.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_delete_entry() {
|
fn create_delete_entry() {
|
||||||
const NAME: &str = "test";
|
let name = "test".to_string();
|
||||||
const PASSWORD: &str = "test";
|
let password = "test".to_string();
|
||||||
const MASTER_PASSWORD: &str = "test";
|
let mpw = "test".to_string();
|
||||||
let created = super::create_entry(NAME, PASSWORD, MASTER_PASSWORD);
|
let created = super::create_entry(&name, password.clone(), mpw.clone());
|
||||||
assert!(created);
|
assert!(created);
|
||||||
let entry = super::read_entry(NAME, MASTER_PASSWORD);
|
let entry = super::read_entry(&name, mpw.clone());
|
||||||
assert!(entry.is_ok());
|
assert!(entry.is_ok());
|
||||||
assert_eq!(entry.unwrap(), PASSWORD);
|
assert_eq!(entry.unwrap(), password);
|
||||||
|
|
||||||
let deleted = std::fs::remove_file(super::get_ipass_folder() + NAME + ".ipass");
|
let deleted = std::fs::remove_file(super::get_ipass_folder()+name.as_str()+".ipass");
|
||||||
assert!(deleted.is_ok());
|
assert!(deleted.is_ok());
|
||||||
let entry = super::read_entry(NAME, MASTER_PASSWORD);
|
let entry = super::read_entry(&name, mpw.clone());
|
||||||
assert!(entry.is_err());
|
assert!(entry.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_ipass_folder() {
|
|
||||||
let path = super::get_ipass_folder();
|
|
||||||
let path = std::path::Path::new(&path);
|
|
||||||
assert!(path.is_dir());
|
|
||||||
assert!(path.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_home_folder() {
|
|
||||||
let path = super::get_home_folder_str();
|
|
||||||
let path = std::path::Path::new(&path);
|
|
||||||
assert!(path.is_dir());
|
|
||||||
assert!(path.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_nonce() {
|
|
||||||
let nonce = super::generate_nonce("test");
|
|
||||||
assert_eq!(nonce, "test ");
|
|
||||||
let nonce = super::generate_nonce("0123456789abcdef");
|
|
||||||
assert_eq!(nonce, "0123456789ab");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_random_pw_length() {
|
|
||||||
for _ in 0..100_000 {
|
|
||||||
assert_eq!(super::random_password().len(), 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sha256hexhash() {
|
|
||||||
let to_hash = "test".as_bytes().to_vec();
|
|
||||||
let hash = super::sha256hexhash(to_hash);
|
|
||||||
assert_eq!(
|
|
||||||
hash,
|
|
||||||
"9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user