forked from novalabio/react-native-bip39
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
176 lines (135 loc) · 4.77 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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
var unorm = require('unorm')
var assert = require('assert')
var pbkdf2 = require('react-native-crypto').pbkdf2Sync
var createHash = require('react-native-crypto').createHash
var DEFAULT_WORDLIST = require('./wordlists/en.json')
var MAX_BYTES = 65536
// Node supports requesting up to this number of bytes
// https://github.com/nodejs/node/blob/master/lib/internal/crypto/random.js#L48
var MAX_UINT32 = 4294967295
function oldBrowser () {
throw new Error('Secure random number generation is not supported by this browser.\nUse Chrome, Firefox or Internet Explorer 11')
}
var Buffer = require('safe-buffer').Buffer
function mnemonicToSeed(mnemonic, password) {
var mnemonicBuffer = new Buffer(mnemonic, 'utf8')
var saltBuffer = new Buffer(salt(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 = mnemonic.split(' ')
assert(words.length % 3 === 0, 'Invalid mnemonic')
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 index = wordlist.indexOf(word)
return lpad(index.toString(2), '0', 11)
}).join('')
// split the binary string into ENT/CS
var dividerIndex = Math.floor(bits.length / 33) * 32
var entropy = bits.slice(0, dividerIndex)
var checksum = bits.slice(dividerIndex)
// calculate the checksum and compare
var entropyBytes = entropy.match(/(.{1,8})/g).map(function(bin) {
return parseInt(bin, 2)
})
var entropyBuffer = new Buffer(entropyBytes)
var newChecksum = checksumBits(entropyBuffer)
assert(newChecksum === checksum, 'Invalid mnemonic checksum')
return entropyBuffer.toString('hex')
}
function entropyToMnemonic(entropy, wordlist) {
wordlist = wordlist || DEFAULT_WORDLIST
var entropyBuffer = new Buffer(entropy, 'hex')
var entropyBits = bytesToBinary([].slice.call(entropyBuffer))
var checksum = checksumBits(entropyBuffer)
var bits = entropyBits + checksum
var chunks = bits.match(/(.{1,11})/g)
var words = chunks.map(function(binary) {
var index = parseInt(binary, 2)
return wordlist[index]
})
return words.join(' ')
}
function randomBytes (size, cb) {
// phantomjs needs to throw
if (size > MAX_UINT32) throw new RangeError('requested too many random bytes')
var bytes = Buffer.allocUnsafe(size)
if (size > 0) { // getRandomValues fails on IE if size == 0
if (size > MAX_BYTES) { // this is the max bytes crypto.getRandomValues
// can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues
for (var generated = 0; generated < size; generated += MAX_BYTES) {
// buffer.slice automatically checks if the end is past the end of
// the buffer so we don't have to here
crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES))
}
} else {
crypto.getRandomValues(bytes)
}
}
if (typeof cb === 'function') {
return process.nextTick(function () {
cb(null, bytes)
})
}
return bytes
}
function generateMnemonic(strength, rng, wordlist) {
return new Promise((resolve, reject) => {
strength = strength || 128
rng = rng || randomBytes
rng(strength / 8, (error, randomBytesBuffer) => {
if (error) {
reject(error)
} else {
resolve(entropyToMnemonic(randomBytesBuffer.toString('hex'), wordlist))
}
})
})
}
function validateMnemonic(mnemonic, wordlist) {
try {
mnemonicToEntropy(mnemonic, wordlist)
} catch (e) {
return false
}
return true
}
function checksumBits(entropyBuffer) {
var hash = createHash('sha256').update(entropyBuffer).digest()
// Calculated constants from BIP39
var ENT = entropyBuffer.length * 8
var CS = ENT / 32
return bytesToBinary([].slice.call(hash)).slice(0, CS)
}
function salt(password) {
return 'mnemonic' + (unorm.nfkd(password) || '') // Use unorm until String.prototype.normalize gets better browser support
}
//=========== helper methods from bitcoinjs-lib ========
function bytesToBinary(bytes) {
return bytes.map(function(x) {
return lpad(x.toString(2), '0', 8)
}).join('');
}
function lpad(str, padString, length) {
while (str.length < length) str = padString + str;
return str;
}
module.exports = {
mnemonicToSeed: mnemonicToSeed,
mnemonicToSeedHex: mnemonicToSeedHex,
mnemonicToEntropy: mnemonicToEntropy,
entropyToMnemonic: entropyToMnemonic,
generateMnemonic: generateMnemonic,
validateMnemonic: validateMnemonic,
wordlists: {
EN: DEFAULT_WORDLIST
}
}