First version
This commit is contained in:
commit
6ca8778985
|
@ -0,0 +1,34 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directory
|
||||||
|
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Alex Beregszaszi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,36 @@
|
||||||
|
# ethereumjs-wallet
|
||||||
|
|
||||||
|
A lightweight wallet implementation. At the moment it supports key creation and conversion between various formats.
|
||||||
|
|
||||||
|
It is complemented by the following packages:
|
||||||
|
- [ethereumjs-tx](https://github.com/ethereumjs/ethereumjs-tx) to sign transactions
|
||||||
|
- [ethereumjs-icap](https://github.com/ethereumjs/ethereumjs-icap) to manipulate ICAP addresses
|
||||||
|
- [store.js](https://github.com/marcuswestin/store.js) to use browser storage
|
||||||
|
|
||||||
|
Motivations are:
|
||||||
|
- be lightweight
|
||||||
|
- work in a browser
|
||||||
|
- use a single, maintained version of crypto library
|
||||||
|
- support import/export between various wallet formats
|
||||||
|
|
||||||
|
Features not supported:
|
||||||
|
- signing transactions
|
||||||
|
- managing storage (neither in node.js or the browser)
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
Constructors:
|
||||||
|
|
||||||
|
* `fromPrivateKey` - create an instance based on a raw key
|
||||||
|
* `fromV1` - import a wallet (Version 1 of the Ethereum wallet format)
|
||||||
|
* `fromV3` - import a wallet (Version 3 of the Ethereum wallet format)
|
||||||
|
* `fromEthSale` - import an Ethereum Pre Sale wallet
|
||||||
|
|
||||||
|
Instance methods:
|
||||||
|
|
||||||
|
* `getPrivateKey` - return the private key
|
||||||
|
* `getPublicKey` - return the public key
|
||||||
|
* `getAddress` - return the address
|
||||||
|
* `toV3` - return the wallet as a JSON string (Version 3 of the Ethereum wallet format)
|
||||||
|
|
||||||
|
All of the above instance methods return a Buffer or JSON. Use the `String` suffixed versions for a string output, such as `getPrivateKeyString`.
|
|
@ -0,0 +1,193 @@
|
||||||
|
var ethUtil = require('ethereumjs-util')
|
||||||
|
var crypto = require('crypto')
|
||||||
|
var scryptsy = require('scryptsy')
|
||||||
|
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.prototype.getPrivateKey = function () {
|
||||||
|
return this.privKey
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.prototype.getPrivateKeyString = function () {
|
||||||
|
return this.getPrivateKey.toString('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.prototype.getPublicKey = function () {
|
||||||
|
return ethUtil.privateToPublic(this.privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.prototype.getPublicKeyString = function () {
|
||||||
|
return this.getPublicKey.toString('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.prototype.getAddress = function () {
|
||||||
|
return ethUtil.privateToAddress(this.privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.prototype.getAddressString = function () {
|
||||||
|
return '0x' + this.getAddress().toString('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 || 'pbkdf2'
|
||||||
|
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 || 1
|
||||||
|
kdfparams.p = opts.p || 8
|
||||||
|
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
|
||||||
|
// Let's just transform it to be compatible with V3
|
||||||
|
// FIXME: this might not be fully correct in all cases
|
||||||
|
Wallet.fromV1 = function (input, password) {
|
||||||
|
var json = (typeof input === 'object') ? input : JSON.parse(input)
|
||||||
|
return Wallet.fromV3({
|
||||||
|
Crypto: {
|
||||||
|
ciphertext: json.Crypto.CipherText,
|
||||||
|
cipherparams: {
|
||||||
|
iv: json.Crypto.IV
|
||||||
|
},
|
||||||
|
cipher: 'aes-128-cbc',
|
||||||
|
kdf: json.Crypto.KeyHeader.Kdf,
|
||||||
|
kdfparams: {
|
||||||
|
dklen: json.Crypto.KeyHeader.KdfParams.DkLen,
|
||||||
|
n: json.Crypto.KeyHeader.KdfParams.N,
|
||||||
|
p: json.Crypto.KeyHeader.KdfParams.P,
|
||||||
|
r: json.Crypto.KeyHeader.KdfParams.R,
|
||||||
|
salt: json.Crypto.Salt
|
||||||
|
},
|
||||||
|
mac: json.Crypto.MAC
|
||||||
|
},
|
||||||
|
id: json.Id,
|
||||||
|
version: 3
|
||||||
|
}, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet.fromV3 = function (input, password) {
|
||||||
|
var json = (typeof input === 'object') ? input : JSON.parse(input)
|
||||||
|
|
||||||
|
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 mac = ethUtil.sha3(Buffer.concat([ derivedKey.slice(16, 32), new Buffer(json.Crypto.ciphertext, 'hex') ]))
|
||||||
|
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, new Buffer(json.Crypto.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
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"name": "ethereumjs-wallet",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Utilities for handling Ethereum keys",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "standard",
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/axic/ethereumjs-wallet.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ethereum",
|
||||||
|
"wallets",
|
||||||
|
"keys"
|
||||||
|
],
|
||||||
|
"author": "Alex Beregszaszi <alex@rtfs.hu>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/axic/ethereumjs-wallet/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/axic/ethereumjs-wallet",
|
||||||
|
"dependencies": {
|
||||||
|
"ethereumjs-util": "^4.0.0",
|
||||||
|
"scryptsy": "^1.2.1",
|
||||||
|
"uuid": "^2.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"standard": "^5.4.1"
|
||||||
|
},
|
||||||
|
"standard": {
|
||||||
|
"globals": [
|
||||||
|
"describe",
|
||||||
|
"it"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue