refactor BN and ECDSA
This commit is contained in:
parent
56591db68d
commit
370f69df88
138
lib/crypto/bn.js
138
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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue