Improve security of encrypted notes
Closes #1. The password is hashed using Argon2, with the salt being `SHA256(password)`. The output hash is then encoded using hex. The password-checking function checks if the "note database" is encrypted using the plain password (and returns it) or if it is encrypted using the hashed+salted password (and returns the value). This allows older databases to work properly. A migration path may be added in the future.
This commit is contained in:
parent
e40c076202
commit
f4b5a0541d
3 changed files with 48 additions and 4 deletions
|
@ -15,4 +15,7 @@ serde_json = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
pwbox = "0.5"
|
pwbox = "0.5"
|
||||||
directories = "4.0"
|
directories = "4.0"
|
||||||
native-dialog = "0.6"
|
native-dialog = "0.6"
|
||||||
|
hex = "0.4"
|
||||||
|
sha2 = "0.10"
|
||||||
|
argon2 = "0.5"
|
12
src/main.rs
12
src/main.rs
|
@ -16,10 +16,12 @@ use log::info;
|
||||||
use native_dialog::MessageDialog;
|
use native_dialog::MessageDialog;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use crate::notes::{Note, NoteMetadata};
|
use crate::notes::{Note, NoteMetadata};
|
||||||
|
use crate::password::{check_password, generate_new_password};
|
||||||
use crate::saving::Saving;
|
use crate::saving::Saving;
|
||||||
|
|
||||||
mod notes;
|
mod notes;
|
||||||
mod saving;
|
mod saving;
|
||||||
|
mod password;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum CurrentMode {
|
enum CurrentMode {
|
||||||
|
@ -101,7 +103,9 @@ impl eframe::App for App {
|
||||||
|
|
||||||
if !self.tested_password && self.password.is_some() {
|
if !self.tested_password && self.password.is_some() {
|
||||||
if let Some(note) = self.notes.get(0) {
|
if let Some(note) = self.notes.get(0) {
|
||||||
if note.decrypt(self.password.as_ref().unwrap()).is_err() {
|
if let Ok(password) = check_password(self.password.as_ref().unwrap(), note) {
|
||||||
|
self.password = Some(password);
|
||||||
|
} else {
|
||||||
let _ = MessageDialog::new()
|
let _ = MessageDialog::new()
|
||||||
.set_title("invalid password")
|
.set_title("invalid password")
|
||||||
.set_text("failed to verify the password against an existing note. please try again")
|
.set_text("failed to verify the password against an existing note. please try again")
|
||||||
|
@ -111,9 +115,11 @@ impl eframe::App for App {
|
||||||
self.tested_password = false;
|
self.tested_password = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
self.tested_password = true;
|
self.password = Some(generate_new_password(self.password.as_ref().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.tested_password = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.update_notes_next {
|
if self.update_notes_next {
|
||||||
|
|
35
src/password/mod.rs
Normal file
35
src/password/mod.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use anyhow::bail;
|
||||||
|
use argon2::Argon2;
|
||||||
|
use sha2::{Sha256, Digest};
|
||||||
|
use crate::notes::Note;
|
||||||
|
|
||||||
|
pub fn generate_new_password(password: &str) -> String {
|
||||||
|
let mut output = [0_u8; 32];
|
||||||
|
let mut output_key = [0u8; 256];
|
||||||
|
|
||||||
|
hash_password(password.as_bytes(), &mut output);
|
||||||
|
Argon2::default().hash_password_into(&password.as_bytes(), &output, &mut output_key).unwrap();
|
||||||
|
|
||||||
|
hex::encode(output_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_password(password: &str, note: &Note) -> anyhow::Result<String> {
|
||||||
|
if note.decrypt(password).is_err() {
|
||||||
|
let new_password = generate_new_password(password);
|
||||||
|
if note.decrypt(&new_password).is_err() {
|
||||||
|
bail!("invalid password");
|
||||||
|
} else {
|
||||||
|
Ok(new_password)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(password.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_password(password: &[u8], output: &mut [u8]) {
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(password);
|
||||||
|
let result = hasher.finalize();
|
||||||
|
|
||||||
|
output.clone_from_slice(&result[..])
|
||||||
|
}
|
Loading…
Reference in a new issue