refactoring interpreter

This commit is contained in:
Manuel Araoz 2014-12-19 18:28:52 -03:00
parent 370f69df88
commit 6c518f1f45
10 changed files with 356 additions and 372 deletions

View File

@ -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'
});
};

View File

@ -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,

View File

@ -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;
};

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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');
});
});

View File

@ -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);
});
});
});

View File

@ -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;