Browse Source

Added more ECDSA function and some fixes.

decodeFrom is no longer destructive with it's inputs.
Added isOnCurve().
Added validate().
Fixed formatting.
Factored out new functions serializeSig and parseSig.
hk-custom-address
Stefan Thomas 14 years ago
parent
commit
0f424562f2
  1. 107
      src/ecdsa.js

107
src/ecdsa.js

@ -40,11 +40,12 @@ ECPointFp.prototype.getEncoded = function (compressed) {
}; };
ECPointFp.decodeFrom = function (curve, enc) { ECPointFp.decodeFrom = function (curve, enc) {
var type = enc.shift(); var type = enc[0];
var dataLen = enc.length-1;
// Extract x and y as byte arrays // Extract x and y as byte arrays
var xBa = enc.slice(0, enc.length/2); var xBa = enc.slice(1, 1 + dataLen/2);
var yBa = enc.slice(enc.length/2, enc.length); var yBa = enc.slice(1 + dataLen/2, 1 + dataLen);
// Prepend zero byte to prevent interpretation as negative integer // Prepend zero byte to prevent interpretation as negative integer
xBa.unshift(0); xBa.unshift(0);
@ -124,6 +125,52 @@ ECPointFp.prototype.multiply2D = function (k) {
return R; return R;
}; };
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;
};
function dmp(v) { function dmp(v) {
if (!(v instanceof BigInteger)) v = v.toBigInteger(); if (!(v instanceof BigInteger)) v = v.toBigInteger();
return Crypto.util.bytesToHex(v.toByteArrayUnsigned()); return Crypto.util.bytesToHex(v.toByteArrayUnsigned());
@ -181,6 +228,10 @@ Bitcoin.ECDSA = (function () {
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
return ECDSA.serializeSig(r, s);
},
serializeSig: function (r, s) {
var rBa = r.toByteArrayUnsigned(); var rBa = r.toByteArrayUnsigned();
var sBa = s.toByteArrayUnsigned(); var sBa = s.toByteArrayUnsigned();
@ -200,31 +251,13 @@ Bitcoin.ECDSA = (function () {
}, },
verify: function (hash, sig, pubkey) { verify: function (hash, sig, pubkey) {
var cursor; var obj = ECDSA.parseSig(sig);
if (sig[0] != 0x30) var r = obj.r;
throw new Error("Signature not a valid DERSequence"); var s = obj.s;
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 n = ecparams.getN();
var e = BigInteger.fromByteArrayUnsigned(hash); var e = BigInteger.fromByteArrayUnsigned(hash);
var r = BigInteger.fromByteArrayUnsigned(rBa);
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)
return false; return false;
@ -246,6 +279,32 @@ Bitcoin.ECDSA = (function () {
var v = point.x.toBigInteger().mod(n); var v = point.x.toBigInteger().mod(n);
return v.equals(r); return v.equals(r);
},
parseSig: function (sig) {
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 r = BigInteger.fromByteArrayUnsigned(rBa);
var s = BigInteger.fromByteArrayUnsigned(sBa);
return {r: r, s: s};
} }
}; };

Loading…
Cancel
Save