bitcoinjs-lib/ecdsa.js
Stefan Thomas 27ec74ba8a 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.
2011-05-08 14:36:11 +01:00

300 lines
8.7 KiB
JavaScript

function integerToBytes(i, len) {
var bytes = i.toByteArrayUnsigned();
if (len < bytes.length) {
bytes = bytes.slice(bytes.length-len);
} else while (len > bytes.length) {
bytes.unshift(0);
}
return bytes;
};
ECFieldElementFp.prototype.getByteLength = function () {
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
};
ECPointFp.prototype.getEncoded = function (compressed) {
var x = this.getX().toBigInteger();
var y = this.getY().toBigInteger();
if (compressed) {
var PC;
}
var len = this.getX().getByteLength();
var enc = integerToBytes(x, len);
if (compressed) {
if (y.testBit(0)) {
enc.unshift(0x02);
} else {
enc.unshift(0x03);
}
} else {
enc.unshift(0x04);
enc = enc.concat(integerToBytes(y, len));
}
return enc;
};
ECPointFp.decodeFrom = function (curve, enc) {
var type = enc.shift();
// Extract x and y as byte arrays
var xBa = enc.slice(0, enc.length/2);
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));
};
ECPointFp.prototype.add2D = function (b) {
if(this.isInfinity()) return b;
if(b.isInfinity()) return this;
if (this.x.equals(b.x)) {
if (this.y.equals(b.y)) {
// this = b, i.e. this must be doubled
return this.twice();
}
// this = -b, i.e. the result is the point at infinity
return this.curve.getInfinity();
}
var x_x = b.x.subtract(this.x);
var y_y = b.y.subtract(this.y);
var gamma = y_y.divide(x_x);
/*console.log("b.y: ", Crypto.util.bytesToHex(b.y.toBigInteger().toByteArrayUnsigned()));
console.log("this.y: ", Crypto.util.bytesToHex(this.y.toBigInteger().toByteArrayUnsigned()));
console.log("b.y-this.y: ", Crypto.util.bytesToHex(b.y.subtract(this.y).toBigInteger().toByteArrayUnsigned()));*/
var x3 = gamma.square().subtract(this.x).subtract(b.x);
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3);
};
ECPointFp.prototype.twice2D = function () {
if (this.isInfinity()) return this;
if (this.y.toBigInteger().signum() == 0) {
// if y1 == 0, then (x1, y1) == (x1, -y1)
// and hence this = -this and thus 2(x1, y1) == infinity
return this.curve.getInfinity();
}
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3));
var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO));
var x3 = gamma.square().subtract(this.x.multiply(TWO));
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3);
};
ECPointFp.prototype.multiply2D = function (k) {
if(this.isInfinity()) return this;
if(k.signum() == 0) return this.curve.getInfinity();
var e = k;
var h = e.multiply(new BigInteger("3"));
var neg = this.negate();
var R = this;
var i;
for (i = h.bitLength() - 2; i > 0; --i) {
R = R.twice();
var hBit = h.testBit(i);
var eBit = e.testBit(i);
if (hBit != eBit) {
R = R.add2D(hBit ? this : neg);
}
}
return R;
};
function dmp(v) {
if (!(v instanceof BigInteger)) v = v.toBigInteger();
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
};
Bitcoin.ECDSA = (function () {
var ecparams = getSECCurveByName("secp256k1");
var rng = new SecureRandom();
function implShamirsTrick(P, k, Q, l)
{
var m = Math.max(k.bitLength(), l.bitLength());
var Z = P.add2D(Q);
var R = P.curve.getInfinity();
console.log(P.curve, Q.curve);
console.log("Px: ", Crypto.util.bytesToHex(P.x.toBigInteger().toByteArrayUnsigned()));
console.log("Py: ", Crypto.util.bytesToHex(P.y.toBigInteger().toByteArrayUnsigned()));
console.log("Pz: ", Crypto.util.bytesToHex(P.z.toByteArrayUnsigned()));
console.log("Qx: ", Crypto.util.bytesToHex(Q.x.toBigInteger().toByteArrayUnsigned()));
console.log("Qy: ", Crypto.util.bytesToHex(Q.y.toBigInteger().toByteArrayUnsigned()));
console.log("Qz: ", Crypto.util.bytesToHex(Q.z.toByteArrayUnsigned()));
console.log("Zx: ", Crypto.util.bytesToHex(Z.x.toBigInteger().toByteArrayUnsigned()));
console.log("Zy: ", Crypto.util.bytesToHex(Z.y.toBigInteger().toByteArrayUnsigned()));
console.log("Zz: ", Crypto.util.bytesToHex(Z.z.toByteArrayUnsigned()));
for (var i = m - 1; i >= 0; --i) {
R = R.twice2D();
R.z = BigInteger.ONE;
if (k.testBit(i)) {
if (l.testBit(i)) {
R = R.add2D(Z);
if (i > (m-5)) console.log("RC: 1");
} else {
R = R.add2D(P);
if (i > (m-5)) console.log("RC: 2");
}
} else {
if (l.testBit(i)) {
R = R.add2D(Q);
if (i > (m-5)) console.log("RC: 3");
} else {
if (i > (m-5)) console.log("RC: 4");
}
}
if (i > (m-5)) {
console.log("Rx: ", Crypto.util.bytesToHex(R.x.toBigInteger().toByteArrayUnsigned()));
console.log("Ry: ", Crypto.util.bytesToHex(R.y.toBigInteger().toByteArrayUnsigned()));
console.log("Rz: ", Crypto.util.bytesToHex(R.z.toByteArrayUnsigned()));
}
}
return R;
};
var ECDSA = {
getBigRandom: function (limit) {
return new BigInteger(limit.bitLength(), rng)
.mod(limit.subtract(BigInteger.ONE))
.add(BigInteger.ONE)
;
},
sign: function (hash, priv) {
var d = priv;
var n = ecparams.getN();
var e = BigInteger.fromByteArrayUnsigned(hash);
console.log("signhash: "+ Crypto.util.bytesToHex(hash));
console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned()));
do {
var k = ECDSA.getBigRandom(n);
var G = ecparams.getG();
var Q = G.multiply(k);
var r = Q.getX().toBigInteger().mod(n);
} while (r.compareTo(BigInteger.ZERO) <= 0);
console.log("k: "+ Crypto.util.bytesToHex(k.toByteArrayUnsigned()));
console.log("r: "+ Crypto.util.bytesToHex(r.toByteArrayUnsigned()));
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
console.log("d*r: "+ Crypto.util.bytesToHex(d.multiply(r).toByteArrayUnsigned()));
console.log("e+d*r: "+ Crypto.util.bytesToHex(e.add(d.multiply(r)).toByteArrayUnsigned()));
console.log("s: "+ Crypto.util.bytesToHex(s.toByteArrayUnsigned()));
var rBa = r.toByteArrayUnsigned();
var sBa = s.toByteArrayUnsigned();
var sequence = [];
sequence.push(0x02); // INTEGER
sequence.push(rBa.length);
sequence = sequence.concat(rBa);
sequence.push(0x02); // INTEGER
sequence.push(sBa.length);
sequence = sequence.concat(sBa);
sequence.unshift(sequence.length);
sequence.unshift(0x30) // SEQUENCE
return sequence;
},
verify: function (hash, sig, pubkey) {
var cursor;
if (sig[0] != 0x30)
throw new Error("Signature not a valid DERSequence");
cursor = 2;
if (sig[cursor] != 0x02)
throw new Error("First element in signature must be a DERInteger");;
var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
cursor += 2+sig[cursor+1];
if (sig[cursor] != 0x02)
throw new Error("Second element in signature must be a DERInteger");
var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1]);
cursor += 2+sig[cursor+1];
//if (cursor != sig.length)
// throw new Error("Extra bytes in signature");
var n = ecparams.getN();
var e = BigInteger.fromByteArrayUnsigned(hash);
console.log("e: "+ Crypto.util.bytesToHex(e.toByteArrayUnsigned()));
var r = BigInteger.fromByteArrayUnsigned(rBa);
var s = BigInteger.fromByteArrayUnsigned(sBa);
if (r.compareTo(BigInteger.ONE) < 0 ||
r.compareTo(n) >= 0)
return false;
if (s.compareTo(BigInteger.ONE) < 0 ||
s.compareTo(n) >= 0)
return false;
var c = s.modInverse(n);
var u1 = e.multiply(c).mod(n);
var u2 = r.multiply(c).mod(n);
console.log("r: "+ Crypto.util.bytesToHex(r.toByteArrayUnsigned()));
console.log("u1: "+ Crypto.util.bytesToHex(u1.toByteArrayUnsigned()));
console.log("u2: "+ Crypto.util.bytesToHex(u2.toByteArrayUnsigned()));
var G = ecparams.getG();
var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
console.log("G.x: ", Crypto.util.bytesToHex(G.x.toBigInteger().toByteArrayUnsigned()));
console.log("G.y: ", Crypto.util.bytesToHex(G.y.toBigInteger().toByteArrayUnsigned()));
console.log("Q.x: ", Crypto.util.bytesToHex(Q.x.toBigInteger().toByteArrayUnsigned()));
console.log("Q.y: ", Crypto.util.bytesToHex(Q.y.toBigInteger().toByteArrayUnsigned()));
var point = implShamirsTrick(G, u1, Q, u2);
console.log("P.x: ", Crypto.util.bytesToHex(point.x.toBigInteger().toByteArrayUnsigned()));
console.log("P.y: ", Crypto.util.bytesToHex(point.y.toBigInteger().toByteArrayUnsigned()));
var v = point.x.toBigInteger().mod(n);
return v.equals(r);
}
};
return ECDSA;
})();