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