From b3709c146ae0ae77ffdc65a3e6263a23be9ac469 Mon Sep 17 00:00:00 2001 From: Jose Storopoli Date: Wed, 9 Oct 2024 11:48:47 -0300 Subject: [PATCH] feat(strata-cli): warn user about password strength --- Cargo.lock | 62 +++++++++++++++++++++++++++- bin/strata-cli/Cargo.toml | 1 + bin/strata-cli/src/cmd/change_pwd.rs | 12 ++++++ bin/strata-cli/src/seed.rs | 12 ++++++ bin/strata-cli/src/seed/password.rs | 6 +++ 5 files changed, 92 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fefda9d76..88902f85e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3476,6 +3476,37 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.71", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -4229,6 +4260,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + [[package]] name = "fast-float" version = "0.2.0" @@ -6915,7 +6957,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.71", @@ -12644,6 +12686,7 @@ dependencies = [ "strata-bridge-tx-builder", "terrors", "tokio", + "zxcvbn", ] [[package]] @@ -15362,3 +15405,20 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zxcvbn" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad76e35b00ad53688d6b90c431cabe3cbf51f7a4a154739e04b63004ab1c736c" +dependencies = [ + "chrono", + "derive_builder", + "fancy-regex", + "itertools 0.13.0", + "lazy_static", + "regex", + "time", + "wasm-bindgen", + "web-sys", +] diff --git a/bin/strata-cli/Cargo.toml b/bin/strata-cli/Cargo.toml index 8fd51524f..52255b391 100644 --- a/bin/strata-cli/Cargo.toml +++ b/bin/strata-cli/Cargo.toml @@ -42,6 +42,7 @@ sled = "0.34.7" strata-bridge-tx-builder.workspace = true terrors.workspace = true tokio.workspace = true +zxcvbn = "3.1.0" # sha2 fails to compile on windows with the "asm" feature [target.'cfg(not(target_os = "windows"))'.dependencies] diff --git a/bin/strata-cli/src/cmd/change_pwd.rs b/bin/strata-cli/src/cmd/change_pwd.rs index 4351a69ba..c1c0d24e6 100644 --- a/bin/strata-cli/src/cmd/change_pwd.rs +++ b/bin/strata-cli/src/cmd/change_pwd.rs @@ -1,6 +1,7 @@ use argh::FromArgs; use console::Term; use rand::thread_rng; +use zxcvbn::Score; use crate::seed::{password::Password, EncryptedSeedPersister, Seed}; @@ -12,6 +13,17 @@ pub struct ChangePwdArgs {} pub async fn change_pwd(_args: ChangePwdArgs, seed: Seed, persister: impl EncryptedSeedPersister) { let term = Term::stdout(); let mut new_pw = Password::read(true).unwrap(); + let entropy = new_pw.entropy(); + let _ = term.write_line(format!("Password strength (Overall strength score from 0-4, where anything below 3 is too weak): {}", entropy.score()).as_str()); + if entropy.score() <= Score::Two { + let _ = term.write_line( + entropy + .feedback() + .expect("No feedback") + .to_string() + .as_str(), + ); + } let encrypted_seed = seed.encrypt(&mut new_pw, &mut thread_rng()).unwrap(); persister.save(&encrypted_seed).unwrap(); let _ = term.write_line("Password changed successfully"); diff --git a/bin/strata-cli/src/seed.rs b/bin/strata-cli/src/seed.rs index 3edb5e14f..00ca8f71f 100644 --- a/bin/strata-cli/src/seed.rs +++ b/bin/strata-cli/src/seed.rs @@ -15,6 +15,7 @@ use password::{HashVersion, IncorrectPassword, Password}; use rand::{thread_rng, Rng, RngCore}; use sha2::{Digest, Sha256}; use terrors::OneOf; +use zxcvbn::Score; use crate::constants::{AES_NONCE_LEN, AES_TAG_LEN, PW_SALT_LEN, SEED_LEN}; @@ -194,6 +195,17 @@ pub fn load_or_create( }; let mut password = Password::read(true).map_err(OneOf::new)?; + let entropy = password.entropy(); + let _ = term.write_line(format!("Password strength (Overall strength score from 0-4, where anything below 3 is too weak): {}", entropy.score()).as_str()); + if entropy.score() <= Score::Two { + let _ = term.write_line( + entropy + .feedback() + .expect("No feedback") + .to_string() + .as_str(), + ); + } let encrypted_seed = match seed.encrypt(&mut password, &mut thread_rng()) { Ok(es) => es, Err(e) => { diff --git a/bin/strata-cli/src/seed/password.rs b/bin/strata-cli/src/seed/password.rs index 3f960460f..9880e9ddc 100644 --- a/bin/strata-cli/src/seed/password.rs +++ b/bin/strata-cli/src/seed/password.rs @@ -1,5 +1,6 @@ use argon2::{Algorithm, Argon2, Params, Version}; use dialoguer::Password as InputPassword; +use zxcvbn::{zxcvbn, Entropy}; use super::PW_SALT_LEN; @@ -49,6 +50,11 @@ impl Password { }) } + /// Returns the password entropy. + pub fn entropy(&self) -> Entropy { + zxcvbn(self.inner.as_str(), &[]) + } + pub fn seed_encryption_key( &mut self, salt: &[u8; PW_SALT_LEN],