Browse Source

Merge pull request #6 from dcousens/master

BIP39: allow for custom wordlists
300
Wei Lu 10 years ago
parent
commit
ee4f7ead7f
  1. 13
      README.md
  2. 11
      index.js
  3. 8
      package.json
  4. 71
      test/index.js
  5. 12
      test/vectors.json
  6. 2050
      test/wordlist.json

13
README.md

@ -10,16 +10,19 @@ JavaScript implementation of [Bitcoin BIP39](https://github.com/bitcoin/bips/blo
```javascript ```javascript
var BIP39 = require('bip39') var BIP39 = require('bip39')
bip39 = new BIP39() // 'en' is the default language // defaults to BIP39 English word list
var bip39 = new BIP39()
bip39.entropyToMnemonic('1337') // hex input var mnemonic = bip39.entropyToMnemonic('1337') // hex input, defaults to BIP39 English word list
// 'basket actual' // 'basket actual'
bip39.mnemonicToSeed('basket actual') // wait for it... // or
mnemonic = bip39.generateMnemonic() // strength defaults to 128 bits
// 'seed sock milk update focus rotate barely fade car face mechanic mercy'
bip39.mnemonicToSeedHex('basket actual') // wait for it...
// '5cf2d4a8b0355e90295bdfc565a022a409af063d5365bb57bf74d9528f494bfa4400f53d8349b80fdae44082d7f9541e1dba2b003bcfec9d0d53781ca676651f' // '5cf2d4a8b0355e90295bdfc565a022a409af063d5365bb57bf74d9528f494bfa4400f53d8349b80fdae44082d7f9541e1dba2b003bcfec9d0d53781ca676651f'
bip39.generateMnemonic() // strength defaults to 128 bits
// 'seed sock milk update focus rotate barely fade car face mechanic mercy'
``` ```
### Browser ### Browser

11
index.js

@ -2,13 +2,10 @@ var CryptoJS = require('crypto-js')
var crypto = require('crypto') var crypto = require('crypto')
var secureRandom = require('secure-random') var secureRandom = require('secure-random')
var includeFolder = require('include-folder') var DEFAULT_WORDLIST = require('./wordlists/en.json')
var path = require('path')
var wordlists = includeFolder(path.join(__dirname, 'wordlists'))
function BIP39(language) { function BIP39(wordlist) {
language = language || 'en' this.wordlist = wordlist || DEFAULT_WORDLIST
this.wordlist = JSON.parse(wordlists[language])
} }
BIP39.prototype.mnemonicToSeed = function(mnemonic, password) { BIP39.prototype.mnemonicToSeed = function(mnemonic, password) {
@ -38,7 +35,7 @@ BIP39.prototype.generateMnemonic = function(strength, rng) {
rng = rng || secureRandom.randomBuffer rng = rng || secureRandom.randomBuffer
var hex = rng(strength / 8).toString('hex') var hex = rng(strength / 8).toString('hex')
return this.entropyToMnemonic(hex) return this.entropyToMnemonic(hex, this.wordlist)
} }
BIP39.prototype.validate = function(mnemonic) { BIP39.prototype.validate = function(mnemonic) {

8
package.json

@ -15,18 +15,10 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"crypto-js": "^3.1.2-2", "crypto-js": "^3.1.2-2",
"require-json-tree": "~1.1.0",
"include-folder": "~0.7.0",
"secure-random": "1.0.0" "secure-random": "1.0.0"
}, },
"devDependencies": { "devDependencies": {
"browserify": "^5.9.1", "browserify": "^5.9.1",
"folderify": "~0.6.0",
"mocha": "^1.17.1" "mocha": "^1.17.1"
},
"browserify": {
"transform": [
"folderify"
]
} }
} }

71
test/index.js

@ -1,18 +1,35 @@
var assert = require('assert') var assert = require('assert')
var wordlist = require('../wordlists/en.json')
var vectors = require('./vectors.json').english
var BIP39 = require('../index.js') var BIP39 = require('../index.js')
var bip39 = new BIP39()
var wordlists = {
english: require('../wordlists/en.json'),
custom: require('./wordlist.json')
}
var vectors = require('./vectors.json')
describe('BIP39', function() {
describe('constructor', function() { describe('constructor', function() {
it('defaults language to english', function() { it('defaults language to english', function() {
assert.deepEqual(bip39.wordlist, wordlist) var bip39 = new BIP39()
assert.deepEqual(bip39.wordlist, wordlists.english)
})
it('accepts a custom wordlist', function() {
var bip39 = new BIP39(wordlists.custom)
assert.deepEqual(bip39.wordlist, wordlists.custom)
}) })
}) })
describe('mnemonicToSeed', function() { describe('mnemonicToSeed', function() {
vectors.forEach(function(v, i) { this.timeout(20000)
var bip39
beforeEach(function() {
bip39 = new BIP39()
})
vectors.english.forEach(function(v, i) {
it('works for tests vector ' + i, function() { it('works for tests vector ' + i, function() {
assert.equal(bip39.mnemonicToSeed(v[1], 'TREZOR'), v[2]) assert.equal(bip39.mnemonicToSeed(v[1], 'TREZOR'), v[2])
}) })
@ -20,8 +37,16 @@ describe('mnemonicToSeed', function() {
}) })
describe('entropyToMnemonic', function() { describe('entropyToMnemonic', function() {
vectors.forEach(function(v, i) { vectors.english.forEach(function(v, i) {
it('works for tests vector ' + i, function() { it('works for tests vector ' + i, function() {
var bip39 = new BIP39()
assert.equal(bip39.entropyToMnemonic(v[0]), v[1])
})
})
vectors.custom.forEach(function(v, i) {
it('works for custom test vector ' + i, function() {
var bip39 = new BIP39(wordlists.custom)
assert.equal(bip39.entropyToMnemonic(v[0]), v[1]) assert.equal(bip39.entropyToMnemonic(v[0]), v[1])
}) })
}) })
@ -29,6 +54,7 @@ describe('entropyToMnemonic', function() {
describe('generateMnemonic', function() { describe('generateMnemonic', function() {
it('generates a mnemonic', function() { it('generates a mnemonic', function() {
var bip39 = new BIP39()
var mnemonic = bip39.generateMnemonic(96) var mnemonic = bip39.generateMnemonic(96)
var words = mnemonic.split(' ') var words = mnemonic.split(' ')
@ -41,29 +67,58 @@ describe('generateMnemonic', function() {
buffer.fill(4) // guaranteed random buffer.fill(4) // guaranteed random
return buffer return buffer
} }
var bip39 = new BIP39()
var mnemonic = bip39.generateMnemonic(64, rng) var mnemonic = bip39.generateMnemonic(64, rng)
assert.equal(mnemonic, 'advice cage absurd amount doctor act') assert.equal(mnemonic, 'advice cage absurd amount doctor act')
}) })
it('adheres to a custom wordlist', function() {
var rng = function(size) {
var buffer = new Buffer(size)
buffer.fill(4) // guaranteed random
return buffer
}
var bip39 = new BIP39(wordlists.custom)
var mnemonic = bip39.generateMnemonic(64, rng)
assert.equal(mnemonic, 'adv1c3 cag3 ab5urd am0unt d0ct0r act')
})
}) })
describe('validate', function() { describe('validate', function() {
vectors.forEach(function(v, i) { vectors.english.forEach(function(v, i) {
var bip39 = new BIP39()
it('passes check ' + i, function() { it('passes check ' + i, function() {
assert(bip39.validate(v[1])) assert(bip39.validate(v[1]))
}) })
}) })
describe('with a custom wordlist', function() {
vectors.custom.forEach(function(v, i) {
var bip39 = new BIP39(wordlists.custom)
it('passes custom check ' + i, function() {
assert(bip39.validate(v[1]))
})
})
})
it('fails for mnemonics of wrong length', function() { it('fails for mnemonics of wrong length', function() {
var bip39 = new BIP39()
assert(!bip39.validate('sleep kitten')) assert(!bip39.validate('sleep kitten'))
assert(!bip39.validate('sleep kitten sleep kitten sleep kitten')) assert(!bip39.validate('sleep kitten sleep kitten sleep kitten'))
}) })
it('fails for mnemonics that contains words not from the word list', function() { it('fails for mnemonics that contains words not from the word list', function() {
var bip39 = new BIP39()
assert(!bip39.validate("turtle front uncle idea crush write shrug there lottery flower risky shell")) assert(!bip39.validate("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() {
var bip39 = new BIP39()
assert(!bip39.validate('sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten')) assert(!bip39.validate('sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten'))
}) })
}) })
})

12
test/vectors.json

@ -120,5 +120,17 @@
"beyond stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut", "beyond stage sleep clip because twist token leaf atom beauty genius food business side grid unable middle armed observe pair crouch tonight away coconut",
"b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd" "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"
] ]
],
"custom": [
[
"00000000000000000000000000000000",
"aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n aband0n ab0ut",
"c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"
],
[
"15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419",
"b3y0nd 5tag3 5l33p cl1p b3cau53 tw15t t0k3n l3af at0m b3auty g3n1u5 f00d bu51n355 51d3 gr1d unabl3 m1ddl3 arm3d 0b53rv3 pa1r cr0uch t0n1ght away c0c0nut",
"b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"
]
] ]
} }

2050
test/wordlist.json

File diff suppressed because it is too large
Loading…
Cancel
Save