-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmnemonic.js
229 lines (171 loc) · 6.94 KB
/
mnemonic.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//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 WORD_BIT_SIZE = require('./word-bit-size');
var ENGLISH_WORDLIST = require('./wordlists/english.json')
var DEFAULT_WORDLIST = ENGLISH_WORDLIST
var JAPANESE_WORDLIST = null;
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', WORD_BIT_SIZE)
}).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 mnemonicToEntropy2(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', WORD_BIT_SIZE)
}).join('')
console.log(bits)
// split the binary string into ENT/CS
var dividerIndex = Math.floor(bits.length / WORD_BIT_SIZE) * (WORD_BIT_SIZE - 1);
var entropyBits = bits.slice(0, dividerIndex)
var versionByte = bits.slice(dividerIndex)
// calculate the checksum and compare
var entropyBytes = entropyBits.match(/(.{1,8})/g).map(binaryToByte)
versionByte = versionByte.match(/(.{1,8})/g).map(binaryToByte)
var entropy = Buffer.from(entropyBytes)
var version = Buffer.from(versionByte)
console.log(entropy)
console.log(version)
return {
entropy: entropy,
version: version
};
//return {entropy.toString('hex'),version.toString('hex')}
}
function entropyToMnemonic2(entropy, numOfWords, versionByte) {
if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex')
wordlist = DEFAULT_WORDLIST
const requiredNumOfWords = WORD_BIT_SIZE * numOfWords - 8;
// console.log(requiredNumOfWords)
//console.log(entropy.length*8 )
if ((entropy.length * 8) < requiredNumOfWords) { throw new TypeError(INVALID_ENTROPY); }
var tmp;
tmp = entropy;
while (entropy.length * 8 > requiredNumOfWords) {
// console.log(entropy)
tmp = entropy;
entropy = entropy.slice(0, entropy.length - 1);
}
//console.log(tmp.length*8)
entropy = tmp;
entropy = bytesToBinary([].slice.call(entropy));
entropy = entropy.substring(0, entropy.length - (entropy.length) % requiredNumOfWords);
//console.log(entropy.length -(entropy.length) % requiredNumOfWords)
//if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex')
if (!Buffer.isBuffer(versionByte)) versionByte = Buffer.from(versionByte, 'hex')
//entropy = Buffer.concat([entropy, versionByte], entropy.length + versionByte.length);
//TODO: add more restrictions to catch bad input
//if (entropy.length > 32) throw new TypeError(INVALID_ENTROPY)
// if (entropy.length % 4 !== 0) throw new TypeError(INVALID_ENTROPY)
var versionBits = bytesToBinary([].slice.call(versionByte))
// console.log(versionBits)
//console.log(entropyBits)
//console.log(binaryToByte(entropyBits).toString(16))
var bits = entropy + versionBits;
console.log(bits)
var chunks = bits.match(/(.{1,10})/g)
var words = chunks.map(function (binary) {
var index = binaryToByte(binary)
return wordlist[index]
})
return wordlist === JAPANESE_WORDLIST ? words.join('\u3000') : words.join(' ')
}
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,10})/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
}
//console.log("dic: " + entropyToMnemonic2('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',7, '00'))
// var tmp = mnemonicToEntropy2('primary fetch primary fetch primary fetch parade');
// console.log("entropy: " + tmp.entropy )
// console.log("version: " + tmp.version )
module.exports = {
entropyToMnemonic: entropyToMnemonic2,
mnemonicToEntropy: mnemonicToEntropy2,
}