2011-05-04 18:02:56 +02:00
|
|
|
function integerToBytes(i, len) {
|
2012-01-11 02:40:45 +01:00
|
|
|
var bytes = i.toByteArrayUnsigned();
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
if (len < bytes.length) {
|
|
|
|
bytes = bytes.slice(bytes.length-len);
|
|
|
|
} else while (len > bytes.length) {
|
|
|
|
bytes.unshift(0);
|
|
|
|
}
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
return bytes;
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ECFieldElementFp.prototype.getByteLength = function () {
|
2012-01-11 02:40:45 +01:00
|
|
|
return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ECPointFp.prototype.getEncoded = function (compressed) {
|
2012-01-11 02:40:45 +01:00
|
|
|
var x = this.getX().toBigInteger();
|
|
|
|
var y = this.getY().toBigInteger();
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2011-09-27 14:47:47 +02:00
|
|
|
// Get value as a 32-byte Buffer
|
|
|
|
// Fixed length based on a patch by bitaddress.org and Casascius
|
2012-01-11 02:40:45 +01:00
|
|
|
var enc = integerToBytes(x, 32);
|
|
|
|
|
|
|
|
if (compressed) {
|
|
|
|
if (y.testBit(0)) {
|
|
|
|
enc.unshift(0x02);
|
|
|
|
} else {
|
|
|
|
enc.unshift(0x03);
|
|
|
|
}
|
2011-09-27 14:44:47 +02:00
|
|
|
// TODO: Implement
|
2012-01-11 02:40:45 +01:00
|
|
|
} else {
|
|
|
|
enc.unshift(0x04);
|
|
|
|
enc = enc.concat(integerToBytes(y, 32));
|
|
|
|
}
|
|
|
|
return enc;
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ECPointFp.decodeFrom = function (curve, enc) {
|
2012-01-11 02:40:45 +01:00
|
|
|
var type = enc[0];
|
2011-08-26 21:43:08 +02:00
|
|
|
var dataLen = enc.length-1;
|
2011-05-08 15:36:11 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
// Extract x and y as byte arrays
|
|
|
|
var xBa = enc.slice(1, 1 + dataLen/2);
|
|
|
|
var yBa = enc.slice(1 + dataLen/2, 1 + dataLen);
|
2011-05-08 15:36:11 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
// Prepend zero byte to prevent interpretation as negative integer
|
|
|
|
xBa.unshift(0);
|
|
|
|
yBa.unshift(0);
|
2011-05-08 15:36:11 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
// Convert to BigIntegers
|
|
|
|
var x = new BigInteger(xBa);
|
|
|
|
var y = new BigInteger(yBa);
|
2011-05-08 15:36:11 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
// Return point
|
|
|
|
return new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y));
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ECPointFp.prototype.add2D = function (b) {
|
2012-01-11 02:40:45 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ECPointFp.prototype.twice2D = function () {
|
2012-01-11 02:40:45 +01:00
|
|
|
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();
|
|
|
|
}
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
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));
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var x3 = gamma.square().subtract(this.x.multiply(TWO));
|
|
|
|
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
return new ECPointFp(this.curve, x3, y3);
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
ECPointFp.prototype.multiply2D = function (k) {
|
2012-01-11 02:40:45 +01:00
|
|
|
if(this.isInfinity()) return this;
|
2011-08-26 21:43:08 +02:00
|
|
|
if(k.signum() == 0) return this.curve.getInfinity();
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2011-08-26 21:43:08 +02:00
|
|
|
var e = k;
|
|
|
|
var h = e.multiply(new BigInteger("3"));
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2011-08-26 21:43:08 +02:00
|
|
|
var neg = this.negate();
|
|
|
|
var R = this;
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2011-08-26 21:43:08 +02:00
|
|
|
var i;
|
|
|
|
for (i = h.bitLength() - 2; i > 0; --i) {
|
2012-01-11 02:40:45 +01:00
|
|
|
R = R.twice();
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var hBit = h.testBit(i);
|
|
|
|
var eBit = e.testBit(i);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
if (hBit != eBit) {
|
|
|
|
R = R.add2D(hBit ? this : neg);
|
|
|
|
}
|
2011-08-26 21:43:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return R;
|
|
|
|
};
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2011-08-26 21:43:08 +02:00
|
|
|
ECPointFp.prototype.isOnCurve = function () {
|
|
|
|
var x = this.getX().toBigInteger();
|
|
|
|
var y = this.getY().toBigInteger();
|
|
|
|
var a = this.curve.getA().toBigInteger();
|
|
|
|
var b = this.curve.getB().toBigInteger();
|
|
|
|
var n = this.curve.getQ();
|
|
|
|
var lhs = y.multiply(y).mod(n);
|
|
|
|
var rhs = x.multiply(x).multiply(x)
|
|
|
|
.add(a.multiply(x)).add(b).mod(n);
|
|
|
|
return lhs.equals(rhs);
|
|
|
|
};
|
|
|
|
|
|
|
|
ECPointFp.prototype.validate = function () {
|
|
|
|
var n = this.curve.getQ();
|
|
|
|
|
|
|
|
// Check Q != O
|
|
|
|
if (this.isInfinity()) {
|
|
|
|
throw new Error("Point is at infinity.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check coordinate bounds
|
|
|
|
var x = this.getX().toBigInteger();
|
|
|
|
var y = this.getY().toBigInteger();
|
|
|
|
if (x.compareTo(BigInteger.ONE) < 0 ||
|
|
|
|
x.compareTo(n.subtract(BigInteger.ONE)) > 0) {
|
|
|
|
throw new Error('x coordinate out of bounds');
|
|
|
|
}
|
|
|
|
if (y.compareTo(BigInteger.ONE) < 0 ||
|
|
|
|
y.compareTo(n.subtract(BigInteger.ONE)) > 0) {
|
|
|
|
throw new Error('y coordinate out of bounds');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check y^2 = x^3 + ax + b (mod n)
|
|
|
|
if (!this.isOnCurve()) {
|
|
|
|
throw new Error("Point is not on the curve.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check nQ = 0 (Q is a scalar multiple of G)
|
|
|
|
if (this.multiply(n).isInfinity()) {
|
|
|
|
// TODO: This check doesn't work - fix.
|
|
|
|
throw new Error("Point is not a scalar multiple of G.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
function dmp(v) {
|
2012-01-11 02:40:45 +01:00
|
|
|
if (!(v instanceof BigInteger)) v = v.toBigInteger();
|
|
|
|
return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
|
2011-05-04 18:02:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Bitcoin.ECDSA = (function () {
|
2012-01-11 02:40:45 +01:00
|
|
|
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();
|
|
|
|
|
|
|
|
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);
|
|
|
|
} else {
|
|
|
|
R = R.add2D(P);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (l.testBit(i)) {
|
|
|
|
R = R.add2D(Q);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2011-08-26 21:43:08 +02:00
|
|
|
return ECDSA.serializeSig(r, s);
|
2012-01-11 02:40:45 +01:00
|
|
|
},
|
2011-08-26 21:43:08 +02:00
|
|
|
|
|
|
|
serializeSig: function (r, s) {
|
2012-01-11 02:40:45 +01:00
|
|
|
var rBa = r.toByteArrayUnsigned();
|
|
|
|
var sBa = s.toByteArrayUnsigned();
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var sequence = [];
|
|
|
|
sequence.push(0x02); // INTEGER
|
|
|
|
sequence.push(rBa.length);
|
|
|
|
sequence = sequence.concat(rBa);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
sequence.push(0x02); // INTEGER
|
|
|
|
sequence.push(sBa.length);
|
|
|
|
sequence = sequence.concat(sBa);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
sequence.unshift(sequence.length);
|
|
|
|
sequence.unshift(0x30) // SEQUENCE
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
return sequence;
|
2011-08-26 21:43:08 +02:00
|
|
|
},
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
verify: function (hash, sig, pubkey) {
|
2011-08-26 21:43:08 +02:00
|
|
|
var obj = ECDSA.parseSig(sig);
|
|
|
|
var r = obj.r;
|
|
|
|
var s = obj.s;
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var n = ecparams.getN();
|
|
|
|
var e = BigInteger.fromByteArrayUnsigned(hash);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
if (r.compareTo(BigInteger.ONE) < 0 ||
|
|
|
|
r.compareTo(n) >= 0)
|
|
|
|
return false;
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
if (s.compareTo(BigInteger.ONE) < 0 ||
|
|
|
|
s.compareTo(n) >= 0)
|
|
|
|
return false;
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var c = s.modInverse(n);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var u1 = e.multiply(c).mod(n);
|
|
|
|
var u2 = r.multiply(c).mod(n);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var G = ecparams.getG();
|
|
|
|
var Q = ECPointFp.decodeFrom(ecparams.getCurve(), pubkey);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var point = implShamirsTrick(G, u1, Q, u2);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var v = point.x.toBigInteger().mod(n);
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
return v.equals(r);
|
|
|
|
},
|
2011-08-26 21:43:08 +02:00
|
|
|
|
|
|
|
parseSig: function (sig) {
|
2012-01-11 02:40:45 +01:00
|
|
|
var cursor;
|
|
|
|
if (sig[0] != 0x30)
|
|
|
|
throw new Error("Signature not a valid DERSequence");
|
2011-08-26 21:43:08 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
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]);
|
2011-08-26 21:43:08 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
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]);
|
2011-08-26 21:43:08 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
cursor += 2+sig[cursor+1];
|
2011-08-26 21:43:08 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
//if (cursor != sig.length)
|
|
|
|
// throw new Error("Extra bytes in signature");
|
2011-08-26 21:43:08 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
var r = BigInteger.fromByteArrayUnsigned(rBa);
|
|
|
|
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
2011-08-26 21:43:08 +02:00
|
|
|
|
|
|
|
return {r: r, s: s};
|
|
|
|
}
|
2012-01-11 02:40:45 +01:00
|
|
|
};
|
2011-05-04 18:02:56 +02:00
|
|
|
|
2012-01-11 02:40:45 +01:00
|
|
|
return ECDSA;
|
2011-05-04 18:02:56 +02:00
|
|
|
})();
|