@scure/bip39
Advanced tools
+86
-31
@@ -0,8 +1,17 @@ | ||
| import { type TArg, type TRet } from '@noble/hashes/utils.js'; | ||
| /** | ||
| * Generate x random words. Uses Cryptographically-Secure Random Number Generator. | ||
| * @param wordlist imported wordlist for specific language | ||
| * @param strength mnemonic strength 128-256 bits | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @param strength - Mnemonic strength, from 128 to 256 bits. | ||
| * @returns 12-24 word mnemonic phrase. | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * generateMnemonic(wordlist, 128) | ||
| * Generate a new English mnemonic. | ||
| * ```ts | ||
| * import { generateMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const mnemonic = generateMnemonic(wordlist, 128); | ||
| * // 'legal winner thank year wave sausage worth useful legal winner thank yellow' | ||
| * ``` | ||
| */ | ||
@@ -12,8 +21,16 @@ export declare function generateMnemonic(wordlist: string[], strength?: number): string; | ||
| * Reversible: Converts mnemonic string to raw entropy in form of byte array. | ||
| * @param mnemonic 12-24 words | ||
| * @param wordlist imported wordlist for specific language | ||
| * @param mnemonic - 12-24 words. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns Raw entropy bytes. | ||
| * @throws If the mnemonic shape or checksum is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * Decode a mnemonic back into its original entropy bytes. | ||
| * ```ts | ||
| * import { mnemonicToEntropy } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToEntropy(mnem, wordlist) | ||
| * // Produces | ||
| * const entropy = mnemonicToEntropy(mnem, wordlist); | ||
| * // Produces the original 16-byte entropy payload. | ||
| * new Uint8Array([ | ||
@@ -23,10 +40,17 @@ * 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
| * ]) | ||
| * ``` | ||
| */ | ||
| export declare function mnemonicToEntropy(mnemonic: string, wordlist: string[]): Uint8Array; | ||
| export declare function mnemonicToEntropy(mnemonic: string, wordlist: string[]): TRet<Uint8Array>; | ||
| /** | ||
| * Reversible: Converts raw entropy in form of byte array to mnemonic string. | ||
| * @param entropy byte array | ||
| * @param wordlist imported wordlist for specific language | ||
| * @returns 12-24 words | ||
| * @param entropy - Byte array. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns 12-24 words. | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * Convert raw entropy into an English mnemonic. | ||
| * ```ts | ||
| * import { entropyToMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const ent = new Uint8Array([ | ||
@@ -36,8 +60,23 @@ * 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
| * ]); | ||
| * entropyToMnemonic(ent, wordlist); | ||
| * const mnemonic = entropyToMnemonic(ent, wordlist); | ||
| * // 'legal winner thank year wave sausage worth useful legal winner thank yellow' | ||
| * ``` | ||
| */ | ||
| export declare function entropyToMnemonic(entropy: Uint8Array, wordlist: string[]): string; | ||
| export declare function entropyToMnemonic(entropy: TArg<Uint8Array>, wordlist: string[]): string; | ||
| /** | ||
| * Validates mnemonic for being 12-24 words contained in `wordlist`. | ||
| * @param mnemonic - 12-24 words. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns `true` when mnemonic checksum and words are valid. | ||
| * @example | ||
| * Validate one English mnemonic. | ||
| * ```ts | ||
| * import { validateMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const ok = validateMnemonic( | ||
| * 'legal winner thank year wave sausage worth useful legal winner thank yellow', | ||
| * wordlist | ||
| * ); | ||
| * // => true | ||
| * ``` | ||
| */ | ||
@@ -47,33 +86,49 @@ export declare function validateMnemonic(mnemonic: string, wordlist: string[]): boolean; | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed from a mnemonic with the async PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * await mnemonicToSeed(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = await mnemonicToSeed(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export declare function mnemonicToSeed(mnemonic: string, passphrase?: string): Promise<Uint8Array>; | ||
| export declare function mnemonicToSeed(mnemonic: string, passphrase?: string): Promise<TRet<Uint8Array>>; | ||
| /** | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed from a mnemonic with the sync PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToSeedSync(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = mnemonicToSeedSync(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export declare function mnemonicToSeedSync(mnemonic: string, passphrase?: string): Uint8Array; | ||
| export declare function mnemonicToSeedSync(mnemonic: string, passphrase?: string): TRet<Uint8Array>; | ||
| /** | ||
| * Uses native, built-in functionality, provided by globalThis.crypto. | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed with the native WebCrypto PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToSeedWebcrypto(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = await mnemonicToSeedWebcrypto(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export declare function mnemonicToSeedWebcrypto(mnemonic: string, passphrase?: string): Promise<Uint8Array>; | ||
| export declare function mnemonicToSeedWebcrypto(mnemonic: string, passphrase?: string): Promise<TRet<Uint8Array>>; | ||
| //# sourceMappingURL=index.d.ts.map |
+108
-33
@@ -8,2 +8,4 @@ /*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */ | ||
| // Japanese wordlist | ||
| // The canonical BIP-39 Japanese wordlist starts with あいこくしん. | ||
| // Use that sentinel so generated phrases use U+3000 ideographic spaces. | ||
| const isJapanese = (wordlist) => wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093'; | ||
@@ -14,2 +16,4 @@ // Normalization replaces equivalent sequences of characters | ||
| // https://tonsky.me/blog/unicode/#why-is-a---- | ||
| // BIP-39 requires UTF-8 NFKD for localized wordlists and mnemonic sentences. | ||
| // It also applies NFKD to the "mnemonic" + passphrase salt. | ||
| function nfkd(str) { | ||
@@ -20,2 +24,4 @@ if (typeof str !== 'string') | ||
| } | ||
| // BIP-39 mnemonics are consumed in NFKD form. | ||
| // They must contain 12, 15, 18, 21, or 24 words before checksum validation. | ||
| function normalize(str) { | ||
@@ -28,14 +34,23 @@ const norm = nfkd(str); | ||
| } | ||
| // BIP-39 entropy payloads are 128-256 bits in 32-bit increments, i.e. 16/20/24/28/32 bytes. | ||
| function aentropy(ent) { | ||
| abytes(ent); | ||
| if (![16, 20, 24, 28, 32].includes(ent.length)) | ||
| throw new Error('invalid entropy length'); | ||
| throw new RangeError('invalid entropy length'); | ||
| } | ||
| /** | ||
| * Generate x random words. Uses Cryptographically-Secure Random Number Generator. | ||
| * @param wordlist imported wordlist for specific language | ||
| * @param strength mnemonic strength 128-256 bits | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @param strength - Mnemonic strength, from 128 to 256 bits. | ||
| * @returns 12-24 word mnemonic phrase. | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * generateMnemonic(wordlist, 128) | ||
| * Generate a new English mnemonic. | ||
| * ```ts | ||
| * import { generateMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const mnemonic = generateMnemonic(wordlist, 128); | ||
| * // 'legal winner thank year wave sausage worth useful legal winner thank yellow' | ||
| * ``` | ||
| */ | ||
@@ -45,3 +60,3 @@ export function generateMnemonic(wordlist, strength = 128) { | ||
| if (strength % 32 !== 0 || strength > 256) | ||
| throw new TypeError('Invalid entropy'); | ||
| throw new RangeError('Invalid entropy'); | ||
| return entropyToMnemonic(randomBytes(strength / 8), wordlist); | ||
@@ -58,7 +73,9 @@ } | ||
| if (!Array.isArray(wordlist) || wordlist.length !== 2048 || typeof wordlist[0] !== 'string') | ||
| throw new Error('Wordlist: expected array of 2048 strings'); | ||
| throw new TypeError('Wordlist: expected array of 2048 strings'); | ||
| wordlist.forEach((i) => { | ||
| if (typeof i !== 'string') | ||
| throw new Error('wordlist: non-string element: ' + i); | ||
| throw new TypeError('wordlist: non-string element: ' + i); | ||
| }); | ||
| // BIP-39 appends checksum bits to entropy. | ||
| // It then splits the bitstream into 11-bit indexes for a 2048-word list. | ||
| return baseUtils.chain(baseUtils.checksum(1, calcChecksum), baseUtils.radix2(11, true), baseUtils.alphabet(wordlist)); | ||
@@ -68,8 +85,16 @@ } | ||
| * Reversible: Converts mnemonic string to raw entropy in form of byte array. | ||
| * @param mnemonic 12-24 words | ||
| * @param wordlist imported wordlist for specific language | ||
| * @param mnemonic - 12-24 words. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns Raw entropy bytes. | ||
| * @throws If the mnemonic shape or checksum is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * Decode a mnemonic back into its original entropy bytes. | ||
| * ```ts | ||
| * import { mnemonicToEntropy } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToEntropy(mnem, wordlist) | ||
| * // Produces | ||
| * const entropy = mnemonicToEntropy(mnem, wordlist); | ||
| * // Produces the original 16-byte entropy payload. | ||
| * new Uint8Array([ | ||
@@ -79,2 +104,3 @@ * 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
| * ]) | ||
| * ``` | ||
| */ | ||
@@ -89,6 +115,12 @@ export function mnemonicToEntropy(mnemonic, wordlist) { | ||
| * Reversible: Converts raw entropy in form of byte array to mnemonic string. | ||
| * @param entropy byte array | ||
| * @param wordlist imported wordlist for specific language | ||
| * @returns 12-24 words | ||
| * @param entropy - Byte array. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns 12-24 words. | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * Convert raw entropy into an English mnemonic. | ||
| * ```ts | ||
| * import { entropyToMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const ent = new Uint8Array([ | ||
@@ -98,4 +130,5 @@ * 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
| * ]); | ||
| * entropyToMnemonic(ent, wordlist); | ||
| * const mnemonic = entropyToMnemonic(ent, wordlist); | ||
| * // 'legal winner thank year wave sausage worth useful legal winner thank yellow' | ||
| * ``` | ||
| */ | ||
@@ -109,2 +142,16 @@ export function entropyToMnemonic(entropy, wordlist) { | ||
| * Validates mnemonic for being 12-24 words contained in `wordlist`. | ||
| * @param mnemonic - 12-24 words. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns `true` when mnemonic checksum and words are valid. | ||
| * @example | ||
| * Validate one English mnemonic. | ||
| * ```ts | ||
| * import { validateMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const ok = validateMnemonic( | ||
| * 'legal winner thank year wave sausage worth useful legal winner thank yellow', | ||
| * wordlist | ||
| * ); | ||
| * // => true | ||
| * ``` | ||
| */ | ||
@@ -120,28 +167,47 @@ export function validateMnemonic(mnemonic, wordlist) { | ||
| } | ||
| // BIP-39 salts PBKDF2 with the UTF-8 NFKD string "mnemonic" + passphrase. | ||
| const psalt = (passphrase) => nfkd('mnemonic' + passphrase); | ||
| /** | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed from a mnemonic with the async PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * await mnemonicToSeed(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = await mnemonicToSeed(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| // BIP-39 seed derivation is independent from mnemonic generation. | ||
| // These helpers normalize the phrase but do not verify checksum or wordlist membership. | ||
| export function mnemonicToSeed(mnemonic, passphrase = '') { | ||
| return pbkdf2Async(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); | ||
| return pbkdf2Async(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { | ||
| c: 2048, | ||
| dkLen: 64, | ||
| }); | ||
| } | ||
| /** | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed from a mnemonic with the sync PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToSeedSync(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = mnemonicToSeedSync(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export function mnemonicToSeedSync(mnemonic, passphrase = '') { | ||
| return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); | ||
| return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { | ||
| c: 2048, | ||
| dkLen: 64, | ||
| }); | ||
| } | ||
@@ -151,12 +217,21 @@ /** | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed with the native WebCrypto PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToSeedWebcrypto(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = await mnemonicToSeedWebcrypto(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export function mnemonicToSeedWebcrypto(mnemonic, passphrase = '') { | ||
| return pbkdf2web(sha512web, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); | ||
| return pbkdf2web(sha512web, normalize(mnemonic).nfkd, psalt(passphrase), { | ||
| c: 2048, | ||
| dkLen: 64, | ||
| }); | ||
| } | ||
| //# sourceMappingURL=index.js.map |
+10
-6
| { | ||
| "name": "@scure/bip39", | ||
| "version": "2.0.1", | ||
| "version": "2.2.0", | ||
| "description": "Secure, audited & minimal implementation of BIP39 mnemonic phrases", | ||
@@ -13,9 +13,9 @@ "files": [ | ||
| "dependencies": { | ||
| "@noble/hashes": "2.0.1", | ||
| "@scure/base": "2.0.0" | ||
| "@noble/hashes": "2.2.0", | ||
| "@scure/base": "2.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@paulmillr/jsbt": "0.4.4", | ||
| "@paulmillr/jsbt": "0.5.0", | ||
| "prettier": "3.6.2", | ||
| "typescript": "5.9.2" | ||
| "typescript": "6.0.2" | ||
| }, | ||
@@ -25,2 +25,6 @@ "scripts": { | ||
| "build:release": "npx --no @paulmillr/jsbt esbuild test/build", | ||
| "check": "npx --no @paulmillr/jsbt check package.json", | ||
| "check:readme": "npx --no @paulmillr/jsbt readme package.json", | ||
| "check:treeshake": "npx --no @paulmillr/jsbt treeshake package.json test/build/out-treeshake", | ||
| "check:jsdoc": "npx --no @paulmillr/jsbt tsdoc package.json", | ||
| "format": "prettier --write 'src/**/*.ts' 'test/*.test.ts' 'test/scripts/*.js'", | ||
@@ -45,3 +49,3 @@ "bench": "node test/benchmark.js", | ||
| "./wordlists/spanish.js": "./wordlists/spanish.js", | ||
| "./wordlists/traditional-chinese.js": "./wordlists/traditional-chinesejss" | ||
| "./wordlists/traditional-chinese.js": "./wordlists/traditional-chinese.js" | ||
| }, | ||
@@ -48,0 +52,0 @@ "keywords": [ |
+21
-14
@@ -47,2 +47,7 @@ # scure-bip39 | ||
| // You can customize the strength of the generated mnemonic by passing a value between 128 and 256 as the second argument to the generateMnemonic function. | ||
| // This value must be a multiple of 32. Default is 128. | ||
| const mn256 = bip39.generateMnemonic(wordlist, 256); | ||
| console.log(mn256); | ||
| // Reversible: Converts mnemonic string to raw entropy in form of byte array. | ||
@@ -56,2 +61,3 @@ const ent = bip39.mnemonicToEntropy(mn, wordlist); | ||
| bip39.validateMnemonic(mn, wordlist); | ||
| bip39.validateMnemonic(mn256, wordlist); | ||
@@ -93,6 +99,7 @@ // Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| To audit wordlist content, run `node scripts/fetch-wordlist.js`. | ||
| The library has been audited: | ||
| The library has been independently audited: | ||
| - at version 2.2.0, in Apr 2026, by ourselves (self-audited) | ||
| - Scope: everything | ||
| - [Changes since audit](https://github.com/paulmillr/scure-bip39/compare/2.2.0..main) | ||
| - at version 1.0.0, in Jan 2022, by [cure53](https://cure53.de) | ||
@@ -108,13 +115,13 @@ - PDFs: [online](https://cure53.de/pentest-report_hashing-libs.pdf), [offline](./audit/2022-01-05-cure53-audit-nbl2.pdf) | ||
| To audit wordlist content, run `node scripts/fetch-wordlist.js`. | ||
| ### Supply chain security | ||
| - **Commits** are signed with PGP keys, to prevent forgery. Make sure to verify commit signatures | ||
| - **Releases** are transparent and built on GitHub CI. Make sure to verify [provenance](https://docs.npmjs.com/generating-provenance-statements) logs | ||
| - Use GitHub CLI to verify single-file builds: | ||
| `gh attestation verify --owner paulmillr scure-bip39.js` | ||
| - **Rare releasing** is followed to ensure less re-audit need for end-users | ||
| - **Dependencies** are minimized and locked-down: any dependency could get hacked and users will be downloading malware with every install. | ||
| - We make sure to use as few dependencies as possible | ||
| - Automatic dep updates are prevented by locking-down version ranges; diffs are checked with `npm-diff` | ||
| - **Dev Dependencies** are disabled for end-users; they are only used to develop / build the source code | ||
| - **Commits** are signed with PGP keys to prevent forgery. Be sure to verify the commit signatures | ||
| - **Releases** are made transparently through token-less GitHub CI and Trusted Publishing. Be sure to verify the [provenance logs](https://docs.npmjs.com/generating-provenance-statements) for authenticity. | ||
| - **Rare releasing** is practiced to minimize the need for re-audits by end-users. | ||
| - **Dependencies** are minimized and strictly pinned to reduce supply-chain risk. | ||
| - We use as few dependencies as possible. | ||
| - Version ranges are locked, and changes are checked with npm-diff. | ||
| - **Dev dependencies** are excluded from end-user installs; they’re only used for development and build steps. | ||
@@ -125,4 +132,4 @@ For this package, there are 2 dependencies; and a few dev dependencies: | ||
| - [scure-base](https://github.com/paulmillr/scure-base) provides low-level wordlist utilities | ||
| - micro-bmark, micro-should and jsbt are used for benchmarking / testing / build tooling and developed by the same author | ||
| - prettier, fast-check and typescript are used for code quality / test generation / ts compilation. It's hard to audit their source code thoroughly and fully because of their size | ||
| - jsbt is used for benchmarking / testing / build tooling and developed by the same author | ||
| - prettier, fast-check and typescript are used for code quality / test generation / ts compilation | ||
@@ -129,0 +136,0 @@ ## Contributing & testing |
+119
-42
| /*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */ | ||
| import { pbkdf2, pbkdf2Async } from '@noble/hashes/pbkdf2.js'; | ||
| import { sha256, sha512 } from '@noble/hashes/sha2.js'; | ||
| import { abytes, anumber, randomBytes } from '@noble/hashes/utils.js'; | ||
| import { abytes, anumber, randomBytes, type TArg, type TRet } from '@noble/hashes/utils.js'; | ||
| import { pbkdf2 as pbkdf2web, sha512 as sha512web } from '@noble/hashes/webcrypto.js'; | ||
@@ -9,2 +9,4 @@ import { utils as baseUtils } from '@scure/base'; | ||
| // Japanese wordlist | ||
| // The canonical BIP-39 Japanese wordlist starts with あいこくしん. | ||
| // Use that sentinel so generated phrases use U+3000 ideographic spaces. | ||
| const isJapanese = (wordlist: string[]) => wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093'; | ||
@@ -16,2 +18,4 @@ | ||
| // https://tonsky.me/blog/unicode/#why-is-a---- | ||
| // BIP-39 requires UTF-8 NFKD for localized wordlists and mnemonic sentences. | ||
| // It also applies NFKD to the "mnemonic" + passphrase salt. | ||
| function nfkd(str: string) { | ||
@@ -22,2 +26,4 @@ if (typeof str !== 'string') throw new TypeError('invalid mnemonic type: ' + typeof str); | ||
| // BIP-39 mnemonics are consumed in NFKD form. | ||
| // They must contain 12, 15, 18, 21, or 24 words before checksum validation. | ||
| function normalize(str: string) { | ||
@@ -30,5 +36,6 @@ const norm = nfkd(str); | ||
| function aentropy(ent: Uint8Array) { | ||
| // BIP-39 entropy payloads are 128-256 bits in 32-bit increments, i.e. 16/20/24/28/32 bytes. | ||
| function aentropy(ent: TArg<Uint8Array>) { | ||
| abytes(ent); | ||
| if (![16, 20, 24, 28, 32].includes(ent.length)) throw new Error('invalid entropy length'); | ||
| if (![16, 20, 24, 28, 32].includes(ent.length)) throw new RangeError('invalid entropy length'); | ||
| } | ||
@@ -38,15 +45,23 @@ | ||
| * Generate x random words. Uses Cryptographically-Secure Random Number Generator. | ||
| * @param wordlist imported wordlist for specific language | ||
| * @param strength mnemonic strength 128-256 bits | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @param strength - Mnemonic strength, from 128 to 256 bits. | ||
| * @returns 12-24 word mnemonic phrase. | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * generateMnemonic(wordlist, 128) | ||
| * Generate a new English mnemonic. | ||
| * ```ts | ||
| * import { generateMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const mnemonic = generateMnemonic(wordlist, 128); | ||
| * // 'legal winner thank year wave sausage worth useful legal winner thank yellow' | ||
| * ``` | ||
| */ | ||
| export function generateMnemonic(wordlist: string[], strength: number = 128): string { | ||
| anumber(strength); | ||
| if (strength % 32 !== 0 || strength > 256) throw new TypeError('Invalid entropy'); | ||
| if (strength % 32 !== 0 || strength > 256) throw new RangeError('Invalid entropy'); | ||
| return entropyToMnemonic(randomBytes(strength / 8), wordlist); | ||
| } | ||
| const calcChecksum = (entropy: Uint8Array) => { | ||
| const calcChecksum = (entropy: TArg<Uint8Array>) => { | ||
| // Checksum is ent.length/4 bits long | ||
@@ -61,6 +76,8 @@ const bitsLeft = 8 - entropy.length / 4; | ||
| if (!Array.isArray(wordlist) || wordlist.length !== 2048 || typeof wordlist[0] !== 'string') | ||
| throw new Error('Wordlist: expected array of 2048 strings'); | ||
| throw new TypeError('Wordlist: expected array of 2048 strings'); | ||
| wordlist.forEach((i) => { | ||
| if (typeof i !== 'string') throw new Error('wordlist: non-string element: ' + i); | ||
| if (typeof i !== 'string') throw new TypeError('wordlist: non-string element: ' + i); | ||
| }); | ||
| // BIP-39 appends checksum bits to entropy. | ||
| // It then splits the bitstream into 11-bit indexes for a 2048-word list. | ||
| return baseUtils.chain( | ||
@@ -75,8 +92,16 @@ baseUtils.checksum(1, calcChecksum), | ||
| * Reversible: Converts mnemonic string to raw entropy in form of byte array. | ||
| * @param mnemonic 12-24 words | ||
| * @param wordlist imported wordlist for specific language | ||
| * @param mnemonic - 12-24 words. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns Raw entropy bytes. | ||
| * @throws If the mnemonic shape or checksum is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * Decode a mnemonic back into its original entropy bytes. | ||
| * ```ts | ||
| * import { mnemonicToEntropy } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToEntropy(mnem, wordlist) | ||
| * // Produces | ||
| * const entropy = mnemonicToEntropy(mnem, wordlist); | ||
| * // Produces the original 16-byte entropy payload. | ||
| * new Uint8Array([ | ||
@@ -86,8 +111,9 @@ * 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
| * ]) | ||
| * ``` | ||
| */ | ||
| export function mnemonicToEntropy(mnemonic: string, wordlist: string[]): Uint8Array { | ||
| export function mnemonicToEntropy(mnemonic: string, wordlist: string[]): TRet<Uint8Array> { | ||
| const { words } = normalize(mnemonic); | ||
| const entropy = getCoder(wordlist).decode(words); | ||
| aentropy(entropy); | ||
| return entropy; | ||
| return entropy as TRet<Uint8Array>; | ||
| } | ||
@@ -97,6 +123,12 @@ | ||
| * Reversible: Converts raw entropy in form of byte array to mnemonic string. | ||
| * @param entropy byte array | ||
| * @param wordlist imported wordlist for specific language | ||
| * @returns 12-24 words | ||
| * @param entropy - Byte array. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns 12-24 words. | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @throws On wrong argument ranges or values. {@link RangeError} | ||
| * @example | ||
| * Convert raw entropy into an English mnemonic. | ||
| * ```ts | ||
| * import { entropyToMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const ent = new Uint8Array([ | ||
@@ -106,6 +138,7 @@ * 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, | ||
| * ]); | ||
| * entropyToMnemonic(ent, wordlist); | ||
| * const mnemonic = entropyToMnemonic(ent, wordlist); | ||
| * // 'legal winner thank year wave sausage worth useful legal winner thank yellow' | ||
| * ``` | ||
| */ | ||
| export function entropyToMnemonic(entropy: Uint8Array, wordlist: string[]): string { | ||
| export function entropyToMnemonic(entropy: TArg<Uint8Array>, wordlist: string[]): string { | ||
| aentropy(entropy); | ||
@@ -118,2 +151,16 @@ const words = getCoder(wordlist).encode(entropy); | ||
| * Validates mnemonic for being 12-24 words contained in `wordlist`. | ||
| * @param mnemonic - 12-24 words. | ||
| * @param wordlist - Imported wordlist for a specific language. | ||
| * @returns `true` when mnemonic checksum and words are valid. | ||
| * @example | ||
| * Validate one English mnemonic. | ||
| * ```ts | ||
| * import { validateMnemonic } from '@scure/bip39'; | ||
| * import { wordlist } from '@scure/bip39/wordlists/english.js'; | ||
| * const ok = validateMnemonic( | ||
| * 'legal winner thank year wave sausage worth useful legal winner thank yellow', | ||
| * wordlist | ||
| * ); | ||
| * // => true | ||
| * ``` | ||
| */ | ||
@@ -129,2 +176,3 @@ export function validateMnemonic(mnemonic: string, wordlist: string[]): boolean { | ||
| // BIP-39 salts PBKDF2 with the UTF-8 NFKD string "mnemonic" + passphrase. | ||
| const psalt = (passphrase: string) => nfkd('mnemonic' + passphrase); | ||
@@ -134,12 +182,22 @@ | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed from a mnemonic with the async PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * await mnemonicToSeed(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = await mnemonicToSeed(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export function mnemonicToSeed(mnemonic: string, passphrase = ''): Promise<Uint8Array> { | ||
| return pbkdf2Async(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); | ||
| // BIP-39 seed derivation is independent from mnemonic generation. | ||
| // These helpers normalize the phrase but do not verify checksum or wordlist membership. | ||
| export function mnemonicToSeed(mnemonic: string, passphrase = ''): Promise<TRet<Uint8Array>> { | ||
| return pbkdf2Async(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { | ||
| c: 2048, | ||
| dkLen: 64, | ||
| }) as Promise<TRet<Uint8Array>>; | ||
| } | ||
@@ -149,12 +207,20 @@ | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed from a mnemonic with the sync PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToSeedSync(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = mnemonicToSeedSync(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export function mnemonicToSeedSync(mnemonic: string, passphrase = ''): Uint8Array { | ||
| return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); | ||
| export function mnemonicToSeedSync(mnemonic: string, passphrase = ''): TRet<Uint8Array> { | ||
| return pbkdf2(sha512, normalize(mnemonic).nfkd, psalt(passphrase), { | ||
| c: 2048, | ||
| dkLen: 64, | ||
| }) as TRet<Uint8Array>; | ||
| } | ||
@@ -165,12 +231,23 @@ | ||
| * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. | ||
| * @param mnemonic 12-24 words | ||
| * @param passphrase string that will additionally protect the key | ||
| * @returns 64 bytes of key data | ||
| * @param mnemonic - 12-24 words. | ||
| * @param passphrase - String that will additionally protect the key. | ||
| * @returns 64 bytes of key data. | ||
| * @throws If the mnemonic shape is invalid. {@link Error} | ||
| * @throws On wrong argument types. {@link TypeError} | ||
| * @example | ||
| * Derive a seed with the native WebCrypto PBKDF2 helper. | ||
| * ```ts | ||
| * const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; | ||
| * mnemonicToSeedWebcrypto(mnem, 'password'); | ||
| * // new Uint8Array([...64 bytes]) | ||
| * const seed = await mnemonicToSeedWebcrypto(mnem, 'password'); | ||
| * // => new Uint8Array([...64 bytes]) | ||
| * ``` | ||
| */ | ||
| export function mnemonicToSeedWebcrypto(mnemonic: string, passphrase = ''): Promise<Uint8Array> { | ||
| return pbkdf2web(sha512web, normalize(mnemonic).nfkd, psalt(passphrase), { c: 2048, dkLen: 64 }); | ||
| export function mnemonicToSeedWebcrypto( | ||
| mnemonic: string, | ||
| passphrase = '' | ||
| ): Promise<TRet<Uint8Array>> { | ||
| return pbkdf2web(sha512web, normalize(mnemonic).nfkd, psalt(passphrase), { | ||
| c: 2048, | ||
| dkLen: 64, | ||
| }) as Promise<TRet<Uint8Array>>; | ||
| } |
@@ -0,1 +1,3 @@ | ||
| /** Czech BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=czech.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `abdikace | ||
| /** Czech BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`abdikace | ||
| abeceda | ||
@@ -2048,2 +2049,3 @@ adresa | ||
| zvukovod | ||
| zvyk`.split('\n'); | ||
| zvyk`.split('\n')); | ||
| //# sourceMappingURL=czech.js.map |
@@ -0,1 +1,3 @@ | ||
| /** English BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=english.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `abandon | ||
| /** English BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`abandon | ||
| ability | ||
@@ -2048,2 +2049,3 @@ able | ||
| zone | ||
| zoo`.split('\n'); | ||
| zoo`.split('\n')); | ||
| //# sourceMappingURL=english.js.map |
@@ -0,1 +1,3 @@ | ||
| /** French BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=french.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `abaisser | ||
| /** French BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`abaisser | ||
| abandon | ||
@@ -2048,2 +2049,3 @@ abdiquer | ||
| zeste | ||
| zoologie`.split('\n'); | ||
| zoologie`.split('\n')); | ||
| //# sourceMappingURL=french.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Italian BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=italian.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `abaco | ||
| /** Italian BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`abaco | ||
| abbaglio | ||
@@ -2048,2 +2049,3 @@ abbinato | ||
| zulu | ||
| zuppa`.split('\n'); | ||
| zuppa`.split('\n')); | ||
| //# sourceMappingURL=italian.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Japanese BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=japanese.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `あいこくしん | ||
| /** Japanese BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`あいこくしん | ||
| あいさつ | ||
@@ -2048,2 +2049,3 @@ あいだ | ||
| わらう | ||
| われる`.split('\n'); | ||
| われる`.split('\n')); | ||
| //# sourceMappingURL=japanese.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Korean BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=korean.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `가격 | ||
| /** Korean BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`가격 | ||
| 가끔 | ||
@@ -2048,2 +2049,3 @@ 가난 | ||
| 흰색 | ||
| 힘껏`.split('\n'); | ||
| 힘껏`.split('\n')); | ||
| //# sourceMappingURL=korean.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Portuguese BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=portuguese.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `abacate | ||
| /** Portuguese BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`abacate | ||
| abaixo | ||
@@ -2048,2 +2049,3 @@ abalar | ||
| zoologia | ||
| zumbido`.split('\n'); | ||
| zumbido`.split('\n')); | ||
| //# sourceMappingURL=portuguese.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Simplified Chinese BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=simplified-chinese.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `的 | ||
| /** Simplified Chinese BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`的 | ||
| 一 | ||
@@ -2048,2 +2049,3 @@ 是 | ||
| 矮 | ||
| 歇`.split('\n'); | ||
| 歇`.split('\n')); | ||
| //# sourceMappingURL=simplified-chinese.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Spanish BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=spanish.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `ábaco | ||
| /** Spanish BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`ábaco | ||
| abdomen | ||
@@ -2048,2 +2049,3 @@ abeja | ||
| zumo | ||
| zurdo`.split('\n'); | ||
| zurdo`.split('\n')); | ||
| //# sourceMappingURL=spanish.js.map |
@@ -0,1 +1,3 @@ | ||
| /** Traditional Chinese BIP39 wordlist. */ | ||
| export declare const wordlist: string[]; | ||
| //# sourceMappingURL=traditional-chinese.d.ts.map |
@@ -1,2 +0,3 @@ | ||
| export const wordlist = `的 | ||
| /** Traditional Chinese BIP39 wordlist. */ | ||
| export const wordlist = /* @__PURE__ */ Object.freeze(`的 | ||
| 一 | ||
@@ -2048,2 +2049,3 @@ 是 | ||
| 矮 | ||
| 歇`.split('\n'); | ||
| 歇`.split('\n')); | ||
| //# sourceMappingURL=traditional-chinese.js.map |
209288
5.73%21086
1.08%142
5.19%+ Added
+ Added
- Removed
- Removed
Updated
Updated