]> git.codecow.com Git - libnemo.git/commitdiff
Enforce strong wallet passwords.
authorChris Duncan <chris@codecow.com>
Mon, 3 Aug 2026 14:38:41 +0000 (07:38 -0700)
committerChris Duncan <chris@codecow.com>
Mon, 3 Aug 2026 14:38:41 +0000 (07:38 -0700)
src/lib/wallet/create.ts
src/lib/wallet/index.ts
src/lib/wallet/load.ts
src/lib/wallet/update.ts

index d9d237636fdb52aaa8a0c93457c19a1e3b09056b..cf6fb52cc0904376f307760afb5bbd728770fd8a 100644 (file)
@@ -19,9 +19,7 @@ export async function _create (wallet: Wallet, vault: Vault, password: unknown,
                        encrypted: new ArrayBuffer(0)
                }
                if (wallet.type !== 'Ledger') {
-                       if (typeof password !== 'string') {
-                               throw new TypeError('Password must be a string')
-                       }
+                       Wallet.zxcvbn(password)
                        if (mnemonicSalt !== undefined && typeof mnemonicSalt !== 'string') {
                                throw new TypeError('Mnemonic salt must be a string')
                        }
index 9579d4afa4dfc858829c40e33955d5e6667920f0..bc0bb80d8bd883eb2096c4ee5aed85c76968f986 100644 (file)
@@ -1,6 +1,9 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>\r
 //! SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
+import { ZxcvbnFactory } from '@zxcvbn-ts/core'\r
+import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'\r
+import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'\r
 import { UUID } from 'crypto'\r
 import { Account } from '../account'\r
 import { Block } from '../block'\r
@@ -33,6 +36,23 @@ export type WalletType = 'BIP-44' | 'BLAKE2b' | 'Exodus' | 'Ledger'
 */\r
 export class Wallet {\r
        static #isInternal: boolean = false\r
+       static #zxcvbn: ZxcvbnFactory = new ZxcvbnFactory({\r
+               translations: zxcvbnEnPackage.translations,\r
+               graphs: zxcvbnCommonPackage.adjacencyGraphs,\r
+               dictionary: {\r
+                       ...zxcvbnCommonPackage.dictionary,\r
+                       ...zxcvbnEnPackage.dictionary,\r
+               }\r
+       })\r
+       static zxcvbn (password: unknown): asserts password is string {\r
+               if (typeof password !== 'string') {\r
+                       throw new TypeError('Password must be a string')\r
+               }\r
+               const passwordCheck = this.#zxcvbn.check(password)\r
+               if (passwordCheck.score < 4) {\r
+                       throw new RangeError('Weak password', { cause: passwordCheck })\r
+               }\r
+       }\r
 \r
        /**\r
        * @returns {boolean}\r
index bedb522338c6319be7ee0937923fb7d1e76c921d..49bac7698faa78ab2c15dc34c34a0a30971458c4 100644 (file)
@@ -23,12 +23,10 @@ export async function _load (wallet: Wallet, vault: Vault, password: unknown, se
                                throw new Error('Failed to initialize Ledger wallet', { cause: 'Browser is unsupported' })
                        }
                } else {
+                       Wallet.zxcvbn(password)
                        if (wallet.type !== 'BIP-44' && wallet.type !== 'BLAKE2b' && wallet.type !== 'Exodus') {
                                throw new TypeError('Invalid wallet type', { cause: wallet.type })
                        }
-                       if (typeof password !== 'string') {
-                               throw new TypeError('Password must be a string')
-                       }
                        if (typeof secret !== 'string') {
                                throw new TypeError('Wallet secret must be a string')
                        }
index fb9e33f48e80c9cbbbcecadbdb86c68ea72156f5..9ed6dfdbdb778c7eaf346c167dec7ea7dcf043cc 100644 (file)
@@ -10,9 +10,7 @@ export async function _update (wallet: Wallet, vault: Vault, password?: string):
 export async function _update (wallet: Wallet, vault: Vault, password: unknown): Promise<void> {
        try {
                if (wallet.type !== 'Ledger') {
-                       if (typeof password !== 'string') {
-                               throw new TypeError('Password must be a string')
-                       }
+                       Wallet.zxcvbn(password)
                        const pending = vault.request<ArrayBuffer>({
                                action: 'update',
                                password: utf8.toBuffer(password)