Comparing version 0.6.0 to 0.7.0
@@ -0,8 +1,11 @@ | ||
0.7.0 / 2015-12-13 | ||
------------------ | ||
- extracted bitcoinjs-lib `ecdsa`. Fixes a number of issues of security issues. | ||
0.6.0 / 2014-09-30 | ||
------------------ | ||
- dropped alternative curve support, only `secp256k1` for now; if we find we need others in the future we can just grab | ||
- dropped alternative curve support, only `secp256k1` for now; if we find we need others in the future we can just grab | ||
them from Git history | ||
- upgraded from `"ecurve": "^0.6.0"`to `"ecurve": "^1.0.0"` | ||
0.5.3 / 2014-07-04 | ||
@@ -48,3 +51,3 @@ ------------------ | ||
* renamed `calcPubkeyRecoveryParam()` to `calcPubKeyRecoveryParam()`, changed signature | ||
* added Testling support | ||
* added Testling support | ||
@@ -62,3 +65,3 @@ | ||
* moved from 4 spaces to 2 spaces (Node style) | ||
* made class based so that multiple instantiations can be made with different curves, i.e. not just tied to `secp256k1`. Closes #2 | ||
* made class based so that multiple instantiations can be made with different curves, i.e. not just tied to `secp256k1`. Closes #2 | ||
BREAKING CHANGE. Set `ECDSA.ecparams` before using `ECDSA`. | ||
@@ -81,3 +84,3 @@ | ||
------------------ | ||
* changed package name | ||
* changed package name | ||
* removed AMD support | ||
@@ -84,0 +87,0 @@ |
372
lib/ecdsa.js
@@ -1,104 +0,155 @@ | ||
var crypto = require('crypto') | ||
var assert = require('assert') | ||
var createHmac = require('create-hmac') | ||
var typeforce = require('typeforce') | ||
var types = require('./types') | ||
var ecurve = require('ecurve') | ||
var Point = ecurve.Point | ||
var BigInteger = require('bigi') | ||
var ECSignature = require('./ecsignature') | ||
var util = require('./util') | ||
var ZERO = new Buffer([0]) | ||
var ONE = new Buffer([1]) | ||
//dropped support for all others | ||
var curve = ecurve.getCurveByName('secp256k1') | ||
var ecurve = require('ecurve') | ||
var secp256k1 = ecurve.getCurveByName('secp256k1') | ||
// https://tools.ietf.org/html/rfc6979#section-3.2 | ||
function deterministicGenerateK (hash, x, checkSig) { | ||
typeforce(types.tuple( | ||
types.Hash256bit, | ||
types.Buffer256bit, | ||
types.Function | ||
), arguments) | ||
/** | ||
* Calculate pubkey extraction parameter. | ||
* | ||
* When extracting a pubkey from a signature, we have to | ||
* distinguish four different cases. Rather than putting this | ||
* burden on the verifier, Bitcoin includes a 2-bit value with the | ||
* signature. | ||
* | ||
* This function simply tries all four cases and returns the value | ||
* that resulted in a successful pubkey recovery. | ||
*/ | ||
function calcPubKeyRecoveryParam(e, signature, Q) { | ||
for (var i = 0; i < 4; i++) { | ||
var Qprime = recoverPubKey(e, signature, i) | ||
var k = new Buffer(32) | ||
var v = new Buffer(32) | ||
if (Qprime.equals(Q)) { | ||
return i | ||
} | ||
// Step A, ignored as hash already provided | ||
// Step B | ||
v.fill(1) | ||
// Step C | ||
k.fill(0) | ||
// Step D | ||
k = createHmac('sha256', k) | ||
.update(v) | ||
.update(ZERO) | ||
.update(x) | ||
.update(hash) | ||
.digest() | ||
// Step E | ||
v = createHmac('sha256', k).update(v).digest() | ||
// Step F | ||
k = createHmac('sha256', k) | ||
.update(v) | ||
.update(ONE) | ||
.update(x) | ||
.update(hash) | ||
.digest() | ||
// Step G | ||
v = createHmac('sha256', k).update(v).digest() | ||
// Step H1/H2a, ignored as tlen === qlen (256 bit) | ||
// Step H2b | ||
v = createHmac('sha256', k).update(v).digest() | ||
var T = BigInteger.fromBuffer(v) | ||
// Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA | ||
while (T.signum() <= 0 || T.compareTo(secp256k1.n) >= 0 || !checkSig(T)) { | ||
k = createHmac('sha256', k) | ||
.update(v) | ||
.update(ZERO) | ||
.digest() | ||
v = createHmac('sha256', k).update(v).digest() | ||
// Step H1/H2a, again, ignored as tlen === qlen (256 bit) | ||
// Step H2b again | ||
v = createHmac('sha256', k).update(v).digest() | ||
T = BigInteger.fromBuffer(v) | ||
} | ||
throw new Error('Unable to find valid recovery factor') | ||
return T | ||
} | ||
function deterministicGenerateK(hash, D) { | ||
assert(Buffer.isBuffer(hash), 'Hash must be a Buffer, not ' + hash) | ||
assert.equal(hash.length, 32, 'Hash must be 256 bit') | ||
assert(BigInteger.isBigInteger(D, true), 'Private key must be a BigInteger') | ||
var N_OVER_TWO = secp256k1.n.shiftRight(1) | ||
var x = D.toBuffer(32) | ||
var k = new Buffer(32) | ||
var v = new Buffer(32) | ||
k.fill(0) | ||
v.fill(1) | ||
function sign (hash, d) { | ||
typeforce(types.tuple(types.Hash256bit, types.BigInt), arguments) | ||
k = util.hmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k) | ||
v = util.hmacSHA256(v, k) | ||
var x = d.toBuffer(32) | ||
var e = BigInteger.fromBuffer(hash) | ||
var n = secp256k1.n | ||
var G = secp256k1.G | ||
k = util.hmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k) | ||
v = util.hmacSHA256(v, k) | ||
v = util.hmacSHA256(v, k) | ||
var r, s | ||
deterministicGenerateK(hash, x, function (k) { | ||
var Q = G.multiply(k) | ||
var n = curve.n | ||
var kB = BigInteger.fromBuffer(v).mod(n) | ||
assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value') | ||
assert(kB.compareTo(curve.n) < 0, 'Invalid k value') | ||
if (secp256k1.isInfinity(Q)) return false | ||
return kB | ||
r = Q.affineX.mod(n) | ||
if (r.signum() === 0) return false | ||
s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) | ||
if (s.signum() === 0) return false | ||
return true | ||
}) | ||
// enforce low S values, see bip62: 'low s values in signatures' | ||
if (s.compareTo(N_OVER_TWO) > 0) { | ||
s = n.subtract(s) | ||
} | ||
return new ECSignature(r, s) | ||
} | ||
function parseSig(buffer) { | ||
assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence') | ||
assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length') | ||
function verify (hash, signature, Q) { | ||
typeforce(types.tuple( | ||
types.Hash256bit, | ||
types.ECSignature, | ||
types.ECPoint | ||
), arguments) | ||
assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer') | ||
var rLen = buffer.readUInt8(3) | ||
var rB = buffer.slice(4, 4 + rLen) | ||
var n = secp256k1.n | ||
var G = secp256k1.G | ||
var offset = 4 + rLen | ||
assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)') | ||
var sLen = buffer.readUInt8(1 + offset) | ||
var sB = buffer.slice(2 + offset) | ||
offset += 2 + sLen | ||
var r = signature.r | ||
var s = signature.s | ||
assert.equal(offset, buffer.length, 'Invalid DER encoding') | ||
// 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] | ||
if (r.signum() <= 0 || r.compareTo(n) >= 0) return false | ||
if (s.signum() <= 0 || s.compareTo(n) >= 0) return false | ||
return { r: BigInteger.fromDERInteger(rB), s: BigInteger.fromDERInteger(sB) } | ||
} | ||
// 1.4.2 H = Hash(M), already done by the user | ||
// 1.4.3 e = H | ||
var e = BigInteger.fromBuffer(hash) | ||
function parseSigCompact(buffer) { | ||
assert.equal(buffer.length, 65, 'Invalid signature length') | ||
var i = buffer.readUInt8(0) - 27 | ||
// Compute s^-1 | ||
var sInv = s.modInverse(n) | ||
// At most 3 bits | ||
assert.equal(i, i & 7, 'Invalid signature parameter') | ||
var compressed = !!(i & 4) | ||
// 1.4.4 Compute u1 = es^−1 mod n | ||
// u2 = rs^−1 mod n | ||
var u1 = e.multiply(sInv).mod(n) | ||
var u2 = r.multiply(sInv).mod(n) | ||
// Recovery param only | ||
i = i & 3 | ||
// 1.4.5 Compute R = (xR, yR) | ||
// R = u1G + u2Q | ||
var R = G.multiplyTwo(u1, Q, u2) | ||
var r = BigInteger.fromBuffer(buffer.slice(1, 33)) | ||
var s = BigInteger.fromBuffer(buffer.slice(33)) | ||
// 1.4.5 (cont.) Enforce R is not at infinity | ||
if (secp256k1.isInfinity(R)) return false | ||
return { | ||
signature: { | ||
r: r, | ||
s: s | ||
}, | ||
i: i, | ||
compressed: compressed | ||
} | ||
// 1.4.6 Convert the field element R.x to an integer | ||
var xR = R.affineX | ||
// 1.4.7 Set v = xR mod n | ||
var v = xR.mod(n) | ||
// 1.4.8 If v = r, output "valid", and if v != r, output "invalid" | ||
return v.equals(r) | ||
} | ||
@@ -114,13 +165,16 @@ | ||
*/ | ||
function recoverPubKey(e, signature, i) { | ||
assert.strictEqual(i & 3, i, 'Recovery param is more than two bits') | ||
function recoverPubKey (e, signature, i) { | ||
typeforce(types.tuple( | ||
types.BigInt, | ||
types.ECSignature, | ||
types.UInt2 | ||
), arguments) | ||
var n = curve.n | ||
var G = curve.G | ||
var n = secp256k1.n | ||
var G = secp256k1.G | ||
var r = signature.r | ||
var s = signature.s | ||
assert(r.signum() > 0 && r.compareTo(n) < 0, 'Invalid r value') | ||
assert(s.signum() > 0 && s.compareTo(n) < 0, 'Invalid s value') | ||
if (r.signum() <= 0 || r.compareTo(n) >= 0) throw new Error('Invalid r value') | ||
if (s.signum() <= 0 || s.compareTo(n) >= 0) throw new Error('Invalid s value') | ||
@@ -136,8 +190,11 @@ // A set LSB signifies that the y-coordinate is odd | ||
var x = isSecondKey ? r.add(n) : r | ||
var R = curve.pointFromX(isYOdd, x) | ||
var R = secp256k1.pointFromX(isYOdd, x) | ||
// 1.4 Check that nR is at infinity | ||
var nR = R.multiply(n) | ||
assert(curve.isInfinity(nR), 'nR is not a valid curve point') | ||
if (!secp256k1.isInfinity(nR)) throw new Error('nR is not a valid curve point') | ||
// Compute r^-1 | ||
var rInv = r.modInverse(n) | ||
// Compute -e from e | ||
@@ -148,135 +205,46 @@ var eNeg = e.negate().mod(n) | ||
// Q = r^-1 (sR + -eG) | ||
var rInv = r.modInverse(n) | ||
var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv) | ||
curve.validate(Q) | ||
secp256k1.validate(Q) | ||
return Q | ||
} | ||
/** | ||
* Calculate pubkey extraction parameter. | ||
* | ||
* When extracting a pubkey from a signature, we have to | ||
* distinguish four different cases. Rather than putting this | ||
* burden on the verifier, Bitcoin includes a 2-bit value with the | ||
* signature. | ||
* | ||
* This function simply tries all four cases and returns the value | ||
* that resulted in a successful pubkey recovery. | ||
*/ | ||
function calcPubKeyRecoveryParam (e, signature, Q) { | ||
typeforce(types.tuple( | ||
types.BigInt, | ||
types.ECSignature, | ||
types.ECPoint | ||
), arguments) | ||
function serializeSig(signature) { | ||
//var rBa = r.toByteArraySigned(); | ||
//var sBa = s.toByteArraySigned(); | ||
var rBa = signature.r.toDERInteger() | ||
var sBa = signature.s.toDERInteger() | ||
for (var i = 0; i < 4; i++) { | ||
var Qprime = recoverPubKey(e, signature, i) | ||
var sequence = []; | ||
sequence.push(0x02); // INTEGER | ||
sequence.push(rBa.length); | ||
sequence = sequence.concat(rBa); | ||
sequence.push(0x02); // INTEGER | ||
sequence.push(sBa.length); | ||
sequence = sequence.concat(sBa); | ||
sequence.unshift(sequence.length); | ||
sequence.unshift(0x30); // SEQUENCE | ||
return sequence; | ||
} | ||
function serializeSigCompact(signature, i, compressed) { | ||
if (compressed) { | ||
i += 4 | ||
// 1.6.2 Verify Q | ||
if (Qprime.equals(Q)) { | ||
return i | ||
} | ||
} | ||
i += 27 | ||
var buffer = new Buffer(65) | ||
buffer.writeUInt8(i, 0) | ||
signature.r.toBuffer(32).copy(buffer, 1) | ||
signature.s.toBuffer(32).copy(buffer, 33) | ||
return buffer | ||
throw new Error('Unable to find valid recovery factor') | ||
} | ||
function sign(hash, privateKey) { | ||
if (Buffer.isBuffer(privateKey)) | ||
var D = BigInteger.fromBuffer(privateKey) | ||
else | ||
var D = privateKey //big integer for legacy compatiblity | ||
var k = deterministicGenerateK(hash, D) | ||
var n = curve.n | ||
var G = curve.G | ||
var Q = G.multiply(k) | ||
var e = BigInteger.fromBuffer(hash) | ||
var r = Q.affineX.mod(n) | ||
assert.notEqual(r.signum(), 0, 'Invalid R value') | ||
var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n) | ||
assert.notEqual(s.signum(), 0, 'Invalid S value') | ||
var N_OVER_TWO = n.shiftRight(1) | ||
// enforce low S values, see bip62: 'low s values in signatures' | ||
if (s.compareTo(N_OVER_TWO) > 0) { | ||
s = n.subtract(s) | ||
} | ||
return {r: r, s: s} | ||
} | ||
function verify(hash, signature, pubkey) { | ||
assert(signature.r && signature.s, "Invalid signature.") | ||
var Q; | ||
if (Buffer.isBuffer(pubkey)) { | ||
Q = Point.decodeFrom(curve, pubkey); | ||
} else { | ||
throw new Error("Invalid format for pubkey value, must be Buffer"); | ||
} | ||
var e = BigInteger.fromBuffer(hash); | ||
return verifyRaw(e, {r: signature.r, s: signature.s}, Q) | ||
} | ||
function verifyRaw(e, signature, Q) { | ||
var n = curve.n | ||
var G = curve.G | ||
var r = signature.r | ||
var s = signature.s | ||
// 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] | ||
if (r.signum() <= 0 || r.compareTo(n) >= 0) return false | ||
if (s.signum() <= 0 || s.compareTo(n) >= 0) return false | ||
// c = s^-1 mod n | ||
var c = s.modInverse(n) | ||
// 1.4.4 Compute u1 = es^−1 mod n | ||
// u2 = rs^−1 mod n | ||
var u1 = e.multiply(c).mod(n) | ||
var u2 = r.multiply(c).mod(n) | ||
// 1.4.5 Compute R = (xR, yR) = u1G + u2Q | ||
var R = G.multiplyTwo(u1, Q, u2) | ||
var v = R.affineX.mod(n) | ||
// 1.4.5 (cont.) Enforce R is not at infinity | ||
if (curve.isInfinity(R)) return false | ||
// 1.4.8 If v = r, output "valid", and if v != r, output "invalid" | ||
return v.equals(r) | ||
} | ||
module.exports = { | ||
curve: curve, | ||
calcPubKeyRecoveryParam: calcPubKeyRecoveryParam, | ||
deterministicGenerateK: deterministicGenerateK, | ||
parseSig: parseSig, | ||
parseSigCompact: parseSigCompact, | ||
recoverPubKey: recoverPubKey, | ||
serializeSig: serializeSig, | ||
serializeSigCompact: serializeSigCompact, | ||
sign: sign, | ||
verify: verify, | ||
verifyRaw: verifyRaw | ||
curve: secp256k1 | ||
} | ||
var crypto = require('crypto') | ||
function hmacSHA256 (v, k) { | ||
return crypto.createHmac('sha256', k).update(v).digest() | ||
} | ||
module.exports = { | ||
hmacSHA256: hmacSHA256 | ||
} | ||
function hmacSHA256(v, k) { | ||
return crypto.createHmac('sha256', k).update(v).digest() | ||
} |
{ | ||
"name": "ecdsa", | ||
"version": "0.6.0", | ||
"description": "Elliptical Curve Cryptography Digital Signing", | ||
"version": "0.7.0", | ||
"description": "Elliptic Curve Cryptography Digital Signing", | ||
"keywords": [ | ||
@@ -10,3 +10,3 @@ "cryptography", | ||
"litecoin", | ||
"elliptical", | ||
"elliptic", | ||
"curve", | ||
@@ -18,9 +18,10 @@ "digital", | ||
"devDependencies": { | ||
"mocha": "1.*", | ||
"terst": "~0.1.0", | ||
"coveralls": "^2.10.0", | ||
"electron-mocha": "^0.6.3", | ||
"istanbul": "^0.2.10", | ||
"mocha": "^2.3.4", | ||
"mocha-lcov-reporter": "0.0.1", | ||
"istanbul": "^0.2.10", | ||
"secure-random": "^0.2.1", | ||
"mochify": "^1.1.3" | ||
"sinon": "^1.17.2", | ||
"standard": "^5.4.1" | ||
}, | ||
@@ -31,8 +32,14 @@ "repository": { | ||
}, | ||
"main": "./lib/ecdsa.js", | ||
"license": "MIT", | ||
"main": "./lib/index.js", | ||
"dependencies": { | ||
"bigi": "^1.2.1", | ||
"ecurve": "^1.0.0" | ||
"bip66": "^1.1.0", | ||
"create-hmac": "^1.1.4", | ||
"ecurve": "^1.0.0", | ||
"typeforce": "^1.6.1" | ||
}, | ||
"scripts": { | ||
"browser-test": "electron-mocha", | ||
"standard": "standard", | ||
"test": "mocha --ui bdd", | ||
@@ -39,0 +46,0 @@ "unit": "./node_modules/.bin/mocha", |
@@ -7,16 +7,6 @@ ecdsa | ||
JavaScript component for Elliptical Curve Cryptography signing and verification. This module is important to sign transactions. Works | ||
JavaScript component for Elliptic Curve Cryptography signing and verification. This module is important to sign transactions. Works | ||
with both Node.js and the browser. | ||
### Official documentation: | ||
http://cryptocoinjs.com/modules/crypto/ecdsa/ | ||
**This has been extracted from [bitcoinjs-lib@2.1.4](https://github.com/bitcoinjs/bitcoinjs-lib/tree/v2.1.4) and will track | ||
it for the foreseeable future.** |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
15063
11
0
322
5
8
12
2
+ Addedbip66@^1.1.0
+ Addedcreate-hmac@^1.1.4
+ Addedtypeforce@^1.6.1
+ Addedbip66@1.1.5(transitive)
+ Addedcipher-base@1.0.4(transitive)
+ Addedcreate-hash@1.2.0(transitive)
+ Addedcreate-hmac@1.1.7(transitive)
+ Addedhash-base@3.1.0(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedmd5.js@1.3.5(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedripemd160@2.0.2(transitive)
+ Addedsha.js@2.4.11(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedtypeforce@1.18.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)