diff options
| author | Adam Soltys <[email protected]> | 2026-08-17 08:12:34 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-17 08:12:34 -0700 |
| commit | b62ca1099ff996e333015142cb712687737d4637 (patch) | |
| tree | b385b675aefbf9718f8774fe0e94bda41256a614 /src/wallet.js | |
| parent | f6120c49d80e0c8f2103a9adfd928f7004645aaf (diff) | |
| download | coinosv3-b62ca1099ff996e333015142cb712687737d4637.tar.gz coinosv3-b62ca1099ff996e333015142cb712687737d4637.zip | |
The seed takes dice: an optional entropy field folds open on the generate step, hashed with the device's own randomness so it can only strengthen the phrase — and the forget option now says all data, which is what it takes
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
Diffstat (limited to 'src/wallet.js')
| -rw-r--r-- | src/wallet.js | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/src/wallet.js b/src/wallet.js index 662ef0f..2ede69d 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -9,7 +9,7 @@ // transactions, only the (txid, vout, value, address) of each UTXO. import { HDKey } from '@scure/bip32'; -import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'; +import { generateMnemonic, mnemonicToSeedSync, validateMnemonic, entropyToMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { hex, base64urlnopad, base32nopad, base58check } from '@scure/base'; import { sha256 } from '@noble/hashes/sha256'; @@ -64,8 +64,16 @@ export const NETS = { regtest: { net: { ...btc.TEST_NETWORK, bech32: 'bcrt' }, coin: 1 }, }; -export function newMnemonic(strengthBits = 128) { - return generateMnemonic(wordlist, strengthBits); +export function newMnemonic(strengthBits = 128, extraEntropy = '') { + if (!extraEntropy) return generateMnemonic(wordlist, strengthBits); + // User entropy is MIXED with the CSPRNG, never used alone: hashing the two + // together can only add randomness — weak dice can't weaken the seed, and + // a distrusted device RNG can't fully determine it either. + const txt = new TextEncoder().encode(extraEntropy); + const rnd = crypto.getRandomValues(new Uint8Array(strengthBits / 8)); + const mix = new Uint8Array(txt.length + rnd.length); + mix.set(txt); mix.set(rnd, txt.length); + return entropyToMnemonic(sha256(mix).slice(0, strengthBits / 8), wordlist); } // The base localStorage cache key for a wallet identity (xpub, xprv, or |
