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.
This commit is contained in:
parent
6fdba19373
commit
0f424562f2
1 changed files with 94 additions and 35 deletions
107
src/ecdsa.js
107
src/ecdsa.js
|
@ -40,11 +40,12 @@ ECPointFp.prototype.getEncoded = function (compressed) {
|
|||
};
|
||||
|
||||
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
|
||||
var xBa = enc.slice(0, enc.length/2);
|
||||
var yBa = enc.slice(enc.length/2, enc.length);
|
||||
var xBa = enc.slice(1, 1 + dataLen/2);
|
||||
var yBa = enc.slice(1 + dataLen/2, 1 + dataLen);
|
||||
|
||||
// Prepend zero byte to prevent interpretation as negative integer
|
||||
xBa.unshift(0);
|
||||
|
@ -124,6 +125,52 @@ ECPointFp.prototype.multiply2D = function (k) {
|
|||
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) {
|
||||
if (!(v instanceof BigInteger)) v = v.toBigInteger();
|
||||
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);
|
||||
|
||||
return ECDSA.serializeSig(r, s);
|
||||
},
|
||||
|
||||
serializeSig: function (r, s) {
|
||||
var rBa = r.toByteArrayUnsigned();
|
||||
var sBa = s.toByteArrayUnsigned();
|
||||
|
||||
|
@ -200,31 +251,13 @@ Bitcoin.ECDSA = (function () {
|
|||
},
|
||||
|
||||
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 obj = ECDSA.parseSig(sig);
|
||||
var r = obj.r;
|
||||
var s = obj.s;
|
||||
|
||||
var n = ecparams.getN();
|
||||
var e = BigInteger.fromByteArrayUnsigned(hash);
|
||||
|
||||
var r = BigInteger.fromByteArrayUnsigned(rBa);
|
||||
var s = BigInteger.fromByteArrayUnsigned(sBa);
|
||||
|
||||
if (r.compareTo(BigInteger.ONE) < 0 ||
|
||||
r.compareTo(n) >= 0)
|
||||
return false;
|
||||
|
@ -246,6 +279,32 @@ Bitcoin.ECDSA = (function () {
|
|||
var v = point.x.toBigInteger().mod(n);
|
||||
|
||||
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…
Add table
Add a link
Reference in a new issue