diff --git a/index.js b/index.js index 72be9aa..8581c32 100644 --- a/index.js +++ b/index.js @@ -9,31 +9,31 @@ var unorm = require('unorm') var DEFAULT_WORDLIST = require('./wordlists/en.json') var JAPANESE_WORDLIST = require('./wordlists/ja.json') -function mnemonicToSeed(mnemonic, password) { +function mnemonicToSeed (mnemonic, password) { var mnemonicBuffer = new Buffer(unorm.nfkd(mnemonic), 'utf8') var saltBuffer = new Buffer(salt(password), 'utf8') return pbkdf2(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512') } -function mnemonicToSeedHex(mnemonic, password) { +function mnemonicToSeedHex (mnemonic, password) { return mnemonicToSeed(mnemonic, password).toString('hex') } -function mnemonicToEntropy(mnemonic, wordlist) { +function mnemonicToEntropy (mnemonic, wordlist) { wordlist = wordlist || DEFAULT_WORDLIST var words = unorm.nfkd(mnemonic).split(' ') assert(words.length % 3 === 0, 'Invalid mnemonic') - var belongToList = words.every(function(word) { + 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 bits = words.map(function (word) { var index = wordlist.indexOf(word) return lpad(index.toString(2), '0', 11) }).join('') @@ -44,7 +44,7 @@ function mnemonicToEntropy(mnemonic, wordlist) { var checksum = bits.slice(dividerIndex) // calculate the checksum and compare - var entropyBytes = entropy.match(/(.{1,8})/g).map(function(bin) { + var entropyBytes = entropy.match(/(.{1,8})/g).map(function (bin) { return parseInt(bin, 2) }) var entropyBuffer = new Buffer(entropyBytes) @@ -55,7 +55,7 @@ function mnemonicToEntropy(mnemonic, wordlist) { return entropyBuffer.toString('hex') } -function entropyToMnemonic(entropy, wordlist) { +function entropyToMnemonic (entropy, wordlist) { wordlist = wordlist || DEFAULT_WORDLIST var entropyBuffer = new Buffer(entropy, 'hex') @@ -65,7 +65,7 @@ function entropyToMnemonic(entropy, wordlist) { var bits = entropyBits + checksum var chunks = bits.match(/(.{1,11})/g) - var words = chunks.map(function(binary) { + var words = chunks.map(function (binary) { var index = parseInt(binary, 2) return wordlist[index] @@ -74,7 +74,7 @@ function entropyToMnemonic(entropy, wordlist) { return wordlist == JAPANESE_WORDLIST ? words.join('\u3000') : words.join(' ') } -function generateMnemonic(strength, rng, wordlist) { +function generateMnemonic (strength, rng, wordlist) { strength = strength || 128 rng = rng || randomBytes @@ -82,7 +82,7 @@ function generateMnemonic(strength, rng, wordlist) { return entropyToMnemonic(hex, wordlist) } -function validateMnemonic(mnemonic, wordlist) { +function validateMnemonic (mnemonic, wordlist) { try { mnemonicToEntropy(mnemonic, wordlist) } catch (e) { @@ -92,7 +92,7 @@ function validateMnemonic(mnemonic, wordlist) { return true } -function checksumBits(entropyBuffer) { +function checksumBits (entropyBuffer) { var hash = createHash('sha256').update(entropyBuffer).digest() // Calculated constants from BIP39 @@ -102,21 +102,21 @@ function checksumBits(entropyBuffer) { return bytesToBinary([].slice.call(hash)).slice(0, CS) } -function salt(password) { +function salt (password) { return 'mnemonic' + (unorm.nfkd(password) || '') } -//=========== helper methods from bitcoinjs-lib ======== +// =========== helper methods from bitcoinjs-lib ======== -function bytesToBinary(bytes) { - return bytes.map(function(x) { +function bytesToBinary (bytes) { + return bytes.map(function (x) { return lpad(x.toString(2), '0', 8) - }).join(''); + }).join('') } -function lpad(str, padString, length) { - while (str.length < length) str = padString + str; - return str; +function lpad (str, padString, length) { + while (str.length < length) str = padString + str + return str } module.exports = { diff --git a/test/index.js b/test/index.js index ec4cfce..58fadb2 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,9 @@ +/* global describe it */ + var assert = require('assert') var mock = require('mock-require') -mock('randombytes', function(size) { +mock('randombytes', function (size) { return new Buffer('qwertyuiopasdfghjklzxcvbnm[];,./'.slice(0, size)) }) @@ -15,79 +17,79 @@ var wordlists = { var vectors = require('./vectors.json') -describe('BIP39', function() { - describe('mnemonicToSeedHex', function() { +describe('BIP39', function () { + describe('mnemonicToSeedHex', function () { this.timeout(20000) - vectors.english.forEach(function(v, i) { - it('works for tests vector ' + i, function() { + vectors.english.forEach(function (v, i) { + it('works for tests vector ' + i, function () { assert.equal(BIP39.mnemonicToSeedHex(v[1], 'TREZOR'), v[2]) }) }) }) - describe('mnemonicToEntropy', function() { - vectors.english.forEach(function(v, i) { - it('works for tests vector ' + i, 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.japanese.forEach(function(v, i) { - it('works for japanese tests vector ' + i, function() { + vectors.japanese.forEach(function (v, i) { + it('works for japanese tests vector ' + i, function () { assert.equal(BIP39.mnemonicToEntropy(v[1], wordlists.japanese), v[0]) }) }) - vectors.custom.forEach(function(v, i) { - it('works for custom test vector ' + i, function() { + 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() { + describe('entropyToMnemonic', function () { + vectors.english.forEach(function (v, i) { + it('works for tests vector ' + i, function () { assert.equal(BIP39.entropyToMnemonic(v[0]), v[1]) }) }) - vectors.japanese.forEach(function(v, i) { - it('works for japanese test vector ' + i, function() { + vectors.japanese.forEach(function (v, i) { + it('works for japanese test vector ' + i, function () { assert.equal(BIP39.entropyToMnemonic(v[0], wordlists.japanese), v[1]) }) }) - vectors.custom.forEach(function(v, i) { - it('works for custom test vector ' + i, function() { + vectors.custom.forEach(function (v, i) { + it('works for custom test vector ' + i, function () { assert.equal(BIP39.entropyToMnemonic(v[0], wordlists.custom), v[1]) }) }) }) - describe('generateMnemonic', function() { - vectors.english.forEach(function(v, i) { - it('works for tests vector ' + i, function() { - function rng() { return new Buffer(v[0], 'hex') } + describe('generateMnemonic', function () { + vectors.english.forEach(function (v, i) { + it('works for tests vector ' + i, function () { + function rng () { return new Buffer(v[0], 'hex') } assert.equal(BIP39.generateMnemonic(undefined, rng), v[1]) }) }) - it('can vary generated entropy bit length', function() { + it('can vary generated entropy bit length', function () { var mnemonic = BIP39.generateMnemonic(96) var words = mnemonic.split(' ') assert.equal(words.length, 9) }) - it('defaults to randombytes for the RNG', function() { + it('defaults to randombytes for the RNG', function () { assert.equal(BIP39.generateMnemonic(32), 'imitate robot frequent') }) - it('allows a custom RNG to be used', function() { - var rng = function(size) { + it('allows a custom RNG to be used', function () { + var rng = function (size) { var buffer = new Buffer(size) buffer.fill(4) // guaranteed random return buffer @@ -97,8 +99,8 @@ describe('BIP39', function() { assert.equal(mnemonic, 'advice cage absurd amount doctor act') }) - it('adheres to a custom wordlist', function() { - var rng = function(size) { + it('adheres to a custom wordlist', function () { + var rng = function (size) { var buffer = new Buffer(size) buffer.fill(4) // guaranteed random return buffer @@ -109,52 +111,50 @@ describe('BIP39', function() { }) }) - describe('validateMnemonic', function() { - vectors.english.forEach(function(v, i) { - - it('passes check ' + i, function() { + describe('validateMnemonic', function () { + vectors.english.forEach(function (v, i) { + it('passes check ' + i, function () { assert(BIP39.validateMnemonic(v[1])) }) }) - describe('with a custom wordlist', function() { - vectors.custom.forEach(function(v, i) { - - it('passes custom check ' + i, function() { + describe('with a custom wordlist', function () { + vectors.custom.forEach(function (v, i) { + it('passes custom check ' + i, function () { assert(BIP39.validateMnemonic(v[1], wordlists.custom)) }) }) }) - it('fails for mnemonics of wrong length', function() { + it('fails for mnemonics of wrong length', function () { assert(!BIP39.validateMnemonic('sleep kitten')) assert(!BIP39.validateMnemonic('sleep kitten sleep kitten sleep kitten')) }) - it('fails for mnemonics that contains words not from the word list', function() { - assert(!BIP39.validateMnemonic("turtle front uncle idea crush write shrug there lottery flower risky shell")) + it('fails for mnemonics that contains words not from the word list', function () { + assert(!BIP39.validateMnemonic('turtle front uncle idea crush write shrug there lottery flower risky shell')) }) - it('fails for mnemonics of invalid checksum', function() { + it('fails for mnemonics of invalid checksum', function () { assert(!BIP39.validateMnemonic('sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten')) }) }) - describe('utf8 passwords', function() { - vectors.japanese.forEach(function(v) { - it ('creates the correct seed', function() { - var utf8Password = "㍍ガバヴァぱばぐゞちぢ十人十色" + describe('utf8 passwords', function () { + vectors.japanese.forEach(function (v) { + it('creates the correct seed', function () { + var utf8Password = '㍍ガバヴァぱばぐゞちぢ十人十色' assert.equal(BIP39.mnemonicToSeedHex(v[1], utf8Password), v[2]) }) - it ('works with already normalized password', function() { - var normalizedPassword = "メートルガバヴァぱばぐゞちぢ十人十色" + it('works with already normalized password', function () { + var normalizedPassword = 'メートルガバヴァぱばぐゞちぢ十人十色' assert.equal(BIP39.mnemonicToSeedHex(v[1], normalizedPassword), v[2]) }) }) }) - describe('Examples in readme', function() { + describe('Examples in readme', function () { var bip39 = BIP39 var mnemonic = bip39.entropyToMnemonic('133755ff') // hex input, defaults to BIP39 English word list @@ -167,15 +167,15 @@ describe('BIP39', function() { // Generate a random mnemonic using crypto.randomBytes mnemonic = bip39.generateMnemonic() // strength defaults to 128 bits - //'bench maximum balance appear cousin negative muscle inform enjoy chief vocal hello' + // 'bench maximum balance appear cousin negative muscle inform enjoy chief vocal hello' assert.ok(/^(\w+ ){11}\w+$/.test(mnemonic)) var str = bip39.mnemonicToSeedHex('basket actual') - //'5cf2d4a8b0355e90295bdfc565a022a409af063d5365bb57bf74d9528f494bfa4400f53d8349b80fdae44082d7f9541e1dba2b003bcfec9d0d53781ca676651f' + // '5cf2d4a8b0355e90295bdfc565a022a409af063d5365bb57bf74d9528f494bfa4400f53d8349b80fdae44082d7f9541e1dba2b003bcfec9d0d53781ca676651f' assert.equal(str, '5cf2d4a8b0355e90295bdfc565a022a409af063d5365bb57bf74d9528f494bfa4400f53d8349b80fdae44082d7f9541e1dba2b003bcfec9d0d53781ca676651f') var buff = bip39.mnemonicToSeed('basket actual') - var fiveC = 5*16+12 + var fiveC = 5 * 16 + 12 assert.equal(buff[0], fiveC) // @@ -184,8 +184,9 @@ describe('BIP39', function() { assert.ok(bool) bool = bip39.validateMnemonic('basket actual') + // false - assert.ok(! bool) + assert.ok(!bool) }) it('exposes standard wordlists', function () {