diff --git a/index.js b/index.js index ad76478..f30ccbc 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +var assert = require('assert') var CryptoJS = require('crypto-js') var crypto = require('crypto') var secureRandom = require('secure-random') @@ -9,6 +10,41 @@ function mnemonicToSeedHex(mnemonic, password) { return CryptoJS.PBKDF2(mnemonic, salt(password), options).toString(CryptoJS.enc.Hex) } +function mnemonicToEntropy(mnemonic, wordlist) { + wordlist = wordlist || DEFAULT_WORDLIST + + var words = mnemonic.split(' ') + assert(words.length % 3 === 0, 'Invalid mnemonic') + + var belongToList = words.every(function(word) { + return wordlist.indexOf(word) > -1 + }) + + assert(belongToList, 'Invalid mnemonic') + + // convert word indices to 11 bit binary strings + var bits = words.map(function(word) { + var index = wordlist.indexOf(word) + return lpad(index.toString(2), '0', 11) + }).join('') + + // split the binary string into ENT/CS + var dividerIndex = Math.floor(bits.length / 33) * 32 + var entropy = bits.slice(0, dividerIndex) + var checksum = bits.slice(dividerIndex) + + // calculate the checksum and compare + var entropyBytes = entropy.match(/(.{1,8})/g).map(function(bin) { + return parseInt(bin, 2) + }) + var entropyBuffer = new Buffer(entropyBytes) + var newChecksum = checksumBits(entropyBuffer) + + assert(newChecksum === checksum, 'Invalid mnemonic checksum') + + return entropyBuffer.toString('hex') +} + function entropyToMnemonic(entropy, wordlist) { wordlist = wordlist || DEFAULT_WORDLIST @@ -37,37 +73,13 @@ function generateMnemonic(strength, rng, wordlist) { } function validateMnemonic(mnemonic, wordlist) { - wordlist = wordlist || DEFAULT_WORDLIST + try { + mnemonicToEntropy(mnemonic, wordlist) + } catch (e) { + return false + } - var words = mnemonic.split(' ') - - if (words.length % 3 !== 0) return false - - var belongToList = words.every(function(word) { - return wordlist.indexOf(word) > -1 - }) - - if (!belongToList) return false - - // convert word indices to 11 bit binary strings - var bits = words.map(function(word) { - var index = wordlist.indexOf(word) - return lpad(index.toString(2), '0', 11) - }).join('') - - // split the binary string into ENT/CS - var dividerIndex = Math.floor(bits.length / 33) * 32 - var entropy = bits.slice(0, dividerIndex) - var checksum = bits.slice(dividerIndex) - - // calculate the checksum and compare - var entropyBytes = entropy.match(/(.{1,8})/g).map(function(bin) { - return parseInt(bin, 2) - }) - var entropyBuffer = new Buffer(entropyBytes) - var newChecksum = checksumBits(entropyBuffer) - - return newChecksum === checksum + return true } function checksumBits(entropyBuffer) { @@ -103,6 +115,7 @@ function lpad(str, padString, length) { module.exports = { mnemonicToSeedHex: mnemonicToSeedHex, + mnemonicToEntropy: mnemonicToEntropy, entropyToMnemonic: entropyToMnemonic, generateMnemonic: generateMnemonic, validateMnemonic: validateMnemonic diff --git a/test/index.js b/test/index.js index 19f4563..62e8105 100644 --- a/test/index.js +++ b/test/index.js @@ -19,6 +19,20 @@ describe('BIP39', function() { }) }) + describe('mnemonicToEntropy', function() { + vectors.english.forEach(function(v, i) { + it('works for tests vector ' + i, function() { + assert.equal(BIP39.mnemonicToEntropy(v[1]), v[0]) + }) + }) + + vectors.custom.forEach(function(v, i) { + it('works for custom test vector ' + i, function() { + assert.equal(BIP39.mnemonicToEntropy(v[1], wordlists.custom), v[0]) + }) + }) + }) + describe('entropyToMnemonic', function() { vectors.english.forEach(function(v, i) { it('works for tests vector ' + i, function() {