Support encrypted KryptoKit seed

This commit is contained in:
Alex Beregszaszi 2016-03-08 21:36:41 +00:00
parent eacd3c0692
commit 80f758413b
3 changed files with 72 additions and 1 deletions

View File

@ -3,6 +3,7 @@ var crypto = require('crypto')
var scryptsy = require('scrypt.js')
var uuid = require('uuid')
var utf8 = require('utf8')
var aesjs = require('aes-js')
function assert (val, msg) {
if (!val) {
@ -334,7 +335,36 @@ Wallet.fromEtherCamp = function (passphrase) {
return new Wallet(ethUtil.sha3(new Buffer(passphrase)))
}
Wallet.fromKryptoKit = function (entropy) {
Wallet.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)
}
@ -345,6 +375,42 @@ Wallet.fromKryptoKit = function (entropy) {
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')
}

View File

@ -24,6 +24,7 @@
},
"homepage": "https://github.com/axic/ethereumjs-wallet",
"dependencies": {
"aes-js": "^0.2.3",
"ethereumjs-util": "^4.1.0",
"scrypt.js": "^0.1.0",
"uuid": "^2.0.1",

View File

@ -143,4 +143,8 @@ describe('.fromKryptoKit()', function () {
var wallet = Wallet.fromKryptoKit('dBWfH8QZSGbg1sAYHLBhqE5R8VGAoM7')
assert.equal(wallet.getAddressString(), '0x3611981ad2d6fc1d7579d6ce4c6bc37e272c369c')
})
it('should work with encrypted input (q-type)', function () {
var wallet = Wallet.fromKryptoKit('qhah1VeT0RgTvff1UKrUrxtFViiQuki16dd353d59888c25', 'testtest')
assert.equal(wallet.getAddressString(), '0x3c753e27834db67329d1ec1fab67970ec1e27112')
})
})