From 0b56813eceec20434cd811f4dd20e495b1f88991 Mon Sep 17 00:00:00 2001 From: artur Date: Wed, 14 Feb 2024 09:55:11 +0300 Subject: [PATCH] Add hyphen and underscore as valid password symbols, increase valid password to 6 chars, add password validity and strength indicator --- .../Modal/ModalSettings/ModalSettings.tsx | 40 +++++++++++++------ src/modules/keys.ts | 28 ++++++++++--- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/components/Modal/ModalSettings/ModalSettings.tsx b/src/components/Modal/ModalSettings/ModalSettings.tsx index 97a5502..89f2ab7 100644 --- a/src/components/Modal/ModalSettings/ModalSettings.tsx +++ b/src/components/Modal/ModalSettings/ModalSettings.tsx @@ -16,6 +16,7 @@ import { dbi } from '@/modules/db' import { usePassword } from '@/hooks/usePassword' import { useAppSelector } from '@/store/hooks/redux' import { selectKeys } from '@/store' +import { isValidPassphase, isWeakPassphase } from '@/modules/keys' type ModalSettingsProps = { isSynced: boolean @@ -58,8 +59,9 @@ export const ModalSettings: FC = ({ isSynced }) => { } const handlePasswordChange = (e: ChangeEvent) => { - setIsPasswordInvalid(false) - setEnteredPassword(e.target.value) + const password = e.target.value + setIsPasswordInvalid(!!password && !isValidPassphase(password)) + setEnteredPassword(password) } const onClose = () => { @@ -76,7 +78,7 @@ export const ModalSettings: FC = ({ isSynced }) => { e.preventDefault() setIsPasswordInvalid(false) - if (enteredPassword.trim().length < 6) { + if (!isValidPassphase(enteredPassword)) { return setIsPasswordInvalid(true) } try { @@ -114,18 +116,30 @@ export const ModalSettings: FC = ({ isSynced }) => { {...inputProps} onChange={handlePasswordChange} value={enteredPassword} - helperText={isPasswordInvalid ? 'Invalid password' : ''} + // helperText={isPasswordInvalid ? 'Invalid password' : ''} placeholder="Enter a password" - helperTextProps={{ - sx: { - '&.helper_text': { - color: 'red', - }, - }, - }} + // helperTextProps={{ + // sx: { + // '&.helper_text': { + // color: 'red', + // }, + // }, + // }} disabled={!isChecked} /> - {isSynced ? ( + {isPasswordInvalid ? ( + + Password must include 6+ English letters, numbers or punctuation marks. + + ) : !!enteredPassword && isWeakPassphase(enteredPassword) ? ( + + Weak password + + ) : !!enteredPassword && !isPasswordInvalid ? ( + + Good password + + ) : isSynced ? ( To change your password, type a new one and sync. @@ -139,7 +153,7 @@ export const ModalSettings: FC = ({ isSynced }) => { Sync {isLoading && } - + {/* */} ) diff --git a/src/modules/keys.ts b/src/modules/keys.ts index 847829b..70af6b8 100644 --- a/src/modules/keys.ts +++ b/src/modules/keys.ts @@ -21,11 +21,31 @@ const ALGO = 'aes-256-cbc' const IV_SIZE = 16 // valid passwords are a limited ASCII only, see notes below -const ASCII_REGEX = /^[A-Za-z0-9!@#$%^&*()]{4,}$/ +const ASCII_REGEX = /^[A-Za-z0-9!@#$%^&*()\-_]{6,}$/ const ALGO_LOCAL = 'AES-CBC' const KEY_SIZE_LOCAL = 256 +export function isValidPassphase(passphrase: string): boolean { + return ASCII_REGEX.test(passphrase) +} + +export function isWeakPassphase(passphrase: string): boolean { + const BIG_LETTER_REGEX = /[A-Z]+/ + const SMALL_LETTER_REGEX = /[a-z]+/ + const NUMBER_REGEX = /[0-9]+/ + const PUNCT_REGEX = /[!@#$%^&*()\-_]+/ + const big = BIG_LETTER_REGEX.test(passphrase) ? 1 : 0 + const small = SMALL_LETTER_REGEX.test(passphrase) ? 1 : 0 + const number = NUMBER_REGEX.test(passphrase) ? 1 : 0 + const punct = PUNCT_REGEX.test(passphrase) ? 1 : 0 + const base = big * 26 + small * 26 + number * 10 + punct * 12 + const compl = Math.pow(base, passphrase.length) + const thresh = Math.pow(11, 14) + // console.log({ big, small, number, punct, base, compl, thresh }); + return compl < thresh; +} + export class Keys { subtle: any @@ -33,10 +53,6 @@ export class Keys { this.subtle = cryptoSubtle } - public isValidPassphase(passphrase: string): boolean { - return ASCII_REGEX.test(passphrase) - } - public async generatePassKey(pubkey: string, passphrase: string): Promise<{ passkey: Buffer; pwh: string }> { const salt = Buffer.from(pubkey, 'hex') @@ -45,7 +61,7 @@ export class Keys { // We could use string.normalize() to make sure all JS implementations // are compatible, but since we're looking to make this thing a standard // then the simplest way is to exclude unicode and only work with ASCII - if (!this.isValidPassphase(passphrase)) throw new Error('Password must be 4+ ASCII chars') + if (!isValidPassphase(passphrase)) throw new Error('Password must be 4+ ASCII chars') return new Promise((ok, fail) => { // NOTE: we should use Argon2 or scrypt later, for now