bitcore/lib/crypto/bn.js

203 lines
4.9 KiB
JavaScript
Raw Normal View History

2014-11-20 07:16:27 -08:00
'use strict';
2015-02-05 11:17:46 -08:00
var BN = require('bn.js');
2014-12-19 12:10:58 -08:00
var $ = require('../util/preconditions');
var _ = require('lodash');
var reversebuf = function(buf) {
var buf2 = new Buffer(buf.length);
for (var i = 0; i < buf.length; i++) {
2014-12-19 12:10:58 -08:00
buf2[i] = buf[buf.length - 1 - i];
}
return buf2;
};
BN.Zero = new BN(0);
BN.One = new BN(1);
BN.Minus1 = new BN(-1);
2014-12-19 12:10:58 -08:00
BN.fromNumber = function(n) {
$.checkArgument(_.isNumber(n));
return new BN(n);
};
2015-05-27 11:11:43 -07:00
BN.fromString = function(str, base) {
2014-12-19 12:10:58 -08:00
$.checkArgument(_.isString(str));
2015-05-27 11:11:43 -07:00
return new BN(str, base);
};
2014-09-17 15:32:58 -07:00
BN.fromBuffer = function(buf, opts) {
if (typeof opts !== 'undefined' && opts.endian === 'little') {
buf = reversebuf(buf);
}
var hex = buf.toString('hex');
2014-09-17 15:32:58 -07:00
var bn = new BN(hex, 16);
return bn;
};
2015-02-05 11:17:46 -08:00
/**
* Instantiate a BigNumber from a "signed magnitude buffer"
* (a buffer where the most significant bit represents the sign (0 = positive, -1 = negative))
*/
BN.fromSM = function(buf, opts) {
var ret;
if (buf.length === 0) {
return BN.fromBuffer(new Buffer([0]));
}
2014-08-13 16:23:45 -07:00
2015-02-05 11:17:46 -08:00
var endian = 'big';
if (opts) {
endian = opts.endian;
2014-12-19 12:10:58 -08:00
}
2015-02-05 11:17:46 -08:00
if (endian === 'little') {
buf = reversebuf(buf);
2014-12-19 12:10:58 -08:00
}
2015-02-05 11:17:46 -08:00
if (buf[0] & 0x80) {
buf[0] = buf[0] & 0x7f;
ret = BN.fromBuffer(buf);
ret.neg().copy(ret);
} else {
ret = BN.fromBuffer(buf);
}
return ret;
};
BN.prototype.toNumber = function() {
return parseInt(this.toString(10), 10);
2014-08-13 16:23:45 -07:00
};
2014-09-17 15:32:58 -07:00
BN.prototype.toBuffer = function(opts) {
2014-12-19 12:10:58 -08:00
var buf, hex;
if (opts && opts.size) {
2014-12-19 12:10:58 -08:00
hex = this.toString(16, 2);
var natlen = hex.length / 2;
buf = new Buffer(hex, 'hex');
2014-12-19 12:10:58 -08:00
if (natlen === opts.size) {
buf = buf;
2014-12-19 12:10:58 -08:00
} else if (natlen > opts.size) {
2014-12-19 13:28:52 -08:00
buf = BN.trim(buf, natlen);
2014-12-19 12:10:58 -08:00
} else if (natlen < opts.size) {
buf = BN.pad(buf, natlen, opts.size);
}
2014-12-19 12:10:58 -08:00
} else {
hex = this.toString(16, 2);
buf = new Buffer(hex, 'hex');
}
if (typeof opts !== 'undefined' && opts.endian === 'little') {
buf = reversebuf(buf);
}
return buf;
};
2014-12-19 12:10:58 -08:00
BN.prototype.toSMBigEndian = function() {
var buf;
if (this.cmp(BN.Zero) === -1) {
buf = this.neg().toBuffer();
2014-12-19 12:10:58 -08:00
if (buf[0] & 0x80) {
buf = Buffer.concat([new Buffer([0x80]), buf]);
2014-12-19 12:10:58 -08:00
} else {
buf[0] = buf[0] | 0x80;
2014-12-19 12:10:58 -08:00
}
} else {
buf = this.toBuffer();
2014-12-19 12:10:58 -08:00
if (buf[0] & 0x80) {
buf = Buffer.concat([new Buffer([0x00]), buf]);
2014-12-19 12:10:58 -08:00
}
}
2014-12-19 12:10:58 -08:00
if (buf.length === 1 & buf[0] === 0) {
buf = new Buffer([]);
2014-12-19 12:10:58 -08:00
}
return buf;
};
2015-02-05 11:17:46 -08:00
2014-12-19 12:10:58 -08:00
BN.prototype.toSM = function(opts) {
var endian = opts ? opts.endian : 'big';
var buf = this.toSMBigEndian();
2014-12-19 12:10:58 -08:00
if (endian === 'little') {
buf = reversebuf(buf);
2014-12-19 12:10:58 -08:00
}
return buf;
};
2015-02-05 11:17:46 -08:00
/**
* Create a BN from a "ScriptNum":
* This is analogous to the constructor for CScriptNum in bitcoind. Many ops in
* bitcoind's script interpreter use CScriptNum, which is not really a proper
* bignum. Instead, an error is thrown if trying to input a number bigger than
2015-07-01 08:58:34 -07:00
* 4 bytes. We copy that behavior here. A third argument, `size`, is provided to
* extend the hard limit of 4 bytes, as some usages require more than 4 bytes.
2015-02-05 11:17:46 -08:00
*/
2015-07-01 08:58:34 -07:00
BN.fromScriptNumBuffer = function(buf, fRequireMinimal, size) {
var nMaxNumSize = size || 4;
2014-12-19 12:10:58 -08:00
$.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.
//
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if ((buf[buf.length - 1] & 0x7f) === 0) {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set
// it would conflict with the sign bit. An example of this case
// is +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if (buf.length <= 1 || (buf[buf.length - 2] & 0x80) === 0) {
2014-12-19 12:10:58 -08:00
throw new Error('non-minimally encoded script number');
}
}
}
2014-12-19 13:28:52 -08:00
return BN.fromSM(buf, {
2014-12-19 12:10:58 -08:00
endian: 'little'
});
};
2015-02-05 11:17:46 -08:00
/**
* 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).
*/
2014-12-19 12:10:58 -08:00
BN.prototype.toScriptNumBuffer = function() {
return this.toSM({
endian: 'little'
});
};
BN.prototype.gt = function(b) {
return this.cmp(b) > 0;
};
2015-07-01 08:58:34 -07:00
BN.prototype.gte = function(b) {
return this.cmp(b) >= 0;
};
BN.prototype.lt = function(b) {
return this.cmp(b) < 0;
};
2015-02-05 11:17:46 -08:00
BN.trim = function(buf, natlen) {
return buf.slice(natlen - buf.length, buf.length);
};
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;
};
2014-09-17 15:32:58 -07:00
module.exports = BN;