diff --git a/src/main.rs b/src/main.rs index 01e559b..604dd4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,12 +11,12 @@ use std::fs; use std::time::SystemTime; use directories::ProjectDirs; use eframe::{CreationContext, Frame}; -use egui::{Context, RichText, TextEdit, Widget}; +use egui::{Color32, Context, RichText, TextEdit, Widget, WidgetText}; use log::info; use native_dialog::MessageDialog; use simple_logger::SimpleLogger; use crate::notes::{Note, NoteMetadata}; -use crate::password::{check_password, generate_new_password}; +use crate::password::{check_password, entropy, generate_new_password}; use crate::saving::Saving; mod notes; @@ -223,7 +223,20 @@ impl eframe::App for App { ui.label(RichText::new("after starting the app, the password will be tested against the latest note").italics()); ui.add_space(10.0); - TextEdit::singleline(&mut self.password_buffer).password(true).ui(ui); + ui.horizontal(|ui| { + TextEdit::singleline(&mut self.password_buffer).password(true).ui(ui); + if let Some(entropy) = entropy(&self.password_buffer) { + let text = if entropy < 60_f64 { + WidgetText::from(format!("entropy: {:.2}", entropy)) + .color(Color32::from_rgb(220, 88, 42)) + } else { + WidgetText::from(format!("entropy: {:.2}", entropy)) + .color(Color32::from_rgb(144, 238, 144)) + }; + + ui.label(text); + } + }); ui.add_space(7.5); if ui.button("start app").clicked() { diff --git a/src/password/mod.rs b/src/password/mod.rs index 6aa3f70..dc05018 100644 --- a/src/password/mod.rs +++ b/src/password/mod.rs @@ -8,11 +8,27 @@ pub fn generate_new_password(password: &str) -> String { 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(); + Argon2::default().hash_password_into(password.as_bytes(), &output, &mut output_key).unwrap(); hex::encode(output_key) } +pub fn entropy(password: &str) -> Option { + if password.is_empty() { + return None; + } + + let length = password.len() as f64; + let unique_characters = { + let mut unique_chars: Vec = password.chars().collect(); + unique_chars.dedup(); + + unique_chars.len() + } as f64; + + Some(length * unique_characters.log2()) +} + pub fn check_password(password: &str, note: &Note) -> anyhow::Result { if note.decrypt(password).is_err() { let new_password = generate_new_password(password);