ECC initial import.
This commit is contained in:
parent
6efa96883e
commit
017ff8c82b
|
@ -31,7 +31,7 @@
|
||||||
sjcl.bitArray = {
|
sjcl.bitArray = {
|
||||||
/**
|
/**
|
||||||
* Array slices in units of bits.
|
* 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} 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,
|
* @param {Number} bend The offset to the end of the slice, in bits. If this is undefined,
|
||||||
* slice until the end of the array.
|
* slice until the end of the array.
|
||||||
|
@ -42,6 +42,24 @@ sjcl.bitArray = {
|
||||||
return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart);
|
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<<blength) - 1);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concatenate two bit arrays.
|
* Concatenate two bit arrays.
|
||||||
* @param {bitArray} a1 The first array.
|
* @param {bitArray} a1 The first array.
|
||||||
|
|
|
@ -0,0 +1,402 @@
|
||||||
|
/**
|
||||||
|
* Constructs a new bignum from another bignum, a number or a hex string.
|
||||||
|
*/
|
||||||
|
function bn(it) {
|
||||||
|
this.initWith(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
bn.prototype = {
|
||||||
|
radix: 24,
|
||||||
|
maxMul: 8,
|
||||||
|
_class: 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, n, l;
|
||||||
|
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;
|
||||||
|
for (i=this.limbs.length; i<that.limbs.length; i++) {
|
||||||
|
this.limbs[i] = 0;
|
||||||
|
}
|
||||||
|
for (i=0; i<that.limbs.length; i++) {
|
||||||
|
this.limbs[i] += that.limbs[i];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/** this -= that. Does not normalize. */
|
||||||
|
subM: function(that) {
|
||||||
|
if (typeof(that) !== "object") { that = new this._class(that); }
|
||||||
|
var i;
|
||||||
|
for (i=this.limbs.length; i<that.limbs.length; i++) {
|
||||||
|
this.limbs[i] = 0;
|
||||||
|
}
|
||||||
|
for (i=0; i<that.limbs.length; i++) {
|
||||||
|
this.limbs[i] -= that.limbs[i];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 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); }
|
||||||
|
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) {
|
||||||
|
if (typeof(l) == "number") {
|
||||||
|
l = [l];
|
||||||
|
} else if (l.limbs !== undefined) {
|
||||||
|
l = l.normalize().limbs;
|
||||||
|
}
|
||||||
|
var 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);
|
||||||
|
}
|
||||||
|
pow = pow.square();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 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] -= this.placeVal;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialization routines for bignum library. */
|
||||||
|
(function init(){
|
||||||
|
bn.prototype.ipv = 1 / (bn.prototype.placeVal = Math.pow(2,bn.prototype.radix));
|
||||||
|
bn.prototype.radixMask = (1 << 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.
|
||||||
|
*/
|
||||||
|
function pseudoMersennePrime(exponent, coeff) {
|
||||||
|
function p(it) {
|
||||||
|
this.initWith(it);
|
||||||
|
/*if (this.limbs[this.modOffset]) {
|
||||||
|
this.reduce();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
var ppr = p.prototype = new 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 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.ceil(coeff[i][0] / ppr.radix - tmp);
|
||||||
|
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 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. */
|
||||||
|
ppr.reduce = function() {
|
||||||
|
var i, k, l, mo = this.modOffset, limbs = this.limbs, aff, 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 == 0) {
|
||||||
|
limbs.push(0);
|
||||||
|
this.cnormalize();
|
||||||
|
i = this.minOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cnormalize();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
ppr._strongReduce = (ppr.fullMask == -1) ? ppr.reduce : function() {
|
||||||
|
var limbs = this.limbs, i = limbs.length - 1, 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. */
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
ppr.inverse = function() {
|
||||||
|
return (this.power(this.modulus.sub(2)));
|
||||||
|
};
|
||||||
|
|
||||||
|
ppr.toBits = function() {
|
||||||
|
this.fullReduce();
|
||||||
|
var i=this.modOffset - 1, w=sjcl.bitArray, e = (this.exponent + 7 & -8) % this.radix || this.radix;
|
||||||
|
out = [w.partial(e, this.getLimb(i))];
|
||||||
|
for (i--; i >= 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<l-1; i++) {
|
||||||
|
words[i] &= modulus.radixMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the rest of the digitssj
|
||||||
|
out.limbs = words;
|
||||||
|
if (!out.greaterEquals(modulus)) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
if (window.sjcl == undefined) {
|
||||||
|
window.sjcl = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
if (S.curve !== T.curve) {
|
||||||
|
throw("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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var
|
||||||
|
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);
|
||||||
|
var U = new sjcl.ecc.pointJac(this.curve,x,y,z);
|
||||||
|
if (!U.isValid()) { throw "FOOOOOOOO"; }
|
||||||
|
return U;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.x.sub(z2).mul(3).mul(this.x.add(z2)),
|
||||||
|
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), this.y.mul(zi2.mul(zi)));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = this, multiples, aff2;
|
||||||
|
|
||||||
|
if (affine === undefined) {
|
||||||
|
affine = this.toAffine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (affine.multiples === undefined) {
|
||||||
|
j = this.doubl();
|
||||||
|
affine.multiples = [new sjcl.ecc.point(this.curve), affine, j.toAffine()];
|
||||||
|
for (i=3; i<16; i++) {
|
||||||
|
j = j.add(affine);
|
||||||
|
affine.multiples[i] = j.toAffine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
multiples = affine.multiples;
|
||||||
|
|
||||||
|
for (i=k.length-1; 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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ sjcl.random = {
|
||||||
var out = [], i, readiness = this.isReady(paranoia), g;
|
var out = [], i, readiness = this.isReady(paranoia), g;
|
||||||
|
|
||||||
if (readiness === this._NOT_READY) {
|
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) {
|
} else if (readiness & this._REQUIRES_RESEED) {
|
||||||
this._reseedFromPools(!(readiness & this._READY));
|
this._reseedFromPools(!(readiness & this._READY));
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,12 @@ var sjcl = {
|
||||||
bug: function(message) {
|
bug: function(message) {
|
||||||
this.toString = function() { return "BUG: "+this.message; };
|
this.toString = function() { return "BUG: "+this.message; };
|
||||||
this.message = 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue