From 017ff8c82bb7cfe4ab9ec04fccef17b9a981db3d Mon Sep 17 00:00:00 2001 From: Mike Hamburg Date: Tue, 17 Aug 2010 19:42:58 -0700 Subject: [PATCH] ECC initial import. --- core/bitArray.js | 20 ++- core/bn.js | 402 +++++++++++++++++++++++++++++++++++++++++++++++ core/ecc.js | 306 ++++++++++++++++++++++++++++++++++++ core/random.js | 2 +- core/sjcl.js | 6 + 5 files changed, 734 insertions(+), 2 deletions(-) create mode 100644 core/bn.js create mode 100644 core/ecc.js diff --git a/core/bitArray.js b/core/bitArray.js index 40e4acd..03032ab 100644 --- a/core/bitArray.js +++ b/core/bitArray.js @@ -31,7 +31,7 @@ sjcl.bitArray = { /** * Array slices in units of bits. - * @param {bitArray a} The array to slice. + * @param {bitArray} a The array to slice. * @param {Number} bstart The offset to the start of the slice, in bits. * @param {Number} bend The offset to the end of the slice, in bits. If this is undefined, * slice until the end of the array. @@ -42,6 +42,24 @@ sjcl.bitArray = { return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart); }, + /** + * Extract a number packed into a bit array. + * @param {bitArray} a The array to slice. + * @param {Number} bstart The offset to the start of the slice, in bits. + * @param {Number} length The length of the number to extract. + * @return {Number} The requested slice. + */ + extract: function(a, bstart, blength) { + var x, sh = (-bstart-blength) & 31; + if ((bstart + blength - 1 ^ bstart) & -32) { + // it crosses a boundary + x = (a[bstart/32|0] << (32 - sh)) ^ (a[bstart/32+1|0] >>> sh); + } else { + x = a[bstart/32|0] >>> sh; + } + return x & ((1<= 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; + for (i=this.limbs.length; i mo) { + l = limbs.pop(); + ll = limbs.length; + for (k=0; k= 0; i--) { + out = w.concat(out, [w.partial(this.radix, this.getLimb(i))]); + } + return out; + }; + + p.fromBits = function(bits) { + + var out = new this(), words=[], w=sjcl.bitArray, t = this.prototype, + l = Math.min(w.bitLength(bits), t.exponent + 7 & -8), 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; + }; + + return p; +} + +// a small Mersenne prime +p127 = pseudoMersennePrime(127, [[0,-1]]); + +// Bernstein's prime for Curve25519 +p25519 = pseudoMersennePrime(255, [[0,-19]]); + +// NIST primes +p192 = pseudoMersennePrime(192, [[0,-1],[64,-1]]); +p224 = pseudoMersennePrime(224, [[0,1],[96,-1]]); +p256 = pseudoMersennePrime(256, [[0,-1],[96,1],[192,1],[224,-1]]); +p384 = pseudoMersennePrime(384, [[0,-1],[32,1],[96,-1],[128,-1]]); +p521 = pseudoMersennePrime(521, [[0,-1]]); + +bn.random = function(modulus, paranoia) { + if (typeof modulus != "object") { modulus = new bn(modulus); } + var words, i, l = modulus.limbs.length, m = modulus.limbs[l-1]+1, out = new 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=0; i--) { + for (j=bn.prototype.radix-4; j>=0; j-=4) { + out = out.doubl().doubl().doubl().doubl().add(multiples[k[i]>>j & 0xF]); + } + } + + return out; + }, + + 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 = field.prototype.modulus.sub(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.p192curve = new sjcl.ecc.curve( + p192, + "0x662107c8eb94364e4b2dd7ce", + -3, + "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", + "0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012", + "0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811"); + +sjcl.ecc.p224curve = new sjcl.ecc.curve( + p224, + "0xe95c1f470fc1ec22d6baa3a3d5c4", + -3, + "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", + "0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21", + "0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"); + +sjcl.ecc.p256curve = new sjcl.ecc.curve( + p256, + "0x4319055358e8617b0c46353d039cdaae", + -3, + "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", + "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"); + +sjcl.ecc.p384curve = new sjcl.ecc.curve( + p384, + "0x389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c", + -3, + "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", + "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", + "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"); + +sjcl.ecc.curves = { + 192: sjcl.ecc.p192curve, + 224: sjcl.ecc.p224curve, + 256: sjcl.ecc.p256curve, + 384: sjcl.ecc.p384curve +}; + +sjcl.ecc.elGamal = { + publicKey: function(curve, point) { + this._curve = curve; + if (point instanceof Array) { + this._point = curve.fromBits(point); + } else { + this._point = point; + } + }, + secretKey: function(curve, exponent) { + this._curve = curve; + this._exponent = exponent; + }, + + generateKeys: function(curve, paranoia) { + if (typeof curve == "number") { + curve = sjcl.ecc.curves[curve]; + if (curve === undefined) { + throw new sjcl.exception.invalid("no such curve"); + } + } + var sec = bn.random(curve.r, paranoia), pub = curve.G.mult(sec); + return { pub: new sjcl.ecc.elGamal.publicKey(curve, pub), + sec: new sjcl.ecc.elGamal.secretKey(curve, sec) }; + } +}; + +sjcl.ecc.elGamal.publicKey.prototype = { + kem: function(paranoia) { + var sec = 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 }; + } +}; + +sjcl.ecc.elGamal.secretKey.prototype = { + unkem: function(tag) { + return sjcl.hash.sha256.hash(this._curve.fromBits(tag).mult(this._exponent).toBits()); + } +}; + + + diff --git a/core/random.js b/core/random.js index 44948ac..c91cb32 100644 --- a/core/random.js +++ b/core/random.js @@ -47,7 +47,7 @@ sjcl.random = { var out = [], i, readiness = this.isReady(paranoia), g; if (readiness === this._NOT_READY) { - throw new sjcl.exception.notready("generator isn't seeded"); + throw new sjcl.exception.notReady("generator isn't seeded"); } else if (readiness & this._REQUIRES_RESEED) { this._reseedFromPools(!(readiness & this._READY)); } diff --git a/core/sjcl.js b/core/sjcl.js index e364421..a6a65ce 100644 --- a/core/sjcl.js +++ b/core/sjcl.js @@ -55,6 +55,12 @@ var sjcl = { bug: function(message) { this.toString = function() { return "BUG: "+this.message; }; this.message = message; + }, + + /** @class Bug or missing feature in SJCL. */ + notReady: function(message) { + this.toString = function() { return "GENERATOR NOT READY: "+this.message; }; + this.message = message; } } };