UNPKG

react-native-hdwallet

Version:

hdwallet for react-native

140 lines (117 loc) 4.93 kB
import { randomBytes } from 'react-native-randombytes' import { createHash, pbkdf2Sync } from 'react-native-crypto' import unorm from 'unorm' import assert from 'assert' import CHINESE_SIMPLIFIED_WORDLIST from '../wordlists/chinese_simplified.json' import CHINESE_TRADITIONAL_WORDLIST from '../wordlists/chinese_traditional.json' import ENGLISH_WORDLIST from '../wordlists/english.json' import FRENCH_WORDLIST from '../wordlists/french.json' import ITALIAN_WORDLIST from '../wordlists/italian.json' import JAPANESE_WORDLIST from '../wordlists/japanese.json' import KOREAN_WORDLIST from '../wordlists/korean.json' import SPANISH_WORDLIST from '../wordlists/spanish.json' const DEFAULT_WORDLIST = ENGLISH_WORDLIST const INVALID_MNEMONIC = 'Invalid mnemonic' const INVALID_ENTROPY = 'Invalid entropy' const INVALID_CHECKSUM = 'Invalid mnemonic checksum' export default class rnBip39 { static generateMnemonic(strength, rng, wordlist) { return new Promise((resolve, reject) => { strength = strength || 128 assert(strength % 32 === 0, INVALID_ENTROPY) rng = rng || randomBytes rng(strength / 8, (error, randomBytesBuffer) => { if (error) { reject(error) } else { resolve(this.entropyToMnemonic(randomBytesBuffer.toString('hex'), wordlist)) } }) }) } static entropyToMnemonic(entropy, wordlist) { if (!Buffer.isBuffer(entropy)) { entropy = Buffer.from(entropy, 'hex') } wordlist = wordlist || DEFAULT_WORDLIST assert(entropy.length >= 16 && entropy.length <= 32, INVALID_ENTROPY) assert(entropy.length % 4 === 0, INVALID_ENTROPY) var entropyBits = this.bytesToBinary([].slice.call(entropy)) var checksumBits = this.deriveChecksumBits(entropy) var bits = entropyBits + checksumBits var chunks = bits.match(/(.{1,11})/g) var words = chunks.map((binary) => { var index = this.binaryToByte(binary) return wordlist[index] }) return wordlist === JAPANESE_WORDLIST ? words.join('\u3000') : words.join(' ') } static mnemonicToSeedHex(mnemonic, password) { return this.mnemonicToSeed(mnemonic, password).toString('hex') } static mnemonicToSeed(mnemonic, password) { var mnemonicBuffer = Buffer.from(unorm.nfkd(mnemonic), 'utf8') var saltBuffer = Buffer.from(this.salt(unorm.nfkd(password)), 'utf8') return pbkdf2Sync(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512') } static mnemonicToEntropy(mnemonic, wordlist) { wordlist = wordlist || DEFAULT_WORDLIST var words = unorm.nfkd(mnemonic).split(' ') assert(words.length % 3 === 0, INVALID_MNEMONIC) var bits = words.map((word) => { var index = wordlist.indexOf(word) assert(index !== -1, INVALID_MNEMONIC) return this.lpad(index.toString(2), '0', 11) }).join('') var dividerIndex = Math.floor(bits.length / 33) * 32 var entropyBits = bits.slice(0, dividerIndex) var checksumBits = bits.slice(dividerIndex) var entropyBytes = entropyBits.match(/(.{1,8})/g).map(this.binaryToByte) assert(entropyBytes.length >= 16, INVALID_ENTROPY) assert(entropyBytes.length <= 32, INVALID_ENTROPY) assert(entropyBytes.length % 4 === 0, INVALID_ENTROPY) var entropy = Buffer.from(entropyBytes) var newChecksum = this.deriveChecksumBits(entropy) assert(newChecksum === checksumBits, INVALID_CHECKSUM) return entropy.toString('hex') } static validateMnemonic(mnemonic, wordlist) { try { this.mnemonicToEntropy(mnemonic, wordlist) } catch (e) { console.error(e) return false } return true } static deriveChecksumBits(entropyBuffer) { var ENT = entropyBuffer.length * 8 var CS = ENT / 32 var hash = createHash('sha256').update(entropyBuffer).digest() return this.bytesToBinary([].slice.call(hash)).slice(0, CS) } static salt(password) { return 'mnemonic' + (password || '') } static lpad(str, padString, length) { while (str.length < length) str = padString + str return str } static bytesToBinary(bytes) { return bytes.map((x) => { return this.lpad(x.toString(2), '0', 8) }).join('') } static binaryToByte(bin) { return parseInt(bin, 2) } static wordlists = { chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST, chinese_traditional: CHINESE_TRADITIONAL_WORDLIST, english: ENGLISH_WORDLIST, french: FRENCH_WORDLIST, italian: ITALIAN_WORDLIST, japanese: JAPANESE_WORDLIST, korean: KOREAN_WORDLIST, } }