Merge pull request #679 from eordano/feature/ecdsabackport

Backport changes to ECDSA from fullnode
This commit is contained in:
Manuel Aráoz 2014-12-09 15:09:33 -03:00
commit fb1978c5f5
6 changed files with 510 additions and 85 deletions

View File

@ -1,5 +1,9 @@
Copyright (c) 2013-2014 BitPay, Inc.
Parts of this software are based on fullnode
Copyright (c) 2014 Ryan X. Charles
Copyright (c) 2014 reddit, Inc.
Parts of this software are based on BitcoinJS
Copyright (c) 2011 Stefan Thomas <justmoon@members.fsf.org>

View File

@ -2,22 +2,21 @@
var _BN = require('bn.js');
var BN = function BN_extended(n) {
if (!(this instanceof BN_extended)) {
return new BN(n);
var BN = function BN(n, base) {
if (!(this instanceof BN)) {
return new BN(n, base);
}
arguments[0] = n;
return _BN.apply(this, arguments);
_BN.apply(this, arguments);
};
module.exports = BN;
BN.prototype = _BN.prototype;
var reversebuf = function(buf, nbuf) {
var reversebuf = function(buf) {
var buf2 = new Buffer(buf.length);
for (var i = 0; i < buf.length; i++) {
nbuf[i] = buf[buf.length-1-i];
buf2[i] = buf[buf.length-1-i];
}
return buf2;
};
BN.prototype.toJSON = function() {
@ -30,6 +29,16 @@ BN.prototype.fromJSON = function(str) {
return this;
};
BN.prototype.fromNumber = function(n) {
var bn = BN(n);
bn.copy(this);
return this;
};
BN.prototype.toNumber = function() {
return parseInt(this['toString'](10), 10);
};
BN.prototype.fromString = function(str) {
var bn = BN(str);
bn.copy(this);
@ -38,9 +47,7 @@ BN.prototype.fromString = function(str) {
BN.fromBuffer = function(buf, opts) {
if (typeof opts !== 'undefined' && opts.endian === 'little') {
var nbuf = new Buffer(buf.length);
reversebuf(buf, nbuf);
buf = nbuf;
buf = reversebuf(buf);
}
var hex = buf.toString('hex');
var bn = new BN(hex, 16);
@ -70,7 +77,6 @@ BN.prototype.toBuffer = function(opts) {
else if (natlen < opts.size) {
var rbuf = new Buffer(opts.size);
//rbuf.fill(0);
for (var i = 0; i < buf.length; i++)
rbuf[rbuf.length-1-i] = buf[buf.length-1-i];
for (var i = 0; i < opts.size - natlen; i++)
@ -84,31 +90,116 @@ BN.prototype.toBuffer = function(opts) {
}
if (typeof opts !== 'undefined' && opts.endian === 'little') {
var nbuf = new Buffer(buf.length);
reversebuf(buf, nbuf);
buf = nbuf;
buf = reversebuf(buf);
}
return buf;
};
// signed magnitude buffer
// most significant bit represents sign (0 = positive, -1 = negative)
BN.prototype.fromSM = function(buf, opts) {
if (buf.length === 0)
this.fromBuffer(new Buffer([0]));
var endian = 'big';
if (opts)
endian = opts.endian;
if (endian == 'little')
buf = reversebuf(buf);
if (buf[0] & 0x80) {
buf[0] = buf[0] & 0x7f;
this.fromBuffer(buf);
this.neg().copy(this);
} else {
this.fromBuffer(buf);
}
return this;
};
BN.prototype.toSM = function(opts) {
var endian = 'big';
if (opts)
endian = opts.endian;
var buf;
if (this.cmp(0) == -1) {
buf = this.neg().toBuffer();
if (buf[0] & 0x80)
buf = Buffer.concat([new Buffer([0x80]), buf]);
else
buf[0] = buf[0] | 0x80;
} else {
buf = this.toBuffer();
if (buf[0] & 0x80)
buf = Buffer.concat([new Buffer([0x00]), buf]);
}
if (buf.length === 1 & buf[0] === 0)
buf = new Buffer([]);
if (endian == 'little')
buf = reversebuf(buf);
return buf;
};
// This is analogous to the constructor for CScriptNum in bitcoind. Many ops in
// bitcoind's script interpreter use CScriptNum, which is not really a proper
// bignum. Instead, an error is thrown if trying to input a number bigger than
// 4 bytes. We copy that behavior here.
BN.prototype.fromCScriptNumBuffer = function(buf, fRequireMinimal) {
var nMaxNumSize = 4;
if (buf.length > nMaxNumSize)
throw new Error('script number overflow');
if (fRequireMinimal && buf.length > 0) {
// Check that the number is encoded with the minimum possible
// number of bytes.
//
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if ((buf[buf.length - 1] & 0x7f) === 0) {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) {
throw new Error("non-minimally encoded script number");
}
}
}
return this.fromSM(buf, {endian: 'little'});
};
// The corollary to the above, with the notable exception that we do not throw
// an error if the output is larger than four bytes. (Which can happen if
// performing a numerical operation that results in an overflow to more than 4
// bytes).
BN.prototype.toCScriptNumBuffer = function(buf) {
return this.toSM({endian: 'little'});
};
function decorate(name) {
BN.prototype['_' + name] = _BN.prototype[name];
BN.prototype['_' + name] = BN.prototype[name];
var f = function(b) {
if (typeof b === 'string')
b = new _BN(b);
b = new BN(b);
else if (typeof b === 'number')
b = new _BN(b.toString());
b = new BN(b.toString());
return this['_' + name](b);
};
BN.prototype[name] = f;
};
_BN.prototype.gt = function(b) {
BN.prototype.gt = function(b) {
return this.cmp(b) > 0;
};
_BN.prototype.lt = function(b) {
BN.prototype.lt = function(b) {
return this.cmp(b) < 0;
};
@ -121,8 +212,4 @@ decorate('cmp');
decorate('gt');
decorate('lt');
BN.prototype.toNumber = function() {
return parseInt(this['toString'](10), 10);
};
module.exports = BN;

View File

@ -3,9 +3,10 @@
var BN = require('./bn');
var Point = require('./point');
var Signature = require('./signature');
var Random = require('./random');
var PublicKey = require('../publickey');
var PrivateKey = require('../privatekey');
var Random = require('./random');
var Hash = require('./hash');
var ECDSA = function ECDSA(obj) {
if (!(this instanceof ECDSA)) {
@ -18,16 +19,17 @@ var ECDSA = function ECDSA(obj) {
ECDSA.prototype.set = function(obj) {
this.hashbuf = obj.hashbuf || this.hashbuf;
this.endian = obj.endian || this.endian; //the endianness of hashbuf
this.privkey = obj.privkey || this.privkey;
this.pubkey = obj.pubkey || this.pubkey;
this.pubkey = obj.pubkey || (this.privkey ? this.privkey.publicKey : this.pubkey);
this.sig = obj.sig || this.sig;
this.k = obj.k || this.k;
this.verified = obj.verified || this.verified;
return this;
};
ECDSA.prototype.privkey2pubkey = function(){
this.pubkey = PublicKey.fromPrivateKey(this.privkey);
ECDSA.prototype.privkey2pubkey = function() {
this.pubkey = this.privkey.toPublicKey();
};
ECDSA.prototype.calci = function() {
@ -37,9 +39,10 @@ ECDSA.prototype.calci = function() {
try {
Qprime = this.sig2pubkey();
} catch (e) {
console.log(e);
console.error(e);
continue;
}
if (Qprime.point.eq(this.pubkey.point)) {
this.sig.compressed = this.pubkey.compressed;
return this;
@ -52,16 +55,21 @@ ECDSA.prototype.calci = function() {
ECDSA.prototype.fromString = function(str) {
var obj = JSON.parse(str);
if (obj.hashbuf)
if (obj.hashbuf) {
this.hashbuf = new Buffer(obj.hashbuf, 'hex');
if (obj.pubkey)
}
if (obj.pubkey) {
this.pubkey = PublicKey.fromString(obj.pubkey);
if (obj.privkey)
}
if (obj.privkey) {
this.privkey = PrivateKey.fromString(obj.privkey);
if (obj.sig)
}
if (obj.sig) {
this.sig = Signature().fromString(obj.sig);
if (obj.k)
}
if (obj.k) {
this.k = BN(obj.k, 10);
}
return this;
};
@ -75,6 +83,37 @@ ECDSA.prototype.randomK = function() {
return this;
};
// https://tools.ietf.org/html/rfc6979#section-3.2
ECDSA.prototype.deterministicK = function(badrs) {
var v = new Buffer(32);
v.fill(0x01);
var k = new Buffer(32);
k.fill(0x00);
var x = this.privkey.bn.toBuffer({size: 32});
k = Hash.sha256hmac(Buffer.concat([v, new Buffer([0x00]), x, this.hashbuf]), k);
v = Hash.sha256hmac(v, k);
k = Hash.sha256hmac(Buffer.concat([v, new Buffer([0x01]), x, this.hashbuf]), k);
v = Hash.sha256hmac(v, k);
v = Hash.sha256hmac(v, k);
var T = BN().fromBuffer(v);
var N = Point.getN();
// if r or s were invalid when this function was used in signing,
// we do not want to actually compute r, s here for efficiency, so,
// we can increment badrs. explained at end of RFC 6979 section 3.2
if (typeof badrs === 'undefined')
badrs = 0;
// also explained in 3.2, we must ensure T is in the proper range (0, N)
for (var i = 0; i < badrs || !(T.lt(N) && T.gt(0)); i++) {
k = Hash.sha256hmac(Buffer.concat([v, new Buffer([0x00])]), k);
v = Hash.sha256hmac(v, k);
T = BN().fromBuffer(v);
}
this.k = T;
return this;
};
// Information about public key recovery:
// https://bitcointalk.org/index.php?topic=6430.0
// http://stackoverflow.com/questions/19665491/how-do-i-get-an-ecdsa-public-key-from-just-a-bitcoin-signature-sec1-4-1-6-k
@ -123,8 +162,9 @@ ECDSA.prototype.sig2pubkey = function() {
};
ECDSA.prototype.sigError = function() {
if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32)
if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) {
return 'hashbuf must be a 32 byte buffer';
}
var r = this.sig.r;
var s = this.sig.s;
@ -132,7 +172,7 @@ ECDSA.prototype.sigError = function() {
|| !(s.gt(0) && s.lt(Point.getN())))
return 'r and s not in range';
var e = BN().fromBuffer(this.hashbuf);
var e = BN().fromBuffer(this.hashbuf, this.endian ? {endian: this.endian} : undefined);
var n = Point.getN();
var sinv = s.invm(n);
var u1 = sinv.mul(e).mod(n);
@ -151,11 +191,8 @@ ECDSA.prototype.sigError = function() {
ECDSA.prototype.sign = function() {
var hashbuf = this.hashbuf;
var privkey = this.privkey;
var k = this.k;
var d = privkey.bn;
if (!k)
throw new Error('You must specify k - perhaps you should run signRandomK instead');
var d = privkey.bn;
if (!hashbuf || !privkey || !d)
throw new Error('invalid parameters');
@ -165,60 +202,79 @@ ECDSA.prototype.sign = function() {
var N = Point.getN();
var G = Point.getG();
var e = BN().fromBuffer(hashbuf);
var e = BN().fromBuffer(hashbuf, this.endian ? {endian: this.endian} : undefined);
// try different values of k until r, s are valid
var badrs = 0;
do {
if (!this.k || badrs > 0)
this.deterministicK(badrs);
badrs++;
var k = this.k;
var Q = G.mul(k);
var r = Q.x.mod(N);
var s = k.invm(N).mul(e.add(d.mul(r))).mod(N);
} while (r.cmp(0) <= 0 || s.cmp(0) <= 0);
this.sig = new Signature({r: r, s: s, compressed: this.privkey.compressed});
return this.sig;
//enforce low s
//see BIP 62, "low S values in signatures"
if (s.gt(BN().fromBuffer(new Buffer('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex')))) {
s = Point.getN().sub(s);
}
this.sig = new Signature({r: r, s: s, compressed: this.pubkey.compressed});
return this;
};
ECDSA.prototype.signRandomK = function() {
var k = this.randomK();
this.randomK();
return this.sign();
};
ECDSA.prototype.toString = function() {
var obj = {};
if (this.hashbuf)
if (this.hashbuf) {
obj.hashbuf = this.hashbuf.toString('hex');
if (this.pubkey)
obj.pubkey = this.pubkey.toString();
if (this.privkey)
}
if (this.privkey) {
obj.privkey = this.privkey.toString();
if (this.keypair)
obj.keypair = this.keypair.toString();
if (this.sig)
}
if (this.pubkey) {
obj.pubkey = this.pubkey.toString();
}
if (this.sig) {
obj.sig = this.sig.toString();
if (this.k)
}
if (this.k) {
obj.k = this.k.toString();
}
return JSON.stringify(obj);
};
ECDSA.prototype.verify = function() {
if (!this.sigError())
return true;
this.verified = true;
else
return false;
this.verified = false;
return this;
};
ECDSA.sign = function(hashbuf, privkey) {
ECDSA.sign = function(hashbuf, privkey, endian) {
console.log('Signing', hashbuf, 'with key', privkey);
return ECDSA().set({
hashbuf: hashbuf,
endian: endian,
privkey: privkey
}).signRandomK();
}).sign().sig;
};
ECDSA.verify = function(hashbuf, sig, pubkey) {
ECDSA.verify = function(hashbuf, sig, pubkey, endian) {
return ECDSA().set({
hashbuf: hashbuf,
endian: endian,
sig: sig,
pubkey: pubkey
}).verify();
}).verify().verified;
};
module.exports = ECDSA;

View File

@ -168,4 +168,9 @@ Signature.prototype.toString = function() {
return buf.toString('hex');
};
Signature.SIGHASH_ALL = 0x01;
Signature.SIGHASH_NONE = 0x02;
Signature.SIGHASH_SINGLE = 0x03;
Signature.SIGHASH_ANYONECANPAY = 0x80;
module.exports = Signature;

View File

@ -1,24 +1,23 @@
'use strict';
var ECDSA = require('../../lib/crypto/ecdsa');
var Hash = require('../../lib/crypto/hash');
var Privkey = require('../../lib/privatekey');
var Pubkey = require('../../lib/publickey');
var Signature = require('../../lib/crypto/signature');
var Point = require('../../lib/crypto/point');
var BN = require('../../lib/crypto/bn');
var point = require('../../lib/crypto/point');
var should = require('chai').should();
var bitcore = require('../..');
var ECDSA = bitcore.crypto.ECDSA;
var Hash = bitcore.crypto.Hash;
var Signature = bitcore.crypto.Signature;
var PrivateKey = bitcore.PrivateKey;
var PublicKey = bitcore.PublicKey;
var BN = bitcore.crypto.BN;
var vectors = require('./vectors/ecdsa');
describe('ECDSA', function() {
describe("ECDSA", function() {
it('should create a blank ecdsa', function() {
var ecdsa = new ECDSA();
should.exist(ecdsa);
});
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('test data'));
ecdsa.privkey = new PrivateKey(BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex')));
ecdsa.privkey = new Privkey(BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex')));
ecdsa.privkey2pubkey();
describe('#set', function() {
@ -42,11 +41,12 @@ describe('ECDSA', function() {
var hashbuf = Hash.sha256(new Buffer('some data'));
var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10);
var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10);
var ecdsa = new ECDSA();
ecdsa.privkey = PrivateKey(BN().fromBuffer(Hash.sha256(new Buffer('test'))));
ecdsa.privkey2pubkey();
ecdsa.hashbuf = hashbuf;
ecdsa.sig = new Signature({r: r, s: s});
var ecdsa = new ECDSA({
privkey: Privkey(BN().fromBuffer(Hash.sha256(new Buffer('test')))),
hashbuf: hashbuf,
sig: new Signature({r: r, s: s})
});
ecdsa.calci();
ecdsa.sig.i.should.equal(1);
});
@ -60,7 +60,6 @@ describe('ECDSA', function() {
var ecdsa2 = new ECDSA();
ecdsa2.fromString(str);
should.exist(ecdsa.hashbuf);
should.exist(ecdsa.pubkey);
should.exist(ecdsa.privkey);
});
@ -85,11 +84,59 @@ describe('ECDSA', function() {
});
describe('#deterministicK', function() {
it('should generate the same deterministic k', function() {
ecdsa.deterministicK();
ecdsa.k.toBuffer().toString('hex').should.equal('fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e');
});
it('should generate the same deterministic k if badrs is set', function() {
ecdsa.deterministicK(0);
ecdsa.k.toBuffer().toString('hex').should.equal('fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e');
ecdsa.deterministicK(1);
ecdsa.k.toBuffer().toString('hex').should.not.equal('fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e');
ecdsa.k.toBuffer().toString('hex').should.equal('6f4dcca6fa7a137ae9d110311905013b3c053c732ad18611ec2752bb3dcef9d8');
});
it('should compute this test vector correctly', function() {
// test fixture from bitcoinjs
// https://github.com/bitcoinjs/bitcoinjs-lib/blob/10630873ebaa42381c5871e20336fbfb46564ac8/test/fixtures/ecdsa.json#L6
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('Everything should be made as simple as possible, but not simpler.'));
ecdsa.privkey = Privkey(BN(1));
ecdsa.privkey2pubkey();
ecdsa.deterministicK();
ecdsa.k.toBuffer().toString('hex').should.equal('ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5');
ecdsa.sign();
ecdsa.sig.r.toString().should.equal('23362334225185207751494092901091441011938859014081160902781146257181456271561');
ecdsa.sig.s.toString().should.equal('50433721247292933944369538617440297985091596895097604618403996029256432099938');
});
});
describe('#sig2pubkey', function() {
it('should calculate the correct public key', function() {
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
ecdsa.sign();
ecdsa.sig.i = 0;
var pubkey = ecdsa.sig2pubkey();
pubkey.point.eq(ecdsa.pubkey.point).should.equal(true);
});
it('should calculate the correct public key for this signature with low s', function() {
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
ecdsa.sig = Signature().fromString('3045022100ec3cfe0e335791ad278b4ec8eac93d0347a97877bb1d54d35d189e225c15f6650220278cf15b05ce47fb37d2233802899d94c774d5480bba9f0f2d996baa13370c43');
ecdsa.sig.i = 0;
var pubkey = ecdsa.sig2pubkey();
pubkey.point.eq(ecdsa.pubkey.point).should.equal(true);
});
it('should calculate the correct public key for this signature with high s', function() {
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
ecdsa.sign();
ecdsa.sig = Signature().fromString('3046022100ec3cfe0e335791ad278b4ec8eac93d0347a97877bb1d54d35d189e225c15f665022100d8730ea4fa31b804c82ddcc7fd766269f33a079ea38e012c9238f2e2bcff34fe');
ecdsa.sig.i = 1;
var pubkey = ecdsa.sig2pubkey();
pubkey.point.eq(ecdsa.pubkey.point).should.equal(true);
@ -104,22 +151,28 @@ describe('ECDSA', function() {
ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer');
});
it.skip('should return an error if the pubkey is invalid', function() {
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('test'));
ecdsa.sigError().indexOf("Invalid pubkey").should.equal(0);
});
it('should return an error if r, s are invalid', function() {
var ecdsa = new ECDSA();
ecdsa.hashbuf = Hash.sha256(new Buffer('test'));
var pk = PublicKey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
var pk = Pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', 'hex'));
ecdsa.pubkey = pk;
ecdsa.sig = new Signature();
ecdsa.sig.r = BN(0);
ecdsa.sig.s = BN(0);
ecdsa.sigError().should.equal('r and s not in range');
ecdsa.sigError().should.equal("r and s not in range");
});
it('should return an error if the signature is incorrect', function() {
ecdsa.sig = new Signature();
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.sig.r = ecdsa.sig.r.add(BN(1));
ecdsa.sigError().should.equal('Invalid signature');
ecdsa.sigError().should.equal("Invalid signature");
});
});
@ -129,13 +182,12 @@ describe('ECDSA', function() {
it('should create a valid signature', function() {
ecdsa.randomK();
ecdsa.sign();
ecdsa.verify().should.equal(true);
ecdsa.verify().verified.should.equal(true);
});
it('should should throw an error if hashbuf is not 32 bytes', function() {
var ecdsa2 = ECDSA().set({
hashbuf: ecdsa.hashbuf.slice(0, 31),
pubkey: ecdsa.pubkey,
privkey: ecdsa.privkey
});
ecdsa2.randomK();
@ -144,13 +196,29 @@ describe('ECDSA', function() {
}).should.throw('hashbuf must be a 32 byte buffer');
});
it('should default to deterministicK', function() {
var ecdsa2 = new ECDSA(ecdsa);
ecdsa2.k = undefined;
var called = 0;
var deterministicK = ecdsa2.deterministicK.bind(ecdsa2);
ecdsa2.deterministicK = function() {
deterministicK();
called++;
};
ecdsa2.sign();
called.should.equal(1);
});
});
describe('#signRandomK', function() {
it('should produce a signature', function() {
it('should produce a signature, and be different when called twice', function() {
ecdsa.signRandomK();
should.exist(ecdsa.sig);
var ecdsa2 = ECDSA(ecdsa);
ecdsa2.signRandomK();
ecdsa.sig.toString().should.not.equal(ecdsa2.sig.toString());
});
});
@ -169,12 +237,12 @@ describe('ECDSA', function() {
it('should verify a signature that was just signed', function() {
ecdsa.sig = new Signature();
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.verify().should.equal(true);
ecdsa.verify().verified.should.equal(true);
});
it('should verify this known good signature', function() {
ecdsa.signRandomK();
ecdsa.verify().should.equal(true);
ecdsa.verify().verified.should.equal(true);
});
});
@ -197,6 +265,53 @@ describe('ECDSA', function() {
ECDSA.verify(ecdsa.hashbuf, fakesig, ecdsa.pubkey).should.equal(false);
});
it('should work with big and little endian', function() {
var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey, 'big');
ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey, 'big').should.equal(true);
ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey, 'little').should.equal(false);
sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey, 'little');
ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey, 'big').should.equal(false);
ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey, 'little').should.equal(true);
});
});
describe('vectors', function() {
vectors.valid.forEach(function(obj, i) {
it('should validate valid vector ' + i, function() {
var ecdsa = ECDSA().set({
privkey: Privkey(BN().fromBuffer(new Buffer(obj.d, 'hex'))),
k: BN().fromBuffer(new Buffer(obj.k, 'hex')),
hashbuf: Hash.sha256(new Buffer(obj.message)),
sig: Signature().set({
r: BN(obj.signature.r),
s: BN(obj.signature.s),
i: obj.i
})
});
var ecdsa2 = ECDSA(ecdsa);
ecdsa2.k = undefined;
ecdsa2.sign();
ecdsa2.calci();
ecdsa2.k.toString().should.equal(ecdsa.k.toString());
ecdsa2.sig.toString().should.equal(ecdsa.sig.toString());
ecdsa2.sig.i.should.equal(ecdsa.sig.i);
ecdsa.verify().verified.should.equal(true);
});
});
vectors.invalid.sigError.forEach(function(obj, i) {
it('should validate invalid.sigError vector ' + i + ': ' + obj.description, function() {
var ecdsa = ECDSA().set({
pubkey: Pubkey.fromPoint(point.fromX(true, 1)),
sig: Signature(BN(obj.signature.r), BN(obj.signature.s)),
hashbuf: Hash.sha256(new Buffer(obj.message))
});
ecdsa.sigError().should.equal(obj.exception);
});
});
});
});

View File

@ -0,0 +1,158 @@
{
"valid": [
{
"d": "01",
"k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
"message": "Everything should be made as simple as possible, but not simpler.",
"i": 0,
"signature": {
"r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
"s": "50433721247292933944369538617440297985091596895097604618403996029256432099938"
}
},
{
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4",
"message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
"i": 0,
"signature": {
"r": "38341707918488238920692284707283974715538935465589664377561695343399725051885",
"s": "3180566392414476763164587487324397066658063772201694230600609996154610926757"
}
},
{
"d": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
"k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5",
"message": "Not only is the Universe stranger than we think, it is stranger than we can think.",
"i": 0,
"signature": {
"r": "115464191557905790016094131873849783294273568009648050793030031933291767741904",
"s": "50562520307781850052192542766631199590053690478900449960232079510155113443971"
}
},
{
"d": "0000000000000000000000000000000000000000000000000000000000000001",
"k": "f0cd2ba5fc7c183de589f6416220a36775a146740798756d8d949f7166dcc87f",
"message": "How wonderful that we have met with a paradox. Now we have some hope of making progress.",
"i": 1,
"signature": {
"r": "87230998027579607140680851455601772643840468630989315269459846730712163783123",
"s": "53231320085894623106179381504478252331065330583563809963303318469380290929875"
}
},
{
"d": "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",
"k": "6bb4a594ad57c1aa22dbe991a9d8501daf4688bf50a4892ef21bd7c711afda97",
"message": "Computer science is no more about computers than astronomy is about telescopes.",
"i": 0,
"signature": {
"r": "51348483531757779992459563033975330355971795607481991320287437101831125115997",
"s": "6277080015686056199074771961940657638578000617958603212944619747099038735862"
}
},
{
"d": "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",
"k": "097b5c8ee22c3ea78a4d3635e0ff6fe85a1eb92ce317ded90b9e71aab2b861cb",
"message": "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",
"i": 1,
"signature": {
"r": "113979859486826658566290715281614250298918272782414232881639314569529560769671",
"s": "6517071009538626957379450615706485096874328019806177698938278220732027419959"
}
},
{
"d": "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",
"k": "19355c36c8cbcdfb2382e23b194b79f8c97bf650040fc7728dfbf6b39a97c25b",
"message": "The question of whether computers can think is like the question of whether submarines can swim.",
"i": 1,
"signature": {
"r": "93122007060065279508564838030979550535085999589142852106617159184757394422777",
"s": "3078539468410661027472930027406594684630312677495124015420811882501887769839"
}
}
],
"invalid": {
"sigError": [
{
"description": "The wrong signature",
"exception": "Invalid signature",
"d": "01",
"message": "foo",
"signature": {
"r": "38341707918488238920692284707283974715538935465589664377561695343399725051885",
"s": "3180566392414476763164587487324397066658063772201694230600609996154610926757"
}
},
{
"description": "Invalid r value (< 0)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "-1",
"s": "2"
}
},
{
"description": "Invalid r value (== 0)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "0",
"s": "2"
}
},
{
"description": "Invalid r value (>= n)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "115792089237316195423570985008687907852837564279074904382605163141518161494337",
"s": "2"
}
},
{
"description": "Invalid s value (< 0)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "2",
"s": "-1"
}
},
{
"description": "Invalid s value (== 0)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "2",
"s": "0"
}
},
{
"description": "Invalid s value (>= n)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "2",
"s": "115792089237316195423570985008687907852837564279074904382605163141518161494337"
}
},
{
"description": "Invalid r, s values (r = s = -n)",
"exception": "r and s not in range",
"d": "01",
"message": "foo",
"signature": {
"r": "-115792089237316195423570985008687907852837564279074904382605163141518161494337",
"s": "-115792089237316195423570985008687907852837564279074904382605163141518161494337"
}
}
]
}
}