forked from kvhnuke/ethereumjs-wallet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththirdparty.js
232 lines (187 loc) · 6.24 KB
/
thirdparty.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
230
231
232
var Wallet = require('./index.js')
var ethUtil = require('ethereumjs-util')
var crypto = require('crypto')
var scryptsy = require('scrypt.js')
var utf8 = require('utf8')
var aesjs = require('aes-js')
function assert (val, msg) {
if (!val) {
throw new Error(msg || 'Assertion failed')
}
}
function decipherBuffer (decipher, data) {
return Buffer.concat([ decipher.update(data), decipher.final() ])
}
var Thirdparty = {}
/*
* opts:
* - digest - digest algorithm, defaults to md5
* - count - hash iterations
* - keysize - desired key size
* - ivsize - desired IV size
*
* Algorithm form https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html
*
* FIXME: not optimised at all
*/
function evp_kdf (data, salt, opts) {
// A single EVP iteration, returns `D_i`, where block equlas to `D_(i-1)`
function iter (block) {
var hash = crypto.createHash(opts.digest || 'md5')
hash.update(block)
hash.update(data)
hash.update(salt)
block = hash.digest()
for (var i = 1; i < (opts.count || 1); i++) {
hash = crypto.createHash(opts.digest || 'md5')
hash.update(block)
block = hash.digest()
}
return block
}
var keysize = opts.keysize || 16
var ivsize = opts.ivsize || 16
var ret = []
var i = 0
while (Buffer.concat(ret).length < (keysize + ivsize)) {
ret[i] = iter((i === 0) ? new Buffer(0) : ret[i - 1])
i++
}
var tmp = Buffer.concat(ret)
return {
key: tmp.slice(0, keysize),
iv: tmp.slice(keysize, keysize + ivsize)
}
}
// http://stackoverflow.com/questions/25288311/cryptojs-aes-pattern-always-ends-with
function decodeCryptojsSalt (input) {
var ciphertext = new Buffer(input, 'base64')
if (ciphertext.slice(0, 8).toString() === 'Salted__') {
return {
salt: ciphertext.slice(8, 16),
ciphertext: ciphertext.slice(16)
}
} else {
return {
ciphertext: ciphertext
}
}
}
/*
* This wallet format is created by https://github.com/SilentCicero/ethereumjs-accounts
* and used on https://www.myetherwallet.com/
*/
Thirdparty.fromEtherWallet = function (input, password) {
var json = (typeof input === 'object') ? input : JSON.parse(input)
var privKey
if (!json.locked) {
if (json.private.length !== 64) {
throw new Error('Invalid private key length')
}
privKey = new Buffer(json.private, 'hex')
} else {
if (typeof password !== 'string') {
throw new Error('Password required')
}
if (password.length < 7) {
throw new Error('Password must be at least 7 characters')
}
// the "encrypted" version has the low 4 bytes
// of the hash of the address appended
var cipher = json.encrypted ? json.private.slice(0, 128) : json.private
// decode openssl ciphertext + salt encoding
cipher = decodeCryptojsSalt(cipher)
if (!cipher.salt) {
throw new Error('Unsupported EtherWallet key format')
}
// derive key/iv using OpenSSL EVP as implemented in CryptoJS
var evp = evp_kdf(new Buffer(password), cipher.salt, { keysize: 32, ivsize: 16 })
var decipher = crypto.createDecipheriv('aes-256-cbc', evp.key, evp.iv)
privKey = decipherBuffer(decipher, new Buffer(cipher.ciphertext))
// NOTE: yes, they've run it through UTF8
privKey = new Buffer(utf8.decode(privKey.toString()), 'hex')
}
var wallet = new Wallet(privKey)
if (wallet.getAddressString() !== json.address) {
throw new Error('Invalid private key or address')
}
return wallet
}
Thirdparty.fromEtherCamp = function (passphrase) {
return new Wallet(ethUtil.sha3(new Buffer(passphrase)))
}
Thirdparty.fromKryptoKit = function (entropy, password) {
function kryptoKitBrokenScryptSeed (buf) {
// js-scrypt calls `new Buffer(String(salt), 'utf8')` on the seed even though it is a buffer
//
// The `buffer`` implementation used does the below transformation (doesn't matches the current version):
// https://github.com/feross/buffer/blob/67c61181b938b17d10dbfc0a545f713b8bd59de8/index.js
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
var res = ''
var tmp = ''
for (var i = 0; i < buf.length; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
}
}
return new Buffer(res + decodeUtf8Char(tmp))
}
if (entropy[0] === '#') {
entropy = entropy.slice(1)
}
var type = entropy[0]
entropy = entropy.slice(1)
var privKey
if (type === 'd') {
privKey = ethUtil.sha256(entropy)
} else if (type === 'q') {
if (typeof password !== 'string') {
throw new Error('Password required')
}
var encryptedSeed = ethUtil.sha256(new Buffer(entropy.slice(0, 30)))
var checksum = entropy.slice(30, 46)
var salt = kryptoKitBrokenScryptSeed(encryptedSeed)
var aesKey = scryptsy(new Buffer(password, 'utf8'), salt, 16384, 8, 1, 32)
/* FIXME: try to use `crypto` instead of `aesjs`
// NOTE: ECB doesn't use the IV, so it can be anything
var decipher = crypto.createDecipheriv("aes-256-ecb", aesKey, new Buffer(0))
// FIXME: this is a clear abuse, but seems to match how ECB in aesjs works
privKey = Buffer.concat([
decipher.update(encryptedSeed).slice(0, 16),
decipher.update(encryptedSeed).slice(0, 16),
])
*/
/* eslint-disable new-cap */
var decipher = new aesjs.ModeOfOperation.ecb(aesKey)
/* eslint-enable new-cap */
privKey = Buffer.concat([
decipher.decrypt(encryptedSeed.slice(0, 16)),
decipher.decrypt(encryptedSeed.slice(16, 32))
])
if (checksum.length > 0) {
if (checksum !== ethUtil.sha256(ethUtil.sha256(privKey)).slice(0, 8).toString('hex')) {
throw new Error('Failed to decrypt input - possibly invalid passphrase')
}
}
} else {
throw new Error('Unsupported or invalid entropy type')
}
return new Wallet(privKey)
}
Thirdparty.fromQuorumWallet = function (passphrase, userid) {
assert(passphrase.length >= 10)
assert(userid.length >= 10)
var seed = passphrase + userid
seed = crypto.pbkdf2Sync(seed, seed, 2000, 32, 'sha256')
return new Wallet(seed)
}
module.exports = Thirdparty