From 27ec74ba8a16ace28fbbbf53b756da0ad6fd24c5 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Sun, 8 May 2011 14:36:11 +0100 Subject: [PATCH] Fixed all cases of unsafe BigInteger <-> byte[] conversion. The BigInteger class we're using supports negative integers and encodes them with a sign bit. Since in our application we are dealing with unsigned integers only, we need to disable this functionality by creating two wrapper functions toByteArrayUnsigned and fromByteArrayUnsigned. --- base58.js | 11 ++++++----- ecdsa.js | 22 ++++++++++++++-------- eckey.js | 6 ++++-- util.js | 29 +++++++++++++++++++++++++---- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/base58.js b/base58.js index 88fd2b8..4bbecca 100644 --- a/base58.js +++ b/base58.js @@ -12,7 +12,11 @@ * Ported to JavaScript by Stefan Thomas. */ encode: function (input) { - var bi = new BigInteger(input); + console.log(input); + // We prepend the input with a zero byte because the BigInteger + // implementation treats it as a negative number if any of the + // four most significant bits are set. + var bi = BigInteger.fromByteArrayUnsigned(input); var chars = []; while (bi.compareTo(B58.base) >= 0) { @@ -55,10 +59,7 @@ } var bytes = bi.toByteArrayUnsigned(); - // Remove leading zeros arbitrarily added by BigInteger - while (bytes[0] == 0) bytes.shift(); - - // Add right amount of leading zeros + // Add leading zeros while (leadingZerosNum-- > 0) bytes.unshift(0); return bytes; diff --git a/ecdsa.js b/ecdsa.js index 1c66791..e0077a7 100644 --- a/ecdsa.js +++ b/ecdsa.js @@ -41,12 +41,20 @@ ECPointFp.prototype.getEncoded = function (compressed) { ECPointFp.decodeFrom = function (curve, enc) { var type = enc.shift(); + + // Extract x and y as byte arrays var xBa = enc.slice(0, enc.length/2); - xBa.unshift(0); - var x = new BigInteger(xBa); var yBa = enc.slice(enc.length/2, enc.length); + + // Prepend zero byte to prevent interpretation as negative integer + xBa.unshift(0); yBa.unshift(0); + + // Convert to BigIntegers + var x = new BigInteger(xBa); var y = new BigInteger(yBa); + + // Return point return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); }; @@ -185,8 +193,7 @@ Bitcoin.ECDSA = (function () { sign: function (hash, priv) { var d = priv; var n = ecparams.getN(); - hash.unshift(0) - var e = new BigInteger(hash); + var e = BigInteger.fromByteArrayUnsigned(hash); console.log("signhash: "+ Crypto.util.bytesToHex(hash)); console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned())); @@ -246,13 +253,12 @@ Bitcoin.ECDSA = (function () { // throw new Error("Extra bytes in signature"); var n = ecparams.getN(); - hash.unshift(0) - var e = new BigInteger(hash); + var e = BigInteger.fromByteArrayUnsigned(hash); console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned())); - var r = new BigInteger(rBa); - var s = new BigInteger(sBa); + var r = BigInteger.fromByteArrayUnsigned(rBa); + var s = BigInteger.fromByteArrayUnsigned(sBa); if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) diff --git a/eckey.js b/eckey.js index 3ebd5f7..5d8e763 100644 --- a/eckey.js +++ b/eckey.js @@ -12,9 +12,11 @@ Bitcoin.ECKey = (function () { // Input is a private key value this.priv = input; } else if (Bitcoin.Util.isArray(input)) { - this.priv = new BigInteger(input); + // Prepend zero byte to prevent interpretation as negative integer + this.priv = BigInteger.fromByteArrayUnsigned(input); } else if ("string" == typeof input) { - this.priv = new BigInteger(Crypto.util.base64ToBytes(input)); + // Prepend zero byte to prevent interpretation as negative integer + this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input)); } }; diff --git a/util.js b/util.js index 97f594d..f58333a 100644 --- a/util.js +++ b/util.js @@ -2,9 +2,28 @@ BigInteger.valueOf = nbv; BigInteger.prototype.toByteArrayUnsigned = function () { var ba = this.toByteArray(); - return ba.map(function (v) { - return (v < 0) ? v + 256 : v; - }); + if (ba.length) { + if (ba[0] == 0) { + ba = ba.slice(1); + } + return ba.map(function (v) { + return (v < 0) ? v + 256 : v; + }); + } else { + // Empty array, nothing to do + return ba; + } +}; +BigInteger.fromByteArrayUnsigned = function (ba) { + if (!ba.length) { + return ba.valueOf(0); + } else if (ba[0] & 0x80) { + // Prepend a zero so the BigInteger class doesn't mistake this + // for a negative integer. + return new BigInteger([0].concat(ba)); + } else { + return new BigInteger(ba); + } }; // Console ignore @@ -48,7 +67,9 @@ Bitcoin.Util = { }, valueToBigInt: function (valueBuffer) { if (valueBuffer instanceof BigInteger) return valueBuffer; - return new BigInteger(valueBuffer); + + // Prepend zero byte to prevent interpretation as negative integer + return BigInteger.fromByteArrayUnsigned(valueBuffer); }, formatValue: function (valueBuffer) { var value = this.valueToBigInt(valueBuffer).toString();