Comparing version 0.3.0 to 0.4.0
@@ -0,1 +1,22 @@ | ||
next release / 2014-03-09 | ||
------------------ | ||
* Dropped sha256 and ripemd160 deps. Upgraded to crypto-hashing library. | ||
* removed function wrapper | ||
* Changed the way the constuctor works. Only supports input types of `Array`, `Buffer`, or `Uint8Array`. Does NOT randomly generate a private key anymore. | ||
* added `publicKey` property | ||
* ~~added `pubKeyHash`/`publicHash` property~~ | ||
* added `publicPoint`, removed `getPubPoint()` | ||
* removed `getPub()`, use `publicKey` instead | ||
* removed `getPubKeyHash()`, use `publicHash` or `pubKeyHash` instead | ||
* removed `sign()` and `verify()`, methods can be accessed from [ecdsa](https://github.com/cryptocoinjs/ecdsa) | ||
* added `privateExportKey` | ||
* removed `getExportedPrivateKey`, note that `getExportedPrivateKey` was essentially just a way to get WIF | ||
* removed `decodeString()`, use package [coinstring][coinstring] in its place | ||
* removed `getBitcoinAddress()`, use package [coinstring][coinstring] in its place | ||
* removed `setCompressed`, use `compressed` property instead | ||
* removed deps: `ecdsa`, `convert-hex`, `btc-address`, `bs58` | ||
* updated deps: `ecurve` and `ecurve-names` | ||
* removed `pubKeyHash` property, removes dependency upon `crypto-hashing` | ||
* removed dep `crypto-hashing` | ||
0.3.0 / 2014-02-03 | ||
@@ -28,2 +49,4 @@ ------------------ | ||
------------------ | ||
* initial release | ||
* initial release | ||
[coinstring]: https://github.com/cryptocoinjs/coinstring |
345
lib/eckey.js
@@ -1,292 +0,119 @@ | ||
!function(globals) { | ||
'use strict' | ||
var ECPointFp = require('ecurve').ECPointFp; | ||
var ecparams = require('ecurve-names')('secp256k1'); | ||
var BigInteger = require('bigi'); | ||
module.exports = ECKey | ||
//*** IMPORTS | ||
var BigInteger = null | ||
var ECPointFp = null | ||
var ecparams = null | ||
var base58 = null | ||
var sha256 = null | ||
var ripe160 = null | ||
var convertHex = null | ||
var Address = null | ||
var ecdsa = null | ||
//*** IMPORTS END | ||
if (typeof module !== 'undefined' && module.exports) { //CommonJS | ||
ECPointFp = require('ecurve').ECPointFp | ||
ecparams = require('ecurve-names')('secp256k1') | ||
sha256 = require('sha256') | ||
ripe160 = require('ripemd160') | ||
convertHex = require('convert-hex') | ||
Address = require('btc-address') | ||
ecdsa = require('ecdsa') | ||
BigInteger = require('bigi') | ||
base58 = require('bs58') | ||
module.exports = ECKey | ||
} else { | ||
BigInteger = globals.BigInteger | ||
ECPointFp = globals.ECCurveFp.ECPointFp | ||
ecparams = globals.getSECCurveByName('secp256k1') | ||
base58 = globals.bs58 | ||
sha256 = globals.sha256 | ||
ripe160 = globals.ripemd160 | ||
convertHex = globals.convertHex | ||
Address = globals.Address | ||
ecdsa = globals.ECDSA | ||
globals.ECKey = ECKey | ||
} | ||
//*** UMD END | ||
function ECKey (bytes, compressed) { | ||
if (!(this instanceof ECKey)) return new ECKey(input); | ||
var util = { | ||
sha256ripe160: function(bytes) { | ||
return ripe160(sha256(bytes, {asBytes: true}), {asBytes: true}) | ||
} | ||
this._compressed = compressed || !!ECKey.compressByDefault; | ||
if (bytes) | ||
this.privateKey = bytes; | ||
} | ||
/******************** | ||
* STATIC PROPERTIES | ||
********************/ | ||
var networkTypes = { | ||
prod: 128, | ||
testnet: 239 | ||
}; | ||
ECKey.compressByDefault = false; | ||
// input can be nothing, array of bytes, hex string, or base58 string | ||
function ECKey (input) { | ||
if (!(this instanceof ECKey)) { | ||
return new ECKey(input); | ||
} | ||
/******************** | ||
* GET/SET PROPERTIES | ||
********************/ | ||
this.compressed = !!ECKey.compressByDefault; | ||
if (!input) { | ||
// Generate new key | ||
var n = ecparams.getN(); | ||
this.priv = ecdsa.getBigRandom(n); | ||
} else if (input instanceof BigInteger) { | ||
// Input is a private key value | ||
this.priv = input; | ||
} else if (Array.isArray(input)) { | ||
// Prepend zero byte to prevent interpretation as negative integer | ||
this.priv = BigInteger.fromByteArrayUnsigned(input); | ||
this.compressed = false; | ||
} else if ("string" == typeof input) { | ||
// A list of base58 encoded prefixes is at https://en.bitcoin.it/wiki/List_of_address_prefixes. | ||
if (input.length == 51 && input[0] == '5') { | ||
// Base58 encoded private key | ||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input, networkTypes.prod)); | ||
this.compressed = false; | ||
Object.defineProperty(ECKey.prototype, 'privateKey', { | ||
enumerable: true, configurable: true, | ||
get: function() { | ||
return this.key; | ||
}, | ||
set: function(bytes) { | ||
var byteArr; | ||
if (Buffer.isBuffer(bytes)) { | ||
this.key = bytes; | ||
byteArr = [].slice.call(bytes); | ||
} else if (bytes instanceof Uint8Array) { | ||
byteArr = [].slice.call(bytes); | ||
this.key = new Buffer(byteArr); | ||
} else if (Array.isArray(bytes)) { | ||
byteArr = bytes; | ||
this.key = new Buffer(byteArr); | ||
} else { | ||
throw new Error('private key bytes must be either a Buffer, Array, or Uint8Array.'); | ||
} | ||
else if (input.length == 51 && input[0] == '9') { | ||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input, networkTypes.testnet)); | ||
this.compressed = false; | ||
} | ||
else if (input.length == 52 && (input[0] === 'K' || input[0] === 'L')) { | ||
// Base58 encoded private key | ||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input, networkTypes.prod)); | ||
this.compressed = true; | ||
} | ||
else if (input.length == 52 && input[0] === 'c') { | ||
// Base58 encoded private key | ||
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input, networkTypes.testnet)); | ||
this.compressed = true; | ||
} | ||
} | ||
}; | ||
// TODO(shtylman) methods | ||
// wallet import format (base58 check with meta info) | ||
// fromWIF | ||
// toWIF | ||
// fromBytes | ||
// toBytes | ||
// fromHex | ||
// toHex | ||
if (bytes.length != 32) | ||
throw new Error("private key bytes must have a length of 32"); | ||
/** | ||
* Whether public keys should be returned compressed by default. | ||
*/ | ||
ECKey.compressByDefault = false; | ||
//_exportKey => privateKey + (0x01 if compressed) | ||
if (this._compressed) | ||
this._exportKey = Buffer.concat([ this.key, new Buffer([0x01]) ]); | ||
else | ||
this._exportKey = Buffer.concat([ this.key ]); //clone key as opposed to passing a reference (relevant to Node.js only) | ||
/** | ||
* Set whether the public key should be returned compressed or not. | ||
*/ | ||
ECKey.prototype.setCompressed = function (v) { | ||
this.compressed = !!v; | ||
}; | ||
this.keyBigInteger = BigInteger.fromByteArrayUnsigned(byteArr); | ||
/** | ||
* Return public key in DER encoding. | ||
*/ | ||
ECKey.prototype.getPub = function () { | ||
return this.getPubPoint().getEncoded(this.compressed); | ||
}; | ||
//reset | ||
this._publicPoint = null; | ||
this._pubKeyHash = null; | ||
} | ||
}) | ||
/** | ||
* Return public point as ECPoint object. | ||
*/ | ||
ECKey.prototype.getPubPoint = function () { | ||
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv); | ||
Object.defineProperty(ECKey.prototype, 'privateExportKey', { | ||
get: function() { | ||
return this._exportKey; | ||
} | ||
}) | ||
return this.pub; | ||
}; | ||
Object.defineProperty(ECKey.prototype, 'publicKey', { | ||
get: function() { | ||
return new Buffer(this.publicPoint.getEncoded(this.compressed)); | ||
} | ||
}) | ||
/** | ||
* Get the pubKeyHash for this key. | ||
* | ||
* This is calculated as RIPE160(SHA256([encoded pubkey])) and returned as | ||
* a byte array. | ||
*/ | ||
ECKey.prototype.getPubKeyHash = function () { | ||
if (this.pubKeyHash) return this.pubKeyHash; | ||
Object.defineProperty(ECKey.prototype, 'publicPoint', { | ||
get: function() { | ||
if (!this._publicPoint) | ||
this._publicPoint = ecparams.getG().multiply(this.keyBigInteger); | ||
return this._publicPoint; | ||
} | ||
}) | ||
return this.pubKeyHash = util.sha256ripe160(this.getPub()); | ||
}; | ||
Object.defineProperty(ECKey.prototype, 'compressed', { | ||
get: function() { | ||
return this._compressed; | ||
}, | ||
set: function(val) { | ||
var c = !!val; | ||
if (c === this._compressed) return; | ||
//reset key stuff | ||
var pk = this.privateKey; | ||
this._compressed = c; | ||
this.privateKey = pk; | ||
} | ||
}) | ||
ECKey.prototype.getBitcoinAddress = function (address_type) { | ||
var hash = this.getPubKeyHash(); | ||
var addr = new Address(hash, address_type); | ||
return addr; | ||
}; | ||
ECKey.prototype.getExportedPrivateKey = function (bitcoinNetwork) { | ||
bitcoinNetwork = bitcoinNetwork || 'prod'; | ||
var hash = this.priv.toByteArrayUnsigned(); | ||
while (hash.length < 32) hash.unshift(0); | ||
if (this.compressed) hash.push(0x01); | ||
hash.unshift(networkTypes[bitcoinNetwork]); | ||
var checksum = sha256.x2(hash, {asBytes: true}) | ||
var bytes = hash.concat(checksum.slice(0,4)); | ||
return base58.encode(bytes); | ||
}; | ||
/************ | ||
* METHODS | ||
************/ | ||
ECKey.prototype.setPub = function (pub) { | ||
/*ECKey.prototype.setPub = function (pub) { | ||
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub); | ||
}; | ||
};*/ | ||
ECKey.prototype.toString = function (format) { | ||
return convertHex.bytesToHex(this.priv.toByteArrayUnsigned()); | ||
}; | ||
ECKey.prototype.sign = function (hash) { | ||
return ecdsa.sign(hash, this.priv); | ||
}; | ||
ECKey.prototype.verify = function (hash, sig) { | ||
return ecdsa.verify(hash, sig, this.getPub()); | ||
}; | ||
/// THIS METHOD WAS ORIGINALLY IN ECDSA | ||
/** | ||
* Recover a public key from a signature. | ||
* | ||
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public | ||
* Key Recovery Operation". | ||
* | ||
* http://www.secg.org/download/aid-780/sec1-v2.pdf | ||
*/ | ||
ECKey.recoverPubKey = function (r, s, hash, i) { | ||
// The recovery parameter i has two bits. | ||
i = i & 3; | ||
// The less significant bit specifies whether the y coordinate | ||
// of the compressed point is even or not. | ||
var isYEven = i & 1; | ||
// The more significant bit specifies whether we should use the | ||
// first or second candidate key. | ||
var isSecondKey = i >> 1; | ||
var n = ecparams.getN(); | ||
var G = ecparams.getG(); | ||
var curve = ecparams.getCurve(); | ||
var p = curve.getQ(); | ||
var a = curve.getA().toBigInteger(); | ||
var b = curve.getB().toBigInteger(); | ||
// We precalculate (p + 1) / 4 where p is if the field order | ||
if (!P_OVER_FOUR) { | ||
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)); | ||
} | ||
// 1.1 Compute x | ||
var x = isSecondKey ? r.add(n) : r; | ||
// 1.3 Convert x to point | ||
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p); | ||
var beta = alpha.modPow(P_OVER_FOUR, p); | ||
var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2); | ||
// If beta is even, but y isn't or vice versa, then convert it, | ||
// otherwise we're done and y == beta. | ||
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta); | ||
// 1.4 Check that nR is at infinity | ||
var R = new ECPointFp(curve, | ||
curve.fromBigInteger(x), | ||
curve.fromBigInteger(y)); | ||
R.validate(); | ||
// 1.5 Compute e from M | ||
var e = BigInteger.fromByteArrayUnsigned(hash); | ||
var eNeg = BigInteger.ZERO.subtract(e).mod(n); | ||
// 1.6 Compute Q = r^-1 (sR - eG) | ||
var rInv = r.modInverse(n); | ||
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv); | ||
Q.validate(); | ||
if (!ECDSA.verifyRaw(e, r, s, Q)) { | ||
throw new Error("Pubkey recovery unsuccessful"); | ||
} | ||
var pubKey = ECKey(); | ||
pubKey.pub = Q; | ||
return pubKey; | ||
return this.privateKey.toString('hex'); | ||
} | ||
/** | ||
* Parse an exported private key contained in a string. | ||
*/ | ||
ECKey.decodeString = function (string, expectedVersion) { | ||
var bytes = base58.decode(string); | ||
if (bytes.length !== 37 && bytes.length !== 38) { | ||
throw new Error('not a valid base58 encoded private key'); | ||
} | ||
//Format: | ||
//* uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of | ||
//previous 33 bytes], base58 encoded | ||
//* compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() | ||
//previous 34 bytes], base58 encoded | ||
if (bytes[33] === 0x01) { | ||
// compressed | ||
} | ||
var hash = bytes.slice(0, 33); | ||
/* | ||
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true}); | ||
if (checksum[0] != bytes[33] || | ||
checksum[1] != bytes[34] || | ||
checksum[2] != bytes[35] || | ||
checksum[3] != bytes[36]) { | ||
throw "Checksum validation failed!"; | ||
} | ||
*/ | ||
var version = hash.shift(); | ||
if (version !== expectedVersion) | ||
throw "Version "+version+" not expected, expected " + expectedVersion + "!"; | ||
return hash; | ||
}; | ||
}(this); |
{ | ||
"name": "eckey", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "Elliptical curve cryptography for crypto currencies such as Litecoin and Bitcoin", | ||
@@ -18,3 +18,6 @@ "keywords": [ | ||
"mocha": "1.*", | ||
"terst": "0.0.1" | ||
"terst": "~0.1.0", | ||
"binstring": "~0.2.0", | ||
"secure-random": "~0.2.0", | ||
"mochify": "~0.4.2" | ||
}, | ||
@@ -27,12 +30,6 @@ "repository": { | ||
"dependencies": { | ||
"ecurve": "0.2.x", | ||
"ecurve-names": "0.2.x", | ||
"sha256": "0.1.x", | ||
"ripemd160": "0.1.x", | ||
"convert-hex": "0.1.x", | ||
"btc-address": "0.2.x", | ||
"ecdsa": "0.3.x", | ||
"bigi": "0.2.x", | ||
"bs58": "0.2.x" | ||
"ecurve": "~0.3.0", | ||
"ecurve-names": "~0.3.0", | ||
"bigi": "0.2.x" | ||
} | ||
} |
142
README.md
eckey | ||
===== | ||
JavaScript component for Elliptical curve cryptography for crypto currencies such as Litecoin and Bitcoin. | ||
JavaScript component for Elliptical curve cryptography for crypto currencies such as Bitcoin, Litecoin, Dogecoin, etc. | ||
See this article for more details: [bitcoin address](http://procbits.com/2013/08/27/generating-a-bitcoin-address-with-javascript). | ||
Why? | ||
---- | ||
This module provides a convenient way to compute relevant crypto currency operations that adhere to elliptical curve cryptography. To | ||
really understand how private keys, public keys, addresses, and how elliptical curve cryptography works with JavaScript, read this: http://procbits.com/2013/08/27/generating-a-bitcoin-address-with-javascript | ||
Install | ||
------- | ||
Installation | ||
------------ | ||
### Node.js/Browserify | ||
npm install --save eckey | ||
### Component | ||
component install cryptocoinjs/eckey | ||
Usage | ||
----- | ||
### API | ||
### Bower | ||
#### ECKey([bytes], [compressed]) | ||
bower install eckey | ||
Constructor function. | ||
- **bytes**: The private key bytes. Must be 32 bytes in length. Should be an `Array`, `Uint8Array`, or a `Buffer`. | ||
- **compressed**: Specify whether the key should be compressed or not. | ||
### Script | ||
```js | ||
var ECKey = require('eckey'); | ||
var secureRandom = require('secure-random'); | ||
```html | ||
<script src="/path/to/eckey.js"></script> | ||
var bytes = secureRandom(32); //https://github.com/jprichardson/secure-random | ||
var key1 = new ECKey(bytes); | ||
var key2 = ECKey(bytes); //<--- can also use without "new" | ||
var compressedKey = new ECKey(bytes, true); | ||
``` | ||
Note: Previous versions of this module would generate a random array of bytes for you if you didn't pass as input any to the constructor. This behavior has been removed to remove complexity and to ensure that the random generation is done securely. In the past, it wasn't. | ||
Contributors (CryptoCoinJS) | ||
--------------------------- | ||
(if you contribute, add your name to the list and increment the number, if you're a member to the organization, just add your name with a `*`) | ||
#### compressed | ||
- (*) [jprichardson](https://github.com/jprichardson) | ||
- (1) [remiq](https://github.com/remiq) | ||
Get/set whether the point on the curve is compressed. Affects the output of the WIF (wallet import format) and the address. | ||
#### privateKey | ||
Credits | ||
------- | ||
Get/set the private key. When setting, the type can be either an `Array`, `Buffer`, or `Uint8Array`. When getting, the type is always `Buffer`. Setting would be useful if you don't pass a private key to the constructor. | ||
Stefen Thomas wrote the majority of this and Roman Shtylman made significant modifcations. | ||
```js | ||
var ECKey = require('eckey'); | ||
var conv = require('binstring'); | ||
var privateKeyHex = "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd"; | ||
var key = new ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), false); | ||
console.log(key.privatekey.toString('hex')); // => 1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd | ||
var keyCompressed = ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), true); | ||
//nothing changes when compressed | ||
console.log(key.privatekey.toString('hex')); // => 1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd | ||
``` | ||
#### privateExportKey | ||
Get the private key along with a byte for compression if `compressed` is true. i.e. | ||
if compressed | ||
privateExportKey = privateKey + 0x01 | ||
else | ||
privateExportKey = privateKey | ||
This is useful inconjunction with the package [coinstring](https://github.com/cryptocoinjs/coinstring) to generate | ||
[Wallet Import Format](https://en.bitcoin.it/wiki/Wallet_import_format) keys. | ||
```js | ||
var ECKey = require('eckey'); | ||
var conv = require('binstring'); | ||
var privateKeyHex = "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd"; | ||
var key = new ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), false); | ||
console.log(key.privateExportKey.toString('hex')); // => 1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd | ||
var keyCompressed = new ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), true); | ||
//notice the extra "01" at the end? | ||
console.log(key.privateExportKey.toString('hex')); // => 1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd01 | ||
``` | ||
#### publicKey | ||
Get the public key. The type is `Buffer`. | ||
```js | ||
var ECKey = require('eckey'); | ||
var conv = require('binstring'); | ||
var privateKeyHex = "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd"; | ||
var key = new ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), false); | ||
console.log(key.publickey.toString('hex')); | ||
// => 04d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6fbdd594388756a7beaf73b4822bc22d36e9bda7db82df2b8b623673eefc0b7495 | ||
var keyCompressed = ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), true); | ||
console.log(key.publickey.toString('hex')); | ||
// => 03d0988bfa799f7d7ef9ab3de97ef481cd0f75d2367ad456607647edde665d6f6f | ||
``` | ||
#### publicPoint | ||
Get the [Public Key Point](https://github.com/cryptocoinjs/ecurve/blob/master/lib/ecurve.js) on the Ellipitical Curve. | ||
#### toString() | ||
Returns the string representation of the private key. | ||
```js | ||
var ECKey = require('eckey'); | ||
var conv = require('binstring'); | ||
var privateKeyHex = "1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd"; | ||
var key = new ECKey(conv(privateKeyHex, {in: 'hex', out: 'buffer'}), true); | ||
console.log(key.toString()) // => 1184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd | ||
``` | ||
References | ||
---------- | ||
- http://procbits.com/2013/08/27/generating-a-bitcoin-address-with-javascript | ||
- https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/eckey.js | ||
- https://github.com/vbuterin/bitcoinjs-lib/blob/master/src/eckey.js | ||
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
10676
3
7
152
5
88
1
+ Addedecurve@0.3.2(transitive)
+ Addedecurve-names@0.3.0(transitive)
- Removedbs58@0.2.x
- Removedbtc-address@0.2.x
- Removedconvert-hex@0.1.x
- Removedecdsa@0.3.x
- Removedripemd160@0.1.x
- Removedsha256@0.1.x
- Removedbs58@0.2.1(transitive)
- Removedbtc-address@0.2.0(transitive)
- Removedconvert-hex@0.1.0(transitive)
- Removedconvert-string@0.1.0(transitive)
- Removedecdsa@0.3.0(transitive)
- Removedecurve@0.2.0(transitive)
- Removedecurve-names@0.2.1(transitive)
- Removedripemd160@0.1.0(transitive)
- Removedsecure-random@0.1.0(transitive)
- Removedsha256@0.1.1(transitive)
Updatedecurve@~0.3.0
Updatedecurve-names@~0.3.0