forked from bitcoinjs/bip39
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
153 lines (122 loc) · 4.72 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
var Buffer = require('safe-buffer').Buffer
var createHash = require('create-hash')
var pbkdf2 = require('pbkdf2').pbkdf2Sync
var randomBytes = require('randombytes')
// use unorm until String.prototype.normalize gets better browser support
var unorm = require('unorm')
var CHINESE_SIMPLIFIED_WORDLIST = require('./wordlists/chinese_simplified.json')
var CHINESE_TRADITIONAL_WORDLIST = require('./wordlists/chinese_traditional.json')
var ENGLISH_WORDLIST = require('./wordlists/english.json')
var FRENCH_WORDLIST = require('./wordlists/french.json')
var ITALIAN_WORDLIST = require('./wordlists/italian.json')
var JAPANESE_WORDLIST = require('./wordlists/japanese.json')
var KOREAN_WORDLIST = require('./wordlists/korean.json')
var SPANISH_WORDLIST = require('./wordlists/spanish.json')
var DEFAULT_WORDLIST = ENGLISH_WORDLIST
var INVALID_MNEMONIC = 'Invalid mnemonic'
var INVALID_ENTROPY = 'Invalid entropy'
var INVALID_CHECKSUM = 'Invalid mnemonic checksum'
function lpad (str, padString, length) {
while (str.length < length) str = padString + str
return str
}
function binaryToByte (bin) {
return parseInt(bin, 2)
}
function bytesToBinary (bytes) {
return bytes.map(function (x) {
return lpad(x.toString(2), '0', 8)
}).join('')
}
function deriveChecksumBits (entropyBuffer) {
var ENT = entropyBuffer.length * 8
var CS = ENT / 32
var hash = createHash('sha256').update(entropyBuffer).digest()
return bytesToBinary([].slice.call(hash)).slice(0, CS)
}
function salt (password) {
return 'mnemonic' + (password || '')
}
function mnemonicToSeed (mnemonic, password) {
var mnemonicBuffer = Buffer.from(unorm.nfkd(mnemonic), 'utf8')
var saltBuffer = Buffer.from(salt(unorm.nfkd(password)), 'utf8')
return pbkdf2(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512')
}
function mnemonicToSeedHex (mnemonic, password) {
return mnemonicToSeed(mnemonic, password).toString('hex')
}
function mnemonicToEntropy (mnemonic, wordlist) {
wordlist = wordlist || DEFAULT_WORDLIST
var words = unorm.nfkd(mnemonic).split(' ')
if (words.length % 3 !== 0) throw new Error(INVALID_MNEMONIC)
// convert word indices to 11 bit binary strings
var bits = words.map(function (word) {
var index = wordlist.indexOf(word)
if (index === -1) throw new Error(INVALID_MNEMONIC)
return lpad(index.toString(2), '0', 11)
}).join('')
// split the binary string into ENT/CS
var dividerIndex = Math.floor(bits.length / 33) * 32
var entropyBits = bits.slice(0, dividerIndex)
var checksumBits = bits.slice(dividerIndex)
// calculate the checksum and compare
var entropyBytes = entropyBits.match(/(.{1,8})/g).map(binaryToByte)
if (entropyBytes.length < 16) throw new Error(INVALID_ENTROPY)
if (entropyBytes.length > 32) throw new Error(INVALID_ENTROPY)
if (entropyBytes.length % 4 !== 0) throw new Error(INVALID_ENTROPY)
var entropy = Buffer.from(entropyBytes)
var newChecksum = deriveChecksumBits(entropy)
if (newChecksum !== checksumBits) throw new Error(INVALID_CHECKSUM)
return entropy.toString('hex')
}
function entropyToMnemonic (entropy, wordlist) {
if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex')
wordlist = wordlist || DEFAULT_WORDLIST
// 128 <= ENT <= 256
if (entropy.length < 16) throw new TypeError(INVALID_ENTROPY)
if (entropy.length > 32) throw new TypeError(INVALID_ENTROPY)
if (entropy.length % 4 !== 0) throw new TypeError(INVALID_ENTROPY)
var entropyBits = bytesToBinary([].slice.call(entropy))
var checksumBits = deriveChecksumBits(entropy)
var bits = entropyBits + checksumBits
var chunks = bits.match(/(.{1,11})/g)
var words = chunks.map(function (binary) {
var index = binaryToByte(binary)
return wordlist[index]
})
return wordlist === JAPANESE_WORDLIST ? words.join('\u3000') : words.join(' ')
}
function generateMnemonic (strength, rng, wordlist) {
strength = strength || 128
if (strength % 32 !== 0) throw new TypeError(INVALID_ENTROPY)
rng = rng || randomBytes
return entropyToMnemonic(rng(strength / 8), wordlist)
}
function validateMnemonic (mnemonic, wordlist) {
try {
mnemonicToEntropy(mnemonic, wordlist)
} catch (e) {
return false
}
return true
}
module.exports = {
mnemonicToSeed: mnemonicToSeed,
mnemonicToSeedHex: mnemonicToSeedHex,
mnemonicToEntropy: mnemonicToEntropy,
entropyToMnemonic: entropyToMnemonic,
generateMnemonic: generateMnemonic,
validateMnemonic: validateMnemonic,
wordlists: {
EN: ENGLISH_WORDLIST,
JA: JAPANESE_WORDLIST,
chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST,
chinese_traditional: CHINESE_TRADITIONAL_WORDLIST,
english: ENGLISH_WORDLIST,
french: FRENCH_WORDLIST,
italian: ITALIAN_WORDLIST,
japanese: JAPANESE_WORDLIST,
korean: KOREAN_WORDLIST,
spanish: SPANISH_WORDLIST
}
}