UNPKG

j-bitcoin

Version:

Comprehensive JavaScript cryptocurrency wallet library for Bitcoin (BTC), Bitcoin Cash (BCH), and Bitcoin SV (BSV) with custodial and non-custodial wallet support, threshold signatures, and multiple address formats

254 lines (216 loc) 10.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: src/BIP39/bip39.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: src/BIP39/bip39.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/** * @fileoverview BIP39 implementation for mnemonic phrase generation and seed derivation * * This module implements the BIP39 specification for generating deterministic keys * from mnemonic phrases. It supports 12-word mnemonics with checksum validation * and PBKDF2-based seed generation with optional passphrases. * * @see {@link https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki|BIP39 Specification} * @author yfbsei * @version 1.0.0 */ import { createHash, randomBytes, pbkdf2Sync } from 'node:crypto'; import wordList_en from './wordList_en.js'; /** * @typedef {Object} MnemonicResult * @property {string} mnemonic - 12-word mnemonic phrase * @property {string} seed - Hex-encoded 64-byte seed derived from mnemonic */ /** * BIP39 mnemonic and seed generation utilities * * Provides functionality for generating secure mnemonic phrases, validating checksums, * and deriving cryptographic seeds according to the BIP39 standard. * * @namespace BIP39 * @example * // Generate a random mnemonic and seed * const { mnemonic, seed } = BIP39.random('my-passphrase'); * * // Validate an existing mnemonic * const isValid = BIP39.checkSum(mnemonic); * * // Convert mnemonic to seed * const seed = BIP39.mnemonic2seed(mnemonic, 'passphrase'); */ const BIP39 = { /** * Generates a random 12-word mnemonic phrase using cryptographically secure entropy * * The function: * 1. Generates 16 bytes (128 bits) of secure random entropy * 2. Computes SHA256 hash and takes first 4 bits as checksum * 3. Concatenates entropy + checksum to create 132 bits * 4. Splits into 12 groups of 11 bits each * 5. Maps each 11-bit value to a word from the BIP39 wordlist * * @returns {string} Space-separated 12-word mnemonic phrase * @example * const mnemonic = BIP39.mnemonic(); * // Returns: "abandon ability able about above absent absorb abstract absurd abuse access accident" */ mnemonic() { // Generate 16 bytes of cryptographically secure random data const buf = randomBytes(16); // Calculate SHA256 hash for checksum const hash = createHash('sha256').update(buf).digest(); // Convert entropy to binary string (128 bits) + checksum (4 bits) = 132 bits total const bin = buf.reduce((str, byte) => str + byte.toString(2).padStart(8, '0'), '') + ('00000000' + hash[0].toString(2)).slice(-8).slice(0, (16 * 8) / 32); // Split 132 bits into 12 groups of 11 bits, convert to word indices, map to words return [...Array(12).keys()] .map(i => parseInt(bin.slice(i * 11, (i + 1) * 11), 2)) .map(w => wordList_en[w]) .toString() .replaceAll(',', ' '); }, /** * Derives a cryptographic seed from a mnemonic phrase using PBKDF2 * * Uses PBKDF2-HMAC-SHA512 with 2048 iterations as specified in BIP39. * The salt is constructed as "mnemonic" + passphrase. * * @param {string} [mnemonic=''] - Space-separated mnemonic phrase * @param {string} [passphrase=''] - Optional passphrase for additional security * @returns {string} Hex-encoded 64-byte (512-bit) seed * @example * const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; * const seed = BIP39.seed(mnemonic, "my-passphrase"); * // Returns 128-character hex string */ seed(mnemonic = '', passphrase = '') { // Prepare secret (mnemonic) and salt ("mnemonic" + passphrase) const secret = Buffer.from(mnemonic); const salt = Buffer.concat([ Buffer.from('mnemonic'), Buffer.from(passphrase) ]); // Derive 64-byte seed using PBKDF2-HMAC-SHA512 with 2048 iterations const seed = pbkdf2Sync(secret, salt, 2048, 64, 'sha512'); return seed.toString('hex'); }, /** * Validates the checksum of a BIP39 mnemonic phrase * * The validation process: * 1. Converts words back to 11-bit indices * 2. Concatenates all indices to reconstruct the binary data * 3. Splits into entropy (128 bits) and checksum (4 bits) * 4. Recalculates checksum from entropy using SHA256 * 5. Compares calculated checksum with embedded checksum * * @param {string} [mnemonic=''] - Space-separated mnemonic phrase to validate * @returns {boolean} True if checksum is valid, false otherwise * @example * const validMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; * const isValid = BIP39.checkSum(validMnemonic); // true * * const invalidMnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"; * const isInvalid = BIP39.checkSum(invalidMnemonic); // false */ checkSum(mnemonic = '') { // Convert mnemonic words back to binary representation const bin = mnemonic.split(' ') .map(x => wordList_en.indexOf(x)) // Get word index .reduce((str, byte) => str + byte.toString(2).padStart(11, '0'), ''); // Convert to 11-bit binary // Extract entropy (first 128 bits) and convert to bytes const buf = Buffer.from([...Array(16).keys()] .map(i => parseInt(bin.slice(i * 8, (i + 1) * 8), 2))); // Calculate expected checksum from entropy const hash = createHash('sha256').update(buf).digest(); const expectedChecksum = [hash[0].toString(2)] .reduce((str, byte) => str + byte.toString(2).padStart(8, '0'), '') .slice(0, 4); // Compare expected checksum with embedded checksum (last 4 bits) return expectedChecksum === bin.slice(-4); }, /** * Generates a random mnemonic with validated checksum and derives its seed * * This is a convenience method that combines mnemonic generation and seed derivation * with built-in checksum validation for additional security. * * @param {string} [passphrase=''] - Optional passphrase for seed derivation * @returns {MnemonicResult} Object containing both mnemonic and seed * @throws {string} Throws 'invalid checksum' if generated mnemonic fails validation * @example * const { mnemonic, seed } = BIP39.random('my-secure-passphrase'); * console.log(mnemonic); // "word1 word2 word3 ..." * console.log(seed); // "a1b2c3d4e5f6..." */ random(passphrase = '') { const mnemonic = this.mnemonic(); // Validate the generated mnemonic before returning if (this.checkSum(mnemonic)) { return { mnemonic, seed: this.seed(mnemonic, passphrase) } } else { throw 'invalid checksum'; } }, /** * Converts a mnemonic phrase to a seed with checksum validation * * This method validates the mnemonic's checksum before deriving the seed, * ensuring that only valid mnemonics are processed. * * @param {string} [mnemonic=''] - Space-separated mnemonic phrase * @param {string} [passphrase=''] - Optional passphrase for additional security * @returns {string} Hex-encoded 64-byte seed * @throws {string} Throws 'invalid checksum' if mnemonic validation fails * @example * const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; * const seed = BIP39.mnemonic2seed(mnemonic, "passphrase"); * * // With invalid mnemonic * try { * const seed = BIP39.mnemonic2seed("invalid mnemonic phrase"); * } catch (error) { * console.log(error); // "invalid checksum" * } */ mnemonic2seed(mnemonic = '', passphrase = '') { if (this.checkSum(mnemonic)) { return this.seed(mnemonic, passphrase); } else { throw 'invalid checksum'; } } } export default BIP39;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Namespaces</h3><ul><li><a href="AddressFormats.html">AddressFormats</a></li><li><a href="BECH32.html">BECH32</a></li><li><a href="BIP32.html">BIP32</a></li><li><a href="BIP39.html">BIP39</a></li><li><a href="CASH_ADDR.html">CASH_ADDR</a></li><li><a href="ECDSA.html">ECDSA</a></li><li><a href="KeyDecoding.html">KeyDecoding</a></li><li><a href="Signatures.html">Signatures</a></li><li><a href="ThresholdCrypto.html">ThresholdCrypto</a></li><li><a href="Utilities.html">Utilities</a></li><li><a href="Wallets.html">Wallets</a></li><li><a href="schnorr_sig.html">schnorr_sig</a></li></ul><h3>Classes</h3><ul><li><a href="Custodial_Wallet.html">Custodial_Wallet</a></li><li><a href="Non_Custodial_Wallet.html">Non_Custodial_Wallet</a></li><li><a href="Polynomial.html">Polynomial</a></li><li><a href="ThresholdSignature.html">ThresholdSignature</a></li></ul><h3>Global</h3><ul><li><a href="global.html#CHARSET">CHARSET</a></li><li><a href="global.html#FEATURES">FEATURES</a></li><li><a href="global.html#NETWORKS">NETWORKS</a></li><li><a href="global.html#address">address</a></li><li><a href="global.html#b58encode">b58encode</a></li><li><a href="global.html#base32_encode">base32_encode</a></li><li><a href="global.html#derive">derive</a></li><li><a href="global.html#fromSeed">fromSeed</a></li><li><a href="global.html#hdKey">hdKey</a></li><li><a href="global.html#legacyAddress_decode">legacyAddress_decode</a></li><li><a href="global.html#privateKey_decode">privateKey_decode</a></li><li><a href="global.html#rmd160">rmd160</a></li><li><a href="global.html#standardKey">standardKey</a></li><li><a href="global.html#table">table</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Wed Jun 04 2025 02:36:39 GMT-0400 (Eastern Daylight Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>