New:Socket for Asana Is Now Available.Learn more
Get Started

@scure/bip39

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@scure/bip39 - npm Package Compare versions

Comparing version
2.2.0
to
2.3.0
+0
-1
index.d.ts

@@ -130,2 +130,1 @@ import { type TArg, type TRet } from '@noble/hashes/utils.js';

export declare function mnemonicToSeedWebcrypto(mnemonic: string, passphrase?: string): Promise<TRet<Uint8Array>>;
//# sourceMappingURL=index.d.ts.map
+61
-10

@@ -6,3 +6,2 @@ /*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */

import { pbkdf2 as pbkdf2web, sha512 as sha512web } from '@noble/hashes/webcrypto.js';
import { utils as baseUtils } from '@scure/base';
// Japanese wordlist

@@ -60,2 +59,4 @@ // The canonical BIP-39 Japanese wordlist starts with あいこくしん.

}
// BIP-39 checksum is the first ENT/32 bits of SHA-256(entropy).
// Returned as a byte with checksum bits on the left and zeroes on the right.
const calcChecksum = (entropy) => {

@@ -66,5 +67,5 @@ // Checksum is ent.length/4 bits long

// For example: bitsLeft=4 val=10111101 -> 10110000
return new Uint8Array([(sha256(entropy)[0] >> bitsLeft) << bitsLeft]);
return (sha256(entropy)[0] >> bitsLeft) << bitsLeft;
};
function getCoder(wordlist) {
function awordlist(wordlist) {
if (!Array.isArray(wordlist) || wordlist.length !== 2048 || typeof wordlist[0] !== 'string')

@@ -76,6 +77,53 @@ throw new TypeError('Wordlist: expected array of 2048 strings');

});
// 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));
}
// BIP-39 appends checksum bits to entropy,
// then splits the bitstream into 11-bit indexes for a 2048-word list.
function encodeWords(entropy, wordlist) {
awordlist(wordlist);
const bytes = new Uint8Array(entropy.length + 1); // entropy || checksum byte
bytes.set(entropy);
bytes[entropy.length] = calcChecksum(entropy);
const words = [];
let carry = 0; // bit accumulator, holds < 19 bits
let bits = 0;
for (const byte of bytes) {
carry = (carry << 8) | byte;
bits += 8;
if (bits >= 11) {
bits -= 11;
words.push(wordlist[(carry >>> bits) & 0x7ff]);
carry &= (1 << bits) - 1;
}
}
// Bits still in carry are the zero right bits of the checksum byte; drop them.
return words;
}
// Reverse of encodeWords: repacks 11-bit indexes into bytes and verifies checksum.
function decodeWords(words, wordlist) {
awordlist(wordlist);
const entLen = (words.length / 3) * 4; // every 3 words hold 32 entropy bits + 1 checksum bit
const bytes = new Uint8Array(entLen + 1); // entropy || checksum byte
let carry = 0; // bit accumulator, holds < 19 bits
let bits = 0;
let pos = 0;
for (const word of words) {
const index = wordlist.indexOf(word);
if (index === -1)
throw new Error('Unknown word: ' + word);
carry = (carry << 11) | index;
bits += 11;
while (bits >= 8) {
bits -= 8;
bytes[pos++] = (carry >>> bits) & 0xff;
}
carry &= (1 << bits) - 1;
}
// Left-align leftover checksum bits, matching calcChecksum output.
if (bits > 0)
bytes[pos] = carry << (8 - bits);
const entropy = bytes.subarray(0, entLen);
if (bytes[entLen] !== calcChecksum(entropy))
throw new Error('Invalid checksum');
return Uint8Array.from(entropy);
}
/**

@@ -105,3 +153,3 @@ * Reversible: Converts mnemonic string to raw entropy in form of byte array.

const { words } = normalize(mnemonic);
const entropy = getCoder(wordlist).decode(words);
const entropy = decodeWords(words, wordlist);
aentropy(entropy);

@@ -132,3 +180,3 @@ return entropy;

aentropy(entropy);
const words = getCoder(wordlist).encode(entropy);
const words = encodeWords(entropy, wordlist);
return words.join(isJapanese(wordlist) ? '\u3000' : ' ');

@@ -163,3 +211,7 @@ }

// BIP-39 salts PBKDF2 with the UTF-8 NFKD string "mnemonic" + passphrase.
const psalt = (passphrase) => nfkd('mnemonic' + passphrase);
const psalt = (passphrase) => {
if (typeof passphrase !== 'string')
throw new TypeError('invalid passphrase type: ' + typeof passphrase);
return nfkd('mnemonic' + passphrase);
};
/**

@@ -231,2 +283,1 @@ * Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password.

}
//# sourceMappingURL=index.js.map
{
"name": "@scure/bip39",
"version": "2.2.0",
"version": "2.3.0",
"description": "Secure, audited & minimal implementation of BIP39 mnemonic phrases",

@@ -13,7 +13,6 @@ "files": [

"dependencies": {
"@noble/hashes": "2.2.0",
"@scure/base": "2.2.0"
"@noble/hashes": "2.3.0"
},
"devDependencies": {
"@paulmillr/jsbt": "0.5.0",
"@paulmillr/jsbt": "0.6.5",
"prettier": "3.6.2",

@@ -23,14 +22,8 @@ "typescript": "6.0.2"

"scripts": {
"benchmark": "node test/benchmark.js",
"benchmark:size": "npx bismar@0.1.3 -s",
"build": "tsc",
"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",
"check": "jsbt-check",
"format": "prettier --write 'src/**/*.ts' 'test/*.test.ts' 'test/scripts/*.js'",
"bench": "node test/benchmark.js",
"test": "node --experimental-strip-types --no-warnings test/index.ts",
"test:bun": "bun test/index.ts",
"test:deno": "deno --allow-env --allow-read test/index.js",
"test:node20": "cd test; npx tsc; node compiled/test/index.js",
"test": "node test/index.ts",
"fetch-wordlist": "./test/scripts/fetch-wordlist.js"

@@ -37,0 +30,0 @@ },

@@ -8,4 +8,4 @@ # scure-bip39

- 🥈 Two implementations: pure JS or friendly WebCrypto wrapper
- ➰ Only 2 audited dependencies by the same author:
[noble-hashes](https://github.com/paulmillr/noble-hashes) and [scure-base](https://github.com/paulmillr/scure-base)
- ➰ Only 1 audited dependency by the same author:
[noble-hashes](https://github.com/paulmillr/noble-hashes)
- 🪶 14KB (gzipped) with one wordlist, 79KB with all of them: much smaller than similar libraries

@@ -125,15 +125,8 @@

For this package, there are 2 dependencies; and a few dev dependencies:
For this package, there is 1 dependency; and a few dev dependencies:
- [noble-hashes](https://github.com/paulmillr/noble-hashes) provides cryptographic hashing functionality
- [scure-base](https://github.com/paulmillr/scure-base) provides low-level wordlist utilities
- 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
## Contributing & testing
- `npm install && npm run build && npm test` will build the code and run tests.
- `npm run lint` / `npm run format` will run linter / fix linter issues.
- `npm run build:release` will build single file
## License

@@ -140,0 +133,0 @@

@@ -6,3 +6,2 @@ /*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */

import { pbkdf2 as pbkdf2web, sha512 as sha512web } from '@noble/hashes/webcrypto.js';
import { utils as baseUtils } from '@scure/base';

@@ -62,3 +61,5 @@ // Japanese wordlist

const calcChecksum = (entropy: TArg<Uint8Array>) => {
// BIP-39 checksum is the first ENT/32 bits of SHA-256(entropy).
// Returned as a byte with checksum bits on the left and zeroes on the right.
const calcChecksum = (entropy: Uint8Array) => {
// Checksum is ent.length/4 bits long

@@ -68,6 +69,6 @@ const bitsLeft = 8 - entropy.length / 4;

// For example: bitsLeft=4 val=10111101 -> 10110000
return new Uint8Array([(sha256(entropy)[0]! >> bitsLeft) << bitsLeft]);
return (sha256(entropy)[0]! >> bitsLeft) << bitsLeft;
};
function getCoder(wordlist: string[]) {
function awordlist(wordlist: string[]) {
if (!Array.isArray(wordlist) || wordlist.length !== 2048 || typeof wordlist[0] !== 'string')

@@ -78,11 +79,53 @@ throw new TypeError('Wordlist: expected array of 2048 strings');

});
// 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)
);
}
// BIP-39 appends checksum bits to entropy,
// then splits the bitstream into 11-bit indexes for a 2048-word list.
function encodeWords(entropy: Uint8Array, wordlist: string[]): string[] {
awordlist(wordlist);
const bytes = new Uint8Array(entropy.length + 1); // entropy || checksum byte
bytes.set(entropy);
bytes[entropy.length] = calcChecksum(entropy);
const words: string[] = [];
let carry = 0; // bit accumulator, holds < 19 bits
let bits = 0;
for (const byte of bytes) {
carry = (carry << 8) | byte;
bits += 8;
if (bits >= 11) {
bits -= 11;
words.push(wordlist[(carry >>> bits) & 0x7ff]!);
carry &= (1 << bits) - 1;
}
}
// Bits still in carry are the zero right bits of the checksum byte; drop them.
return words;
}
// Reverse of encodeWords: repacks 11-bit indexes into bytes and verifies checksum.
function decodeWords(words: string[], wordlist: string[]): Uint8Array {
awordlist(wordlist);
const entLen = (words.length / 3) * 4; // every 3 words hold 32 entropy bits + 1 checksum bit
const bytes = new Uint8Array(entLen + 1); // entropy || checksum byte
let carry = 0; // bit accumulator, holds < 19 bits
let bits = 0;
let pos = 0;
for (const word of words) {
const index = wordlist.indexOf(word);
if (index === -1) throw new Error('Unknown word: ' + word);
carry = (carry << 11) | index;
bits += 11;
while (bits >= 8) {
bits -= 8;
bytes[pos++] = (carry >>> bits) & 0xff;
}
carry &= (1 << bits) - 1;
}
// Left-align leftover checksum bits, matching calcChecksum output.
if (bits > 0) bytes[pos] = carry << (8 - bits);
const entropy = bytes.subarray(0, entLen);
if (bytes[entLen] !== calcChecksum(entropy)) throw new Error('Invalid checksum');
return Uint8Array.from(entropy);
}
/**

@@ -112,3 +155,3 @@ * Reversible: Converts mnemonic string to raw entropy in form of byte array.

const { words } = normalize(mnemonic);
const entropy = getCoder(wordlist).decode(words);
const entropy = decodeWords(words, wordlist);
aentropy(entropy);

@@ -140,3 +183,3 @@ return entropy as TRet<Uint8Array>;

aentropy(entropy);
const words = getCoder(wordlist).encode(entropy);
const words = encodeWords(entropy as Uint8Array, wordlist);
return words.join(isJapanese(wordlist) ? '\u3000' : ' ');

@@ -172,3 +215,7 @@ }

// BIP-39 salts PBKDF2 with the UTF-8 NFKD string "mnemonic" + passphrase.
const psalt = (passphrase: string) => nfkd('mnemonic' + passphrase);
const psalt = (passphrase: string) => {
if (typeof passphrase !== 'string')
throw new TypeError('invalid passphrase type: ' + typeof passphrase);
return nfkd('mnemonic' + passphrase);
};

@@ -175,0 +222,0 @@ /**

/** Czech BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=czech.d.ts.map

@@ -2050,2 +2050,1 @@ /** Czech BIP39 wordlist. */

zvyk`.split('\n'));
//# sourceMappingURL=czech.js.map
/** English BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=english.d.ts.map

@@ -2050,2 +2050,1 @@ /** English BIP39 wordlist. */

zoo`.split('\n'));
//# sourceMappingURL=english.js.map
/** French BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=french.d.ts.map

@@ -2050,2 +2050,1 @@ /** French BIP39 wordlist. */

zoologie`.split('\n'));
//# sourceMappingURL=french.js.map
/** Italian BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=italian.d.ts.map

@@ -2050,2 +2050,1 @@ /** Italian BIP39 wordlist. */

zuppa`.split('\n'));
//# sourceMappingURL=italian.js.map
/** Japanese BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=japanese.d.ts.map

@@ -2050,2 +2050,1 @@ /** Japanese BIP39 wordlist. */

われる`.split('\n'));
//# sourceMappingURL=japanese.js.map
/** Korean BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=korean.d.ts.map

@@ -2050,2 +2050,1 @@ /** Korean BIP39 wordlist. */

힘껏`.split('\n'));
//# sourceMappingURL=korean.js.map
/** Portuguese BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=portuguese.d.ts.map

@@ -2050,2 +2050,1 @@ /** Portuguese BIP39 wordlist. */

zumbido`.split('\n'));
//# sourceMappingURL=portuguese.js.map
/** Simplified Chinese BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=simplified-chinese.d.ts.map

@@ -2050,2 +2050,1 @@ /** Simplified Chinese BIP39 wordlist. */

歇`.split('\n'));
//# sourceMappingURL=simplified-chinese.js.map
/** Spanish BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=spanish.d.ts.map

@@ -2050,2 +2050,1 @@ /** Spanish BIP39 wordlist. */

zurdo`.split('\n'));
//# sourceMappingURL=spanish.js.map
/** Traditional Chinese BIP39 wordlist. */
export declare const wordlist: string[];
//# sourceMappingURL=traditional-chinese.d.ts.map

@@ -2050,2 +2050,1 @@ /** Traditional Chinese BIP39 wordlist. */

歇`.split('\n'));
//# sourceMappingURL=traditional-chinese.js.map