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. 149
      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"
]
} }
} }

149
test/index.js

@ -1,69 +1,124 @@
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()
describe('constructor', function() { var wordlists = {
it('defaults language to english', function() { english: require('../wordlists/en.json'),
assert.deepEqual(bip39.wordlist, wordlist) custom: require('./wordlist.json')
}) }
})
var vectors = require('./vectors.json')
describe('mnemonicToSeed', function() { describe('BIP39', function() {
vectors.forEach(function(v, i) { describe('constructor', function() {
it('works for tests vector ' + i, function() { it('defaults language to english', function() {
assert.equal(bip39.mnemonicToSeed(v[1], 'TREZOR'), v[2]) var bip39 = new BIP39()
assert.deepEqual(bip39.wordlist, wordlists.english)
}) })
})
})
describe('entropyToMnemonic', function() { it('accepts a custom wordlist', function() {
vectors.forEach(function(v, i) { var bip39 = new BIP39(wordlists.custom)
it('works for tests vector ' + i, function() { assert.deepEqual(bip39.wordlist, wordlists.custom)
assert.equal(bip39.entropyToMnemonic(v[0]), v[1])
}) })
}) })
})
describe('generateMnemonic', function() { describe('mnemonicToSeed', function() {
it('generates a mnemonic', function() { this.timeout(20000)
var mnemonic = bip39.generateMnemonic(96)
var words = mnemonic.split(' ') var bip39
beforeEach(function() {
bip39 = new BIP39()
})
assert.equal(words.length, 9) vectors.english.forEach(function(v, i) {
it('works for tests vector ' + i, function() {
assert.equal(bip39.mnemonicToSeed(v[1], 'TREZOR'), v[2])
})
})
}) })
it('allows a custom RNG to be used', function() { describe('entropyToMnemonic', function() {
var rng = function(size) { vectors.english.forEach(function(v, i) {
var buffer = new Buffer(size) it('works for tests vector ' + i, function() {
buffer.fill(4) // guaranteed random var bip39 = new BIP39()
return buffer assert.equal(bip39.entropyToMnemonic(v[0]), v[1])
} })
})
var mnemonic = bip39.generateMnemonic(64, rng) vectors.custom.forEach(function(v, i) {
assert.equal(mnemonic, 'advice cage absurd amount doctor act') it('works for custom test vector ' + i, function() {
var bip39 = new BIP39(wordlists.custom)
assert.equal(bip39.entropyToMnemonic(v[0]), v[1])
})
})
}) })
})
describe('validate', function() { describe('generateMnemonic', function() {
vectors.forEach(function(v, i) { it('generates a mnemonic', function() {
it('passes check ' + i, function() { var bip39 = new BIP39()
assert(bip39.validate(v[1])) var mnemonic = bip39.generateMnemonic(96)
var words = mnemonic.split(' ')
assert.equal(words.length, 9)
}) })
})
it('fails for mnemonics of wrong length', function() { it('allows a custom RNG to be used', function() {
assert(!bip39.validate('sleep kitten')) var rng = function(size) {
assert(!bip39.validate('sleep kitten sleep kitten sleep kitten')) var buffer = new Buffer(size)
}) buffer.fill(4) // guaranteed random
return buffer
}
var bip39 = new BIP39()
it('fails for mnemonics that contains words not from the word list', function() { var mnemonic = bip39.generateMnemonic(64, rng)
assert(!bip39.validate("turtle front uncle idea crush write shrug there lottery flower risky shell")) 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')
})
}) })
it('fails for mnemonics of invalid checksum', function() { describe('validate', function() {
assert(!bip39.validate('sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten sleep kitten')) vectors.english.forEach(function(v, i) {
var bip39 = new BIP39()
it('passes check ' + i, function() {
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() {
var bip39 = new BIP39()
assert(!bip39.validate('sleep kitten'))
assert(!bip39.validate('sleep kitten sleep kitten sleep kitten'))
})
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"))
})
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'))
})
}) })
}) })

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