diff options
author | Steve Thomas <Sc00bz@users.noreply.github.com> | 2015-11-01 14:36:28 -0600 |
---|---|---|
committer | Steve Thomas <Sc00bz@users.noreply.github.com> | 2015-11-01 14:36:28 -0600 |
commit | db7e40d449ab4ae65bacf267b38d1bf836b4fe54 (patch) | |
tree | f6f815673f56490c8ae3bfdc8ed9a5f4d9996f76 /core/ecc.js | |
parent | 025908ef2b091faab75f60188654f789b691ff0d (diff) | |
parent | f9a2494fae0dcddef493de453aa6ab69caa987cf (diff) | |
download | sjcl-db7e40d449ab4ae65bacf267b38d1bf836b4fe54.zip sjcl-db7e40d449ab4ae65bacf267b38d1bf836b4fe54.tar.gz sjcl-db7e40d449ab4ae65bacf267b38d1bf836b4fe54.tar.bz2 |
Merge pull request #1 from bitwiseshiftleft/master
Update
Diffstat (limited to 'core/ecc.js')
-rw-r--r-- | core/ecc.js | 310 |
1 files changed, 245 insertions, 65 deletions
diff --git a/core/ecc.js b/core/ecc.js index 504288f..67a54e7 100644 --- a/core/ecc.js +++ b/core/ecc.js @@ -1,3 +1,6 @@ +/** + * base class for all ecc operations. + */ sjcl.ecc = {}; /** @@ -11,8 +14,16 @@ sjcl.ecc.point = function(curve,x,y) { if (x === undefined) { this.isIdentity = true; } else { + if (x instanceof sjcl.bn) { + x = new curve.field(x); + } + if (y instanceof sjcl.bn) { + y = new curve.field(y); + } + this.x = x; this.y = y; + this.isIdentity = false; } this.curve = curve; @@ -28,7 +39,7 @@ sjcl.ecc.point.prototype = { mult: function(k) { return this.toJac().mult(k, this).toAffine(); }, - + /** * Multiply this point by k, added to affine2*k2, and return the answer in Jacobian coordinates. * @param {bigInt} k The coefficient to multiply this by. @@ -39,7 +50,7 @@ sjcl.ecc.point.prototype = { mult2: function(k, k2, affine2) { return this.toJac().mult2(k, this, k2, affine2).toAffine(); }, - + multiples: function() { var m, i, j; if (this._multiples === undefined) { @@ -53,6 +64,11 @@ sjcl.ecc.point.prototype = { return this._multiples; }, + negate: function() { + var newY = new this.curve.field(0).sub(this.y).normalize().reduce(); + return new sjcl.ecc.point(this.curve, this.x, newY); + }, + isValid: function() { return this.y.square().equals(this.curve.b.add(this.x.mul(this.curve.a.add(this.x.square())))); }, @@ -89,7 +105,7 @@ sjcl.ecc.pointJac.prototype = { * Adds S and T and returns the result in Jacobian coordinates. Note that S must be in Jacobian coordinates and T must be in affine coordinates. * @param {sjcl.ecc.pointJac} S One of the points to add, in Jacobian coordinates. * @param {sjcl.ecc.point} T The other point to add, in affine coordinates. - * @return {sjcl.ecc.pointJac} The sum of the two points, in Jacobian coordinates. + * @return {sjcl.ecc.pointJac} The sum of the two points, in Jacobian coordinates. */ add: function(T) { var S = this, sz2, c, d, c2, x1, x2, x, y1, y2, y, z; @@ -115,7 +131,7 @@ sjcl.ecc.pointJac.prototype = { return new sjcl.ecc.pointJac(S.curve); } } - + d = T.y.mul(sz2.mul(S.z)).subM(S.y); c2 = c.square(); @@ -131,7 +147,7 @@ sjcl.ecc.pointJac.prototype = { return new sjcl.ecc.pointJac(this.curve,x,y,z); }, - + /** * doubles this point. * @return {sjcl.ecc.pointJac} The doubled point. @@ -144,7 +160,9 @@ sjcl.ecc.pointJac.prototype = { a = y2.mul(this.x.mul(4)), b = y2.square().mul(8), z2 = this.z.square(), - c = this.x.sub(z2).mul(3).mul(this.x.add(z2)), + c = this.curve.a.toString() == (new sjcl.bn(-3)).toString() ? + this.x.sub(z2).mul(3).mul(this.x.add(z2)) : + this.x.square().mul(3).add(z2.square().mul(this.curve.a)), x = c.square().subM(a).subM(a), y = a.sub(x).mul(c).subM(b), z = this.y.add(this.y).mul(this.z); @@ -154,7 +172,7 @@ sjcl.ecc.pointJac.prototype = { /** * Returns a copy of this point converted to affine coordinates. * @return {sjcl.ecc.point} The converted point. - */ + */ toAffine: function() { if (this.isIdentity || this.z.equals(0)) { return new sjcl.ecc.point(this.curve); @@ -162,7 +180,7 @@ sjcl.ecc.pointJac.prototype = { var zi = this.z.inverse(), zi2 = zi.square(); return new sjcl.ecc.point(this.curve, this.x.mul(zi2).fullReduce(), this.y.mul(zi2.mul(zi)).fullReduce()); }, - + /** * Multiply this point by k and return the answer in Jacobian coordinates. * @param {bigInt} k The coefficient to multiply by. @@ -175,7 +193,7 @@ sjcl.ecc.pointJac.prototype = { } else if (k.limbs !== undefined) { k = k.normalize().limbs; } - + var i, j, out = new sjcl.ecc.point(this.curve).toJac(), multiples = affine.multiples(); for (i=k.length-1; i>=0; i--) { @@ -183,10 +201,10 @@ sjcl.ecc.pointJac.prototype = { out = out.doubl().doubl().doubl().doubl().add(multiples[k[i]>>j & 0xF]); } } - + return out; }, - + /** * Multiply this point by k, added to affine2*k2, and return the answer in Jacobian coordinates. * @param {bigInt} k The coefficient to multiply this by. @@ -201,13 +219,13 @@ sjcl.ecc.pointJac.prototype = { } else if (k1.limbs !== undefined) { k1 = k1.normalize().limbs; } - + if (typeof(k2) === "number") { k2 = [k2]; } else if (k2.limbs !== undefined) { k2 = k2.normalize().limbs; } - + var i, j, out = new sjcl.ecc.point(this.curve).toJac(), m1 = affine.multiples(), m2 = affine2.multiples(), l1, l2; @@ -218,10 +236,14 @@ sjcl.ecc.pointJac.prototype = { out = out.doubl().doubl().doubl().doubl().add(m1[l1>>j & 0xF]).add(m2[l2>>j & 0xF]); } } - + return out; }, + negate: function() { + return this.toAffine().negate().toJac(); + }, + isValid: function() { var z2 = this.z.square(), z4 = z2.square(), z6 = z4.mul(z2); return this.y.square().equals( @@ -242,7 +264,7 @@ sjcl.ecc.pointJac.prototype = { */ sjcl.ecc.curve = function(Field, r, a, b, x, y) { this.field = Field; - this.r = Field.prototype.modulus.sub(r); + this.r = new sjcl.bn(r); this.a = new Field(a); this.b = new Field(b); this.G = new sjcl.ecc.point(this, new Field(x), new Field(y)); @@ -261,7 +283,7 @@ sjcl.ecc.curve.prototype.fromBits = function (bits) { sjcl.ecc.curves = { c192: new sjcl.ecc.curve( sjcl.bn.prime.p192, - "0x662107c8eb94364e4b2dd7ce", + "0xffffffffffffffffffffffff99def836146bc9b1b4d22831", -3, "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", "0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", @@ -269,7 +291,7 @@ sjcl.ecc.curves = { c224: new sjcl.ecc.curve( sjcl.bn.prime.p224, - "0xe95c1f470fc1ec22d6baa3a3d5c4", + "0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d", -3, "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", "0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", @@ -277,7 +299,7 @@ sjcl.ecc.curves = { c256: new sjcl.ecc.curve( sjcl.bn.prime.p256, - "0x4319055358e8617b0c46353d039cdaae", + "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", -3, "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", @@ -285,51 +307,143 @@ sjcl.ecc.curves = { c384: new sjcl.ecc.curve( sjcl.bn.prime.p384, - "0x389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c", + "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973", -3, "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", - "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f") + "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"), + + c521: new sjcl.ecc.curve( + sjcl.bn.prime.p521, + "0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", + -3, + "0x051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", + "0xC6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", + "0x11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650"), + + k192: new sjcl.ecc.curve( + sjcl.bn.prime.p192k, + "0xfffffffffffffffffffffffe26f2fc170f69466a74defd8d", + 0, + 3, + "0xdb4ff10ec057e9ae26b07d0280b7f4341da5d1b1eae06c7d", + "0x9b2f2f6d9c5628a7844163d015be86344082aa88d95e2f9d"), + + k224: new sjcl.ecc.curve( + sjcl.bn.prime.p224k, + "0x010000000000000000000000000001dce8d2ec6184caf0a971769fb1f7", + 0, + 5, + "0xa1455b334df099df30fc28a169a467e9e47075a90f7e650eb6b7a45c", + "0x7e089fed7fba344282cafbd6f7e319f7c0b0bd59e2ca4bdb556d61a5"), + + k256: new sjcl.ecc.curve( + sjcl.bn.prime.p256k, + "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + 0, + 7, + "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8") + }; +/** our basicKey classes +*/ +sjcl.ecc.basicKey = { + /** ecc publicKey. + * @constructor + * @param {curve} curve the elliptic curve + * @param {point} point the point on the curve + */ + publicKey: function(curve, point) { + this._curve = curve; + this._curveBitLength = curve.r.bitLength(); + if (point instanceof Array) { + this._point = curve.fromBits(point); + } else { + this._point = point; + } -/* Diffie-Hellman-like public-key system */ -sjcl.ecc._dh = function(cn) { - sjcl.ecc[cn] = { - publicKey: function(curve, point) { - this._curve = curve; - if (point instanceof Array) { - this._point = curve.fromBits(point); - } else { - this._point = point; - } - }, + /** get this keys point data + * @return x and y as bitArrays + */ + this.get = function() { + var pointbits = this._point.toBits(); + var len = sjcl.bitArray.bitLength(pointbits); + var x = sjcl.bitArray.bitSlice(pointbits, 0, len/2); + var y = sjcl.bitArray.bitSlice(pointbits, len/2); + return { x: x, y: y }; + }; + }, - secretKey: function(curve, exponent) { - this._curve = curve; - this._exponent = exponent; - }, + /** ecc secretKey + * @constructor + * @param {curve} curve the elliptic curve + * @param exponent + */ + secretKey: function(curve, exponent) { + this._curve = curve; + this._curveBitLength = curve.r.bitLength(); + this._exponent = exponent; + + /** get this keys exponent data + * @return {bitArray} exponent + */ + this.get = function () { + return this._exponent.toBits(); + }; + } +}; - generateKeys: function(curve, paranoia) { +/** @private */ +sjcl.ecc.basicKey.generateKeys = function(cn) { + return function generateKeys(curve, paranoia, sec) { + curve = curve || 256; + + if (typeof curve === "number") { + curve = sjcl.ecc.curves['c'+curve]; if (curve === undefined) { - curve = 256; - } - if (typeof curve === "number") { - curve = sjcl.ecc.curves['c'+curve]; - if (curve === undefined) { - throw new sjcl.exception.invalid("no such curve"); - } + throw new sjcl.exception.invalid("no such curve"); } - var sec = sjcl.bn.random(curve.r, paranoia), pub = curve.G.mult(sec); - return { pub: new sjcl.ecc[cn].publicKey(curve, pub), - sec: new sjcl.ecc[cn].secretKey(curve, sec) }; } - }; + sec = sec || sjcl.bn.random(curve.r, paranoia); + + var pub = curve.G.mult(sec); + return { pub: new sjcl.ecc[cn].publicKey(curve, pub), + sec: new sjcl.ecc[cn].secretKey(curve, sec) }; + }; }; -sjcl.ecc._dh("elGamal"); +/** elGamal keys */ +sjcl.ecc.elGamal = { + /** generate keys + * @function + * @param curve + * @param {int} paranoia Paranoia for generation (default 6) + * @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey + */ + generateKeys: sjcl.ecc.basicKey.generateKeys("elGamal"), + /** elGamal publicKey. + * @constructor + * @augments sjcl.ecc.basicKey.publicKey + */ + publicKey: function (curve, point) { + sjcl.ecc.basicKey.publicKey.apply(this, arguments); + }, + /** elGamal secretKey + * @constructor + * @augments sjcl.ecc.basicKey.secretKey + */ + secretKey: function (curve, exponent) { + sjcl.ecc.basicKey.secretKey.apply(this, arguments); + } +}; sjcl.ecc.elGamal.publicKey.prototype = { + /** Kem function of elGamal Public Key + * @param paranoia paranoia to use for randomization. + * @return {object} key and tag. unkem(tag) with the corresponding secret key results in the key returned. + */ kem: function(paranoia) { var sec = sjcl.bn.random(this._curve.r, paranoia), tag = this._curve.G.mult(sec).toBits(), @@ -339,42 +453,108 @@ sjcl.ecc.elGamal.publicKey.prototype = { }; sjcl.ecc.elGamal.secretKey.prototype = { + /** UnKem function of elGamal Secret Key + * @param {bitArray} tag The Tag to decrypt. + * @return {bitArray} decrypted key. + */ unkem: function(tag) { return sjcl.hash.sha256.hash(this._curve.fromBits(tag).mult(this._exponent).toBits()); }, + /** Diffie-Hellmann function + * @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with + * @return {bitArray} diffie-hellmann result for this key combination. + */ dh: function(pk) { return sjcl.hash.sha256.hash(pk._point.mult(this._exponent).toBits()); - } + }, + + /** Diffie-Hellmann function, compatible with Java generateSecret + * @param {elGamal.publicKey} pk The Public Key to do Diffie-Hellmann with + * @return {bitArray} undigested X value, diffie-hellmann result for this key combination, + * compatible with Java generateSecret(). + */ + dhJavaEc: function(pk) { + return pk._point.mult(this._exponent).x.toBits(); + } }; -sjcl.ecc._dh("ecdsa"); +/** ecdsa keys */ +sjcl.ecc.ecdsa = { + /** generate keys + * @function + * @param curve + * @param {int} paranoia Paranoia for generation (default 6) + * @param {secretKey} sec secret Key to use. used to get the publicKey for ones secretKey + */ + generateKeys: sjcl.ecc.basicKey.generateKeys("ecdsa") +}; -sjcl.ecc.ecdsa.secretKey.prototype = { - sign: function(hash, paranoia) { - var R = this._curve.r, - l = R.bitLength(), - k = sjcl.bn.random(R.sub(1), paranoia).add(1), - r = this._curve.G.mult(k).x.mod(R), - s = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)).inverseMod(R).mul(k).mod(R); - return sjcl.bitArray.concat(r.toBits(l), s.toBits(l)); - } +/** ecdsa publicKey. +* @constructor +* @augments sjcl.ecc.basicKey.publicKey +*/ +sjcl.ecc.ecdsa.publicKey = function (curve, point) { + sjcl.ecc.basicKey.publicKey.apply(this, arguments); }; +/** specific functions for ecdsa publicKey. */ sjcl.ecc.ecdsa.publicKey.prototype = { - verify: function(hash, rs) { + /** Diffie-Hellmann function + * @param {bitArray} hash hash to verify. + * @param {bitArray} rs signature bitArray. + * @param {boolean} fakeLegacyVersion use old legacy version + */ + verify: function(hash, rs, fakeLegacyVersion) { + if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) { + hash = sjcl.bitArray.clamp(hash, this._curveBitLength); + } var w = sjcl.bitArray, R = this._curve.r, - l = R.bitLength(), + l = this._curveBitLength, r = sjcl.bn.fromBits(w.bitSlice(rs,0,l)), - s = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l)), + ss = sjcl.bn.fromBits(w.bitSlice(rs,l,2*l)), + s = fakeLegacyVersion ? ss : ss.inverseMod(R), hG = sjcl.bn.fromBits(hash).mul(s).mod(R), hA = r.mul(s).mod(R), r2 = this._curve.G.mult2(hG, hA, this._point).x; - - if (r.equals(0) || s.equals(0) || r.greaterEquals(R) || s.greaterEquals(R) || !r2.equals(r)) { - throw (new sjcl.exception.corrupt("signature didn't check out")); + if (r.equals(0) || ss.equals(0) || r.greaterEquals(R) || ss.greaterEquals(R) || !r2.equals(r)) { + if (fakeLegacyVersion === undefined) { + return this.verify(hash, rs, true); + } else { + throw (new sjcl.exception.corrupt("signature didn't check out")); + } } return true; } }; + +/** ecdsa secretKey +* @constructor +* @augments sjcl.ecc.basicKey.publicKey +*/ +sjcl.ecc.ecdsa.secretKey = function (curve, exponent) { + sjcl.ecc.basicKey.secretKey.apply(this, arguments); +}; + +/** specific functions for ecdsa secretKey. */ +sjcl.ecc.ecdsa.secretKey.prototype = { + /** Diffie-Hellmann function + * @param {bitArray} hash hash to sign. + * @param {int} paranoia paranoia for random number generation + * @param {boolean} fakeLegacyVersion use old legacy version + */ + sign: function(hash, paranoia, fakeLegacyVersion, fixedKForTesting) { + if (sjcl.bitArray.bitLength(hash) > this._curveBitLength) { + hash = sjcl.bitArray.clamp(hash, this._curveBitLength); + } + var R = this._curve.r, + l = R.bitLength(), + k = fixedKForTesting || sjcl.bn.random(R.sub(1), paranoia).add(1), + r = this._curve.G.mult(k).x.mod(R), + ss = sjcl.bn.fromBits(hash).add(r.mul(this._exponent)), + s = fakeLegacyVersion ? ss.inverseMod(R).mul(k).mod(R) + : ss.mul(k.inverseMod(R)).mod(R); + return sjcl.bitArray.concat(r.toBits(l), s.toBits(l)); + } +}; |