From 1a82686f659fa70b09abbcd7b92dd79f95de35a1 Mon Sep 17 00:00:00 2001 From: Kirill Fomichev Date: Sun, 21 Feb 2021 21:11:06 +0300 Subject: [PATCH] Fix LE constructor for HEX --- lib/bn.js | 121 ++++++++++++++++++++------------------- test/constructor-test.js | 14 ++++- 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/lib/bn.js b/lib/bn.js index 982f6383..fcfa26bb 100644 --- a/lib/bn.js +++ b/lib/bn.js @@ -91,23 +91,19 @@ var start = 0; if (number[0] === '-') { start++; - } - - if (base === 16) { - this._parseHex(number, start); - } else { - this._parseBase(number, base, start); - } - - if (number[0] === '-') { this.negative = 1; } - this._strip(); - - if (endian !== 'le') return; - - this._initArray(this.toArray(), base, endian); + if (start < number.length) { + if (base === 16) { + this._parseHex(number, start, endian); + } else { + this._parseBase(number, base, start); + if (endian === 'le') { + this._initArray(this.toArray(), base, endian); + } + } + } }; BN.prototype._initNumber = function _initNumber (number, base, endian) { @@ -183,39 +179,31 @@ return this._strip(); }; - function parseHex (str, start, end) { - var r = 0; - var len = Math.min(str.length, end); - var z = 0; - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r <<= 4; - - var b; - - // 'a' - 'f' - if (c >= 49 && c <= 54) { - b = c - 49 + 0xa; - - // 'A' - 'F' - } else if (c >= 17 && c <= 22) { - b = c - 17 + 0xa; - - // '0' - '9' - } else { - b = c; - } - - r |= b; - z |= b; + function parseHex4Bits (string, index) { + var c = string.charCodeAt(index); + // '0' - '9' + if (c >= 48 && c <= 57) { + return c - 48; + // 'A' - 'F' + } else if (c >= 65 && c <= 70) { + return c - 55; + // 'a' - 'f' + } else if (c >= 97 && c <= 102) { + return c - 87; + } else { + assert(false, 'Invalid character in ' + string); } + } - assert(!(z & 0xf0), 'Invalid character in ' + str); + function parseHexByte (string, lowerBound, index) { + var r = parseHex4Bits(string, index); + if (index - 1 >= lowerBound) { + r |= parseHex4Bits(string, index - 1) << 4; + } return r; } - BN.prototype._parseHex = function _parseHex (number, start) { + BN.prototype._parseHex = function _parseHex (number, start, endian) { // Create possibly bigger array to ensure that it fits the number this.length = Math.ceil((number.length - start) / 6); this.words = new Array(this.length); @@ -223,25 +211,38 @@ this.words[i] = 0; } - var j, w; - // Scan 24-bit chunks and add them to the number + // 24-bits chunks var off = 0; - for (i = number.length - 6, j = 0; i >= start; i -= 6) { - w = parseHex(number, i, i + 6); - this.words[j] |= (w << off) & 0x3ffffff; - // NOTE: `0x3fffff` is intentional here, 26bits max shift + 24bit hex limb - this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - if (i + 6 !== start) { - w = parseHex(number, start, i + 6); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; + var j = 0; + + var w; + if (endian === 'be') { + for (i = number.length - 1; i >= start; i -= 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + this.words[j + 1] |= w >>> 26; + off -= 18; + j += 1; + } else { + off += 8; + } + } + } else { + var parseLength = number.length - start; + for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + this.words[j + 1] |= w >>> 26; + off -= 18 + j += 1 + } else { + off += 8; + } + } } + this._strip(); }; @@ -315,6 +316,8 @@ this._iaddn(word); } } + + this._strip(); }; BN.prototype.copy = function copy (dest) { diff --git a/test/constructor-test.js b/test/constructor-test.js index 12be5b03..0bd256b8 100644 --- a/test/constructor-test.js +++ b/test/constructor-test.js @@ -91,6 +91,12 @@ describe('BN.js/Constructor', function () { 'df8c5d766b1a'); }); + it('should accept base-16 LE integer with leading zeros', function () { + assert.equal(new BN('0010', 16, 'le').toNumber(), 4096); + assert.equal(new BN('-010', 16, 'le').toNumber(), -4096); + assert.equal(new BN('010', 16, 'le').toNumber(), 4096); + }); + it('should not accept wrong characters for base', function () { assert.throws(function () { return new BN('01FF'); @@ -131,6 +137,7 @@ describe('BN.js/Constructor', function () { }); it('should import/export big endian', function () { + assert.equal(new BN([0, 1], 16).toString(16), '1'); assert.equal(new BN([1, 2, 3]).toString(16), '10203'); assert.equal(new BN([1, 2, 3, 4]).toString(16), '1020304'); assert.equal(new BN([1, 2, 3, 4, 5]).toString(16), '102030405'); @@ -142,9 +149,10 @@ describe('BN.js/Constructor', function () { }); it('should import little endian', function () { - assert.equal(new BN([1, 2, 3], 10, 'le').toString(16), '30201'); - assert.equal(new BN([1, 2, 3, 4], 10, 'le').toString(16), '4030201'); - assert.equal(new BN([1, 2, 3, 4, 5], 10, 'le').toString(16), + assert.equal(new BN([0, 1], 16, 'le').toString(16), '100'); + assert.equal(new BN([1, 2, 3], 16, 'le').toString(16), '30201'); + assert.equal(new BN([1, 2, 3, 4], 16, 'le').toString(16), '4030201'); + assert.equal(new BN([1, 2, 3, 4, 5], 16, 'le').toString(16), '504030201'); assert.equal(new BN([1, 2, 3, 4, 5, 6, 7, 8], 'le').toString(16), '807060504030201');