Tiny Cryptography MCP Server

  • src
import sjcl from "sjcl"; sjcl.bn = function (it) { this.initWith(it); }; sjcl.bn.prototype = { radix: 24, maxMul: 8, _class: sjcl.bn, copy: function () { return new this._class(this); }, /** * Initializes this with it, either as a bn, a number, or a hex string. */ initWith: function (it) { var i = 0, k; switch (typeof it) { case "object": this.limbs = it.limbs.slice(0); break; case "number": this.limbs = [it]; this.normalize(); break; case "string": it = it.replace(/^0x/, ""); this.limbs = []; // hack k = this.radix / 4; for (i = 0; i < it.length; i += k) { this.limbs.push( parseInt( it.substring(Math.max(it.length - i - k, 0), it.length - i), 16 ) ); } break; default: this.limbs = [0]; } return this; }, /** * Returns true if "this" and "that" are equal. Calls fullReduce(). * Equality test is in constant time. */ equals: function (that) { if (typeof that === "number") { that = new this._class(that); } var difference = 0, i; this.fullReduce(); that.fullReduce(); for (i = 0; i < this.limbs.length || i < that.limbs.length; i++) { difference |= this.getLimb(i) ^ that.getLimb(i); } return difference === 0; }, /** * Get the i'th limb of this, zero if i is too large. */ getLimb: function (i) { return i >= this.limbs.length ? 0 : this.limbs[i]; }, /** * Constant time comparison function. * Returns 1 if this >= that, or zero otherwise. */ greaterEquals: function (that) { if (typeof that === "number") { that = new this._class(that); } var less = 0, greater = 0, i, a, b; i = Math.max(this.limbs.length, that.limbs.length) - 1; for (; i >= 0; i--) { a = this.getLimb(i); b = that.getLimb(i); greater |= (b - a) & ~less; less |= (a - b) & ~greater; } return (greater | ~less) >>> 31; }, /** * Convert to a hex string. */ toString: function () { this.fullReduce(); var out = "", i, s, l = this.limbs; for (i = 0; i < this.limbs.length; i++) { s = l[i].toString(16); while (i < this.limbs.length - 1 && s.length < 6) { s = "0" + s; } out = s + out; } return "0x" + out; }, /** this += that. Does not normalize. */ addM: function (that) { if (typeof that !== "object") { that = new this._class(that); } var i, l = this.limbs, ll = that.limbs; for (i = l.length; i < ll.length; i++) { l[i] = 0; } for (i = 0; i < ll.length; i++) { l[i] += ll[i]; } return this; }, /** this *= 2. Requires normalized; ends up normalized. */ doubleM: function () { var i, carry = 0, tmp, r = this.radix, m = this.radixMask, l = this.limbs; for (i = 0; i < l.length; i++) { tmp = l[i]; tmp = tmp + tmp + carry; l[i] = tmp & m; carry = tmp >> r; } if (carry) { l.push(carry); } return this; }, /** this /= 2, rounded down. Requires normalized; ends up normalized. */ halveM: function () { var i, carry = 0, tmp, r = this.radix, l = this.limbs; for (i = l.length - 1; i >= 0; i--) { tmp = l[i]; l[i] = (tmp + carry) >> 1; carry = (tmp & 1) << r; } if (!l[l.length - 1]) { l.pop(); } return this; }, /** this -= that. Does not normalize. */ subM: function (that) { if (typeof that !== "object") { that = new this._class(that); } var i, l = this.limbs, ll = that.limbs; for (i = l.length; i < ll.length; i++) { l[i] = 0; } for (i = 0; i < ll.length; i++) { l[i] -= ll[i]; } return this; }, mod: function (that) { var neg = !this.greaterEquals(new sjcl.bn(0)); that = new sjcl.bn(that).normalize(); // copy before we begin var out = new sjcl.bn(this).normalize(), ci = 0; if (neg) out = new sjcl.bn(0).subM(out).normalize(); for (; out.greaterEquals(that); ci++) { that.doubleM(); } if (neg) out = that.sub(out).normalize(); for (; ci > 0; ci--) { that.halveM(); if (out.greaterEquals(that)) { out.subM(that).normalize(); } } return out.trim(); }, /** return inverse mod prime p. p must be odd. Binary extended Euclidean algorithm mod p. */ inverseMod: function (p) { var a = new sjcl.bn(1), b = new sjcl.bn(0), x = new sjcl.bn(this), y = new sjcl.bn(p), tmp, i, nz = 1; if (!(p.limbs[0] & 1)) { throw new sjcl.exception.invalid("inverseMod: p must be odd"); } // invariant: y is odd do { if (x.limbs[0] & 1) { if (!x.greaterEquals(y)) { // x < y; swap everything tmp = x; x = y; y = tmp; tmp = a; a = b; b = tmp; } x.subM(y); x.normalize(); if (!a.greaterEquals(b)) { a.addM(p); } a.subM(b); } // cut everything in half x.halveM(); if (a.limbs[0] & 1) { a.addM(p); } a.normalize(); a.halveM(); // check for termination: x ?= 0 for (i = nz = 0; i < x.limbs.length; i++) { nz |= x.limbs[i]; } } while (nz); if (!y.equals(1)) { throw new sjcl.exception.invalid( "inverseMod: p and x must be relatively prime" ); } return b; }, /** this + that. Does not normalize. */ add: function (that) { return this.copy().addM(that); }, /** this - that. Does not normalize. */ sub: function (that) { return this.copy().subM(that); }, /** this * that. Normalizes and reduces. */ mul: function (that) { if (typeof that === "number") { that = new this._class(that); } else { that.normalize(); } this.normalize(); var i, j, a = this.limbs, b = that.limbs, al = a.length, bl = b.length, out = new this._class(), c = out.limbs, ai, ii = this.maxMul; for (i = 0; i < this.limbs.length + that.limbs.length + 1; i++) { c[i] = 0; } for (i = 0; i < al; i++) { ai = a[i]; for (j = 0; j < bl; j++) { c[i + j] += ai * b[j]; } if (!--ii) { ii = this.maxMul; out.cnormalize(); } } return out.cnormalize().reduce(); }, /** this ^ 2. Normalizes and reduces. */ square: function () { return this.mul(this); }, /** this ^ n. Uses square-and-multiply. Normalizes and reduces. */ power: function (l) { l = new sjcl.bn(l).normalize().trim().limbs; var i, j, out = new this._class(1), pow = this; for (i = 0; i < l.length; i++) { for (j = 0; j < this.radix; j++) { if (l[i] & (1 << j)) { out = out.mul(pow); } if (i == l.length - 1 && l[i] >> (j + 1) == 0) { break; } pow = pow.square(); } } return out; }, /** this * that mod N */ mulmod: function (that, N) { return this.mod(N).mul(that.mod(N)).mod(N); }, /** this ^ x mod N */ powermod: function (x, N) { x = new sjcl.bn(x); N = new sjcl.bn(N); // Jump to montpowermod if possible. if ((N.limbs[0] & 1) == 1) { var montOut = this.montpowermod(x, N); if (montOut != false) { return montOut; } // else go to slow powermod } var i, j, l = x.normalize().trim().limbs, out = new this._class(1), pow = this; for (i = 0; i < l.length; i++) { for (j = 0; j < this.radix; j++) { if (l[i] & (1 << j)) { out = out.mulmod(pow, N); } if (i == l.length - 1 && l[i] >> (j + 1) == 0) { break; } pow = pow.mulmod(pow, N); } } return out; }, /** this ^ x mod N with Montomery reduction */ montpowermod: function (x, N) { x = new sjcl.bn(x).normalize().trim(); N = new sjcl.bn(N); var i, j, radix = this.radix, out = new this._class(1), pow = this.copy(); // Generate R as a cap of N. var R, s, wind, bitsize = x.bitLength(); R = new sjcl.bn({ limbs: N.copy() .normalize() .trim() .limbs.map(function () { return 0; }), }); for (s = this.radix; s > 0; s--) { if (((N.limbs[N.limbs.length - 1] >> s) & 1) == 1) { R.limbs[R.limbs.length - 1] = 1 << s; break; } } // Calculate window size as a function of the exponent's size. if (bitsize == 0) { return this; } else if (bitsize < 18) { wind = 1; } else if (bitsize < 48) { wind = 3; } else if (bitsize < 144) { wind = 4; } else if (bitsize < 768) { wind = 5; } else { wind = 6; } // Find R' and N' such that R * R' - N * N' = 1. var RR = R.copy(), NN = N.copy(), RP = new sjcl.bn(1), NP = new sjcl.bn(0), RT = R.copy(); while (RT.greaterEquals(1)) { RT.halveM(); if ((RP.limbs[0] & 1) == 0) { RP.halveM(); NP.halveM(); } else { RP.addM(NN); RP.halveM(); NP.halveM(); NP.addM(RR); } } RP = RP.normalize(); NP = NP.normalize(); RR.doubleM(); var R2 = RR.mulmod(RR, N); // Check whether the invariant holds. // If it doesn't, we can't use Montgomery reduction on this modulus. if (!RR.mul(RP).sub(N.mul(NP)).equals(1)) { return false; } var montIn = function (c) { return montMul(c, R2); }, montMul = function (a, b) { // Standard Montgomery reduction var k, ab, right, abBar, mask = (1 << (s + 1)) - 1; ab = a.mul(b); right = ab.mul(NP); right.limbs = right.limbs.slice(0, R.limbs.length); if (right.limbs.length == R.limbs.length) { right.limbs[R.limbs.length - 1] &= mask; } right = right.mul(N); abBar = ab.add(right).normalize().trim(); abBar.limbs = abBar.limbs.slice(R.limbs.length - 1); // Division. Equivelent to calling *.halveM() s times. for (k = 0; k < abBar.limbs.length; k++) { if (k > 0) { abBar.limbs[k - 1] |= (abBar.limbs[k] & mask) << (radix - s - 1); } abBar.limbs[k] = abBar.limbs[k] >> (s + 1); } if (abBar.greaterEquals(N)) { abBar.subM(N); } return abBar; }, montOut = function (c) { return montMul(c, 1); }; pow = montIn(pow); out = montIn(out); // Sliding-Window Exponentiation (HAC 14.85) var h, precomp = {}, cap = (1 << (wind - 1)) - 1; precomp[1] = pow.copy(); precomp[2] = montMul(pow, pow); for (h = 1; h <= cap; h++) { precomp[2 * h + 1] = montMul(precomp[2 * h - 1], precomp[2]); } var getBit = function (exp, i) { // Gets ith bit of exp. var off = i % exp.radix; return (exp.limbs[Math.floor(i / exp.radix)] & (1 << off)) >> off; }; for (i = x.bitLength() - 1; i >= 0; ) { if (getBit(x, i) == 0) { // If the next bit is zero: // Square, move forward one bit. out = montMul(out, out); i = i - 1; } else { // If the next bit is one: // Find the longest sequence of bits after this one, less than `wind` // bits long, that ends with a 1. Convert the sequence into an // integer and look up the pre-computed value to add. var l = i - wind + 1; while (getBit(x, l) == 0) { l++; } var indx = 0; for (j = l; j <= i; j++) { indx += getBit(x, j) << (j - l); out = montMul(out, out); } out = montMul(out, precomp[indx]); i = l - 1; } } return montOut(out); }, trim: function () { var l = this.limbs, p; do { p = l.pop(); } while (l.length && p === 0); l.push(p); return this; }, /** Reduce mod a modulus. Stubbed for subclassing. */ reduce: function () { return this; }, /** Reduce and normalize. */ fullReduce: function () { return this.normalize(); }, /** Propagate carries. */ normalize: function () { var carry = 0, i, pv = this.placeVal, ipv = this.ipv, l, m, limbs = this.limbs, ll = limbs.length, mask = this.radixMask; for (i = 0; i < ll || (carry !== 0 && carry !== -1); i++) { l = (limbs[i] || 0) + carry; m = limbs[i] = l & mask; carry = (l - m) * ipv; } if (carry === -1) { limbs[i - 1] -= pv; } this.trim(); return this; }, /** Constant-time normalize. Does not allocate additional space. */ cnormalize: function () { var carry = 0, i, ipv = this.ipv, l, m, limbs = this.limbs, ll = limbs.length, mask = this.radixMask; for (i = 0; i < ll - 1; i++) { l = limbs[i] + carry; m = limbs[i] = l & mask; carry = (l - m) * ipv; } limbs[i] += carry; return this; }, /** Serialize to a bit array */ toBits: function (len) { this.fullReduce(); len = len || this.exponent || this.bitLength(); var i = Math.floor((len - 1) / 24), w = sjcl.bitArray, e = ((len + 7) & -8) % this.radix || this.radix, out = [w.partial(e, this.getLimb(i))]; for (i--; i >= 0; i--) { out = w.concat(out, [ w.partial(Math.min(this.radix, len), this.getLimb(i)), ]); len -= this.radix; } return out; }, /** Return the length in bits, rounded up to the nearest byte. */ bitLength: function () { this.fullReduce(); var out = this.radix * (this.limbs.length - 1), b = this.limbs[this.limbs.length - 1]; for (; b; b >>>= 1) { out++; } return (out + 7) & -8; }, }; /** @memberOf sjcl.bn * @this { sjcl.bn } */ sjcl.bn.fromBits = function (bits) { var Class = this, out = new Class(), words = [], w = sjcl.bitArray, t = this.prototype, l = Math.min(this.bitLength || 0x100000000, w.bitLength(bits)), e = l % t.radix || t.radix; words[0] = w.extract(bits, 0, e); for (; e < l; e += t.radix) { words.unshift(w.extract(bits, e, t.radix)); } out.limbs = words; return out; }; sjcl.bn.prototype.ipv = 1 / (sjcl.bn.prototype.placeVal = Math.pow(2, sjcl.bn.prototype.radix)); sjcl.bn.prototype.radixMask = (1 << sjcl.bn.prototype.radix) - 1; /** * Creates a new subclass of bn, based on reduction modulo a pseudo-Mersenne prime, * i.e. a prime of the form 2^e + sum(a * 2^b),where the sum is negative and sparse. */ sjcl.bn.pseudoMersennePrime = function (exponent, coeff) { /** @constructor * @private */ function p(it) { this.initWith(it); /*if (this.limbs[this.modOffset]) { this.reduce(); }*/ } var ppr = (p.prototype = new sjcl.bn()), i, tmp, mo; mo = ppr.modOffset = Math.ceil((tmp = exponent / ppr.radix)); ppr.exponent = exponent; ppr.offset = []; ppr.factor = []; ppr.minOffset = mo; ppr.fullMask = 0; ppr.fullOffset = []; ppr.fullFactor = []; ppr.modulus = p.modulus = new sjcl.bn(Math.pow(2, exponent)); ppr.fullMask = 0 | -Math.pow(2, exponent % ppr.radix); for (i = 0; i < coeff.length; i++) { ppr.offset[i] = Math.floor(coeff[i][0] / ppr.radix - tmp); ppr.fullOffset[i] = Math.floor(coeff[i][0] / ppr.radix) - mo + 1; ppr.factor[i] = coeff[i][1] * Math.pow(1 / 2, exponent - coeff[i][0] + ppr.offset[i] * ppr.radix); ppr.fullFactor[i] = coeff[i][1] * Math.pow(1 / 2, exponent - coeff[i][0] + ppr.fullOffset[i] * ppr.radix); ppr.modulus.addM(new sjcl.bn(Math.pow(2, coeff[i][0]) * coeff[i][1])); ppr.minOffset = Math.min(ppr.minOffset, -ppr.offset[i]); // conservative } ppr._class = p; ppr.modulus.cnormalize(); /** Approximate reduction mod p. May leave a number which is negative or slightly larger than p. * @memberof sjcl.bn * @this { sjcl.bn } */ ppr.reduce = function () { var i, k, l, mo = this.modOffset, limbs = this.limbs, off = this.offset, ol = this.offset.length, fac = this.factor, ll; i = this.minOffset; while (limbs.length > mo) { l = limbs.pop(); ll = limbs.length; for (k = 0; k < ol; k++) { limbs[ll + off[k]] -= fac[k] * l; } i--; if (!i) { limbs.push(0); this.cnormalize(); i = this.minOffset; } } this.cnormalize(); return this; }; /** @memberof sjcl.bn * @this { sjcl.bn } */ ppr._strongReduce = ppr.fullMask === -1 ? ppr.reduce : function () { var limbs = this.limbs, i = limbs.length - 1, k, l; this.reduce(); if (i === this.modOffset - 1) { l = limbs[i] & this.fullMask; limbs[i] -= l; for (k = 0; k < this.fullOffset.length; k++) { limbs[i + this.fullOffset[k]] -= this.fullFactor[k] * l; } this.normalize(); } }; /** mostly constant-time, very expensive full reduction. * @memberof sjcl.bn * @this { sjcl.bn } */ ppr.fullReduce = function () { var greater, i; // massively above the modulus, may be negative this._strongReduce(); // less than twice the modulus, may be negative this.addM(this.modulus); this.addM(this.modulus); this.normalize(); // probably 2-3x the modulus this._strongReduce(); // less than the power of 2. still may be more than // the modulus // HACK: pad out to this length for (i = this.limbs.length; i < this.modOffset; i++) { this.limbs[i] = 0; } // constant-time subtract modulus greater = this.greaterEquals(this.modulus); for (i = 0; i < this.limbs.length; i++) { this.limbs[i] -= this.modulus.limbs[i] * greater; } this.cnormalize(); return this; }; /** @memberof sjcl.bn * @this { sjcl.bn } */ ppr.inverse = function () { return this.power(this.modulus.sub(2)); }; p.fromBits = sjcl.bn.fromBits; return p; }; // a small Mersenne prime var sbp = sjcl.bn.pseudoMersennePrime; sjcl.bn.prime = { p127: sbp(127, [[0, -1]]), // Bernstein's prime for Curve25519 p25519: sbp(255, [[0, -19]]), // Koblitz primes p192k: sbp(192, [ [32, -1], [12, -1], [8, -1], [7, -1], [6, -1], [3, -1], [0, -1], ]), p224k: sbp(224, [ [32, -1], [12, -1], [11, -1], [9, -1], [7, -1], [4, -1], [1, -1], [0, -1], ]), p256k: sbp(256, [ [32, -1], [9, -1], [8, -1], [7, -1], [6, -1], [4, -1], [0, -1], ]), // NIST primes p192: sbp(192, [ [0, -1], [64, -1], ]), p224: sbp(224, [ [0, 1], [96, -1], ]), p256: sbp(256, [ [0, -1], [96, 1], [192, 1], [224, -1], ]), p384: sbp(384, [ [0, -1], [32, 1], [96, -1], [128, -1], ]), p521: sbp(521, [[0, -1]]), }; sjcl.bn.random = function (modulus, paranoia) { if (typeof modulus !== "object") { modulus = new sjcl.bn(modulus); } var words, i, l = modulus.limbs.length, m = modulus.limbs[l - 1] + 1, out = new sjcl.bn(); while (true) { // get a sequence whose first digits make sense do { words = sjcl.random.randomWords(l, paranoia); if (words[l - 1] < 0) { words[l - 1] += 0x100000000; } } while (Math.floor(words[l - 1] / m) === Math.floor(0x100000000 / m)); words[l - 1] %= m; // mask off all the limbs for (i = 0; i < l - 1; i++) { words[i] &= modulus.radixMask; } // check the rest of the digitssj out.limbs = words; if (!out.greaterEquals(modulus)) { return out; } } }; sjcl.ecc = {}; /** * Represents a point on a curve in affine coordinates. * @constructor * @param {sjcl.ecc.curve} curve The curve that this point lies on. * @param {bigInt} x The x coordinate. * @param {bigInt} y The y coordinate. */ 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; }; sjcl.ecc.point.prototype = { toJac: function () { return new sjcl.ecc.pointJac( this.curve, this.x, this.y, new this.curve.field(1) ); }, 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. * @param {bigInt} k2 The coefficient to multiply affine2 this by. * @param {sjcl.ecc.point} affine The other point in affine coordinates. * @return {sjcl.ecc.pointJac} The result of the multiplication and addition, in Jacobian coordinates. */ mult2: function (k, k2, affine2) { return this.toJac().mult2(k, this, k2, affine2).toAffine(); }, multiples: function () { var m, i, j; if (this._multiples === undefined) { j = this.toJac().doubl(); m = this._multiples = [ new sjcl.ecc.point(this.curve), this, j.toAffine(), ]; for (i = 3; i < 16; i++) { j = j.add(this); m.push(j.toAffine()); } } 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())))); }, toBits: function () { return sjcl.bitArray.concat(this.x.toBits(), this.y.toBits()); }, }; /** * Represents a point on a curve in Jacobian coordinates. Coordinates can be specified as bigInts or strings (which * will be converted to bigInts). * * @constructor * @param {bigInt/string} x The x coordinate. * @param {bigInt/string} y The y coordinate. * @param {bigInt/string} z The z coordinate. * @param {sjcl.ecc.curve} curve The curve that this point lies on. */ sjcl.ecc.pointJac = function (curve, x, y, z) { if (x === undefined) { this.isIdentity = true; } else { this.x = x; this.y = y; this.z = z; this.isIdentity = false; } this.curve = curve; }; 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. */ add: function (T) { var S = this, sz2, c, d, c2, x1, x2, x, y1, y2, y, z; if (S.curve !== T.curve) { throw new sjcl.exception.invalid( "sjcl.ecc.add(): Points must be on the same curve to add them!" ); } if (S.isIdentity) { return T.toJac(); } else if (T.isIdentity) { return S; } sz2 = S.z.square(); c = T.x.mul(sz2).subM(S.x); if (c.equals(0)) { if (S.y.equals(T.y.mul(sz2.mul(S.z)))) { // same point return S.doubl(); } else { // inverses return new sjcl.ecc.pointJac(S.curve); } } d = T.y.mul(sz2.mul(S.z)).subM(S.y); c2 = c.square(); x1 = d.square(); x2 = c.square().mul(c).addM(S.x.add(S.x).mul(c2)); x = x1.subM(x2); y1 = S.x.mul(c2).subM(x).mul(d); y2 = S.y.mul(c.square().mul(c)); y = y1.subM(y2); z = S.z.mul(c); return new sjcl.ecc.pointJac(this.curve, x, y, z); }, /** * doubles this point. * @return {sjcl.ecc.pointJac} The doubled point. */ doubl: function () { if (this.isIdentity) { return this; } var y2 = this.y.square(), a = y2.mul(this.x.mul(4)), b = y2.square().mul(8), z2 = this.z.square(), 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); return new sjcl.ecc.pointJac(this.curve, x, y, z); }, /** * 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); } 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. * @param {sjcl.ecc.point} affine This point in affine coordinates. * @return {sjcl.ecc.pointJac} The result of the multiplication, in Jacobian coordinates. */ mult: function (k, affine) { if (typeof k === "number") { k = [k]; } 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--) { for (j = sjcl.bn.prototype.radix - 4; j >= 0; j -= 4) { 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. * @param {sjcl.ecc.point} affine This point in affine coordinates. * @param {bigInt} k2 The coefficient to multiply affine2 this by. * @param {sjcl.ecc.point} affine The other point in affine coordinates. * @return {sjcl.ecc.pointJac} The result of the multiplication and addition, in Jacobian coordinates. */ mult2: function (k1, affine, k2, affine2) { if (typeof k1 === "number") { k1 = [k1]; } 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; for (i = Math.max(k1.length, k2.length) - 1; i >= 0; i--) { l1 = k1[i] | 0; l2 = k2[i] | 0; for (j = sjcl.bn.prototype.radix - 4; j >= 0; j -= 4) { 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( this.curve.b .mul(z6) .add(this.x.mul(this.curve.a.mul(z4).add(this.x.square()))) ); }, }; /** * Construct an elliptic curve. Most users will not use this and instead start with one of the NIST curves defined below. * * @constructor * @param {bigInt} p The prime modulus. * @param {bigInt} r The prime order of the curve. * @param {bigInt} a The constant a in the equation of the curve y^2 = x^3 + ax + b (for NIST curves, a is always -3). * @param {bigInt} x The x coordinate of a base point of the curve. * @param {bigInt} y The y coordinate of a base point of the curve. */ sjcl.ecc.curve = function (Field, r, a, b, x, y) { this.field = Field; 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)); }; sjcl.ecc.curve.prototype.fromBits = function (bits) { var w = sjcl.bitArray, l = (this.field.prototype.exponent + 7) & -8, p = new sjcl.ecc.point( this, this.field.fromBits(w.bitSlice(bits, 0, l)), this.field.fromBits(w.bitSlice(bits, l, 2 * l)) ); if (!p.isValid()) { throw new sjcl.exception.corrupt("not on the curve!"); } return p; }; sjcl.ecc.curves = { c192: new sjcl.ecc.curve( sjcl.bn.prime.p192, "0xffffffffffffffffffffffff99def836146bc9b1b4d22831", -3, "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", "0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", "0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811" ), c224: new sjcl.ecc.curve( sjcl.bn.prime.p224, "0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d", -3, "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", "0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", "0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34" ), c256: new sjcl.ecc.curve( sjcl.bn.prime.p256, "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", -3, "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5" ), c384: new sjcl.ecc.curve( sjcl.bn.prime.p384, "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973", -3, "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", "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" ), }; sjcl.ecc.curveName = function (curve) { var curcurve; for (curcurve in sjcl.ecc.curves) { if (sjcl.ecc.curves.hasOwnProperty(curcurve)) { if (sjcl.ecc.curves[curcurve] === curve) { return curcurve; } } } throw new sjcl.exception.invalid("no such curve"); }; sjcl.ecc.deserialize = function (key) { var types = ["elGamal", "ecdsa"]; if (!key || !key.curve || !sjcl.ecc.curves[key.curve]) { throw new sjcl.exception.invalid("invalid serialization"); } if (types.indexOf(key.type) === -1) { throw new sjcl.exception.invalid("invalid type"); } var curve = sjcl.ecc.curves[key.curve]; if (key.secretKey) { if (!key.exponent) { throw new sjcl.exception.invalid("invalid exponent"); } var exponent = new sjcl.bn(key.exponent); return new sjcl.ecc[key.type].secretKey(curve, exponent); } else { if (!key.point) { throw new sjcl.exception.invalid("invalid point"); } var point = curve.fromBits(sjcl.codec.hex.toBits(key.point)); return new sjcl.ecc[key.type].publicKey(curve, point); } }; /** 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; } this.serialize = function () { var curveName = sjcl.ecc.curveName(curve); return { type: this.getType(), secretKey: false, point: sjcl.codec.hex.fromBits(this._point.toBits()), curve: curveName, }; }; /** 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 }; }; }, /** 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; this.serialize = function () { var exponent = this.get(); var curveName = sjcl.ecc.curveName(curve); return { type: this.getType(), secretKey: true, exponent: sjcl.codec.hex.fromBits(exponent), curve: curveName, }; }; /** get this keys exponent data * @return {bitArray} exponent */ this.get = function () { return this._exponent.toBits(); }; }, }; /** @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) { throw new sjcl.exception.invalid("no such curve"); } } 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), }; }; }; /** 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(), key = sjcl.hash.sha256.hash(this._point.mult(sec).toBits()); return { key: key, tag: tag }; }, getType: function () { return "elGamal"; }, }; 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(); }, getType: function () { return "elGamal"; }, }; /** 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"), }; /** 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 = { /** 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 = this._curveBitLength, r = sjcl.bn.fromBits(w.bitSlice(rs, 0, 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) || 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; }, getType: function () { return "ecdsa"; }, }; /** 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)); }, getType: function () { return "ecdsa"; }, }; export default sjcl;