mirror of https://github.com/status-im/bip39.git
adhere to standard
This commit is contained in:
parent
967512dc25
commit
889f233bdd
38
index.js
38
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 = {
|
||||
|
|
105
test/index.js
105
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)
|
||||
// <Buffer 5c f2 d4 a8 b0 35 5e 90 29 5b df c5 65 a0 22 a4 09 af 06 3d 53 65 bb 57 bf 74 d9 52 8f 49 4b fa 44 00 f5 3d 83 49 b8 0f da e4 40 82 d7 f9 54 1e 1d ba 2b ...>
|
||||
|
||||
|
@ -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 () {
|
||||
|
|
Loading…
Reference in New Issue