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.
This commit is contained in:
Stefan Thomas 2011-05-08 14:36:11 +01:00
parent a4de03af4d
commit 27ec74ba8a
4 changed files with 49 additions and 19 deletions

View file

@ -12,7 +12,11 @@
* Ported to JavaScript by Stefan Thomas. * Ported to JavaScript by Stefan Thomas.
*/ */
encode: function (input) { 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 = []; var chars = [];
while (bi.compareTo(B58.base) >= 0) { while (bi.compareTo(B58.base) >= 0) {
@ -55,10 +59,7 @@
} }
var bytes = bi.toByteArrayUnsigned(); var bytes = bi.toByteArrayUnsigned();
// Remove leading zeros arbitrarily added by BigInteger // Add leading zeros
while (bytes[0] == 0) bytes.shift();
// Add right amount of leading zeros
while (leadingZerosNum-- > 0) bytes.unshift(0); while (leadingZerosNum-- > 0) bytes.unshift(0);
return bytes; return bytes;

View file

@ -41,12 +41,20 @@ ECPointFp.prototype.getEncoded = function (compressed) {
ECPointFp.decodeFrom = function (curve, enc) { ECPointFp.decodeFrom = function (curve, enc) {
var type = enc.shift(); var type = enc.shift();
// Extract x and y as byte arrays
var xBa = enc.slice(0, enc.length/2); 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); var yBa = enc.slice(enc.length/2, enc.length);
// Prepend zero byte to prevent interpretation as negative integer
xBa.unshift(0);
yBa.unshift(0); yBa.unshift(0);
// Convert to BigIntegers
var x = new BigInteger(xBa);
var y = new BigInteger(yBa); var y = new BigInteger(yBa);
// Return point
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)); return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
}; };
@ -185,8 +193,7 @@ Bitcoin.ECDSA = (function () {
sign: function (hash, priv) { sign: function (hash, priv) {
var d = priv; var d = priv;
var n = ecparams.getN(); var n = ecparams.getN();
hash.unshift(0) var e = BigInteger.fromByteArrayUnsigned(hash);
var e = new BigInteger(hash);
console.log("signhash: "+ Crypto.util.bytesToHex(hash)); console.log("signhash: "+ Crypto.util.bytesToHex(hash));
console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned())); console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned()));
@ -246,13 +253,12 @@ Bitcoin.ECDSA = (function () {
// throw new Error("Extra bytes in signature"); // throw new Error("Extra bytes in signature");
var n = ecparams.getN(); var n = ecparams.getN();
hash.unshift(0) var e = BigInteger.fromByteArrayUnsigned(hash);
var e = new BigInteger(hash);
console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned())); console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned()));
var r = new BigInteger(rBa); var r = BigInteger.fromByteArrayUnsigned(rBa);
var s = new BigInteger(sBa); var s = BigInteger.fromByteArrayUnsigned(sBa);
if (r.compareTo(BigInteger.ONE) < 0 || if (r.compareTo(BigInteger.ONE) < 0 ||
r.compareTo(n) >= 0) r.compareTo(n) >= 0)

View file

@ -12,9 +12,11 @@ Bitcoin.ECKey = (function () {
// Input is a private key value // Input is a private key value
this.priv = input; this.priv = input;
} else if (Bitcoin.Util.isArray(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) { } 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));
} }
}; };

23
util.js
View file

@ -2,9 +2,28 @@
BigInteger.valueOf = nbv; BigInteger.valueOf = nbv;
BigInteger.prototype.toByteArrayUnsigned = function () { BigInteger.prototype.toByteArrayUnsigned = function () {
var ba = this.toByteArray(); var ba = this.toByteArray();
if (ba.length) {
if (ba[0] == 0) {
ba = ba.slice(1);
}
return ba.map(function (v) { return ba.map(function (v) {
return (v < 0) ? v + 256 : 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 // Console ignore
@ -48,7 +67,9 @@ Bitcoin.Util = {
}, },
valueToBigInt: function (valueBuffer) { valueToBigInt: function (valueBuffer) {
if (valueBuffer instanceof BigInteger) return 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) { formatValue: function (valueBuffer) {
var value = this.valueToBigInt(valueBuffer).toString(); var value = this.valueToBigInt(valueBuffer).toString();