ethereumjs-wallet/index.js

222 lines
6.6 KiB
JavaScript

var ethUtil = require('ethereumjs-util')
var crypto = require('crypto')
var scryptsy = require('scrypt.js')
var uuid = require('uuid')
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 Wallet = function (priv) {
this.privKey = priv
}
Wallet.generate = function (icapDirect) {
if (icapDirect) {
while (true) {
var privKey = crypto.randomBytes(32)
if (ethUtil.privateToAddress(privKey)[0] === 0) {
return new Wallet(privKey)
}
}
} else {
return new Wallet(crypto.randomBytes(32))
}
}
Wallet.prototype.getPrivateKey = function () {
return this.privKey
}
Wallet.prototype.getPrivateKeyString = function () {
return '0x' + this.getPrivateKey().toString('hex')
}
Wallet.prototype.getPublicKey = function () {
return ethUtil.privateToPublic(this.privKey)
}
Wallet.prototype.getPublicKeyString = function () {
return '0x' + this.getPublicKey().toString('hex')
}
Wallet.prototype.getAddress = function () {
return ethUtil.privateToAddress(this.privKey)
}
Wallet.prototype.getAddressString = function () {
return '0x' + this.getAddress().toString('hex')
}
Wallet.prototype.getChecksumAddressString = function () {
return ethUtil.toChecksumAddress(this.getAddressString())
}
// https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
Wallet.prototype.toV3 = function (password, opts) {
opts = opts || {}
var salt = opts.salt || crypto.randomBytes(32)
var iv = opts.iv || crypto.randomBytes(16)
var derivedKey
var kdf = opts.kdf || 'scrypt'
var kdfparams = {
dklen: opts.dklen || 32,
salt: salt.toString('hex')
}
if (kdf === 'pbkdf2') {
kdfparams.c = opts.c || 262144
kdfparams.prf = 'hmac-sha256'
derivedKey = crypto.pbkdf2Sync(new Buffer(password), salt, kdfparams.c, kdfparams.dklen, 'sha256')
} else if (kdf === 'scrypt') {
// FIXME: support progress reporting callback
kdfparams.n = opts.n || 262144
kdfparams.r = opts.r || 8
kdfparams.p = opts.p || 1
derivedKey = scryptsy(new Buffer(password), salt, kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen)
} else {
throw new Error('Unsupported kdf')
}
var cipher = crypto.createCipheriv(opts.cipher || 'aes-128-ctr', derivedKey.slice(0, 16), iv)
if (!cipher) {
throw new Error('Unsupported cipher')
}
var ciphertext = Buffer.concat([ cipher.update(this.privKey), cipher.final() ])
var mac = ethUtil.sha3(Buffer.concat([ derivedKey.slice(16, 32), new Buffer(ciphertext, 'hex') ]))
return {
version: 3,
id: uuid.v4({ random: opts.uuid || crypto.randomBytes(16) }),
address: this.getAddress().toString('hex'),
Crypto: {
ciphertext: ciphertext.toString('hex'),
cipherparams: {
iv: iv.toString('hex')
},
cipher: opts.cipher || 'aes-128-ctr',
kdf: kdf,
kdfparams: kdfparams,
mac: mac.toString('hex')
}
}
}
Wallet.prototype.toV3String = function (password, opts) {
return JSON.stringify(this.toV3(password, opts))
}
Wallet.fromPrivateKey = function (priv) {
return new Wallet(priv)
}
// https://github.com/ethereum/go-ethereum/wiki/Passphrase-protected-key-store-spec
Wallet.fromV1 = function (input, password) {
var json = (typeof input === 'object') ? input : JSON.parse(input)
if (json.Version !== '1') {
throw new Error('Not a V1 wallet')
}
if (json.Crypto.KeyHeader.Kdf !== 'scrypt') {
throw new Error('Unsupported key derivation scheme')
}
var kdfparams = json.Crypto.KeyHeader.KdfParams
var derivedKey = scryptsy(new Buffer(password), new Buffer(json.Crypto.Salt, 'hex'), kdfparams.N, kdfparams.R, kdfparams.P, kdfparams.DkLen)
var ciphertext = new Buffer(json.Crypto.CipherText, 'hex')
var mac = ethUtil.sha3(Buffer.concat([ derivedKey.slice(16, 32), ciphertext ]))
console.log(mac, json.Crypto.MAC)
if (mac.toString('hex') !== json.Crypto.MAC) {
throw new Error('Key derivation failed - possibly wrong passphrase')
}
var decipher = crypto.createDecipheriv('aes-128-cbc', ethUtil.sha3(derivedKey.slice(0, 16)).slice(0, 16), new Buffer(json.Crypto.IV, 'hex'))
var seed = decipherBuffer(decipher, ciphertext)
// FIXME: Remove PKCS#7 padding here?
return new Wallet(seed)
}
Wallet.fromV3 = function (input, password) {
var json = (typeof input === 'object') ? input : JSON.parse(input)
if (json.version !== 3) {
throw new Error('Not a V3 wallet')
}
var derivedKey
var kdfparams
if (json.Crypto.kdf === 'scrypt') {
kdfparams = json.Crypto.kdfparams
// FIXME: support progress reporting callback
derivedKey = scryptsy(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.n, kdfparams.r, kdfparams.p, kdfparams.dklen)
} else if (json.Crypto.kdf === 'pbkdf2') {
kdfparams = json.Crypto.kdfparams
if (kdfparams.prf !== 'hmac-sha256') {
throw new Error('Unsupported parameters to PBKDF2')
}
derivedKey = crypto.pbkdf2Sync(new Buffer(password), new Buffer(kdfparams.salt, 'hex'), kdfparams.c, kdfparams.dklen, 'sha256')
} else {
throw new Error('Unsupported key derivation scheme')
}
var ciphertext = new Buffer(json.Crypto.ciphertext, 'hex')
var mac = ethUtil.sha3(Buffer.concat([ derivedKey.slice(16, 32), ciphertext ]))
if (mac.toString('hex') !== json.Crypto.mac) {
throw new Error('Key derivation failed - possibly wrong passphrase')
}
var decipher = crypto.createDecipheriv(json.Crypto.cipher, derivedKey.slice(0, 16), new Buffer(json.Crypto.cipherparams.iv, 'hex'))
var seed = decipherBuffer(decipher, ciphertext, 'hex')
// FIXME: Remove PKCS#7 padding here?
return new Wallet(seed)
}
/*
* Based on https://github.com/ethereum/pyethsaletool/blob/master/pyethsaletool.py
* JSON fields: encseed, ethaddr, btcaddr, email
*/
Wallet.fromEthSale = function (input, password) {
assert(typeof password === 'string')
var json = (typeof input === 'object') ? input : JSON.parse(input)
var encseed = new Buffer(json.encseed, 'hex')
// key derivation
var derivedKey = crypto.pbkdf2Sync(password, password, 2000, 32, 'sha256').slice(0, 16)
// seed decoding (IV is first 16 bytes)
var decipher = crypto.createDecipheriv('aes-128-cbc', derivedKey, encseed.slice(0, 16))
var seed = decipherBuffer(decipher, encseed.slice(16))
// FIXME: Remove PKCS#7 padding here?
var wallet = new Wallet(ethUtil.sha3(seed))
if (wallet.getAddress().toString('hex') !== json.ethaddr) {
throw new Error('Decoded key mismatch - possibly wrong passphrase')
}
return wallet
}
module.exports = Wallet