From 370f69df88ed6787525c052e8131acc1a681edca Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 19 Dec 2014 17:10:58 -0300 Subject: [PATCH 1/6] refactor BN and ECDSA --- lib/crypto/bn.js | 138 ++++----- lib/crypto/ecdsa.js | 66 +++-- lib/transaction/output.js | 2 +- test/crypto/ecdsa.js | 559 ++++++++++++++++++------------------- test/script/interpreter.js | 2 +- 5 files changed, 393 insertions(+), 374 deletions(-) diff --git a/lib/crypto/bn.js b/lib/crypto/bn.js index 67881868b..463e094ac 100644 --- a/lib/crypto/bn.js +++ b/lib/crypto/bn.js @@ -1,6 +1,8 @@ 'use strict'; var _BN = require('bn.js'); +var $ = require('../util/preconditions'); +var _ = require('lodash'); var BN = function BN(n, base) { if (!(this instanceof BN)) { @@ -14,25 +16,23 @@ BN.prototype = _BN.prototype; var reversebuf = function(buf) { var buf2 = new Buffer(buf.length); for (var i = 0; i < buf.length; i++) { - buf2[i] = buf[buf.length-1-i]; + buf2[i] = buf[buf.length - 1 - i]; } return buf2; }; -BN.prototype.fromNumber = function(n) { - var bn = BN(n); - bn.copy(this); - return this; +BN.fromNumber = function(n) { + $.checkArgument(_.isNumber(n)); + return BN(n); }; BN.prototype.toNumber = function() { - return parseInt(this['toString'](10), 10); + return parseInt(this.toString(10), 10); }; -BN.prototype.fromString = function(str) { - var bn = BN(str); - bn.copy(this); - return this; +BN.fromString = function(str) { + $.checkArgument(_.isString(str)); + return BN(str); }; BN.fromBuffer = function(buf, opts) { @@ -44,38 +44,37 @@ BN.fromBuffer = function(buf, opts) { return bn; }; -BN.prototype.fromBuffer = function(buf, opts) { - var bn = BN.fromBuffer(buf, opts); - bn.copy(this); +BN.trim = function(buf, natlen) { + return buf.slice(natlen - buf.length, buf.length); +}; - return this; +BN.pad = function(buf, natlen, size) { + var rbuf = new Buffer(size); + for (var i = 0; i < buf.length; i++) { + rbuf[rbuf.length - 1 - i] = buf[buf.length - 1 - i]; + } + for (i = 0; i < size - natlen; i++) { + rbuf[i] = 0; + } + return rbuf; }; BN.prototype.toBuffer = function(opts) { - var buf; + var buf, hex; if (opts && opts.size) { - var hex = this.toString(16, 2); - var natlen = hex.length/2; + hex = this.toString(16, 2); + var natlen = hex.length / 2; buf = new Buffer(hex, 'hex'); - if (natlen == opts.size) + if (natlen === opts.size) { buf = buf; - - else if (natlen > opts.size) { - buf = buf.slice(natlen - buf.length, buf.length); + } else if (natlen > opts.size) { + buf = buf.trim(buf, natlen); + } else if (natlen < opts.size) { + buf = BN.pad(buf, natlen, opts.size); } - - else if (natlen < opts.size) { - var rbuf = new Buffer(opts.size); - 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++) - rbuf[i] = 0; - buf = rbuf; - } - } - else { - var hex = this.toString(16, 2); + } else { + hex = this.toString(16, 2); buf = new Buffer(hex, 'hex'); } @@ -88,51 +87,58 @@ BN.prototype.toBuffer = function(opts) { // 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])); +BN.fromSM = function(buf, opts) { + var ret; + if (buf.length === 0) { + return BN.fromBuffer(new Buffer([0])); + } var endian = 'big'; - if (opts) + if (opts) { endian = opts.endian; - - if (endian == 'little') + } + if (endian === 'little') { buf = reversebuf(buf); + } if (buf[0] & 0x80) { buf[0] = buf[0] & 0x7f; - this.fromBuffer(buf); - this.neg().copy(this); + ret = BN.fromBuffer(buf); + ret.neg().copy(this); } else { - this.fromBuffer(buf); + ret = BN.fromBuffer(buf); } - return this; + return ret; }; -BN.prototype.toSM = function(opts) { - var endian = 'big'; - if (opts) - endian = opts.endian; - +BN.prototype.toSMBigEndian = function() { var buf; - if (this.cmp(0) == -1) { + if (this.cmp(0) === -1) { buf = this.neg().toBuffer(); - if (buf[0] & 0x80) + if (buf[0] & 0x80) { buf = Buffer.concat([new Buffer([0x80]), buf]); - else + } else { buf[0] = buf[0] | 0x80; + } } else { buf = this.toBuffer(); - if (buf[0] & 0x80) + if (buf[0] & 0x80) { buf = Buffer.concat([new Buffer([0x00]), buf]); + } } - if (buf.length === 1 & buf[0] === 0) + if (buf.length === 1 & buf[0] === 0) { buf = new Buffer([]); + } + return buf; +}; +BN.prototype.toSM = function(opts) { + var endian = opts ? opts.endian : 'big'; + var buf = this.toSMBigEndian(); - if (endian == 'little') + if (endian === 'little') { buf = reversebuf(buf); - + } return buf; }; @@ -142,8 +148,7 @@ BN.prototype.toSM = function(opts) { // 4 bytes. We copy that behavior here. BN.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) { var nMaxNumSize = 4; - if (buf.length > nMaxNumSize) - throw new Error('script number overflow'); + $.checkArgument(buf.length <= nMaxNumSize, new Error('script number overflow')); if (fRequireMinimal && buf.length > 0) { // Check that the number is encoded with the minimum possible // number of bytes. @@ -158,28 +163,33 @@ BN.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) { // 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"); + throw new Error('non-minimally encoded script number'); } } } - return this.fromSM(buf, {endian: 'little'}); + 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.toScriptNumBuffer = function(buf) { - return this.toSM({endian: 'little'}); +BN.prototype.toScriptNumBuffer = function() { + return this.toSM({ + endian: 'little' + }); }; -function decorate(name) { +var decorate = function decorate(name) { BN.prototype['_' + name] = BN.prototype[name]; var f = function(b) { - if (typeof b === 'string') + if (typeof b === 'string') { b = new BN(b); - else if (typeof b === 'number') + } else if (typeof b === 'number') { b = new BN(b.toString()); + } return this['_' + name](b); }; BN.prototype[name] = f; diff --git a/lib/crypto/ecdsa.js b/lib/crypto/ecdsa.js index 2b1081e8b..9bd10bf98 100644 --- a/lib/crypto/ecdsa.js +++ b/lib/crypto/ecdsa.js @@ -7,6 +7,7 @@ var PublicKey = require('../publickey'); var PrivateKey = require('../privatekey'); var Random = require('./random'); var Hash = require('./hash'); +var BufferUtil = require('../util/buffer'); var ECDSA = function ECDSA(obj) { if (!(this instanceof ECDSA)) { @@ -17,6 +18,7 @@ var ECDSA = function ECDSA(obj) { } }; +/* jshint maxcomplexity: 9 */ ECDSA.prototype.set = function(obj) { this.hashbuf = obj.hashbuf || this.hashbuf; this.endian = obj.endian || this.endian; //the endianness of hashbuf @@ -37,7 +39,7 @@ ECDSA.prototype.calci = function() { this.sig.i = i; var Qprime; try { - Qprime = this.sig2pubkey(); + Qprime = this.toPublicKey(); } catch (e) { console.error(e); continue; @@ -53,8 +55,10 @@ ECDSA.prototype.calci = function() { throw new Error('Unable to find valid recovery factor'); }; -ECDSA.prototype.fromString = function(str) { +ECDSA.fromString = function(str) { var obj = JSON.parse(str); + return new ECDSA(obj); + if (obj.hashbuf) { this.hashbuf = new Buffer(obj.hashbuf, 'hex'); } @@ -77,7 +81,7 @@ ECDSA.prototype.randomK = function() { var N = Point.getN(); var k; do { - k = BN().fromBuffer(Random.getRandomBuffer(32)); + k = BN.fromBuffer(Random.getRandomBuffer(32)); } while (!(k.lt(N) && k.gt(0))); this.k = k; return this; @@ -89,13 +93,15 @@ ECDSA.prototype.deterministicK = function(badrs) { v.fill(0x01); var k = new Buffer(32); k.fill(0x00); - var x = this.privkey.bn.toBuffer({size: 32}); + 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 T = BN.fromBuffer(v); var N = Point.getN(); // if r or s were invalid when this function was used in signing, @@ -107,7 +113,7 @@ ECDSA.prototype.deterministicK = function(badrs) { 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); + T = BN.fromBuffer(v); } this.k = T; @@ -117,12 +123,13 @@ ECDSA.prototype.deterministicK = function(badrs) { // 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 -ECDSA.prototype.sig2pubkey = function() { +ECDSA.prototype.toPublicKey = function() { var i = this.sig.i; - if (!(i === 0 || i === 1 || i === 2 || i === 3)) + if (!(i === 0 || i === 1 || i === 2 || i === 3)) { throw new Error('i must be equal to 0, 1, 2, or 3'); + } - var e = BN().fromBuffer(this.hashbuf); + var e = BN.fromBuffer(this.hashbuf); var r = this.sig.r; var s = this.sig.s; @@ -143,8 +150,9 @@ ECDSA.prototype.sig2pubkey = function() { // 1.4 Check that nR is at infinity var nR = R.mul(n); - if (!nR.isInfinity()) + if (!nR.isInfinity()) { throw new Error('nR is not a valid curve point'); + } // Compute -e from e var eNeg = e.neg().mod(n); @@ -168,11 +176,13 @@ ECDSA.prototype.sigError = function() { var r = this.sig.r; var s = this.sig.s; - if (!(r.gt(0) && r.lt(Point.getN())) - || !(s.gt(0) && s.lt(Point.getN()))) + if (!(r.gt(0) && r.lt(Point.getN())) || !(s.gt(0) && s.lt(Point.getN()))) { return 'r and s not in range'; + } - var e = BN().fromBuffer(this.hashbuf, this.endian ? {endian: this.endian} : undefined); + 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); @@ -194,35 +204,45 @@ ECDSA.prototype.sign = function() { var d = privkey.bn; - if (!hashbuf || !privkey || !d) + if (!hashbuf || !privkey || !d) { throw new Error('invalid parameters'); + } - if (!Buffer.isBuffer(hashbuf) || hashbuf.length !== 32) + if (!BufferUtil.isBuffer(hashbuf) || hashbuf.length !== 32) { throw new Error('hashbuf must be a 32 byte buffer'); + } var N = Point.getN(); var G = Point.getG(); - var e = BN().fromBuffer(hashbuf, this.endian ? {endian: this.endian} : undefined); + var e = BN.fromBuffer(hashbuf, this.endian ? { + endian: this.endian + } : undefined); // try different values of k until r, s are valid var badrs = 0; + var k, Q, r, s; do { - if (!this.k || badrs > 0) + 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); + k = this.k; + Q = G.mul(k); + r = Q.x.mod(N); + s = k.invm(N).mul(e.add(d.mul(r))).mod(N); } while (r.cmp(0) <= 0 || s.cmp(0) <= 0); //enforce low s //see BIP 62, "low S values in signatures" - if (s.gt(BN().fromBuffer(new Buffer('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex')))) { + 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}); + this.sig = new Signature({ + r: r, + s: s, + compressed: this.pubkey.compressed + }); return this; }; diff --git a/lib/transaction/output.js b/lib/transaction/output.js index e1a89c323..896f8335f 100644 --- a/lib/transaction/output.js +++ b/lib/transaction/output.js @@ -38,7 +38,7 @@ Object.defineProperty(Output.prototype, 'satoshis', { if (num instanceof BN) { this._satoshis = num; } else { - this._satoshis = BN().fromNumber(num); + this._satoshis = BN.fromNumber(num); } } }); diff --git a/test/crypto/ecdsa.js b/test/crypto/ecdsa.js index 9a918c021..26711821e 100644 --- a/test/crypto/ecdsa.js +++ b/test/crypto/ecdsa.js @@ -10,306 +10,295 @@ var point = require('../../lib/crypto/point'); var should = require('chai').should(); var vectors = require('../data/ecdsa'); -describe("ECDSA", function() { +describe.only('ECDSA', function() { + describe.only('ECDSA', function() { + + it('instantiation', function() { + var ecdsa = new ECDSA(); + should.exist(ecdsa); + }); - it('should create a blank ecdsa', function() { var ecdsa = new ECDSA(); - }); + ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); + ecdsa.privkey = new Privkey(BN().fromBuffer( + new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex') + )); + ecdsa.privkey2pubkey(); - var ecdsa = new ECDSA(); - ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); - ecdsa.privkey = new Privkey(BN().fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex'))); - ecdsa.privkey2pubkey(); - - describe('#set', function() { - - it('should set hashbuf', function() { - should.exist(ECDSA().set({ - hashbuf: ecdsa.hashbuf - }).hashbuf); + describe('#set', function() { + it('sets hashbuf', function() { + should.exist(ECDSA().set({ + hashbuf: ecdsa.hashbuf + }).hashbuf); + }); }); - }); - - describe('#calci', function() { - - it('should calculate i', function() { - ecdsa.randomK(); - ecdsa.sign(); - ecdsa.calci(); - should.exist(ecdsa.sig.i); - }); - - it('should calulate this known i', function() { - var hashbuf = Hash.sha256(new Buffer('some data')); - var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); - var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); - var ecdsa = new ECDSA({ - privkey: Privkey(BN().fromBuffer(Hash.sha256(new Buffer('test')))), - hashbuf: hashbuf, - sig: new Signature({ - r: r, - s: s - }) + describe('#calci', function() { + it('calculates i correctly', function() { + ecdsa.randomK(); + ecdsa.sign(); + ecdsa.calci(); + should.exist(ecdsa.sig.i); }); - ecdsa.calci(); - ecdsa.sig.i.should.equal(1); - }); - - }); - - describe('#fromString', function() { - - it('should to a round trip with to string', function() { - var str = ecdsa.toString(); - var ecdsa2 = new ECDSA(); - ecdsa2.fromString(str); - should.exist(ecdsa.hashbuf); - should.exist(ecdsa.privkey); - }); - - }); - - describe('#randomK', function() { - - it('should generate a new random k when called twice in a row', function() { - ecdsa.randomK(); - var k1 = ecdsa.k; - ecdsa.randomK(); - var k2 = ecdsa.k; - (k1.cmp(k2) === 0).should.equal(false); - }); - - it('should generate a random k that is (almost always) greater than this relatively small number', function() { - ecdsa.randomK(); - var k1 = ecdsa.k; - var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32))); - k2.gt(k1).should.equal(false); - }); - - }); - - 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); - }); - - }); - - describe('#sigError', function() { - - it('should return an error if the hash is invalid', function() { - var ecdsa = new ECDSA(); - ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer'); - }); - - it('should return an error if r, s are invalid', function() { - var ecdsa = new ECDSA(); - ecdsa.hashbuf = Hash.sha256(new Buffer('test')); - 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"); - }); - - it('should return an error if the signature is incorrect', function() { - ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); - ecdsa.sig.r = ecdsa.sig.r.add(BN(1)); - ecdsa.sigError().should.equal("Invalid signature"); - }); - - }); - - describe('#sign', function() { - - it('should create a valid signature', function() { - ecdsa.randomK(); - ecdsa.sign(); - 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), - privkey: ecdsa.privkey - }); - ecdsa2.randomK(); - (function() { - ecdsa2.sign(); - }).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, 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()); - }); - - }); - - describe('#toString', function() { - - it('should convert this to a string', function() { - var str = ecdsa.toString(); - (typeof str === 'string').should.equal(true); - }); - - }); - - describe('#verify', function() { - - it('should verify a signature that was just signed', function() { - ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); - ecdsa.verify().verified.should.equal(true); - }); - - it('should verify this known good signature', function() { - ecdsa.signRandomK(); - ecdsa.verify().verified.should.equal(true); - }); - - }); - - describe('@sign', function() { - - it('should produce a signature', function() { - var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); - (sig instanceof Signature).should.equal(true); - }); - - }); - - describe('@verify', function() { - - it('should verify a valid signature, and unverify an invalid signature', function() { - var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); - ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey).should.equal(true); - var fakesig = Signature(sig.r.add(1), sig.s); - 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 + it('calulates this known i', function() { + var hashbuf = Hash.sha256(new Buffer('some data')); + var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); + var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); + var ecdsa = new ECDSA({ + privkey: new Privkey(BN().fromBuffer(Hash.sha256(new Buffer('test')))), + hashbuf: hashbuf, + sig: new Signature({ + r: r, + s: s }) }); - 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.calci(); + ecdsa.sig.i.should.equal(1); + }); + + }); + + describe('#fromString', function() { + + it('round trip with fromString', function() { + var str = ecdsa.toString(); + var ecdsa2 = new ECDSA(); + ecdsa2.fromString(str); + should.exist(ecdsa.hashbuf); + should.exist(ecdsa.privkey); + }); + + }); + + describe('#randomK', function() { + + it('should generate a new random k when called twice in a row', function() { + ecdsa.randomK(); + var k1 = ecdsa.k; + ecdsa.randomK(); + var k2 = ecdsa.k; + (k1.cmp(k2) === 0).should.equal(false); + }); + + it('should generate a random k that is (almost always) greater than this relatively small number', function() { + ecdsa.randomK(); + var k1 = ecdsa.k; + var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32))); + k2.gt(k1).should.equal(false); + }); + + }); + + 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 = new 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('3045022100ec3cfe0e335791ad278b4ec8eac93d0347' + + 'a97877bb1d54d35d189e225c15f6650220278cf15b05ce47fb37d2233802899d94c774d5480bba9f0f2d996baa13370c43'); + 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('3046022100ec3cfe0e335791ad278b4ec8eac93d0347' + + 'a97877bb1d54d35d189e225c15f665022100d8730ea4fa31b804c82ddcc7fd766269f33a079ea38e012c9238f2e2bcff34fe'); + ecdsa.sig.i = 1; + var pubkey = ecdsa.sig2pubkey(); + pubkey.point.eq(ecdsa.pubkey.point).should.equal(true); + }); + + }); + + describe('#sigError', function() { + + it('should return an error if the hash is invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer'); + }); + + it('should return an error if r, s are invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.hashbuf = Hash.sha256(new Buffer('test')); + var pk = Pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49' + + '710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', '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'); + }); + + it('should return an error if the signature is incorrect', function() { + ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40' + + 'ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); + ecdsa.sig.r = ecdsa.sig.r.add(BN(1)); + ecdsa.sigError().should.equal('Invalid signature'); + }); + + }); + + describe('#sign', function() { + + it('should create a valid signature', function() { + ecdsa.randomK(); + ecdsa.sign(); 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), + privkey: ecdsa.privkey + }); + ecdsa2.randomK(); + ecdsa2.sign.bind(ecdsa2).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); + }); + }); - 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); + describe('#toString', function() { + it('should convert this to a string', function() { + var str = ecdsa.toString(); + (typeof str === 'string').should.equal(true); }); }); - }); + describe('signing and verification', function() { + describe('@sign', function() { + it('should produce a signature', function() { + var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); + (sig instanceof Signature).should.equal(true); + }); + 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()); + }); + }); + describe('#verify', function() { + it('should verify a signature that was just signed', function() { + ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c' + + '40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); + ecdsa.verify().verified.should.equal(true); + }); + it('should verify this known good signature', function() { + ecdsa.signRandomK(); + ecdsa.verify().verified.should.equal(true); + }); + it('should verify a valid signature, and unverify an invalid signature', function() { + var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); + ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey).should.equal(true); + var fakesig = new Signature(sig.r.add(1), sig.s); + 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: new Privkey(BN().fromBuffer(new Buffer(obj.d, 'hex'))), + k: BN().fromBuffer(new Buffer(obj.k, 'hex')), + hashbuf: Hash.sha256(new Buffer(obj.message)), + sig: new 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: new Signature(BN(obj.signature.r), BN(obj.signature.s)), + hashbuf: Hash.sha256(new Buffer(obj.message)) + }); + ecdsa.sigError().should.equal(obj.exception); + }); + }); + }); + }); + }); }); diff --git a/test/script/interpreter.js b/test/script/interpreter.js index 7be035e3a..05e8f01e6 100644 --- a/test/script/interpreter.js +++ b/test/script/interpreter.js @@ -89,7 +89,7 @@ describe('Interpreter', function() { })).should.equal(true); var buf = new Buffer('00', 'hex'); - var bool = BN().fromSM(buf, { + var bool = BN.fromSM(buf, { endian: 'little' }).cmp(0) !== 0; Interpreter.castToBool(buf).should.equal(bool); From 6c518f1f45c4279435669b30a951d1c09b174f98 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 19 Dec 2014 18:28:52 -0300 Subject: [PATCH 2/6] refactoring interpreter --- lib/crypto/bn.js | 8 +- lib/crypto/signature.js | 73 ++--- lib/encoding/bufferreader.js | 4 +- lib/hdprivatekey.js | 4 +- lib/hdpublickey.js | 4 +- lib/privatekey.js | 2 +- lib/script/interpreter.js | 23 +- test/crypto/bn.js | 61 ++-- test/crypto/ecdsa.js | 535 +++++++++++++++++------------------ test/script/interpreter.js | 14 +- 10 files changed, 356 insertions(+), 372 deletions(-) diff --git a/lib/crypto/bn.js b/lib/crypto/bn.js index 463e094ac..885f81a64 100644 --- a/lib/crypto/bn.js +++ b/lib/crypto/bn.js @@ -69,7 +69,7 @@ BN.prototype.toBuffer = function(opts) { if (natlen === opts.size) { buf = buf; } else if (natlen > opts.size) { - buf = buf.trim(buf, natlen); + buf = BN.trim(buf, natlen); } else if (natlen < opts.size) { buf = BN.pad(buf, natlen, opts.size); } @@ -104,7 +104,7 @@ BN.fromSM = function(buf, opts) { if (buf[0] & 0x80) { buf[0] = buf[0] & 0x7f; ret = BN.fromBuffer(buf); - ret.neg().copy(this); + ret.neg().copy(ret); } else { ret = BN.fromBuffer(buf); } @@ -146,7 +146,7 @@ BN.prototype.toSM = function(opts) { // 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.fromScriptNumBuffer = function(buf, fRequireMinimal) { +BN.fromScriptNumBuffer = function(buf, fRequireMinimal) { var nMaxNumSize = 4; $.checkArgument(buf.length <= nMaxNumSize, new Error('script number overflow')); if (fRequireMinimal && buf.length > 0) { @@ -167,7 +167,7 @@ BN.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) { } } } - return this.fromSM(buf, { + return BN.fromSM(buf, { endian: 'little' }); }; diff --git a/lib/crypto/signature.js b/lib/crypto/signature.js index f2b48935f..2ca0b3340 100644 --- a/lib/crypto/signature.js +++ b/lib/crypto/signature.js @@ -1,10 +1,14 @@ 'use strict'; var BN = require('./bn'); +var _ = require('lodash'); +var $ = require('../util/preconditions'); +var BufferUtil = require('../util/buffer'); var Signature = function Signature(r, s) { - if (!(this instanceof Signature)) + if (!(this instanceof Signature)) { return new Signature(r, s); + } if (r instanceof BN) { this.set({ r: r, @@ -16,37 +20,32 @@ var Signature = function Signature(r, s) { } }; +/* jshint maxcomplexity: 7 */ Signature.prototype.set = function(obj) { this.r = obj.r || this.r || undefined; this.s = obj.s || this.s || undefined; this.i = typeof obj.i !== 'undefined' ? obj.i : this.i; //public key recovery parameter in range [0, 3] - this.compressed = typeof obj.compressed !== 'undefined' ? obj.compressed : this.compressed; //whether the recovered pubkey is compressed + this.compressed = typeof obj.compressed !== 'undefined' ? + obj.compressed : this.compressed; //whether the recovered pubkey is compressed return this; }; Signature.fromCompact = function(buf) { var sig = new Signature(); + //TODO: handle uncompressed pubkeys var compressed = true; var i = buf.slice(0, 1)[0] - 27 - 4; - //TODO: handle uncompressed pubkeys - var b2 = buf.slice(1, 33); var b3 = buf.slice(33, 65); - if (!(i === 0 || i === 1 || i === 2 || i === 3)) { - throw new Error('i must be 0, 1, 2, or 3'); - } - if (b2.length !== 32) { - throw new Error('r must be 32 bytes'); - } - if (b3.length !== 32) { - throw new Error('s must be 32 bytes'); - } + $.checkArgument(i === 0 || i === 1 || i === 2 || i === 3, new Error('i must be 0, 1, 2, or 3')); + $.checkArgument(b2.length === 32, new Error('r must be 32 bytes')); + $.checkArgument(b3.length === 32, new Error('s must be 32 bytes')); sig.compressed = compressed; sig.i = i; - sig.r = BN().fromBuffer(b2); - sig.s = BN().fromBuffer(b3); + sig.r = BN.fromBuffer(b2); + sig.s = BN.fromBuffer(b3); return sig; }; @@ -80,58 +79,40 @@ Signature.fromString = function(str) { * In order to mimic the non-strict DER encoding of OpenSSL, set strict = false. */ Signature.parseDER = function(buf, strict) { - if (typeof strict === 'undefined') { + $.checkArgument(BufferUtil.isBuffer(buf), new Error('DER formatted signature should be a buffer')); + if (_.isUndefined(strict)) { strict = true; } - if (!Buffer.isBuffer(buf)) { - throw new Error('DER formatted signature should be a buffer'); - } - var header = buf[0]; - - if (header !== 0x30) { - throw new Error('Header byte should be 0x30'); - } + $.checkArgument(header === 0x30, new Error('Header byte should be 0x30')); var length = buf[1]; var buflength = buf.slice(2).length; - if (strict && length !== buflength) { - throw new Error('Length byte should length of what follows'); - } else { - length = length < buflength ? length : buflength; - } + $.checkArgument(!strict || length === buflength, new Error('Length byte should length of what follows')); + + length = length < buflength ? length : buflength; var rheader = buf[2 + 0]; - if (rheader !== 0x02) { - throw new Error('Integer byte for r should be 0x02'); - } + $.checkArgument(rheader === 0x02, new Error('Integer byte for r should be 0x02')); var rlength = buf[2 + 1]; var rbuf = buf.slice(2 + 2, 2 + 2 + rlength); - var r = BN().fromBuffer(rbuf); + var r = BN.fromBuffer(rbuf); var rneg = buf[2 + 1 + 1] === 0x00 ? true : false; - if (rlength !== rbuf.length) { - throw new Error('Length of r incorrect'); - } + $.checkArgument(rlength === rbuf.length, new Error('Length of r incorrect')); var sheader = buf[2 + 2 + rlength + 0]; - if (sheader !== 0x02) { - throw new Error('Integer byte for s should be 0x02'); - } + $.checkArgument(sheader === 0x02, new Error('Integer byte for s should be 0x02')); var slength = buf[2 + 2 + rlength + 1]; var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength); - var s = BN().fromBuffer(sbuf); + var s = BN.fromBuffer(sbuf); var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false; - if (slength !== sbuf.length) { - throw new Error('Length of s incorrect'); - } + $.checkArgument(slength === sbuf.length, new Error('Length of s incorrect')); var sumlength = 2 + 2 + rlength + 2 + slength; - if (length !== sumlength - 2) { - throw new Error('Length of signature incorrect'); - } + $.checkArgument(length === sumlength - 2, new Error('Length of signature incorrect')); var obj = { header: header, diff --git a/lib/encoding/bufferreader.js b/lib/encoding/bufferreader.js index 6ed609a50..89f46452d 100644 --- a/lib/encoding/bufferreader.js +++ b/lib/encoding/bufferreader.js @@ -73,7 +73,7 @@ BufferReader.prototype.readUInt32LE = function() { BufferReader.prototype.readUInt64BEBN = function() { var buf = this.buf.slice(this.pos, this.pos + 8); - var bn = BN().fromBuffer(buf); + var bn = BN.fromBuffer(buf); this.pos = this.pos + 8; return bn; }; @@ -81,7 +81,7 @@ BufferReader.prototype.readUInt64BEBN = function() { BufferReader.prototype.readUInt64LEBN = function() { var buf = this.buf.slice(this.pos, this.pos + 8); var reversebuf = BufferReader({buf: buf}).readReverse(); - var bn = BN().fromBuffer(reversebuf); + var bn = BN.fromBuffer(reversebuf); this.pos = this.pos + 8; return bn; }; diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 877f8803c..9d88f85fc 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -117,7 +117,7 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); } var hash = Hash.sha512hmac(data, this._buffers.chainCode); - var leftPart = BN().fromBuffer(hash.slice(0, 32), {size: 32}); + var leftPart = BN.fromBuffer(hash.slice(0, 32), {size: 32}); var chainCode = hash.slice(32, 64); var privateKey = leftPart.add(this.privateKey.toBigNumber()).mod(Point.getN()).toBuffer({size: 32}); @@ -334,7 +334,7 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { xprivkey = arg.xprivkey; } - var privateKey = new PrivateKey(BN().fromBuffer(arg.privateKey)); + var privateKey = new PrivateKey(BN.fromBuffer(arg.privateKey)); var publicKey = privateKey.toPublicKey(); var size = HDPrivateKey.ParentFingerPrintSize; var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size); diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index 0dc3f50ed..63439ca46 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -110,7 +110,7 @@ HDPublicKey.prototype._deriveWithNumber = function (index, hardened) { var indexBuffer = BufferUtil.integerAsBuffer(index); var data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); var hash = Hash.sha512hmac(data, this._buffers.chainCode); - var leftPart = BN().fromBuffer(hash.slice(0, 32), {size: 32}); + var leftPart = BN.fromBuffer(hash.slice(0, 32), {size: 32}); var chainCode = hash.slice(32, 64); var publicKey = PublicKey.fromPoint(Point.getG().mul(leftPart).add(this.publicKey.point)); @@ -219,7 +219,7 @@ HDPublicKey.prototype._buildFromJSON = function (arg) { HDPublicKey.prototype._buildFromPrivate = function (arg) { var args = _.clone(arg._buffers); - var point = Point.getG().mul(BN().fromBuffer(args.privateKey)); + var point = Point.getG().mul(BN.fromBuffer(args.privateKey)); args.publicKey = Point.pointToCompressed(point); args.version = BufferUtil.integerAsBuffer(Network.get(BufferUtil.integerFromBuffer(args.version)).xpubkey); args.privateKey = undefined; diff --git a/lib/privatekey.js b/lib/privatekey.js index 344f5ac8d..c0569fe54 100644 --- a/lib/privatekey.js +++ b/lib/privatekey.js @@ -128,7 +128,7 @@ PrivateKey._getRandomBN = function(){ var bn; do { var privbuf = Random.getRandomBuffer(32); - bn = BN().fromBuffer(privbuf); + bn = BN.fromBuffer(privbuf); condition = bn.lt(Point.getN()); } while (!condition); return bn; diff --git a/lib/script/interpreter.js b/lib/script/interpreter.js index 977da0f7a..a548db9e2 100644 --- a/lib/script/interpreter.js +++ b/lib/script/interpreter.js @@ -682,7 +682,7 @@ Interpreter.prototype.step = function() { return false; } buf = this.stack[this.stack.length - 1]; - bn = BN().fromScriptNumBuffer(buf, fRequireMinimal); + bn = BN.fromScriptNumBuffer(buf, fRequireMinimal); n = bn.toNumber(); this.stack.pop(); if (n < 0 || n >= this.stack.length) { @@ -769,11 +769,6 @@ Interpreter.prototype.step = function() { buf1 = this.stack[this.stack.length - 2]; buf2 = this.stack[this.stack.length - 1]; var fEqual = buf1.toString('hex') === buf2.toString('hex'); - // Opcode.OP_NOTEQUAL is disabled because it would be too easy to say - // something like n != 1 and have some wiseguy pass in 1 with extra - // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) - //if (opcode == Opcode.OP_NOTEQUAL) - // fEqual = !fEqual; this.stack.pop(); this.stack.pop(); this.stack.push(fEqual ? Interpreter.true : Interpreter.false); @@ -805,7 +800,7 @@ Interpreter.prototype.step = function() { return false; } buf = this.stack[this.stack.length - 1]; - bn = BN().fromScriptNumBuffer(buf, fRequireMinimal); + bn = BN.fromScriptNumBuffer(buf, fRequireMinimal); switch (opcodenum) { case Opcode.OP_1ADD: bn = bn.add(1); @@ -853,8 +848,8 @@ Interpreter.prototype.step = function() { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } - bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); - bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); + bn1 = BN.fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); + bn2 = BN.fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); bn = BN(0); switch (opcodenum) { @@ -933,9 +928,9 @@ Interpreter.prototype.step = function() { this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; return false; } - bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 3], fRequireMinimal); - bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); - var bn3 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); + bn1 = BN.fromScriptNumBuffer(this.stack[this.stack.length - 3], fRequireMinimal); + bn2 = BN.fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); + var bn3 = BN.fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); //bool fValue = (bn2 <= bn1 && bn1 < bn3); fValue = (bn2.cmp(bn1) <= 0) && (bn1.cmp(bn3) < 0); this.stack.pop(); @@ -1048,7 +1043,7 @@ Interpreter.prototype.step = function() { return false; } - var nKeysCount = BN().fromScriptNumBuffer(this.stack[this.stack.length - i], fRequireMinimal).toNumber(); + var nKeysCount = BN.fromScriptNumBuffer(this.stack[this.stack.length - i], fRequireMinimal).toNumber(); if (nKeysCount < 0 || nKeysCount > 20) { this.errstr = 'SCRIPT_ERR_PUBKEY_COUNT'; return false; @@ -1066,7 +1061,7 @@ Interpreter.prototype.step = function() { return false; } - var nSigsCount = BN().fromScriptNumBuffer(this.stack[this.stack.length - i], fRequireMinimal).toNumber(); + var nSigsCount = BN.fromScriptNumBuffer(this.stack[this.stack.length - i], fRequireMinimal).toNumber(); if (nSigsCount < 0 || nSigsCount > nKeysCount) { this.errstr = 'SCRIPT_ERR_SIG_COUNT'; return false; diff --git a/test/crypto/bn.js b/test/crypto/bn.js index a771ae0f9..c0af52191 100644 --- a/test/crypto/bn.js +++ b/test/crypto/bn.js @@ -22,7 +22,7 @@ describe('BN', function() { bn = new BN(Math.pow(2, 26)); bn.toString().should.equal((Math.pow(2, 26)).toString()); }); - + describe('#add', function() { it('should add two small numbers together', function() { @@ -67,70 +67,81 @@ describe('BN', function() { }); - describe('#fromString', function() { - - it('should make BN from a string', function() { - BN().fromString('5').toString().should.equal('5'); + describe('to/from ScriptNumBuffer', function() { + [0, 1, 10, 256, 1000, 65536, 65537, -1, -1000, -65536, -65537].forEach(function(n) { + it('rountrips correctly for ' + n, function() { + BN.fromScriptNumBuffer(BN(n).toScriptNumBuffer()).toNumber().should.equal(n); + }); }); + }); + describe('#fromString', function() { + it('should make BN from a string', function() { + BN.fromString('5').toString().should.equal('5'); + }); }); describe('#toString', function() { - it('should make a string', function() { BN(5).toString().should.equal('5'); }); - }); describe('@fromBuffer', function() { - + it('should work with big endian', function() { - var bn = BN.fromBuffer(new Buffer('0001', 'hex'), {endian: 'big'}); + var bn = BN.fromBuffer(new Buffer('0001', 'hex'), { + endian: 'big' + }); bn.toString().should.equal('1'); }); it('should work with big endian 256', function() { - var bn = BN.fromBuffer(new Buffer('0100', 'hex'), {endian: 'big'}); + var bn = BN.fromBuffer(new Buffer('0100', 'hex'), { + endian: 'big' + }); bn.toString().should.equal('256'); }); it('should work with little endian if we specify the size', function() { - var bn = BN.fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'}); + var bn = BN.fromBuffer(new Buffer('0100', 'hex'), { + size: 2, + endian: 'little' + }); bn.toString().should.equal('1'); }); }); - describe('#fromBuffer', function() { - - it('should work as a prototype method', function() { - var bn = BN().fromBuffer(new Buffer('0100', 'hex'), {size: 2, endian: 'little'}); - bn.toString().should.equal('1'); - }); - - }); - describe('#toBuffer', function() { - + it('should create a 4 byte buffer', function() { var bn = new BN(1); - bn.toBuffer({size: 4}).toString('hex').should.equal('00000001'); + bn.toBuffer({ + size: 4 + }).toString('hex').should.equal('00000001'); }); it('should create a 4 byte buffer in little endian', function() { var bn = new BN(1); - bn.toBuffer({size: 4, endian: 'little'}).toString('hex').should.equal('01000000'); + bn.toBuffer({ + size: 4, + endian: 'little' + }).toString('hex').should.equal('01000000'); }); it('should create a 2 byte buffer even if you ask for a 1 byte', function() { var bn = new BN('ff00', 16); - bn.toBuffer({size: 1}).toString('hex').should.equal('ff00'); + bn.toBuffer({ + size: 1 + }).toString('hex').should.equal('ff00'); }); it('should create a 4 byte buffer even if you ask for a 1 byte', function() { var bn = new BN('ffffff00', 16); - bn.toBuffer({size: 4}).toString('hex').should.equal('ffffff00'); + bn.toBuffer({ + size: 4 + }).toString('hex').should.equal('ffffff00'); }); }); diff --git a/test/crypto/ecdsa.js b/test/crypto/ecdsa.js index 26711821e..79cd53bb4 100644 --- a/test/crypto/ecdsa.js +++ b/test/crypto/ecdsa.js @@ -10,293 +10,290 @@ var point = require('../../lib/crypto/point'); var should = require('chai').should(); var vectors = require('../data/ecdsa'); -describe.only('ECDSA', function() { - describe.only('ECDSA', function() { - - it('instantiation', function() { - var ecdsa = new ECDSA(); - should.exist(ecdsa); - }); +describe('ECDSA', function() { + it('instantiation', function() { var ecdsa = new ECDSA(); - ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); - ecdsa.privkey = new Privkey(BN().fromBuffer( - new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex') - )); - ecdsa.privkey2pubkey(); + should.exist(ecdsa); + }); - describe('#set', function() { - it('sets hashbuf', function() { - should.exist(ECDSA().set({ - hashbuf: ecdsa.hashbuf - }).hashbuf); + var ecdsa = new ECDSA(); + ecdsa.hashbuf = Hash.sha256(new Buffer('test data')); + ecdsa.privkey = new Privkey(BN.fromBuffer( + new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex') + )); + ecdsa.privkey2pubkey(); + + describe('#set', function() { + it('sets hashbuf', function() { + should.exist(ECDSA().set({ + hashbuf: ecdsa.hashbuf + }).hashbuf); + }); + }); + + describe('#calci', function() { + it('calculates i correctly', function() { + ecdsa.randomK(); + ecdsa.sign(); + ecdsa.calci(); + should.exist(ecdsa.sig.i); + }); + + it('calulates this known i', function() { + var hashbuf = Hash.sha256(new Buffer('some data')); + var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); + var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); + var ecdsa = new ECDSA({ + privkey: new 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); + }); + + }); + + describe('#fromString', function() { + + it('round trip with fromString', function() { + var str = ecdsa.toString(); + var ecdsa2 = new ECDSA.fromString(str); + should.exist(ecdsa2.hashbuf); + should.exist(ecdsa2.privkey); + }); + + }); + + describe('#randomK', function() { + + it('should generate a new random k when called twice in a row', function() { + ecdsa.randomK(); + var k1 = ecdsa.k; + ecdsa.randomK(); + var k2 = ecdsa.k; + (k1.cmp(k2) === 0).should.equal(false); + }); + + it('should generate a random k that is (almost always) greater than this relatively small number', function() { + ecdsa.randomK(); + var k1 = ecdsa.k; + var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32))); + k2.gt(k1).should.equal(false); + }); + + }); + + 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 = new 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('#toPublicKey', function() { + it('should calculate the correct public key', function() { + ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10); + ecdsa.sign(); + ecdsa.sig.i = 0; + var pubkey = ecdsa.toPublicKey(); + 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('3045022100ec3cfe0e335791ad278b4ec8eac93d0347' + + 'a97877bb1d54d35d189e225c15f6650220278cf15b05ce47fb37d2233802899d94c774d5480bba9f0f2d996baa13370c43'); + ecdsa.sig.i = 0; + var pubkey = ecdsa.toPublicKey(); + 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('3046022100ec3cfe0e335791ad278b4ec8eac93d0347' + + 'a97877bb1d54d35d189e225c15f665022100d8730ea4fa31b804c82ddcc7fd766269f33a079ea38e012c9238f2e2bcff34fe'); + ecdsa.sig.i = 1; + var pubkey = ecdsa.toPublicKey(); + pubkey.point.eq(ecdsa.pubkey.point).should.equal(true); + }); + + }); + + describe('#sigError', function() { + + it('should return an error if the hash is invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer'); + }); + + it('should return an error if r, s are invalid', function() { + var ecdsa = new ECDSA(); + ecdsa.hashbuf = Hash.sha256(new Buffer('test')); + var pk = Pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49' + + '710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', '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'); + }); + + it('should return an error if the signature is incorrect', function() { + ecdsa.sig = Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40' + + 'ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); + ecdsa.sig.r = ecdsa.sig.r.add(BN(1)); + ecdsa.sigError().should.equal('Invalid signature'); + }); + + }); + + describe('#sign', function() { + + it('should create a valid signature', function() { + ecdsa.randomK(); + ecdsa.sign(); + 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), + privkey: ecdsa.privkey + }); + ecdsa2.randomK(); + ecdsa2.sign.bind(ecdsa2).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('#toString', function() { + it('should convert this to a string', function() { + var str = ecdsa.toString(); + (typeof str === 'string').should.equal(true); + }); + }); + + describe('signing and verification', function() { + describe('@sign', function() { + it('should produce a signature', function() { + var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); + (sig instanceof Signature).should.equal(true); + }); + 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()); }); }); - describe('#calci', function() { - it('calculates i correctly', function() { - ecdsa.randomK(); - ecdsa.sign(); - ecdsa.calci(); - should.exist(ecdsa.sig.i); - }); - - it('calulates this known i', function() { - var hashbuf = Hash.sha256(new Buffer('some data')); - var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10); - var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10); - var ecdsa = new ECDSA({ - privkey: new 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); - }); - - }); - - describe('#fromString', function() { - - it('round trip with fromString', function() { - var str = ecdsa.toString(); - var ecdsa2 = new ECDSA(); - ecdsa2.fromString(str); - should.exist(ecdsa.hashbuf); - should.exist(ecdsa.privkey); - }); - - }); - - describe('#randomK', function() { - - it('should generate a new random k when called twice in a row', function() { - ecdsa.randomK(); - var k1 = ecdsa.k; - ecdsa.randomK(); - var k2 = ecdsa.k; - (k1.cmp(k2) === 0).should.equal(false); - }); - - it('should generate a random k that is (almost always) greater than this relatively small number', function() { - ecdsa.randomK(); - var k1 = ecdsa.k; - var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32))); - k2.gt(k1).should.equal(false); - }); - - }); - - 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 = new 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('3045022100ec3cfe0e335791ad278b4ec8eac93d0347' + - 'a97877bb1d54d35d189e225c15f6650220278cf15b05ce47fb37d2233802899d94c774d5480bba9f0f2d996baa13370c43'); - 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('3046022100ec3cfe0e335791ad278b4ec8eac93d0347' + - 'a97877bb1d54d35d189e225c15f665022100d8730ea4fa31b804c82ddcc7fd766269f33a079ea38e012c9238f2e2bcff34fe'); - ecdsa.sig.i = 1; - var pubkey = ecdsa.sig2pubkey(); - pubkey.point.eq(ecdsa.pubkey.point).should.equal(true); - }); - - }); - - describe('#sigError', function() { - - it('should return an error if the hash is invalid', function() { - var ecdsa = new ECDSA(); - ecdsa.sigError().should.equal('hashbuf must be a 32 byte buffer'); - }); - - it('should return an error if r, s are invalid', function() { - var ecdsa = new ECDSA(); - ecdsa.hashbuf = Hash.sha256(new Buffer('test')); - var pk = Pubkey.fromDER(new Buffer('041ff0fe0f7b15ffaa85ff9f4744d539139c252a49' + - '710fb053bb9f2b933173ff9a7baad41d04514751e6851f5304fd243751703bed21b914f6be218c0fa354a341', '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'); - }); - - it('should return an error if the signature is incorrect', function() { - ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40' + - 'ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); - ecdsa.sig.r = ecdsa.sig.r.add(BN(1)); - ecdsa.sigError().should.equal('Invalid signature'); - }); - - }); - - describe('#sign', function() { - - it('should create a valid signature', function() { - ecdsa.randomK(); - ecdsa.sign(); + describe('#verify', function() { + it('should verify a signature that was just signed', function() { + ecdsa.sig = Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c' + + '40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); 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), - privkey: ecdsa.privkey - }); - ecdsa2.randomK(); - ecdsa2.sign.bind(ecdsa2).should.throw('hashbuf must be a 32 byte buffer'); + it('should verify this known good signature', function() { + ecdsa.signRandomK(); + ecdsa.verify().verified.should.equal(true); }); - - 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); + it('should verify a valid signature, and unverify an invalid signature', function() { + var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); + ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey).should.equal(true); + var fakesig = new Signature(sig.r.add(1), sig.s); + ECDSA.verify(ecdsa.hashbuf, fakesig, ecdsa.pubkey).should.equal(false); }); - - }); - - describe('#toString', function() { - it('should convert this to a string', function() { - var str = ecdsa.toString(); - (typeof str === 'string').should.equal(true); + 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('signing and verification', function() { - describe('@sign', function() { - it('should produce a signature', function() { - var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); - (sig instanceof Signature).should.equal(true); - }); - it('should produce a signature, and be different when called twice', function() { - ecdsa.signRandomK(); - should.exist(ecdsa.sig); + describe('vectors', function() { + + vectors.valid.forEach(function(obj, i) { + it('should validate valid vector ' + i, function() { + var ecdsa = ECDSA().set({ + privkey: new Privkey(BN.fromBuffer(new Buffer(obj.d, 'hex'))), + k: BN.fromBuffer(new Buffer(obj.k, 'hex')), + hashbuf: Hash.sha256(new Buffer(obj.message)), + sig: new Signature().set({ + r: BN(obj.signature.r), + s: BN(obj.signature.s), + i: obj.i + }) + }); var ecdsa2 = ECDSA(ecdsa); - ecdsa2.signRandomK(); - ecdsa.sig.toString().should.not.equal(ecdsa2.sig.toString()); + 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); }); }); - describe('#verify', function() { - it('should verify a signature that was just signed', function() { - ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c' + - '40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827'); - ecdsa.verify().verified.should.equal(true); - }); - it('should verify this known good signature', function() { - ecdsa.signRandomK(); - ecdsa.verify().verified.should.equal(true); - }); - it('should verify a valid signature, and unverify an invalid signature', function() { - var sig = ECDSA.sign(ecdsa.hashbuf, ecdsa.privkey); - ECDSA.verify(ecdsa.hashbuf, sig, ecdsa.pubkey).should.equal(true); - var fakesig = new Signature(sig.r.add(1), sig.s); - 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: new Privkey(BN().fromBuffer(new Buffer(obj.d, 'hex'))), - k: BN().fromBuffer(new Buffer(obj.k, 'hex')), - hashbuf: Hash.sha256(new Buffer(obj.message)), - sig: new 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: new Signature(BN(obj.signature.r), BN(obj.signature.s)), - hashbuf: Hash.sha256(new Buffer(obj.message)) - }); - ecdsa.sigError().should.equal(obj.exception); + 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: new Signature(BN(obj.signature.r), BN(obj.signature.s)), + hashbuf: Hash.sha256(new Buffer(obj.message)) }); + ecdsa.sigError().should.equal(obj.exception); }); }); }); diff --git a/test/script/interpreter.js b/test/script/interpreter.js index 05e8f01e6..06084bbf2 100644 --- a/test/script/interpreter.js +++ b/test/script/interpreter.js @@ -201,7 +201,7 @@ describe('Interpreter', function() { var hashbuf = new Buffer(32); hashbuf.fill(0); - var credtx = Transaction(); + var credtx = new Transaction(); credtx.uncheckedAddInput(new Transaction.Input({ prevTxId: '0000000000000000000000000000000000000000000000000000000000000000', outputIndex: 0xffffffff, @@ -214,7 +214,7 @@ describe('Interpreter', function() { })); var idbuf = credtx.id; - var spendtx = Transaction(); + var spendtx = new Transaction(); spendtx.uncheckedAddInput(new Transaction.Input({ prevTxId: idbuf.toString('hex'), outputIndex: 0, @@ -222,11 +222,11 @@ describe('Interpreter', function() { script: scriptSig })); spendtx.addOutput(new Transaction.Output({ - script: Script(), + script: new Script(), satoshis: 0 })); - var interp = Interpreter(); + var interp = new Interpreter(); var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, flags); verified.should.equal(expected); }; @@ -251,7 +251,7 @@ describe('Interpreter', function() { testAllFixtures(script_invalid, false); }); - describe('bitcoind transaction evaluation fixtures', function() { + describe.only('bitcoind transaction evaluation fixtures', function() { var test_txs = function(set, expected) { var c = 0; set.forEach(function(vector) { @@ -275,7 +275,7 @@ describe('Interpreter', function() { map[txid + ':' + txoutnum] = Script.fromBitcoindString(scriptPubKeyStr); }); - var tx = Transaction(txhex); + var tx = new Transaction(txhex); var allInputsVerified = true; tx.inputs.forEach(function(txin, j) { var scriptSig = txin.script; @@ -284,7 +284,7 @@ describe('Interpreter', function() { var scriptPubkey = map[txidhex + ':' + txoutnum]; should.exist(scriptPubkey); should.exist(scriptSig); - var interp = Interpreter(); + var interp = new Interpreter(); var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags); if (!verified) { allInputsVerified = false; From fb57a6066695e9fa22824648b970652697b4057e Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 19 Dec 2014 18:53:21 -0300 Subject: [PATCH 3/6] fix all tests --- lib/transaction/sighash.js | 4 ++-- test/script/interpreter.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/transaction/sighash.js b/lib/transaction/sighash.js index 30ebf6f06..03c2485aa 100644 --- a/lib/transaction/sighash.js +++ b/lib/transaction/sighash.js @@ -73,7 +73,7 @@ var sighash = function sighash(transaction, sighashType, inputNumber, subscript) for (i = 0; i < inputNumber; i++) { txcopy.outputs[i] = new Output({ - satoshis: BN().fromBuffer(new buffer.Buffer(BITS_64_ON, 'hex')), + satoshis: BN.fromBuffer(new buffer.Buffer(BITS_64_ON, 'hex')), script: Script.empty() }); } @@ -83,7 +83,7 @@ var sighash = function sighash(transaction, sighashType, inputNumber, subscript) txcopy.inputs = [txcopy.inputs[inputNumber]]; } - var buf = BufferWriter() + var buf = new BufferWriter() .write(txcopy.toBuffer()) .writeInt32LE(sighashType) .toBuffer(); diff --git a/test/script/interpreter.js b/test/script/interpreter.js index 06084bbf2..46bda124c 100644 --- a/test/script/interpreter.js +++ b/test/script/interpreter.js @@ -251,7 +251,7 @@ describe('Interpreter', function() { testAllFixtures(script_invalid, false); }); - describe.only('bitcoind transaction evaluation fixtures', function() { + describe('bitcoind transaction evaluation fixtures', function() { var test_txs = function(set, expected) { var c = 0; set.forEach(function(vector) { From 686bc091babfa077a7e7a713f99ffe9fdd51be5a Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 19 Dec 2014 18:54:10 -0300 Subject: [PATCH 4/6] remove dead code --- lib/crypto/ecdsa.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/crypto/ecdsa.js b/lib/crypto/ecdsa.js index 9bd10bf98..e9060a00e 100644 --- a/lib/crypto/ecdsa.js +++ b/lib/crypto/ecdsa.js @@ -58,23 +58,6 @@ ECDSA.prototype.calci = function() { ECDSA.fromString = function(str) { var obj = JSON.parse(str); return new ECDSA(obj); - - if (obj.hashbuf) { - this.hashbuf = new Buffer(obj.hashbuf, 'hex'); - } - if (obj.pubkey) { - this.pubkey = PublicKey.fromString(obj.pubkey); - } - if (obj.privkey) { - this.privkey = PrivateKey.fromString(obj.privkey); - } - if (obj.sig) { - this.sig = Signature.fromString(obj.sig); - } - if (obj.k) { - this.k = BN(obj.k, 10); - } - return this; }; ECDSA.prototype.randomK = function() { From aa6ecfd79bea1dc2f12d64cdf19e4b5530220167 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 19 Dec 2014 19:23:30 -0300 Subject: [PATCH 5/6] hash 100% coverage --- lib/crypto/ecdsa.js | 91 +++++++++++++++++++++++++-------------------- test/crypto/hash.js | 84 +++++++++++++++++++++-------------------- 2 files changed, 94 insertions(+), 81 deletions(-) diff --git a/lib/crypto/ecdsa.js b/lib/crypto/ecdsa.js index e9060a00e..6e5c09146 100644 --- a/lib/crypto/ecdsa.js +++ b/lib/crypto/ecdsa.js @@ -4,10 +4,11 @@ var BN = require('./bn'); var Point = require('./point'); var Signature = require('./signature'); var PublicKey = require('../publickey'); -var PrivateKey = require('../privatekey'); var Random = require('./random'); var Hash = require('./hash'); var BufferUtil = require('../util/buffer'); +var _ = require('lodash'); +var $ = require('../util/preconditions'); var ECDSA = function ECDSA(obj) { if (!(this instanceof ECDSA)) { @@ -70,8 +71,13 @@ ECDSA.prototype.randomK = function() { return this; }; + // https://tools.ietf.org/html/rfc6979#section-3.2 ECDSA.prototype.deterministicK = function(badrs) { + /* jshint maxstatements: 25 */ + if (_.isUndefined(badrs)) { + badrs = 0; + } var v = new Buffer(32); v.fill(0x01); var k = new Buffer(32); @@ -87,11 +93,6 @@ ECDSA.prototype.deterministicK = function(badrs) { 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); @@ -107,10 +108,9 @@ ECDSA.prototype.deterministicK = function(badrs) { // 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 ECDSA.prototype.toPublicKey = function() { + /* jshint maxstatements: 25 */ var i = this.sig.i; - if (!(i === 0 || i === 1 || i === 2 || i === 3)) { - throw new Error('i must be equal to 0, 1, 2, or 3'); - } + $.checkArgument(i === 0 || i === 1 || i === 2 || i === 3, new Error('i must be equal to 0, 1, 2, or 3')); var e = BN.fromBuffer(this.hashbuf); var r = this.sig.r; @@ -153,7 +153,8 @@ ECDSA.prototype.toPublicKey = function() { }; ECDSA.prototype.sigError = function() { - if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) { + /* jshint maxstatements: 25 */ + if (!BufferUtil.isBuffer(this.hashbuf) || this.hashbuf.length !== 32) { return 'hashbuf must be a 32 byte buffer'; } @@ -172,35 +173,29 @@ ECDSA.prototype.sigError = function() { var u2 = sinv.mul(r).mod(n); var p = Point.getG().mulAdd(u1, this.pubkey.point, u2); - if (p.isInfinity()) + if (p.isInfinity()) { return 'p is infinity'; + } - if (!(p.getX().mod(n).cmp(r) === 0)) + if (p.getX().mod(n).cmp(r) !== 0) { return 'Invalid signature'; - else + } else { return false; + } }; -ECDSA.prototype.sign = function() { - var hashbuf = this.hashbuf; - var privkey = this.privkey; - - var d = privkey.bn; - - if (!hashbuf || !privkey || !d) { - throw new Error('invalid parameters'); - } - - if (!BufferUtil.isBuffer(hashbuf) || hashbuf.length !== 32) { - throw new Error('hashbuf must be a 32 byte buffer'); +ECDSA.toLowS = function(s) { + //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); } + return s; +}; +ECDSA.prototype._findSignature = function(d, e) { var N = Point.getN(); var G = Point.getG(); - var e = BN.fromBuffer(hashbuf, this.endian ? { - endian: this.endian - } : undefined); - // try different values of k until r, s are valid var badrs = 0; var k, Q, r, s; @@ -215,17 +210,30 @@ ECDSA.prototype.sign = function() { s = k.invm(N).mul(e.add(d.mul(r))).mod(N); } while (r.cmp(0) <= 0 || s.cmp(0) <= 0); - //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 = ECDSA.toLowS(s); + return { s: s, - compressed: this.pubkey.compressed - }); + r: r + }; + +}; + +ECDSA.prototype.sign = function() { + var hashbuf = this.hashbuf; + var privkey = this.privkey; + var d = privkey.bn; + + $.checkState(hashbuf && privkey && d, new Error('invalid parameters')); + $.checkState(BufferUtil.isBuffer(hashbuf) && hashbuf.length === 32, new Error('hashbuf must be a 32 byte buffer')); + + var e = BN.fromBuffer(hashbuf, this.endian ? { + endian: this.endian + } : undefined); + + var obj = this._findSignature(d, e); + obj.compressed = this.pubkey.compressed; + + this.sig = new Signature(obj); return this; }; @@ -255,10 +263,11 @@ ECDSA.prototype.toString = function() { }; ECDSA.prototype.verify = function() { - if (!this.sigError()) + if (!this.sigError()) { this.verified = true; - else + } else { this.verified = false; + } return this; }; diff --git a/test/crypto/hash.js b/test/crypto/hash.js index 7509d924e..c079feee2 100644 --- a/test/crypto/hash.js +++ b/test/crypto/hash.js @@ -10,121 +10,125 @@ describe('Hash', function() { describe('@sha1', function() { - it('should calculate the hash of this buffer correctly', function() { + it('calculates the hash of this buffer correctly', function() { var hash = Hash.sha1(buf); hash.toString('hex').should.equal('de69b8a4a5604d0486e6420db81e39eb464a17b2'); hash = Hash.sha1(new Buffer(0)); hash.toString('hex').should.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709'); }); - it('should throw an error when the input is not a buffer', function() { - (function() { - Hash.sha1(str); - }).should.throw('Invalid Argument'); + it('throws an error when the input is not a buffer', function() { + Hash.sha1.bind(Hash, str).should.throw('Invalid Argument'); }); }); describe('#sha256', function() { - it('should calculate the hash of this buffer correctly', function() { + it('calculates the hash of this buffer correctly', function() { var hash = Hash.sha256(buf); hash.toString('hex').should.equal('6f2c7b22fd1626998287b3636089087961091de80311b9279c4033ec678a83e8'); }); - it('should throw an error when the input is not a buffer', function() { - (function() { - Hash.sha256(str); - }).should.throw('Invalid Argument'); + it('fails when the input is not a buffer', function() { + Hash.sha256.bind(Hash, str).should.throw('Invalid Argument'); }); }); describe('#sha256hmac', function() { - it('should compute this known empty test vector correctly', function() { - var key = new Buffer(''); + it('computes this known big key correctly', function() { + var key = new Buffer('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad' + + 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad' + + 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad' + + 'b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); var data = new Buffer(''); - Hash.sha256hmac(data, key).toString('hex').should.equal('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); + Hash.sha256hmac(data, key).toString('hex') + .should.equal('fb1f87218671f1c0c4593a88498e02b6dfe8afd814c1729e89a1f1f6600faa23'); }); - it('should compute this known non-empty test vector correctly', function() { + it('computes this known empty test vector correctly', function() { + var key = new Buffer(''); + var data = new Buffer(''); + Hash.sha256hmac(data, key).toString('hex') + .should.equal('b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad'); + }); + + it('computes this known non-empty test vector correctly', function() { var key = new Buffer('key'); var data = new Buffer('The quick brown fox jumps over the lazy dog'); - Hash.sha256hmac(data, key).toString('hex').should.equal('f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'); + Hash.sha256hmac(data, key).toString('hex') + .should.equal('f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8'); }); }); describe('#sha256sha256', function() { - it('should calculate the hash of this buffer correctly', function() { + it('calculates the hash of this buffer correctly', function() { var hash = Hash.sha256sha256(buf); hash.toString('hex').should.equal('be586c8b20dee549bdd66018c7a79e2b67bb88b7c7d428fa4c970976d2bec5ba'); }); - it('should throw an error when the input is not a buffer', function() { - (function() { - Hash.sha256sha256(str); - }).should.throw('Invalid Argument'); + it('fails when the input is not a buffer', function() { + Hash.sha256sha256.bind(Hash, str).should.throw('Invalid Argument'); }); }); describe('#sha256ripemd160', function() { - it('should calculate the hash of this buffer correctly', function() { + it('calculates the hash of this buffer correctly', function() { var hash = Hash.sha256ripemd160(buf); hash.toString('hex').should.equal('7322e2bd8535e476c092934e16a6169ca9b707ec'); }); - it('should throw an error when the input is not a buffer', function() { - (function() { - Hash.sha256ripemd160(str); - }).should.throw('Invalid Argument'); + it('fails when the input is not a buffer', function() { + Hash.sha256ripemd160.bind(Hash, str).should.throw('Invalid Argument'); }); }); describe('#ripemd160', function() { - it('should calculate the hash of this buffer correctly', function() { + it('calculates the hash of this buffer correctly', function() { var hash = Hash.ripemd160(buf); hash.toString('hex').should.equal('fa0f4565ff776fee0034c713cbf48b5ec06b7f5c'); }); - it('should throw an error when the input is not a buffer', function() { - (function() { - Hash.ripemd160(str); - }).should.throw('Invalid Argument'); + it('fails when the input is not a buffer', function() { + Hash.ripemd160.bind(Hash, str).should.throw('Invalid Argument'); }); }); describe('#sha512', function() { - it('should calculate the hash of this buffer correctly', function() { + it('calculates the hash of this buffer correctly', function() { var hash = Hash.sha512(buf); - hash.toString('hex').should.equal('c0530aa32048f4904ae162bc14b9eb535eab6c465e960130005feddb71613e7d62aea75f7d3333ba06e805fc8e45681454524e3f8050969fe5a5f7f2392e31d0'); + hash.toString('hex') + .should.equal('c0530aa32048f4904ae162bc14b9eb535eab6c465e960130005fedd' + + 'b71613e7d62aea75f7d3333ba06e805fc8e45681454524e3f8050969fe5a5f7f2392e31d0'); }); - it('should throw an error when the input is not a buffer', function() { - (function() { - Hash.sha512(str); - }).should.throw('Invalid Argument'); + it('fails when the input is not a buffer', function() { + Hash.sha512.bind(Hash, str).should.throw('Invalid Argument'); }); }); describe('#sha512hmac', function() { - it('should calculate this known empty test vector correctly', function() { - var hex = 'b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47'; + it('calculates this known empty test vector correctly', function() { + var hex = 'b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4a' + + 'c6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47'; Hash.sha512hmac(new Buffer([]), new Buffer([])).toString('hex').should.equal(hex); }); - it('should calculate this known non-empty test vector correctly', function() { - var hex = 'c40bd7c15aa493b309c940e08a73ffbd28b2e4cb729eb94480d727e4df577b13cc403a78e6150d83595f3b17c4cc331f12ca5952691de3735a63c1d4c69a2bac'; + it('calculates this known non-empty test vector correctly', function() { + var hex = 'c40bd7c15aa493b309c940e08a73ffbd28b2e4cb729eb94480d727e4df577' + + 'b13cc403a78e6150d83595f3b17c4cc331f12ca5952691de3735a63c1d4c69a2bac'; var data = new Buffer('test1'); var key = new Buffer('test2'); Hash.sha512hmac(data, key).toString('hex').should.equal(hex); From 4e775264fe6392aa11af11f2d93250e30e785568 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Fri, 19 Dec 2014 19:32:26 -0300 Subject: [PATCH 6/6] lint random.js --- test/crypto/random.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/crypto/random.js b/test/crypto/random.js index 4a7c03939..cf08bc222 100644 --- a/test/crypto/random.js +++ b/test/crypto/random.js @@ -21,11 +21,14 @@ describe('Random', function() { it('should generate 100 8 byte buffers in a row that are not equal', function() { var hexs = []; - for (var i = 0; i < 100; i++) + for (var i = 0; i < 100; i++) { hexs[i] = Random.getRandomBuffer(8).toString('hex'); - for (i = 0; i < 100; i++) - for (var j = i + 1; j < 100; j++) + } + for (i = 0; i < 100; i++) { + for (var j = i + 1; j < 100; j++) { hexs[i].should.not.equal(hexs[j]); + } + } }); });