summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bitArray.js16
-rw-r--r--core/bn.js334
-rw-r--r--core/cbc.js16
-rw-r--r--core/ccm.js88
-rw-r--r--core/ccmArrayBuffer.js247
-rw-r--r--core/codecArrayBuffer.js113
-rw-r--r--core/codecBase32.js88
-rw-r--r--core/codecBase64.js8
-rw-r--r--core/codecHex.js2
-rw-r--r--core/convenience.js163
-rw-r--r--core/ecc.js310
-rw-r--r--core/gcm.js183
-rw-r--r--core/hmac.js29
-rw-r--r--core/ocb2progressive.js137
-rw-r--r--core/pbkdf2.js6
-rw-r--r--core/random.js266
-rw-r--r--core/ripemd160.js207
-rw-r--r--core/scrypt.js146
-rw-r--r--core/sha1.js3
-rw-r--r--core/sha256.js2
-rw-r--r--core/sha512.js348
-rw-r--r--core/sjcl.js17
-rw-r--r--core/srp.js133
23 files changed, 2536 insertions, 326 deletions
diff --git a/core/bitArray.js b/core/bitArray.js
index a6d0617..54112ac 100644
--- a/core/bitArray.js
+++ b/core/bitArray.js
@@ -74,7 +74,7 @@ sjcl.bitArray = {
return a1.concat(a2);
}
- var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
+ var last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
if (shift === 32) {
return a1.concat(a2);
} else {
@@ -183,5 +183,19 @@ sjcl.bitArray = {
*/
_xor4: function(x,y) {
return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];
+ },
+
+ /** byteswap a word array inplace.
+ * (does not handle partial words)
+ * @param {sjcl.bitArray} a word array
+ * @return {sjcl.bitArray} byteswapped array
+ */
+ byteswapM: function(a) {
+ var i, v, m = 0xff00;
+ for (i = 0; i < a.length; ++i) {
+ v = a[i];
+ a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
+ }
+ return a;
}
};
diff --git a/core/bn.js b/core/bn.js
index 672fd08..01dd349 100644
--- a/core/bn.js
+++ b/core/bn.js
@@ -1,4 +1,5 @@
/**
+ * @constructor
* Constructs a new bignum from another bignum, a number or a hex string.
*/
sjcl.bn = function(it) {
@@ -9,7 +10,7 @@ sjcl.bn.prototype = {
radix: 24,
maxMul: 8,
_class: sjcl.bn,
-
+
copy: function() {
return new this._class(this);
},
@@ -18,17 +19,17 @@ sjcl.bn.prototype = {
* Initializes this with it, either as a bn, a number, or a hex string.
*/
initWith: function(it) {
- var i=0, k, n, l;
+ 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 = [];
@@ -59,14 +60,14 @@ sjcl.bn.prototype = {
}
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.
@@ -83,7 +84,7 @@ sjcl.bn.prototype = {
}
return (greater | ~less) >>> 31;
},
-
+
/**
* Convert to a hex string.
*/
@@ -99,7 +100,7 @@ sjcl.bn.prototype = {
}
return "0x"+out;
},
-
+
/** this += that. Does not normalize. */
addM: function(that) {
if (typeof(that) !== "object") { that = new this._class(that); }
@@ -112,7 +113,7 @@ sjcl.bn.prototype = {
}
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;
@@ -127,7 +128,7 @@ sjcl.bn.prototype = {
}
return this;
},
-
+
/** this /= 2, rounded down. Requires normalized; ends up normalized. */
halveM: function() {
var i, carry=0, tmp, r=this.radix, l=this.limbs;
@@ -154,14 +155,21 @@ sjcl.bn.prototype = {
}
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)) {
@@ -170,15 +178,15 @@ sjcl.bn.prototype = {
}
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) {
@@ -189,13 +197,13 @@ sjcl.bn.prototype = {
}
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) {
@@ -203,20 +211,20 @@ sjcl.bn.prototype = {
}
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);
@@ -226,7 +234,7 @@ sjcl.bn.prototype = {
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); }
@@ -240,7 +248,7 @@ sjcl.bn.prototype = {
for (j=0; j<bl; j++) {
c[i+j] += ai * b[j];
}
-
+
if (!--ii) {
ii = this.maxMul;
out.cnormalize();
@@ -256,22 +264,18 @@ sjcl.bn.prototype = {
/** 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;
- }
+ 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 (l[i] & (1<<j)) { out = out.mul(pow); }
+ if (i == (l.length - 1) && l[i]>>(j + 1) == 0) { break; }
+
pow = pow.square();
}
}
-
+
return out;
},
@@ -282,14 +286,184 @@ sjcl.bn.prototype = {
/** this ^ x mod N */
powermod: function(x, N) {
- var result = new sjcl.bn(1), a = new sjcl.bn(this), k = new sjcl.bn(x);
- while (true) {
- if (k.limbs[0] & 1) { result = result.mulmod(a, N); }
- k.halveM();
- if (k.equals(0)) { break; }
- a = a.mulmod(a, N);
- }
- return result.normalize().reduce();
+ 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, carry, 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() {
@@ -300,7 +474,7 @@ sjcl.bn.prototype = {
l.push(p);
return this;
},
-
+
/** Reduce mod a modulus. Stubbed for subclassing. */
reduce: function() {
return this;
@@ -310,7 +484,7 @@ sjcl.bn.prototype = {
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;
@@ -320,7 +494,10 @@ sjcl.bn.prototype = {
carry = (l-m)*ipv;
}
if (carry === -1) {
- limbs[i-1] -= this.placeVal;
+ limbs[i-1] -= pv;
+ }
+ while (limbs.length > 0 && limbs[limbs.length-1] === 0) {
+ limbs.pop();
}
return this;
},
@@ -336,35 +513,39 @@ sjcl.bn.prototype = {
limbs[i] += carry;
return this;
},
-
+
/** Serialize to a bit array */
toBits: function(len) {
this.fullReduce();
- len = len || this.exponent || this.limbs.length * this.radix;
+ 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(this.radix, this.getLimb(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) {
+ 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));
@@ -384,6 +565,9 @@ sjcl.bn.prototype.radixMask = (1 << sjcl.bn.prototype.radix) - 1;
* 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]) {
@@ -401,7 +585,7 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
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++) {
@@ -415,9 +599,12 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
ppr._class = p;
ppr.modulus.cnormalize();
- /** Approximate reduction mod p. May leave a number which is negative or slightly larger than p. */
+ /** 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, aff, off = this.offset, ol = this.offset.length, fac = this.factor, ll;
+ 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) {
@@ -426,7 +613,7 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
for (k=0; k<ol; k++) {
limbs[ll+off[k]] -= fac[k] * l;
}
-
+
i--;
if (!i) {
limbs.push(0);
@@ -438,7 +625,10 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
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();
@@ -452,11 +642,14 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
}
};
- /** mostly constant-time, very expensive full reduction. */
+ /** 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
@@ -464,7 +657,7 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
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
@@ -473,7 +666,7 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
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++) {
@@ -484,6 +677,10 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
return this;
};
+
+ /** @memberof sjcl.bn
+ * @this { sjcl.bn }
+ */
ppr.inverse = function() {
return (this.power(this.modulus.sub(2)));
};
@@ -494,18 +691,24 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
};
// a small Mersenne prime
+var sbp = sjcl.bn.pseudoMersennePrime;
sjcl.bn.prime = {
- p127: sjcl.bn.pseudoMersennePrime(127, [[0,-1]]),
+ p127: sbp(127, [[0,-1]]),
// Bernstein's prime for Curve25519
- p25519: sjcl.bn.pseudoMersennePrime(255, [[0,-19]]),
+ 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: sjcl.bn.pseudoMersennePrime(192, [[0,-1],[64,-1]]),
- p224: sjcl.bn.pseudoMersennePrime(224, [[0,1],[96,-1]]),
- p256: sjcl.bn.pseudoMersennePrime(256, [[0,-1],[96,1],[192,1],[224,-1]]),
- p384: sjcl.bn.pseudoMersennePrime(384, [[0,-1],[32,1],[96,-1],[128,-1]]),
- p521: sjcl.bn.pseudoMersennePrime(521, [[0,-1]])
+ 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) {
@@ -531,4 +734,3 @@ sjcl.bn.random = function(modulus, paranoia) {
}
}
};
-
diff --git a/core/cbc.js b/core/cbc.js
index 9a98ed8..39026cd 100644
--- a/core/cbc.js
+++ b/core/cbc.js
@@ -5,18 +5,18 @@
* @author Dan Boneh
*/
-/** @namespace
- * Dangerous: CBC mode with PKCS#5 padding.
- *
- * @author Emily Stark
- * @author Mike Hamburg
- * @author Dan Boneh
- */
if (sjcl.beware === undefined) {
sjcl.beware = {};
}
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."
] = function() {
+ /** @namespace
+ * Dangerous: CBC mode with PKCS#5 padding.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
sjcl.mode.cbc = {
/** The name of the mode.
* @constant
@@ -100,7 +100,7 @@ sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity.
/* check and remove the pad */
bi = output[i-1] & 255;
- if (bi == 0 || bi > 16) {
+ if (bi === 0 || bi > 16) {
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
}
bo = bi * 0x1010101;
diff --git a/core/ccm.js b/core/ccm.js
index 8a32c5a..1efde02 100644
--- a/core/ccm.js
+++ b/core/ccm.js
@@ -15,6 +15,27 @@ sjcl.mode.ccm = {
*/
name: "ccm",
+ _progressListeners: [],
+
+ listenProgress: function (cb) {
+ sjcl.mode.ccm._progressListeners.push(cb);
+ },
+
+ unListenProgress: function (cb) {
+ var index = sjcl.mode.ccm._progressListeners.indexOf(cb);
+ if (index > -1) {
+ sjcl.mode.ccm._progressListeners.splice(index, 1);
+ }
+ },
+
+ _callProgressListener: function (val) {
+ var p = sjcl.mode.ccm._progressListeners.slice(), i;
+
+ for (i = 0; i < p.length; i += 1) {
+ p[i](val);
+ }
+ },
+
/** Encrypt in CCM mode.
* @static
* @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
@@ -25,7 +46,7 @@ sjcl.mode.ccm = {
* @return {bitArray} The encrypted data, an array of bytes.
*/
encrypt: function(prf, plaintext, iv, adata, tlen) {
- var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
+ var L, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
tlen = tlen || 64;
adata = adata || [];
@@ -59,7 +80,7 @@ sjcl.mode.ccm = {
decrypt: function(prf, ciphertext, iv, adata, tlen) {
tlen = tlen || 64;
adata = adata || [];
- var L, i,
+ var L,
w=sjcl.bitArray,
ivl = w.bitLength(iv) / 8,
ol = w.bitLength(ciphertext),
@@ -90,6 +111,36 @@ sjcl.mode.ccm = {
return out.data;
},
+ _macAdditionalData: function (prf, adata, iv, tlen, ol, L) {
+ var mac, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
+
+ // mac the flags
+ mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)];
+
+ // mac the iv and length
+ mac = w.concat(mac, iv);
+ mac[3] |= ol;
+ mac = prf.encrypt(mac);
+
+ if (adata.length) {
+ // mac the associated data. start with its length...
+ tmp = w.bitLength(adata)/8;
+ if (tmp <= 0xFEFF) {
+ macData = [w.partial(16, tmp)];
+ } else if (tmp <= 0xFFFFFFFF) {
+ macData = w.concat([w.partial(16,0xFFFE)], [tmp]);
+ } // else ...
+
+ // mac the data itself
+ macData = w.concat(macData, adata);
+ for (i=0; i<macData.length; i += 4) {
+ mac = prf.encrypt(xor(mac, macData.slice(i,i+4).concat([0,0,0])));
+ }
+ }
+
+ return mac;
+ },
+
/* Compute the (unencrypted) authentication tag, according to the CCM specification
* @param {Object} prf The pseudorandom function.
* @param {bitArray} plaintext The plaintext data.
@@ -101,7 +152,7 @@ sjcl.mode.ccm = {
*/
_computeTag: function(prf, plaintext, iv, adata, tlen, L) {
// compute B[0]
- var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
+ var mac, i, w=sjcl.bitArray, xor = w._xor4;
tlen /= 8;
@@ -115,31 +166,8 @@ sjcl.mode.ccm = {
throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
}
- // mac the flags
- mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)];
+ mac = sjcl.mode.ccm._macAdditionalData(prf, adata, iv, tlen, w.bitLength(plaintext)/8, L);
- // mac the iv and length
- mac = w.concat(mac, iv);
- mac[3] |= w.bitLength(plaintext)/8;
- mac = prf.encrypt(mac);
-
-
- if (adata.length) {
- // mac the associated data. start with its length...
- tmp = w.bitLength(adata)/8;
- if (tmp <= 0xFEFF) {
- macData = [w.partial(16, tmp)];
- } else if (tmp <= 0xFFFFFFFF) {
- macData = w.concat([w.partial(16,0xFFFE)], [tmp]);
- } // else ...
-
- // mac the data itself
- macData = w.concat(macData, adata);
- for (i=0; i<macData.length; i += 4) {
- mac = prf.encrypt(xor(mac, macData.slice(i,i+4).concat([0,0,0])));
- }
- }
-
// mac the plaintext
for (i=0; i<plaintext.length; i+=4) {
mac = prf.encrypt(xor(mac, plaintext.slice(i,i+4).concat([0,0,0])));
@@ -161,7 +189,7 @@ sjcl.mode.ccm = {
* @private
*/
_ctrMode: function(prf, data, iv, tag, tlen, L) {
- var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
+ var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, l = data.length, bl=w.bitLength(data), n = l/50, p = n;
// start the ctr
ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);
@@ -173,6 +201,10 @@ sjcl.mode.ccm = {
if (!l) { return {tag:tag, data:[]}; }
for (i=0; i<l; i+=4) {
+ if (i > n) {
+ sjcl.mode.ccm._callProgressListener(i/l);
+ n += p;
+ }
ctr[3]++;
enc = prf.encrypt(ctr);
data[i] ^= enc[0];
diff --git a/core/ccmArrayBuffer.js b/core/ccmArrayBuffer.js
new file mode 100644
index 0000000..a7b44b0
--- /dev/null
+++ b/core/ccmArrayBuffer.js
@@ -0,0 +1,247 @@
+/** @fileOverview Really fast & small implementation of CCM using JS' array buffers
+ *
+ * @author Marco Munizaga
+ */
+
+/** @namespace CTR mode with CBC MAC. */
+
+sjcl.arrayBuffer = sjcl.arrayBuffer || {};
+
+//patch arraybuffers if they don't exist
+if (typeof(ArrayBuffer) === 'undefined') {
+ (function(globals){
+ "use strict";
+ globals.ArrayBuffer = function(){};
+ globals.DataView = function(){};
+ }(this));
+}
+
+
+sjcl.arrayBuffer.ccm = {
+ mode: "ccm",
+
+ defaults: {
+ tlen:128 //this is M in the NIST paper
+ },
+
+ /** Encrypt in CCM mode. Meant to return the same exact thing as the bitArray ccm to work as a drop in replacement
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {bitArray} plaintext The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=64] the desired tag length, in bits.
+ * @return {bitArray} The encrypted data, an array of bytes.
+ */
+ compat_encrypt: function(prf, plaintext, iv, adata, tlen){
+ var plaintext_buffer = sjcl.codec.arrayBuffer.fromBits(plaintext, true, 16),
+ ol = sjcl.bitArray.bitLength(plaintext)/8,
+ encrypted_obj,
+ ct,
+ tag;
+
+ tlen = tlen || 64;
+ adata = adata || [];
+
+ encrypted_obj = sjcl.arrayBuffer.ccm.encrypt(prf, plaintext_buffer, iv, adata, tlen, ol);
+ ct = sjcl.codec.arrayBuffer.toBits(encrypted_obj.ciphertext_buffer);
+
+ ct = sjcl.bitArray.clamp(ct, ol*8);
+
+
+ return sjcl.bitArray.concat(ct, encrypted_obj.tag);
+ },
+
+ /** Decrypt in CCM mode. Meant to imitate the bitArray ccm
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {bitArray} ciphertext The ciphertext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [[]] adata The authenticated data.
+ * @param {Number} [64] tlen the desired tag length, in bits.
+ * @return {bitArray} The decrypted data.
+ */
+ compat_decrypt: function(prf, ciphertext, iv, adata, tlen){
+ tlen = tlen || 64;
+ adata = adata || [];
+ var L, i,
+ w=sjcl.bitArray,
+ ol = w.bitLength(ciphertext),
+ out = w.clamp(ciphertext, ol - tlen),
+ tag = w.bitSlice(ciphertext, ol - tlen), tag2,
+ ciphertext_buffer = sjcl.codec.arrayBuffer.fromBits(out, true, 16);
+
+ var plaintext_buffer = sjcl.arrayBuffer.ccm.decrypt(prf, ciphertext_buffer, iv, tag, adata, tlen, (ol-tlen)/8);
+ return sjcl.bitArray.clamp(sjcl.codec.arrayBuffer.toBits(plaintext_buffer), ol-tlen);
+
+ },
+
+ /** Really fast ccm encryption, uses arraybufer and mutates the plaintext buffer
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {ArrayBuffer} plaintext_buffer The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {ArrayBuffer} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=128] the desired tag length, in bits.
+ * @return {ArrayBuffer} The encrypted data, in the same array buffer as the given plaintext, but given back anyways
+ */
+ encrypt: function(prf, plaintext_buffer, iv, adata, tlen, ol){
+ var auth_blocks, mac, L, w = sjcl.bitArray,
+ ivl = w.bitLength(iv) / 8;
+
+ //set up defaults
+ adata = adata || [];
+ tlen = tlen || sjcl.arrayBuffer.ccm.defaults.tlen;
+ ol = ol || plaintext_buffer.byteLength;
+ tlen = Math.ceil(tlen/8);
+
+ for (L=2; L<4 && ol >>> 8*L; L++) {}
+ if (L < 15 - ivl) { L = 15-ivl; }
+ iv = w.clamp(iv,8*(15-L));
+
+ //prf should use a 256 bit key to make precomputation attacks infeasible
+
+ mac = sjcl.arrayBuffer.ccm._computeTag(prf, plaintext_buffer, iv, adata, tlen, ol, L);
+
+ //encrypt the plaintext and the mac
+ //returns the mac since the plaintext will be left encrypted inside the buffer
+ mac = sjcl.arrayBuffer.ccm._ctrMode(prf, plaintext_buffer, iv, mac, tlen, L);
+
+
+ //the plaintext_buffer has been modified so it is now the ciphertext_buffer
+ return {'ciphertext_buffer':plaintext_buffer, 'tag':mac};
+ },
+
+ /** Really fast ccm decryption, uses arraybufer and mutates the given buffer
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {ArrayBuffer} ciphertext_buffer The Ciphertext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} The authentication tag for the ciphertext
+ * @param {ArrayBuffer} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=128] the desired tag length, in bits.
+ * @return {ArrayBuffer} The decrypted data, in the same array buffer as the given buffer, but given back anyways
+ */
+ decrypt: function(prf, ciphertext_buffer, iv, tag, adata, tlen, ol){
+ var mac, mac2, i, L, w = sjcl.bitArray,
+ ivl = w.bitLength(iv) / 8;
+
+ //set up defaults
+ adata = adata || [];
+ tlen = tlen || sjcl.arrayBuffer.ccm.defaults.tlen;
+ ol = ol || ciphertext_buffer.byteLength;
+ tlen = Math.ceil(tlen/8) ;
+
+ for (L=2; L<4 && ol >>> 8*L; L++) {}
+ if (L < 15 - ivl) { L = 15-ivl; }
+ iv = w.clamp(iv,8*(15-L));
+
+ //prf should use a 256 bit key to make precomputation attacks infeasible
+
+ //decrypt the buffer
+ mac = sjcl.arrayBuffer.ccm._ctrMode(prf, ciphertext_buffer, iv, tag, tlen, L);
+
+ mac2 = sjcl.arrayBuffer.ccm._computeTag(prf, ciphertext_buffer, iv, adata, tlen, ol, L);
+
+ //check the tag
+ if (!sjcl.bitArray.equal(mac, mac2)){
+ throw new sjcl.exception.corrupt("ccm: tag doesn't match");
+ }
+
+ return ciphertext_buffer;
+
+ },
+
+ /* Compute the (unencrypted) authentication tag, according to the CCM specification
+ * @param {Object} prf The pseudorandom function.
+ * @param {ArrayBuffer} data_buffer The plaintext data in an arraybuffer.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} adata The authenticated data.
+ * @param {Number} tlen the desired tag length, in bits.
+ * @return {bitArray} The tag, but not yet encrypted.
+ * @private
+ */
+ _computeTag: function(prf, data_buffer, iv, adata, tlen, ol, L){
+ var i, plaintext, mac, data, data_blocks_size, data_blocks,
+ w = sjcl.bitArray, tmp, macData;
+
+ mac = sjcl.mode.ccm._macAdditionalData(prf, adata, iv, tlen, ol, L);
+
+ if (data_buffer.byteLength !== 0) {
+ data = new DataView(data_buffer);
+ //set padding bytes to 0
+ for (i=ol; i< data_buffer.byteLength; i++){
+ data.setUint8(i,0);
+ }
+
+ //now to mac the plaintext blocks
+ for (i=0; i < data.byteLength; i+=16){
+ mac[0] ^= data.getUint32(i);
+ mac[1] ^= data.getUint32(i+4);
+ mac[2] ^= data.getUint32(i+8);
+ mac[3] ^= data.getUint32(i+12);
+
+ mac = prf.encrypt(mac);
+ }
+ }
+
+ return sjcl.bitArray.clamp(mac,tlen*8);
+ },
+
+ /** CCM CTR mode.
+ * Encrypt or decrypt data and tag with the prf in CCM-style CTR mode.
+ * Mutates given array buffer
+ * @param {Object} prf The PRF.
+ * @param {ArrayBuffer} data_buffer The data to be encrypted or decrypted.
+ * @param {bitArray} iv The initialization vector.
+ * @param {bitArray} tag The authentication tag.
+ * @param {Number} tlen The length of th etag, in bits.
+ * @return {Object} An object with data and tag, the en/decryption of data and tag values.
+ * @private
+ */
+ _ctrMode: function(prf, data_buffer, iv, mac, tlen, L){
+ var data, ctr, word0, word1, word2, word3, keyblock, i, w = sjcl.bitArray, xor = w._xor4, n = data_buffer.byteLength/50, p = n;
+
+ ctr = new DataView(new ArrayBuffer(16)); //create the first block for the counter
+
+ //prf should use a 256 bit key to make precomputation attacks infeasible
+
+ // start the ctr
+ ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);
+
+ // en/decrypt the tag
+ mac = w.bitSlice(xor(mac,prf.encrypt(ctr)), 0, tlen*8);
+
+ ctr[3]++;
+ if (ctr[3]===0) ctr[2]++; //increment higher bytes if the lowest 4 bytes are 0
+
+ if (data_buffer.byteLength !== 0) {
+ data = new DataView(data_buffer);
+ //now lets encrypt the message
+ for (i=0; i<data.byteLength;i+=16){
+ if (i > n) {
+ sjcl.mode.ccm._callProgressListener(i/data_buffer.byteLength);
+ n += p;
+ }
+ keyblock = prf.encrypt(ctr);
+
+ word0 = data.getUint32(i);
+ word1 = data.getUint32(i+4);
+ word2 = data.getUint32(i+8);
+ word3 = data.getUint32(i+12);
+
+ data.setUint32(i,word0 ^ keyblock[0]);
+ data.setUint32(i+4, word1 ^ keyblock[1]);
+ data.setUint32(i+8, word2 ^ keyblock[2]);
+ data.setUint32(i+12, word3 ^ keyblock[3]);
+
+ ctr[3]++;
+ if (ctr[3]===0) ctr[2]++; //increment higher bytes if the lowest 4 bytes are 0
+ }
+ }
+
+ //return the mac, the ciphered data is available through the same data_buffer that was given
+ return mac;
+ }
+
+};
diff --git a/core/codecArrayBuffer.js b/core/codecArrayBuffer.js
new file mode 100644
index 0000000..c2be4ca
--- /dev/null
+++ b/core/codecArrayBuffer.js
@@ -0,0 +1,113 @@
+/** @fileOverview Bit array codec implementations.
+ *
+ * @author Marco Munizaga
+ */
+
+//patch arraybuffers if they don't exist
+if (typeof(ArrayBuffer) === 'undefined') {
+ (function(globals){
+ "use strict";
+ globals.ArrayBuffer = function(){};
+ globals.DataView = function(){};
+ }(this));
+}
+
+/** @namespace ArrayBuffer */
+sjcl.codec.arrayBuffer = {
+ /** Convert from a bitArray to an ArrayBuffer.
+ * Will default to 8byte padding if padding is undefined*/
+ fromBits: function (arr, padding, padding_count) {
+ var out, i, ol, tmp, smallest;
+ padding = padding==undefined ? true : padding
+ padding_count = padding_count || 8
+
+ if (arr.length === 0) {
+ return new ArrayBuffer(0);
+ }
+
+ ol = sjcl.bitArray.bitLength(arr)/8;
+
+ //check to make sure the bitLength is divisible by 8, if it isn't
+ //we can't do anything since arraybuffers work with bytes, not bits
+ if ( sjcl.bitArray.bitLength(arr)%8 !== 0 ) {
+ throw new sjcl.exception.invalid("Invalid bit size, must be divisble by 8 to fit in an arraybuffer correctly")
+ }
+
+ if (padding && ol%padding_count !== 0){
+ ol += padding_count - (ol%padding_count);
+ }
+
+
+ //padded temp for easy copying
+ tmp = new DataView(new ArrayBuffer(arr.length*4));
+ for (i=0; i<arr.length; i++) {
+ tmp.setUint32(i*4, (arr[i]<<32)); //get rid of the higher bits
+ }
+
+ //now copy the final message if we are not going to 0 pad
+ out = new DataView(new ArrayBuffer(ol));
+
+ //save a step when the tmp and out bytelength are ===
+ if (out.byteLength === tmp.byteLength){
+ return tmp.buffer;
+ }
+
+ smallest = tmp.byteLength < out.byteLength ? tmp.byteLength : out.byteLength;
+ for(i=0; i<smallest; i++){
+ out.setUint8(i,tmp.getUint8(i));
+ }
+
+
+ return out.buffer
+ },
+
+ toBits: function (buffer) {
+ var i, out=[], len, inView, tmp;
+
+ if (buffer.byteLength === 0) {
+ return [];
+ }
+
+ inView = new DataView(buffer);
+ len = inView.byteLength - inView.byteLength%4;
+
+ for (var i = 0; i < len; i+=4) {
+ out.push(inView.getUint32(i));
+ }
+
+ if (inView.byteLength%4 != 0) {
+ tmp = new DataView(new ArrayBuffer(4));
+ for (var i = 0, l = inView.byteLength%4; i < l; i++) {
+ //we want the data to the right, because partial slices off the starting bits
+ tmp.setUint8(i+4-l, inView.getUint8(len+i)); // big-endian,
+ }
+ out.push(
+ sjcl.bitArray.partial( (inView.byteLength%4)*8, tmp.getUint32(0) )
+ );
+ }
+ return out;
+ },
+
+
+
+ /** Prints a hex output of the buffer contents, akin to hexdump **/
+ hexDumpBuffer: function(buffer){
+ var stringBufferView = new DataView(buffer)
+ var string = ''
+ var pad = function (n, width) {
+ n = n + '';
+ return n.length >= width ? n : new Array(width - n.length + 1).join('0') + n;
+ }
+
+ for (var i = 0; i < stringBufferView.byteLength; i+=2) {
+ if (i%16 == 0) string += ('\n'+(i).toString(16)+'\t')
+ string += ( pad(stringBufferView.getUint16(i).toString(16),4) + ' ')
+ }
+
+ if ( typeof console === undefined ){
+ console = console || {log:function(){}} //fix for IE
+ }
+ console.log(string.toUpperCase())
+ }
+};
+
diff --git a/core/codecBase32.js b/core/codecBase32.js
new file mode 100644
index 0000000..9251e04
--- /dev/null
+++ b/core/codecBase32.js
@@ -0,0 +1,88 @@
+/** @fileOverview Bit array codec implementations.
+ *
+ * @author Nils Kenneweg
+ */
+
+/** @namespace Base32 encoding/decoding */
+sjcl.codec.base32 = {
+ /** The base32 alphabet.
+ * @private
+ */
+ _chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
+ _hexChars: "0123456789ABCDEFGHIJKLMNOPQRSTUV",
+
+ /* bits in an array */
+ BITS: 32,
+ /* base to encode at (2^x) */
+ BASE: 5,
+ /* bits - base */
+ REMAINING: 27,
+
+ /** Convert from a bitArray to a base32 string. */
+ fromBits: function (arr, _noEquals, _hex) {
+ var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
+ var out = "", i, bits=0, c = sjcl.codec.base32._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
+
+ if (_hex) {
+ c = sjcl.codec.base32._hexChars;
+ }
+
+ for (i=0; out.length * BASE < bl; ) {
+ out += c.charAt((ta ^ arr[i]>>>bits) >>> REMAINING);
+ if (bits < BASE) {
+ ta = arr[i] << (BASE-bits);
+ bits += REMAINING;
+ i++;
+ } else {
+ ta <<= BASE;
+ bits -= BASE;
+ }
+ }
+ while ((out.length & 7) && !_noEquals) { out += "="; }
+
+ return out;
+ },
+
+ /** Convert from a base32 string to a bitArray */
+ toBits: function(str, _hex) {
+ str = str.replace(/\s|=/g,'').toUpperCase();
+ var BITS = sjcl.codec.base32.BITS, BASE = sjcl.codec.base32.BASE, REMAINING = sjcl.codec.base32.REMAINING;
+ var out = [], i, bits=0, c = sjcl.codec.base32._chars, ta=0, x, format="base32";
+
+ if (_hex) {
+ c = sjcl.codec.base32._hexChars;
+ format = "base32hex"
+ }
+
+ for (i=0; i<str.length; i++) {
+ x = c.indexOf(str.charAt(i));
+ if (x < 0) {
+ // Invalid character, try hex format
+ if (!_hex) {
+ try {
+ return sjcl.codec.base32hex.toBits(str);
+ }
+ catch (e) {}
+ }
+ throw new sjcl.exception.invalid("this isn't " + format + "!");
+ }
+ if (bits > REMAINING) {
+ bits -= REMAINING;
+ out.push(ta ^ x>>>bits);
+ ta = x << (BITS-bits);
+ } else {
+ bits += BASE;
+ ta ^= x << (BITS-bits);
+ }
+ }
+ if (bits&56) {
+ out.push(sjcl.bitArray.partial(bits&56, ta, 1));
+ }
+ return out;
+ }
+};
+
+sjcl.codec.base32hex = {
+ fromBits: function (arr, _noEquals) { return sjcl.codec.base32.fromBits(arr,_noEquals,1); },
+ toBits: function (str) { return sjcl.codec.base32.toBits(str,1); }
+};
diff --git a/core/codecBase64.js b/core/codecBase64.js
index 33b2b7a..f518083 100644
--- a/core/codecBase64.js
+++ b/core/codecBase64.js
@@ -15,7 +15,9 @@ sjcl.codec.base64 = {
/** Convert from a bitArray to a base64 string. */
fromBits: function (arr, _noEquals, _url) {
var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
- if (_url) c = c.substr(0,62) + '-_';
+ if (_url) {
+ c = c.substr(0,62) + '-_';
+ }
for (i=0; out.length * 6 < bl; ) {
out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
if (bits < 6) {
@@ -35,7 +37,9 @@ sjcl.codec.base64 = {
toBits: function(str, _url) {
str = str.replace(/\s|=/g,'');
var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
- if (_url) c = c.substr(0,62) + '-_';
+ if (_url) {
+ c = c.substr(0,62) + '-_';
+ }
for (i=0; i<str.length; i++) {
x = c.indexOf(str.charAt(i));
if (x < 0) {
diff --git a/core/codecHex.js b/core/codecHex.js
index 03fb628..c14d5a0 100644
--- a/core/codecHex.js
+++ b/core/codecHex.js
@@ -9,7 +9,7 @@
sjcl.codec.hex = {
/** Convert from a bitArray to a hex string. */
fromBits: function (arr) {
- var out = "", i, x;
+ var out = "", i;
for (i=0; i<arr.length; i++) {
out += ((arr[i]|0)+0xF00000000000).toString(16).substr(4);
}
diff --git a/core/convenience.js b/core/convenience.js
index 8d68b07..ecc8cfa 100644
--- a/core/convenience.js
+++ b/core/convenience.js
@@ -4,7 +4,7 @@
* @author Mike Hamburg
* @author Dan Boneh
*/
-
+
/** @namespace JSON encapsulation */
sjcl.json = {
/** Default values for encryption */
@@ -15,13 +15,13 @@
* @param {String} plaintext The data to encrypt.
* @param {Object} [params] The parameters including tag, iv and salt.
* @param {Object} [rp] A returned version with filled-in parameters.
- * @return {String} The ciphertext.
+ * @return {Object} The cipher raw data.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
*/
- encrypt: function (password, plaintext, params, rp) {
+ _encrypt: function (password, plaintext, params, rp) {
params = params || {};
rp = rp || {};
-
+
var j = sjcl.json, p = j._add({ iv: sjcl.random.randomWords(4,0) },
j.defaults), tmp, prp, adata;
j._add(p, params);
@@ -32,7 +32,7 @@
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
-
+
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -41,52 +41,73 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json encrypt: invalid parameters");
}
-
+
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
p.salt = tmp.salt;
+ } else if (sjcl.ecc && password instanceof sjcl.ecc.elGamal.publicKey) {
+ tmp = password.kem();
+ p.kemtag = tmp.tag;
+ password = tmp.key.slice(0,p.ks/32);
}
if (typeof plaintext === "string") {
plaintext = sjcl.codec.utf8String.toBits(plaintext);
}
if (typeof adata === "string") {
- adata = sjcl.codec.utf8String.toBits(adata);
+ p.adata = adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
-
+
/* return the json data */
j._add(rp, p);
rp.key = password;
-
+
/* do the encryption */
- p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts);
-
+ if (p.mode === "ccm" && sjcl.arrayBuffer && sjcl.arrayBuffer.ccm && plaintext instanceof ArrayBuffer) {
+ p.ct = sjcl.arrayBuffer.ccm.encrypt(prp, plaintext, p.iv, adata, p.ts);
+ } else {
+ p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts);
+ }
+
//return j.encode(j._subtract(p, j.defaults));
+ return p;
+ },
+
+ /** Simple encryption function.
+ * @param {String|bitArray} password The password or key.
+ * @param {String} plaintext The data to encrypt.
+ * @param {Object} [params] The parameters including tag, iv and salt.
+ * @param {Object} [rp] A returned version with filled-in parameters.
+ * @return {String} The ciphertext serialized data.
+ * @throws {sjcl.exception.invalid} if a parameter is invalid.
+ */
+ encrypt: function (password, plaintext, params, rp) {
+ var j = sjcl.json, p = j._encrypt.apply(j, arguments);
return j.encode(p);
},
-
+
/** Simple decryption function.
* @param {String|bitArray} password The password or key.
- * @param {String} ciphertext The ciphertext to decrypt.
+ * @param {Object} ciphertext The cipher raw data to decrypt.
* @param {Object} [params] Additional non-default parameters.
* @param {Object} [rp] A returned object with filled parameters.
* @return {String} The plaintext.
* @throws {sjcl.exception.invalid} if a parameter is invalid.
* @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
*/
- decrypt: function (password, ciphertext, params, rp) {
+ _decrypt: function (password, ciphertext, params, rp) {
params = params || {};
rp = rp || {};
-
- var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp, adata=p.adata;
+
+ var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),ciphertext), params, true), ct, tmp, prp, adata=p.adata;
if (typeof p.salt === "string") {
p.salt = sjcl.codec.base64.toBits(p.salt);
}
if (typeof p.iv === "string") {
p.iv = sjcl.codec.base64.toBits(p.iv);
}
-
+
if (!sjcl.mode[p.mode] ||
!sjcl.cipher[p.cipher] ||
(typeof password === "string" && p.iter <= 100) ||
@@ -96,27 +117,51 @@
(p.iv.length < 2 || p.iv.length > 4)) {
throw new sjcl.exception.invalid("json decrypt: invalid parameters");
}
-
+
if (typeof password === "string") {
tmp = sjcl.misc.cachedPbkdf2(password, p);
password = tmp.key.slice(0,p.ks/32);
p.salt = tmp.salt;
+ } else if (sjcl.ecc && password instanceof sjcl.ecc.elGamal.secretKey) {
+ password = password.unkem(sjcl.codec.base64.toBits(p.kemtag)).slice(0,p.ks/32);
}
if (typeof adata === "string") {
adata = sjcl.codec.utf8String.toBits(adata);
}
prp = new sjcl.cipher[p.cipher](password);
-
+
/* do the decryption */
- ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts);
-
+ if (p.mode === "ccm" && sjcl.arrayBuffer && sjcl.arrayBuffer.ccm && p.ct instanceof ArrayBuffer) {
+ ct = sjcl.arrayBuffer.ccm.decrypt(prp, p.ct, p.iv, p.tag, adata, p.ts);
+ } else {
+ ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts);
+ }
+
/* return the json data */
j._add(rp, p);
rp.key = password;
-
- return sjcl.codec.utf8String.fromBits(ct);
+
+ if (params.raw === 1) {
+ return ct;
+ } else {
+ return sjcl.codec.utf8String.fromBits(ct);
+ }
},
-
+
+ /** Simple decryption function.
+ * @param {String|bitArray} password The password or key.
+ * @param {String} ciphertext The ciphertext to decrypt.
+ * @param {Object} [params] Additional non-default parameters.
+ * @param {Object} [rp] A returned object with filled parameters.
+ * @return {String} The plaintext.
+ * @throws {sjcl.exception.invalid} if a parameter is invalid.
+ * @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
+ */
+ decrypt: function (password, ciphertext, params, rp) {
+ var j = sjcl.json;
+ return j._decrypt(password, j.decode(ciphertext), params, rp);
+ },
+
/** Encode a flat structure into a JSON string.
* @param {Object} obj The structure to encode.
* @return {String} A JSON string.
@@ -130,31 +175,31 @@
if (!i.match(/^[a-z0-9]+$/i)) {
throw new sjcl.exception.invalid("json encode: invalid property name");
}
- out += comma + '"' + i + '"' + ':';
+ out += comma + '"' + i + '":';
comma = ',';
-
+
switch (typeof obj[i]) {
- case 'number':
- case 'boolean':
- out += obj[i];
- break;
-
- case 'string':
- out += '"' + escape(obj[i]) + '"';
- break;
-
- case 'object':
- out += '"' + sjcl.codec.base64.fromBits(obj[i],1) + '"';
- break;
-
- default:
- throw new sjcl.exception.bug("json encode: unsupported type");
+ case 'number':
+ case 'boolean':
+ out += obj[i];
+ break;
+
+ case 'string':
+ out += '"' + escape(obj[i]) + '"';
+ break;
+
+ case 'object':
+ out += '"' + sjcl.codec.base64.fromBits(obj[i],0) + '"';
+ break;
+
+ default:
+ throw new sjcl.exception.bug("json encode: unsupported type");
}
}
}
return out+'}';
},
-
+
/** Decode a simple (flat) JSON string into a structure. The ciphertext,
* adata, salt and iv will be base64-decoded.
* @param {String} str The string.
@@ -163,23 +208,25 @@
*/
decode: function (str) {
str = str.replace(/\s/g,'');
- if (!str.match(/^\{.*\}$/)) {
+ if (!str.match(/^\{.*\}$/)) {
throw new sjcl.exception.invalid("json decode: this isn't json!");
}
var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m;
for (i=0; i<a.length; i++) {
- if (!(m=a[i].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))) {
+ if (!(m=a[i].match(/^\s*(?:(["']?)([a-z][a-z0-9]*)\1)\s*:\s*(?:(-?\d+)|"([a-z0-9+\/%*_.@=\-]*)"|(true|false))$/i))) {
throw new sjcl.exception.invalid("json decode: this isn't json!");
}
- if (m[3]) {
+ if (m[3] != null) {
out[m[2]] = parseInt(m[3],10);
- } else {
- out[m[2]] = m[2].match(/^(ct|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
+ } else if (m[4] != null) {
+ out[m[2]] = m[2].match(/^(ct|adata|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
+ } else if (m[5] != null) {
+ out[m[2]] = m[5] === 'true';
}
}
return out;
},
-
+
/** Insert all elements of src into target, modifying and returning target.
* @param {Object} target The object to be modified.
* @param {Object} src The object to pull data from.
@@ -201,22 +248,22 @@
}
return target;
},
-
+
/** Remove all elements of minus from plus. Does not modify plus.
* @private
*/
_subtract: function (plus, minus) {
var out = {}, i;
-
+
for (i in plus) {
if (plus.hasOwnProperty(i) && plus[i] !== minus[i]) {
out[i] = plus[i];
}
}
-
+
return out;
},
-
+
/** Return only the specified elements of src.
* @private
*/
@@ -256,24 +303,22 @@ sjcl.misc._pbkdf2Cache = {};
/** Cached PBKDF2 key derivation.
* @param {String} password The password.
- * @param {Object} [params] The derivation params (iteration count and optional salt).
+ * @param {Object} [obj] The derivation params (iteration count and optional salt).
* @return {Object} The derived data in key, the salt in salt.
*/
sjcl.misc.cachedPbkdf2 = function (password, obj) {
var cache = sjcl.misc._pbkdf2Cache, c, cp, str, salt, iter;
-
+
obj = obj || {};
iter = obj.iter || 1000;
-
+
/* open the cache for this password and iteration count */
cp = cache[password] = cache[password] || {};
c = cp[iter] = cp[iter] || { firstSalt: (obj.salt && obj.salt.length) ?
obj.salt.slice(0) : sjcl.random.randomWords(2,0) };
-
+
salt = (obj.salt === undefined) ? c.firstSalt : obj.salt;
-
+
c[salt] = c[salt] || sjcl.misc.pbkdf2(password, salt, obj.iter);
return { key: c[salt].slice(0), salt:salt.slice(0) };
};
-
-
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));
+ }
+};
diff --git a/core/gcm.js b/core/gcm.js
new file mode 100644
index 0000000..61bf59d
--- /dev/null
+++ b/core/gcm.js
@@ -0,0 +1,183 @@
+/** @fileOverview GCM mode implementation.
+ *
+ * @author Juho Vähä-Herttua
+ */
+
+/** @namespace Galois/Counter mode. */
+sjcl.mode.gcm = {
+ /** The name of the mode.
+ * @constant
+ */
+ name: "gcm",
+
+ /** Encrypt in GCM mode.
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {bitArray} plaintext The plaintext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=128] The desired tag length, in bits.
+ * @return {bitArray} The encrypted data, an array of bytes.
+ */
+ encrypt: function (prf, plaintext, iv, adata, tlen) {
+ var out, data = plaintext.slice(0), w=sjcl.bitArray;
+ tlen = tlen || 128;
+ adata = adata || [];
+
+ // encrypt and tag
+ out = sjcl.mode.gcm._ctrMode(true, prf, data, adata, iv, tlen);
+
+ return w.concat(out.data, out.tag);
+ },
+
+ /** Decrypt in GCM mode.
+ * @static
+ * @param {Object} prf The pseudorandom function. It must have a block size of 16 bytes.
+ * @param {bitArray} ciphertext The ciphertext data.
+ * @param {bitArray} iv The initialization value.
+ * @param {bitArray} [adata=[]] The authenticated data.
+ * @param {Number} [tlen=128] The desired tag length, in bits.
+ * @return {bitArray} The decrypted data.
+ */
+ decrypt: function (prf, ciphertext, iv, adata, tlen) {
+ var out, data = ciphertext.slice(0), tag, w=sjcl.bitArray, l=w.bitLength(data);
+ tlen = tlen || 128;
+ adata = adata || [];
+
+ // Slice tag out of data
+ if (tlen <= l) {
+ tag = w.bitSlice(data, l-tlen);
+ data = w.bitSlice(data, 0, l-tlen);
+ } else {
+ tag = data;
+ data = [];
+ }
+
+ // decrypt and tag
+ out = sjcl.mode.gcm._ctrMode(false, prf, data, adata, iv, tlen);
+
+ if (!w.equal(out.tag, tag)) {
+ throw new sjcl.exception.corrupt("gcm: tag doesn't match");
+ }
+ return out.data;
+ },
+
+ /* Compute the galois multiplication of X and Y
+ * @private
+ */
+ _galoisMultiply: function (x, y) {
+ var i, j, xi, Zi, Vi, lsb_Vi, w=sjcl.bitArray, xor=w._xor4;
+
+ Zi = [0,0,0,0];
+ Vi = y.slice(0);
+
+ // Block size is 128 bits, run 128 times to get Z_128
+ for (i=0; i<128; i++) {
+ xi = (x[Math.floor(i/32)] & (1 << (31-i%32))) !== 0;
+ if (xi) {
+ // Z_i+1 = Z_i ^ V_i
+ Zi = xor(Zi, Vi);
+ }
+
+ // Store the value of LSB(V_i)
+ lsb_Vi = (Vi[3] & 1) !== 0;
+
+ // V_i+1 = V_i >> 1
+ for (j=3; j>0; j--) {
+ Vi[j] = (Vi[j] >>> 1) | ((Vi[j-1]&1) << 31);
+ }
+ Vi[0] = Vi[0] >>> 1;
+
+ // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R
+ if (lsb_Vi) {
+ Vi[0] = Vi[0] ^ (0xe1 << 24);
+ }
+ }
+ return Zi;
+ },
+
+ _ghash: function(H, Y0, data) {
+ var Yi, i, l = data.length;
+
+ Yi = Y0.slice(0);
+ for (i=0; i<l; i+=4) {
+ Yi[0] ^= 0xffffffff&data[i];
+ Yi[1] ^= 0xffffffff&data[i+1];
+ Yi[2] ^= 0xffffffff&data[i+2];
+ Yi[3] ^= 0xffffffff&data[i+3];
+ Yi = sjcl.mode.gcm._galoisMultiply(Yi, H);
+ }
+ return Yi;
+ },
+
+ /** GCM CTR mode.
+ * Encrypt or decrypt data and tag with the prf in GCM-style CTR mode.
+ * @param {Boolean} encrypt True if encrypt, false if decrypt.
+ * @param {Object} prf The PRF.
+ * @param {bitArray} data The data to be encrypted or decrypted.
+ * @param {bitArray} iv The initialization vector.
+ * @param {bitArray} adata The associated data to be tagged.
+ * @param {Number} tlen The length of the tag, in bits.
+ */
+ _ctrMode: function(encrypt, prf, data, adata, iv, tlen) {
+ var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray;
+
+ // Calculate data lengths
+ l = data.length;
+ bl = w.bitLength(data);
+ abl = w.bitLength(adata);
+ ivbl = w.bitLength(iv);
+
+ // Calculate the parameters
+ H = prf.encrypt([0,0,0,0]);
+ if (ivbl === 96) {
+ J0 = iv.slice(0);
+ J0 = w.concat(J0, [1]);
+ } else {
+ J0 = sjcl.mode.gcm._ghash(H, [0,0,0,0], iv);
+ J0 = sjcl.mode.gcm._ghash(H, J0, [0,0,Math.floor(ivbl/0x100000000),ivbl&0xffffffff]);
+ }
+ S0 = sjcl.mode.gcm._ghash(H, [0,0,0,0], adata);
+
+ // Initialize ctr and tag
+ ctr = J0.slice(0);
+ tag = S0.slice(0);
+
+ // If decrypting, calculate hash
+ if (!encrypt) {
+ tag = sjcl.mode.gcm._ghash(H, S0, data);
+ }
+
+ // Encrypt all the data
+ for (i=0; i<l; i+=4) {
+ ctr[3]++;
+ enc = prf.encrypt(ctr);
+ data[i] ^= enc[0];
+ data[i+1] ^= enc[1];
+ data[i+2] ^= enc[2];
+ data[i+3] ^= enc[3];
+ }
+ data = w.clamp(data, bl);
+
+ // If encrypting, calculate hash
+ if (encrypt) {
+ tag = sjcl.mode.gcm._ghash(H, S0, data);
+ }
+
+ // Calculate last block from bit lengths, ugly because bitwise operations are 32-bit
+ last = [
+ Math.floor(abl/0x100000000), abl&0xffffffff,
+ Math.floor(bl/0x100000000), bl&0xffffffff
+ ];
+
+ // Calculate the final tag block
+ tag = sjcl.mode.gcm._ghash(H, tag, last);
+ enc = prf.encrypt(J0);
+ tag[0] ^= enc[0];
+ tag[1] ^= enc[1];
+ tag[2] ^= enc[2];
+ tag[3] ^= enc[3];
+
+ return { tag:w.bitSlice(tag, 0, tlen), data:data };
+ }
+};
diff --git a/core/hmac.js b/core/hmac.js
index 87237b2..84f9a15 100644
--- a/core/hmac.js
+++ b/core/hmac.js
@@ -27,14 +27,35 @@ sjcl.misc.hmac = function (key, Hash) {
this._baseHash[0].update(exKey[0]);
this._baseHash[1].update(exKey[1]);
+ this._resultHash = new Hash(this._baseHash[0]);
};
/** HMAC with the specified hash function. Also called encrypt since it's a prf.
* @param {bitArray|String} data The data to mac.
- * @param {Codec} [encoding] the encoding function to use.
*/
-sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data, encoding) {
- var w = new (this._hash)(this._baseHash[0]).update(data, encoding).finalize();
- return new (this._hash)(this._baseHash[1]).update(w).finalize();
+sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data) {
+ if (!this._updated) {
+ this.update(data);
+ return this.digest(data);
+ } else {
+ throw new sjcl.exception.invalid("encrypt on already updated hmac called!");
+ }
+};
+
+sjcl.misc.hmac.prototype.reset = function () {
+ this._resultHash = new this._hash(this._baseHash[0]);
+ this._updated = false;
};
+sjcl.misc.hmac.prototype.update = function (data) {
+ this._updated = true;
+ this._resultHash.update(data);
+};
+
+sjcl.misc.hmac.prototype.digest = function () {
+ var w = this._resultHash.finalize(), result = new (this._hash)(this._baseHash[1]).update(w).finalize();
+
+ this.reset();
+
+ return result;
+}; \ No newline at end of file
diff --git a/core/ocb2progressive.js b/core/ocb2progressive.js
new file mode 100644
index 0000000..0cb1790
--- /dev/null
+++ b/core/ocb2progressive.js
@@ -0,0 +1,137 @@
+/**
+ * OCB2.0 implementation slightly modified by Yifan Gu
+ * to support progressive encryption
+ * @author Yifan Gu
+ */
+
+/** @fileOverview OCB 2.0 implementation
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+/** @namespace
+ * Phil Rogaway's Offset CodeBook mode, version 2.0.
+ * May be covered by US and international patents.
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ */
+
+sjcl.mode.ocb2progressive = {
+ createEncryptor: function(prp, iv, adata, tlen, premac) {
+ if (sjcl.bitArray.bitLength(iv) !== 128) {
+ throw new sjcl.exception.invalid("ocb iv must be 128 bits");
+ }
+ var i,
+ times2 = sjcl.mode.ocb2._times2,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ checksum = [0,0,0,0],
+ delta = times2(prp.encrypt(iv)),
+ bi, bl,
+ datacache = [],
+ pad;
+
+ adata = adata || [];
+ tlen = tlen || 64;
+
+ return {
+ process: function(data){
+ var datalen = sjcl.bitArray.bitLength(data);
+ if (datalen == 0){ // empty input natrually gives empty output
+ return [];
+ }
+ var output = [];
+ datacache = datacache.concat(data);
+ for (i=0; i+4 < datacache.length; i+=4) {
+ /* Encrypt a non-final block */
+ bi = datacache.slice(i,i+4);
+ checksum = xor(checksum, bi);
+ output = output.concat(xor(delta,prp.encrypt(xor(delta, bi))));
+ delta = times2(delta);
+ }
+ datacache = datacache.slice(i); // at end of each process we ensure size of datacache is smaller than 4
+ return output; //spits out the result.
+ },
+ finalize: function(){
+ // the final block
+ bi = datacache;
+ bl = w.bitLength(bi);
+ pad = prp.encrypt(xor(delta,[0,0,0,bl]));
+ bi = w.clamp(xor(bi.concat([0,0,0]),pad), bl);
+
+ /* Checksum the final block, and finalize the checksum */
+ checksum = xor(checksum,xor(bi.concat([0,0,0]),pad));
+ checksum = prp.encrypt(xor(checksum,xor(delta,times2(delta))));
+
+ /* MAC the header */
+ if (adata.length) {
+ checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
+ }
+
+ return w.concat(bi, w.clamp(checksum, tlen)); // spits out the last block
+ }
+ };
+ },
+ createDecryptor: function(prp, iv, adata, tlen, premac){
+ if (sjcl.bitArray.bitLength(iv) !== 128) {
+ throw new sjcl.exception.invalid("ocb iv must be 128 bits");
+ }
+ tlen = tlen || 64;
+ var i,
+ times2 = sjcl.mode.ocb2._times2,
+ w = sjcl.bitArray,
+ xor = w._xor4,
+ checksum = [0,0,0,0],
+ delta = times2(prp.encrypt(iv)),
+ bi, bl,
+ datacache = [],
+ pad;
+
+ adata = adata || [];
+
+ return {
+ process: function(data){
+ if (data.length == 0){ // empty input natrually gives empty output
+ return [];
+ }
+ var output = [];
+ datacache = datacache.concat(data);
+ var cachelen = sjcl.bitArray.bitLength(datacache);
+ for (i=0; i+4 < (cachelen-tlen)/32; i+=4) {
+ /* Decrypt a non-final block */
+ bi = xor(delta, prp.decrypt(xor(delta, datacache.slice(i,i+4))));
+ checksum = xor(checksum, bi);
+ output = output.concat(bi);
+ delta = times2(delta);
+ }
+ datacache = datacache.slice(i);
+ return output;
+ },
+ finalize: function(){
+ /* Chop out and decrypt the final block */
+ bl = sjcl.bitArray.bitLength(datacache) - tlen;
+ pad = prp.encrypt(xor(delta,[0,0,0,bl]));
+ bi = xor(pad, w.clamp(datacache,bl).concat([0,0,0]));
+
+ /* Checksum the final block, and finalize the checksum */
+ checksum = xor(checksum, bi);
+ checksum = prp.encrypt(xor(checksum, xor(delta, times2(delta))));
+
+ /* MAC the header */
+ if (adata.length) {
+ checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
+ }
+
+ if (!w.equal(w.clamp(checksum, tlen), w.bitSlice(datacache, bl))) {
+ throw new sjcl.exception.corrupt("ocb: tag doesn't match");
+ }
+
+ return w.clamp(bi,bl);
+ }
+ };
+ }
+}
diff --git a/core/pbkdf2.js b/core/pbkdf2.js
index 869e4df..3e9aa3f 100644
--- a/core/pbkdf2.js
+++ b/core/pbkdf2.js
@@ -12,7 +12,7 @@
* This is the method specified by RSA's PKCS #5 standard.
*
* @param {bitArray|String} password The password.
- * @param {bitArray} salt The salt. Should have lots of entropy.
+ * @param {bitArray|String} salt The salt. Should have lots of entropy.
* @param {Number} [count=1000] The number of iterations. Higher numbers make the function slower but more secure.
* @param {Number} [length] The length of the derived key. Defaults to the
output size of the hash function.
@@ -30,6 +30,10 @@ sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
password = sjcl.codec.utf8String.toBits(password);
}
+ if (typeof salt === "string") {
+ salt = sjcl.codec.utf8String.toBits(salt);
+ }
+
Prff = Prff || sjcl.misc.hmac;
var prf = new Prff(password),
diff --git a/core/random.js b/core/random.js
index 6def0d3..fed0ebf 100644
--- a/core/random.js
+++ b/core/random.js
@@ -3,11 +3,13 @@
* @author Emily Stark
* @author Mike Hamburg
* @author Dan Boneh
+ * @author Michael Brooks
*/
-/** @namespace Random number generator
- *
+/** @constructor
+ * @class Random number generator
* @description
+ * <b>Use sjcl.random as a singleton for this class!</b>
* <p>
* This random number generator is a derivative of Ferguson and Schneier's
* generator Fortuna. It collects entropy from various events into several
@@ -39,8 +41,45 @@
* look for improvements in future versions.
* </p>
*/
-sjcl.random = {
- /** Generate several random words, and return them in an array
+sjcl.prng = function(defaultParanoia) {
+
+ /* private */
+ this._pools = [new sjcl.hash.sha256()];
+ this._poolEntropy = [0];
+ this._reseedCount = 0;
+ this._robins = {};
+ this._eventId = 0;
+
+ this._collectorIds = {};
+ this._collectorIdNext = 0;
+
+ this._strength = 0;
+ this._poolStrength = 0;
+ this._nextReseed = 0;
+ this._key = [0,0,0,0,0,0,0,0];
+ this._counter = [0,0,0,0];
+ this._cipher = undefined;
+ this._defaultParanoia = defaultParanoia;
+
+ /* event listener stuff */
+ this._collectorsStarted = false;
+ this._callbacks = {progress: {}, seeded: {}};
+ this._callbackI = 0;
+
+ /* constants */
+ this._NOT_READY = 0;
+ this._READY = 1;
+ this._REQUIRES_RESEED = 2;
+
+ this._MAX_WORDS_PER_BURST = 65536;
+ this._PARANOIA_LEVELS = [0,48,64,96,128,192,256,384,512,768,1024];
+ this._MILLISECONDS_PER_RESEED = 30000;
+ this._BITS_PER_RESEED = 80;
+};
+
+sjcl.prng.prototype = {
+ /** Generate several random words, and return them in an array.
+ * A word consists of 32 bits (4 bytes)
* @param {Number} nwords The number of words to generate.
*/
randomWords: function (nwords, paranoia) {
@@ -65,7 +104,11 @@ sjcl.random = {
return out.slice(0,nwords);
},
- setDefaultParanoia: function (paranoia) {
+ setDefaultParanoia: function (paranoia, allowZeroParanoia) {
+ if (paranoia === 0 && allowZeroParanoia !== "Setting paranoia=0 will ruin your security; use it only for testing") {
+ throw "Setting paranoia=0 will ruin your security; use it only for testing";
+ }
+
this._defaultParanoia = paranoia;
},
@@ -82,7 +125,7 @@ sjcl.random = {
i, tmp,
t = (new Date()).valueOf(),
robin = this._robins[source],
- oldReady = this.isReady(), err = 0;
+ oldReady = this.isReady(), err = 0, objName;
id = this._collectorIds[source];
if (id === undefined) { id = this._collectorIds[source] = this._collectorIdNext ++; }
@@ -100,7 +143,7 @@ sjcl.random = {
break;
case "object":
- var objName = Object.prototype.toString.call(data);
+ objName = Object.prototype.toString.call(data);
if (objName === "[object Uint32Array]") {
tmp = [];
for (i = 0; i < data.length; i++) {
@@ -112,7 +155,7 @@ sjcl.random = {
err = 1;
}
for (i=0; i<data.length && !err; i++) {
- if (typeof(data[i]) != "number") {
+ if (typeof(data[i]) !== "number") {
err = 1;
}
}
@@ -197,14 +240,25 @@ sjcl.random = {
startCollectors: function () {
if (this._collectorsStarted) { return; }
+ this._eventListener = {
+ loadTimeCollector: this._bind(this._loadTimeCollector),
+ mouseCollector: this._bind(this._mouseCollector),
+ keyboardCollector: this._bind(this._keyboardCollector),
+ accelerometerCollector: this._bind(this._accelerometerCollector),
+ touchCollector: this._bind(this._touchCollector)
+ };
+
if (window.addEventListener) {
- window.addEventListener("load", this._loadTimeCollector, false);
- window.addEventListener("mousemove", this._mouseCollector, false);
+ window.addEventListener("load", this._eventListener.loadTimeCollector, false);
+ window.addEventListener("mousemove", this._eventListener.mouseCollector, false);
+ window.addEventListener("keypress", this._eventListener.keyboardCollector, false);
+ window.addEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
+ window.addEventListener("touchmove", this._eventListener.touchCollector, false);
} else if (document.attachEvent) {
- document.attachEvent("onload", this._loadTimeCollector);
- document.attachEvent("onmousemove", this._mouseCollector);
- }
- else {
+ document.attachEvent("onload", this._eventListener.loadTimeCollector);
+ document.attachEvent("onmousemove", this._eventListener.mouseCollector);
+ document.attachEvent("keypress", this._eventListener.keyboardCollector);
+ } else {
throw new sjcl.exception.bug("can't attach event");
}
@@ -216,12 +270,17 @@ sjcl.random = {
if (!this._collectorsStarted) { return; }
if (window.removeEventListener) {
- window.removeEventListener("load", this._loadTimeCollector, false);
- window.removeEventListener("mousemove", this._mouseCollector, false);
- } else if (window.detachEvent) {
- window.detachEvent("onload", this._loadTimeCollector);
- window.detachEvent("onmousemove", this._mouseCollector);
+ window.removeEventListener("load", this._eventListener.loadTimeCollector, false);
+ window.removeEventListener("mousemove", this._eventListener.mouseCollector, false);
+ window.removeEventListener("keypress", this._eventListener.keyboardCollector, false);
+ window.removeEventListener("devicemotion", this._eventListener.accelerometerCollector, false);
+ window.removeEventListener("touchmove", this._eventListener.touchCollector, false);
+ } else if (document.detachEvent) {
+ document.detachEvent("onload", this._eventListener.loadTimeCollector);
+ document.detachEvent("onmousemove", this._eventListener.mouseCollector);
+ document.detachEvent("keypress", this._eventListener.keyboardCollector);
}
+
this._collectorsStarted = false;
},
@@ -238,56 +297,30 @@ sjcl.random = {
/** remove an event listener for progress or seeded-ness */
removeEventListener: function (name, cb) {
var i, j, cbs=this._callbacks[name], jsTemp=[];
-
+
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
-
+
for (j in cbs) {
- if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
+ if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
jsTemp.push(j);
}
}
-
+
for (i=0; i<jsTemp.length; i++) {
j = jsTemp[i];
delete cbs[j];
}
},
- /* private */
- _pools : [new sjcl.hash.sha256()],
- _poolEntropy : [0],
- _reseedCount : 0,
- _robins : {},
- _eventId : 0,
-
- _collectorIds : {},
- _collectorIdNext : 0,
-
- _strength : 0,
- _poolStrength : 0,
- _nextReseed : 0,
- _key : [0,0,0,0,0,0,0,0],
- _counter : [0,0,0,0],
- _cipher : undefined,
- _defaultParanoia : 6,
-
- /* event listener stuff */
- _collectorsStarted : false,
- _callbacks : {progress: {}, seeded: {}},
- _callbackI : 0,
-
- /* constants */
- _NOT_READY : 0,
- _READY : 1,
- _REQUIRES_RESEED : 2,
+ _bind: function (func) {
+ var that = this;
+ return function () {
+ func.apply(that, arguments);
+ };
+ },
- _MAX_WORDS_PER_BURST : 65536,
- _PARANOIA_LEVELS : [0,48,64,96,128,192,256,384,512,768,1024],
- _MILLISECONDS_PER_RESEED : 30000,
- _BITS_PER_RESEED : 80,
-
/** Generate 4 random words, no reseed, no gate.
* @private
*/
@@ -359,42 +392,131 @@ sjcl.random = {
this._reseed(reseedData);
},
- _mouseCollector: function (ev) {
- var x = ev.x || ev.clientX || ev.offsetX, y = ev.y || ev.clientY || ev.offsetY;
- sjcl.random.addEntropy([x,y], 2, "mouse");
+ _keyboardCollector: function () {
+ this._addCurrentTimeToEntropy(1);
},
- _loadTimeCollector: function (ev) {
- sjcl.random.addEntropy((new Date()).valueOf(), 2, "loadtime");
+ _mouseCollector: function (ev) {
+ var x, y;
+
+ try {
+ x = ev.x || ev.clientX || ev.offsetX || 0;
+ y = ev.y || ev.clientY || ev.offsetY || 0;
+ } catch (err) {
+ // Event originated from a secure element. No mouse position available.
+ x = 0;
+ y = 0;
+ }
+
+ if (x != 0 && y!= 0) {
+ sjcl.random.addEntropy([x,y], 2, "mouse");
+ }
+
+ this._addCurrentTimeToEntropy(0);
+ },
+
+ _touchCollector: function(ev) {
+ var touch = ev.touches[0] || ev.changedTouches[0];
+ var x = touch.pageX || touch.clientX,
+ y = touch.pageY || touch.clientY;
+
+ sjcl.random.addEntropy([x,y],1,"touch");
+
+ this._addCurrentTimeToEntropy(0);
},
+ _loadTimeCollector: function () {
+ this._addCurrentTimeToEntropy(2);
+ },
+
+ _addCurrentTimeToEntropy: function (estimatedEntropy) {
+ if (typeof window !== 'undefined' && window.performance && typeof window.performance.now === "function") {
+ //how much entropy do we want to add here?
+ sjcl.random.addEntropy(window.performance.now(), estimatedEntropy, "loadtime");
+ } else {
+ sjcl.random.addEntropy((new Date()).valueOf(), estimatedEntropy, "loadtime");
+ }
+ },
+ _accelerometerCollector: function (ev) {
+ var ac = ev.accelerationIncludingGravity.x||ev.accelerationIncludingGravity.y||ev.accelerationIncludingGravity.z;
+ if(window.orientation){
+ var or = window.orientation;
+ if (typeof or === "number") {
+ sjcl.random.addEntropy(or, 1, "accelerometer");
+ }
+ }
+ if (ac) {
+ sjcl.random.addEntropy(ac, 2, "accelerometer");
+ }
+ this._addCurrentTimeToEntropy(0);
+ },
+
_fireEvent: function (name, arg) {
var j, cbs=sjcl.random._callbacks[name], cbsTemp=[];
- /* TODO: there is a race condition between removing collectors and firing them */
+ /* TODO: there is a race condition between removing collectors and firing them */
/* I'm not sure if this is necessary; in C++, iterating over a
* collection and modifying it at the same time is a no-no.
*/
-
+
for (j in cbs) {
- if (cbs.hasOwnProperty(j)) {
+ if (cbs.hasOwnProperty(j)) {
cbsTemp.push(cbs[j]);
- }
+ }
}
-
+
for (j=0; j<cbsTemp.length; j++) {
- cbsTemp[j](arg);
+ cbsTemp[j](arg);
}
}
};
+/** an instance for the prng.
+* @see sjcl.prng
+*/
+sjcl.random = new sjcl.prng(6);
+
(function(){
+ // function for getting nodejs crypto module. catches and ignores errors.
+ function getCryptoModule() {
+ try {
+ return require('crypto');
+ }
+ catch (e) {
+ return null;
+ }
+ }
+
try {
- // get cryptographically strong entropy in Webkit
- var ab = new Uint32Array(32);
- crypto.getRandomValues(ab);
- sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
+ var buf, crypt, ab;
+
+ // get cryptographically strong entropy depending on runtime environment
+ if (typeof module !== 'undefined' && module.exports && (crypt = getCryptoModule()) && crypt.randomBytes) {
+ buf = crypt.randomBytes(1024/8);
+ buf = new Uint32Array(new Uint8Array(buf).buffer);
+ sjcl.random.addEntropy(buf, 1024, "crypto.randomBytes");
+
+ } else if (typeof window !== 'undefined' && typeof Uint32Array !== 'undefined') {
+ ab = new Uint32Array(32);
+ if (window.crypto && window.crypto.getRandomValues) {
+ window.crypto.getRandomValues(ab);
+ } else if (window.msCrypto && window.msCrypto.getRandomValues) {
+ window.msCrypto.getRandomValues(ab);
+ } else {
+ return;
+ }
+
+ // get cryptographically strong entropy in Webkit
+ sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
+
+ } else {
+ // no getRandomValues :-(
+ }
} catch (e) {
- // no getRandomValues :-(
+ if (typeof window !== 'undefined' && window.console) {
+ console.log("There was an error collecting entropy from the browser:");
+ console.log(e);
+ //we do not want the library to fail due to randomness not being maintained.
+ }
}
-})();
+}());
diff --git a/core/ripemd160.js b/core/ripemd160.js
new file mode 100644
index 0000000..4ee7b92
--- /dev/null
+++ b/core/ripemd160.js
@@ -0,0 +1,207 @@
+/** @fileOverview Javascript RIPEMD-160 implementation.
+ *
+ * @author Artem S Vybornov <vybornov@gmail.com>
+ */
+(function() {
+
+/**
+ * Context for a RIPEMD-160 operation in progress.
+ * @constructor
+ * @class RIPEMD, 160 bits.
+ */
+sjcl.hash.ripemd160 = function (hash) {
+ if (hash) {
+ this._h = hash._h.slice(0);
+ this._buffer = hash._buffer.slice(0);
+ this._length = hash._length;
+ } else {
+ this.reset();
+ }
+};
+
+/**
+ * Hash a string or an array of words.
+ * @static
+ * @param {bitArray|String} data the data to hash.
+ * @return {bitArray} The hash value, an array of 5 big-endian words.
+ */
+sjcl.hash.ripemd160.hash = function (data) {
+ return (new sjcl.hash.ripemd160()).update(data).finalize();
+};
+
+sjcl.hash.ripemd160.prototype = {
+ /**
+ * Reset the hash state.
+ * @return this
+ */
+ reset: function () {
+ this._h = _h0.slice(0);
+ this._buffer = [];
+ this._length = 0;
+ return this;
+ },
+
+ /**
+ * Reset the hash state.
+ * @param {bitArray|String} data the data to hash.
+ * @return this
+ */
+ update: function (data) {
+ if ( typeof data === "string" )
+ data = sjcl.codec.utf8String.toBits(data);
+
+ var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data),
+ ol = this._length,
+ nl = this._length = ol + sjcl.bitArray.bitLength(data);
+ for (i = 512+ol & -512; i <= nl; i+= 512) {
+ var words = b.splice(0,16);
+ for ( var w = 0; w < 16; ++w )
+ words[w] = _cvt(words[w]);
+
+ _block.call( this, words );
+ }
+
+ return this;
+ },
+
+ /**
+ * Complete hashing and output the hash value.
+ * @return {bitArray} The hash value, an array of 5 big-endian words.
+ */
+ finalize: function () {
+ var b = sjcl.bitArray.concat( this._buffer, [ sjcl.bitArray.partial(1,1) ] ),
+ l = ( this._length + 1 ) % 512,
+ z = ( l > 448 ? 512 : 448 ) - l % 448,
+ zp = z % 32;
+
+ if ( zp > 0 )
+ b = sjcl.bitArray.concat( b, [ sjcl.bitArray.partial(zp,0) ] )
+ for ( ; z >= 32; z -= 32 )
+ b.push(0);
+
+ b.push( _cvt( this._length | 0 ) );
+ b.push( _cvt( Math.floor(this._length / 0x100000000) ) );
+
+ while ( b.length ) {
+ var words = b.splice(0,16);
+ for ( var w = 0; w < 16; ++w )
+ words[w] = _cvt(words[w]);
+
+ _block.call( this, words );
+ }
+
+ var h = this._h;
+ this.reset();
+
+ for ( var w = 0; w < 5; ++w )
+ h[w] = _cvt(h[w]);
+
+ return h;
+ }
+};
+
+var _h0 = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ];
+
+var _k1 = [ 0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e ];
+var _k2 = [ 0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000 ];
+for ( var i = 4; i >= 0; --i ) {
+ for ( var j = 1; j < 16; ++j ) {
+ _k1.splice(i,0,_k1[i]);
+ _k2.splice(i,0,_k2[i]);
+ }
+}
+
+var _r1 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
+ 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
+ 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
+ 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 ];
+var _r2 = [ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
+ 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
+ 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
+ 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
+ 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 ];
+
+var _s1 = [ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
+ 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
+ 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
+ 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
+ 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 ];
+var _s2 = [ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
+ 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
+ 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
+ 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
+ 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 ];
+
+function _f0(x,y,z) {
+ return x ^ y ^ z;
+};
+
+function _f1(x,y,z) {
+ return (x & y) | (~x & z);
+};
+
+function _f2(x,y,z) {
+ return (x | ~y) ^ z;
+};
+
+function _f3(x,y,z) {
+ return (x & z) | (y & ~z);
+};
+
+function _f4(x,y,z) {
+ return x ^ (y | ~z);
+};
+
+function _rol(n,l) {
+ return (n << l) | (n >>> (32-l));
+}
+
+function _cvt(n) {
+ return ( (n & 0xff << 0) << 24 )
+ | ( (n & 0xff << 8) << 8 )
+ | ( (n & 0xff << 16) >>> 8 )
+ | ( (n & 0xff << 24) >>> 24 );
+}
+
+function _block(X) {
+ var A1 = this._h[0], B1 = this._h[1], C1 = this._h[2], D1 = this._h[3], E1 = this._h[4],
+ A2 = this._h[0], B2 = this._h[1], C2 = this._h[2], D2 = this._h[3], E2 = this._h[4];
+
+ var j = 0, T;
+
+ for ( ; j < 16; ++j ) {
+ T = _rol( A1 + _f0(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
+ A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
+ T = _rol( A2 + _f4(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
+ A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
+ for ( ; j < 32; ++j ) {
+ T = _rol( A1 + _f1(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
+ A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
+ T = _rol( A2 + _f3(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
+ A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
+ for ( ; j < 48; ++j ) {
+ T = _rol( A1 + _f2(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
+ A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
+ T = _rol( A2 + _f2(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
+ A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
+ for ( ; j < 64; ++j ) {
+ T = _rol( A1 + _f3(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
+ A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
+ T = _rol( A2 + _f1(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
+ A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
+ for ( ; j < 80; ++j ) {
+ T = _rol( A1 + _f4(B1,C1,D1) + X[_r1[j]] + _k1[j], _s1[j] ) + E1;
+ A1 = E1; E1 = D1; D1 = _rol(C1,10); C1 = B1; B1 = T;
+ T = _rol( A2 + _f0(B2,C2,D2) + X[_r2[j]] + _k2[j], _s2[j] ) + E2;
+ A2 = E2; E2 = D2; D2 = _rol(C2,10); C2 = B2; B2 = T; }
+
+ T = this._h[1] + C1 + D2;
+ this._h[1] = this._h[2] + D1 + E2;
+ this._h[2] = this._h[3] + E1 + A2;
+ this._h[3] = this._h[4] + A1 + B2;
+ this._h[4] = this._h[0] + B1 + C2;
+ this._h[0] = T;
+}
+
+})();
diff --git a/core/scrypt.js b/core/scrypt.js
new file mode 100644
index 0000000..78dbc9d
--- /dev/null
+++ b/core/scrypt.js
@@ -0,0 +1,146 @@
+/** scrypt Password-Based Key-Derivation Function.
+ *
+ * @param {bitArray|String} password The password.
+ * @param {bitArray|String} salt The salt. Should have lots of entropy.
+ *
+ * @param {Number} [N=16384] CPU/Memory cost parameter.
+ * @param {Number} [r=8] Block size parameter.
+ * @param {Number} [p=1] Parallelization parameter.
+ *
+ * @param {Number} [length] The length of the derived key. Defaults to the
+ * output size of the hash function.
+ * @param {Object} [Prff=sjcl.misc.hmac] The pseudorandom function family.
+ *
+ * @return {bitArray} The derived key.
+ */
+sjcl.misc.scrypt = function (password, salt, N, r, p, length, Prff) {
+ var SIZE_MAX = Math.pow(2, 32) - 1,
+ self = sjcl.misc.scrypt;
+
+ N = N || 16384;
+ r = r || 8;
+ p = p || 1;
+
+ if (r * p >= Math.pow(2, 30)) {
+ throw sjcl.exception.invalid("The parameters r, p must satisfy r * p < 2^30");
+ }
+
+ if ((N < 2) || (N & (N - 1) != 0)) {
+ throw sjcl.exception.invalid("The parameter N must be a power of 2.");
+ }
+
+ if (N > SIZE_MAX / 128 / r) {
+ throw sjcl.exception.invalid("N too big.");
+ }
+
+ if (r > SIZE_MAX / 128 / p) {
+ throw sjcl.exception.invalid("r too big.");
+ }
+
+ var blocks = sjcl.misc.pbkdf2(password, salt, 1, p * 128 * r * 8, Prff),
+ len = blocks.length / p;
+
+ self.reverse(blocks);
+
+ for (var i = 0; i < p; i++) {
+ var block = blocks.slice(i * len, (i + 1) * len);
+ self.blockcopy(self.ROMix(block, N), 0, blocks, i * len);
+ }
+
+ self.reverse(blocks);
+
+ return sjcl.misc.pbkdf2(password, blocks, 1, length, Prff);
+}
+
+sjcl.misc.scrypt.salsa20Core = function (word, rounds) {
+ var R = function(a, b) { return (a << b) | (a >>> (32 - b)); }
+ var x = word.slice(0);
+
+ for (var i = rounds; i > 0; i -= 2) {
+ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9);
+ x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18);
+ x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9);
+ x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18);
+ x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9);
+ x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18);
+ x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9);
+ x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18);
+ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9);
+ x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18);
+ x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9);
+ x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18);
+ x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9);
+ x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18);
+ x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9);
+ x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18);
+ }
+
+ for (i = 0; i < 16; i++) word[i] = x[i]+word[i];
+}
+
+sjcl.misc.scrypt.blockMix = function(blocks) {
+ var X = blocks.slice(-16),
+ out = [],
+ len = blocks.length / 16,
+ self = sjcl.misc.scrypt;
+
+ for (var i = 0; i < len; i++) {
+ self.blockxor(blocks, 16 * i, X, 0, 16);
+ self.salsa20Core(X, 8);
+
+ if ((i & 1) == 0) {
+ self.blockcopy(X, 0, out, 8 * i);
+ } else {
+ self.blockcopy(X, 0, out, 8 * (i^1 + len));
+ }
+ }
+
+ return out;
+}
+
+sjcl.misc.scrypt.ROMix = function(block, N) {
+ var X = block.slice(0),
+ V = [],
+ self = sjcl.misc.scrypt;
+
+ for (var i = 0; i < N; i++) {
+ V.push(X.slice(0));
+ X = self.blockMix(X);
+ }
+
+ for (i = 0; i < N; i++) {
+ var j = X[X.length - 16] & (N - 1);
+
+ self.blockxor(V[j], 0, X, 0);
+ X = self.blockMix(X);
+ }
+
+ return X;
+}
+
+sjcl.misc.scrypt.reverse = function (words) { // Converts Big <-> Little Endian words
+ for (var i in words) {
+ var out = words[i] & 0xFF;
+ out = (out << 8) | (words[i] >>> 8) & 0xFF;
+ out = (out << 8) | (words[i] >>> 16) & 0xFF;
+ out = (out << 8) | (words[i] >>> 24) & 0xFF;
+
+ words[i] = out;
+ }
+}
+
+sjcl.misc.scrypt.blockcopy = function (S, Si, D, Di, len) {
+ var i;
+
+ len = len || (S.length - Si);
+
+ for (i = 0; i < len; i++) D[Di + i] = S[Si + i] | 0;
+}
+
+sjcl.misc.scrypt.blockxor = function(S, Si, D, Di, len) {
+ var i;
+
+ len = len || (S.length - Si);
+
+ for (i = 0; i < len; i++) D[Di + i] = (D[Di + i] ^ S[Si + i]) | 0;
+}
diff --git a/core/sha1.js b/core/sha1.js
index 73d05b1..708e10b 100644
--- a/core/sha1.js
+++ b/core/sha1.js
@@ -138,8 +138,7 @@ sjcl.hash.sha1.prototype = {
_block:function (words) {
var t, tmp, a, b, c, d, e,
w = words.slice(0),
- h = this._h,
- k = this._key;
+ h = this._h;
a = h[0]; b = h[1]; c = h[2]; d = h[3]; e = h[4];
diff --git a/core/sha256.js b/core/sha256.js
index fd51609..6ff5294 100644
--- a/core/sha256.js
+++ b/core/sha256.js
@@ -77,7 +77,7 @@ sjcl.hash.sha256.prototype = {
/**
* Complete hashing and output the hash value.
- * @return {bitArray} The hash value, an array of 16 big-endian words.
+ * @return {bitArray} The hash value, an array of 8 big-endian words.
*/
finalize:function () {
var i, b = this._buffer, h = this._h;
diff --git a/core/sha512.js b/core/sha512.js
new file mode 100644
index 0000000..6e628c7
--- /dev/null
+++ b/core/sha512.js
@@ -0,0 +1,348 @@
+/** @fileOverview Javascript SHA-512 implementation.
+ *
+ * This implementation was written for CryptoJS by Jeff Mott and adapted for
+ * SJCL by Stefan Thomas.
+ *
+ * CryptoJS (c) 2009–2012 by Jeff Mott. All rights reserved.
+ * Released with New BSD License
+ *
+ * @author Emily Stark
+ * @author Mike Hamburg
+ * @author Dan Boneh
+ * @author Jeff Mott
+ * @author Stefan Thomas
+ */
+
+/**
+ * Context for a SHA-512 operation in progress.
+ * @constructor
+ * @class Secure Hash Algorithm, 512 bits.
+ */
+sjcl.hash.sha512 = function (hash) {
+ if (!this._key[0]) { this._precompute(); }
+ if (hash) {
+ this._h = hash._h.slice(0);
+ this._buffer = hash._buffer.slice(0);
+ this._length = hash._length;
+ } else {
+ this.reset();
+ }
+};
+
+/**
+ * Hash a string or an array of words.
+ * @static
+ * @param {bitArray|String} data the data to hash.
+ * @return {bitArray} The hash value, an array of 16 big-endian words.
+ */
+sjcl.hash.sha512.hash = function (data) {
+ return (new sjcl.hash.sha512()).update(data).finalize();
+};
+
+sjcl.hash.sha512.prototype = {
+ /**
+ * The hash's block size, in bits.
+ * @constant
+ */
+ blockSize: 1024,
+
+ /**
+ * Reset the hash state.
+ * @return this
+ */
+ reset:function () {
+ this._h = this._init.slice(0);
+ this._buffer = [];
+ this._length = 0;
+ return this;
+ },
+
+ /**
+ * Input several words to the hash.
+ * @param {bitArray|String} data the data to hash.
+ * @return this
+ */
+ update: function (data) {
+ if (typeof data === "string") {
+ data = sjcl.codec.utf8String.toBits(data);
+ }
+ var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data),
+ ol = this._length,
+ nl = this._length = ol + sjcl.bitArray.bitLength(data);
+ for (i = 1024+ol & -1024; i <= nl; i+= 1024) {
+ this._block(b.splice(0,32));
+ }
+ return this;
+ },
+
+ /**
+ * Complete hashing and output the hash value.
+ * @return {bitArray} The hash value, an array of 16 big-endian words.
+ */
+ finalize:function () {
+ var i, b = this._buffer, h = this._h;
+
+ // Round out and push the buffer
+ b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1,1)]);
+
+ // Round out the buffer to a multiple of 32 words, less the 4 length words.
+ for (i = b.length + 4; i & 31; i++) {
+ b.push(0);
+ }
+
+ // append the length
+ b.push(0);
+ b.push(0);
+ b.push(Math.floor(this._length / 0x100000000));
+ b.push(this._length | 0);
+
+ while (b.length) {
+ this._block(b.splice(0,32));
+ }
+
+ this.reset();
+ return h;
+ },
+
+ /**
+ * The SHA-512 initialization vector, to be precomputed.
+ * @private
+ */
+ _init:[],
+
+ /**
+ * Least significant 24 bits of SHA512 initialization values.
+ *
+ * Javascript only has 53 bits of precision, so we compute the 40 most
+ * significant bits and add the remaining 24 bits as constants.
+ *
+ * @private
+ */
+ _initr: [ 0xbcc908, 0xcaa73b, 0x94f82b, 0x1d36f1, 0xe682d1, 0x3e6c1f, 0x41bd6b, 0x7e2179 ],
+
+ /*
+ _init:
+ [0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,
+ 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179],
+ */
+
+ /**
+ * The SHA-512 hash key, to be precomputed.
+ * @private
+ */
+ _key:[],
+
+ /**
+ * Least significant 24 bits of SHA512 key values.
+ * @private
+ */
+ _keyr:
+ [0x28ae22, 0xef65cd, 0x4d3b2f, 0x89dbbc, 0x48b538, 0x05d019, 0x194f9b, 0x6d8118,
+ 0x030242, 0x706fbe, 0xe4b28c, 0xffb4e2, 0x7b896f, 0x1696b1, 0xc71235, 0x692694,
+ 0xf14ad2, 0x4f25e3, 0x8cd5b5, 0xac9c65, 0x2b0275, 0xa6e483, 0x41fbd4, 0x1153b5,
+ 0x66dfab, 0xb43210, 0xfb213f, 0xef0ee4, 0xa88fc2, 0x0aa725, 0x03826f, 0x0e6e70,
+ 0xd22ffc, 0x26c926, 0xc42aed, 0x95b3df, 0xaf63de, 0x77b2a8, 0xedaee6, 0x82353b,
+ 0xf10364, 0x423001, 0xf89791, 0x54be30, 0xef5218, 0x65a910, 0x71202a, 0xbbd1b8,
+ 0xd2d0c8, 0x41ab53, 0x8eeb99, 0x9b48a8, 0xc95a63, 0x418acb, 0x63e373, 0xb2b8a3,
+ 0xefb2fc, 0x172f60, 0xf0ab72, 0x6439ec, 0x631e28, 0x82bde9, 0xc67915, 0x72532b,
+ 0x26619c, 0xc0c207, 0xe0eb1e, 0x6ed178, 0x176fba, 0xc898a6, 0xf90dae, 0x1c471b,
+ 0x047d84, 0xc72493, 0xc9bebc, 0x100d4c, 0x3e42b6, 0x657e2a, 0xd6faec, 0x475817],
+
+ /*
+ _key:
+ [0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,
+ 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,
+ 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,
+ 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,
+ 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,
+ 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,
+ 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,
+ 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,
+ 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,
+ 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,
+ 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,
+ 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,
+ 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,
+ 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,
+ 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,
+ 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,
+ 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,
+ 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,
+ 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,
+ 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817],
+ */
+
+ /**
+ * Function to precompute _init and _key.
+ * @private
+ */
+ _precompute: function () {
+ // XXX: This code is for precomputing the SHA256 constants, change for
+ // SHA512 and re-enable.
+ var i = 0, prime = 2, factor;
+
+ function frac(x) { return (x-Math.floor(x)) * 0x100000000 | 0; }
+ function frac2(x) { return (x-Math.floor(x)) * 0x10000000000 & 0xff; }
+
+ outer: for (; i<80; prime++) {
+ for (factor=2; factor*factor <= prime; factor++) {
+ if (prime % factor === 0) {
+ // not a prime
+ continue outer;
+ }
+ }
+
+ if (i<8) {
+ this._init[i*2] = frac(Math.pow(prime, 1/2));
+ this._init[i*2+1] = (frac2(Math.pow(prime, 1/2)) << 24) | this._initr[i];
+ }
+ this._key[i*2] = frac(Math.pow(prime, 1/3));
+ this._key[i*2+1] = (frac2(Math.pow(prime, 1/3)) << 24) | this._keyr[i];
+ i++;
+ }
+ },
+
+ /**
+ * Perform one cycle of SHA-512.
+ * @param {bitArray} words one block of words.
+ * @private
+ */
+ _block:function (words) {
+ var i, wrh, wrl,
+ w = words.slice(0),
+ h = this._h,
+ k = this._key,
+ h0h = h[ 0], h0l = h[ 1], h1h = h[ 2], h1l = h[ 3],
+ h2h = h[ 4], h2l = h[ 5], h3h = h[ 6], h3l = h[ 7],
+ h4h = h[ 8], h4l = h[ 9], h5h = h[10], h5l = h[11],
+ h6h = h[12], h6l = h[13], h7h = h[14], h7l = h[15];
+
+ // Working variables
+ var ah = h0h, al = h0l, bh = h1h, bl = h1l,
+ ch = h2h, cl = h2l, dh = h3h, dl = h3l,
+ eh = h4h, el = h4l, fh = h5h, fl = h5l,
+ gh = h6h, gl = h6l, hh = h7h, hl = h7l;
+
+ for (i=0; i<80; i++) {
+ // load up the input word for this round
+ if (i<16) {
+ wrh = w[i * 2];
+ wrl = w[i * 2 + 1];
+ } else {
+ // Gamma0
+ var gamma0xh = w[(i-15) * 2];
+ var gamma0xl = w[(i-15) * 2 + 1];
+ var gamma0h =
+ ((gamma0xl << 31) | (gamma0xh >>> 1)) ^
+ ((gamma0xl << 24) | (gamma0xh >>> 8)) ^
+ (gamma0xh >>> 7);
+ var gamma0l =
+ ((gamma0xh << 31) | (gamma0xl >>> 1)) ^
+ ((gamma0xh << 24) | (gamma0xl >>> 8)) ^
+ ((gamma0xh << 25) | (gamma0xl >>> 7));
+
+ // Gamma1
+ var gamma1xh = w[(i-2) * 2];
+ var gamma1xl = w[(i-2) * 2 + 1];
+ var gamma1h =
+ ((gamma1xl << 13) | (gamma1xh >>> 19)) ^
+ ((gamma1xh << 3) | (gamma1xl >>> 29)) ^
+ (gamma1xh >>> 6);
+ var gamma1l =
+ ((gamma1xh << 13) | (gamma1xl >>> 19)) ^
+ ((gamma1xl << 3) | (gamma1xh >>> 29)) ^
+ ((gamma1xh << 26) | (gamma1xl >>> 6));
+
+ // Shortcuts
+ var wr7h = w[(i-7) * 2];
+ var wr7l = w[(i-7) * 2 + 1];
+
+ var wr16h = w[(i-16) * 2];
+ var wr16l = w[(i-16) * 2 + 1];
+
+ // W(round) = gamma0 + W(round - 7) + gamma1 + W(round - 16)
+ wrl = gamma0l + wr7l;
+ wrh = gamma0h + wr7h + ((wrl >>> 0) < (gamma0l >>> 0) ? 1 : 0);
+ wrl += gamma1l;
+ wrh += gamma1h + ((wrl >>> 0) < (gamma1l >>> 0) ? 1 : 0);
+ wrl += wr16l;
+ wrh += wr16h + ((wrl >>> 0) < (wr16l >>> 0) ? 1 : 0);
+ }
+
+ w[i*2] = wrh |= 0;
+ w[i*2 + 1] = wrl |= 0;
+
+ // Ch
+ var chh = (eh & fh) ^ (~eh & gh);
+ var chl = (el & fl) ^ (~el & gl);
+
+ // Maj
+ var majh = (ah & bh) ^ (ah & ch) ^ (bh & ch);
+ var majl = (al & bl) ^ (al & cl) ^ (bl & cl);
+
+ // Sigma0
+ var sigma0h = ((al << 4) | (ah >>> 28)) ^ ((ah << 30) | (al >>> 2)) ^ ((ah << 25) | (al >>> 7));
+ var sigma0l = ((ah << 4) | (al >>> 28)) ^ ((al << 30) | (ah >>> 2)) ^ ((al << 25) | (ah >>> 7));
+
+ // Sigma1
+ var sigma1h = ((el << 18) | (eh >>> 14)) ^ ((el << 14) | (eh >>> 18)) ^ ((eh << 23) | (el >>> 9));
+ var sigma1l = ((eh << 18) | (el >>> 14)) ^ ((eh << 14) | (el >>> 18)) ^ ((el << 23) | (eh >>> 9));
+
+ // K(round)
+ var krh = k[i*2];
+ var krl = k[i*2+1];
+
+ // t1 = h + sigma1 + ch + K(round) + W(round)
+ var t1l = hl + sigma1l;
+ var t1h = hh + sigma1h + ((t1l >>> 0) < (hl >>> 0) ? 1 : 0);
+ t1l += chl;
+ t1h += chh + ((t1l >>> 0) < (chl >>> 0) ? 1 : 0);
+ t1l += krl;
+ t1h += krh + ((t1l >>> 0) < (krl >>> 0) ? 1 : 0);
+ t1l = t1l + wrl|0; // FF32..FF34 perf issue https://bugzilla.mozilla.org/show_bug.cgi?id=1054972
+ t1h += wrh + ((t1l >>> 0) < (wrl >>> 0) ? 1 : 0);
+
+ // t2 = sigma0 + maj
+ var t2l = sigma0l + majl;
+ var t2h = sigma0h + majh + ((t2l >>> 0) < (sigma0l >>> 0) ? 1 : 0);
+
+ // Update working variables
+ hh = gh;
+ hl = gl;
+ gh = fh;
+ gl = fl;
+ fh = eh;
+ fl = el;
+ el = (dl + t1l) | 0;
+ eh = (dh + t1h + ((el >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;
+ dh = ch;
+ dl = cl;
+ ch = bh;
+ cl = bl;
+ bh = ah;
+ bl = al;
+ al = (t1l + t2l) | 0;
+ ah = (t1h + t2h + ((al >>> 0) < (t1l >>> 0) ? 1 : 0)) | 0;
+ }
+
+ // Intermediate hash
+ h0l = h[1] = (h0l + al) | 0;
+ h[0] = (h0h + ah + ((h0l >>> 0) < (al >>> 0) ? 1 : 0)) | 0;
+ h1l = h[3] = (h1l + bl) | 0;
+ h[2] = (h1h + bh + ((h1l >>> 0) < (bl >>> 0) ? 1 : 0)) | 0;
+ h2l = h[5] = (h2l + cl) | 0;
+ h[4] = (h2h + ch + ((h2l >>> 0) < (cl >>> 0) ? 1 : 0)) | 0;
+ h3l = h[7] = (h3l + dl) | 0;
+ h[6] = (h3h + dh + ((h3l >>> 0) < (dl >>> 0) ? 1 : 0)) | 0;
+ h4l = h[9] = (h4l + el) | 0;
+ h[8] = (h4h + eh + ((h4l >>> 0) < (el >>> 0) ? 1 : 0)) | 0;
+ h5l = h[11] = (h5l + fl) | 0;
+ h[10] = (h5h + fh + ((h5l >>> 0) < (fl >>> 0) ? 1 : 0)) | 0;
+ h6l = h[13] = (h6l + gl) | 0;
+ h[12] = (h6h + gh + ((h6l >>> 0) < (gl >>> 0) ? 1 : 0)) | 0;
+ h7l = h[15] = (h7l + hl) | 0;
+ h[14] = (h7h + hh + ((h7l >>> 0) < (hl >>> 0) ? 1 : 0)) | 0;
+ }
+};
+
+
diff --git a/core/sjcl.js b/core/sjcl.js
index 50e5307..8c9425d 100644
--- a/core/sjcl.js
+++ b/core/sjcl.js
@@ -10,7 +10,7 @@
"use strict";
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
-/*global document, window, escape, unescape */
+/*global document, window, escape, unescape, module, require, Uint32Array */
/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */
var sjcl = {
@@ -42,25 +42,25 @@ var sjcl = {
/** @namespace Exceptions. */
exception: {
- /** @class Ciphertext is corrupt. */
+ /** @constructor Ciphertext is corrupt. */
corrupt: function(message) {
this.toString = function() { return "CORRUPT: "+this.message; };
this.message = message;
},
- /** @class Invalid parameter. */
+ /** @constructor Invalid parameter. */
invalid: function(message) {
this.toString = function() { return "INVALID: "+this.message; };
this.message = message;
},
- /** @class Bug or missing feature in SJCL. */
+ /** @constructor Bug or missing feature in SJCL. @constructor */
bug: function(message) {
this.toString = function() { return "BUG: "+this.message; };
this.message = message;
},
- /** @class Something isn't ready. */
+ /** @constructor Something isn't ready. */
notReady: function(message) {
this.toString = function() { return "NOT READY: "+this.message; };
this.message = message;
@@ -68,6 +68,11 @@ var sjcl = {
}
};
-if(typeof module != 'undefined' && module.exports){
+if(typeof module !== 'undefined' && module.exports){
module.exports = sjcl;
}
+if (typeof define === "function") {
+ define([], function () {
+ return sjcl;
+ });
+}
diff --git a/core/srp.js b/core/srp.js
index d1dba28..1f92d57 100644
--- a/core/srp.js
+++ b/core/srp.js
@@ -16,7 +16,7 @@
*/
sjcl.keyexchange.srp = {
/**
- * Calculates SRP v, the verifier.
+ * Calculates SRP v, the verifier.
* v = g^x mod N [RFC 5054]
* @param {String} I The username.
* @param {String} P The password.
@@ -27,7 +27,7 @@ sjcl.keyexchange.srp = {
*/
makeVerifier: function(I, P, s, group) {
var x;
- x = this.makeX(I, P, s);
+ x = sjcl.keyexchange.srp.makeX(I, P, s);
x = sjcl.bn.fromBits(x);
return group.g.powermod(x, group.N);
},
@@ -52,8 +52,8 @@ sjcl.keyexchange.srp = {
*/
knownGroup:function(i) {
if (typeof i !== "string") { i = i.toString(); }
- if (!this._didInitKnownGroups) { this._initKnownGroups(); }
- return this._knownGroups[i];
+ if (!sjcl.keyexchange.srp._didInitKnownGroups) { sjcl.keyexchange.srp._initKnownGroups(); }
+ return sjcl.keyexchange.srp._knownGroups[i];
},
/**
@@ -63,16 +63,16 @@ sjcl.keyexchange.srp = {
_didInitKnownGroups: false,
_initKnownGroups:function() {
var i, size, group;
- for (i=0; i < this._knownGroupSizes.length; i++) {
- size = this._knownGroupSizes[i].toString();
- group = this._knownGroups[size];
+ for (i=0; i < sjcl.keyexchange.srp._knownGroupSizes.length; i++) {
+ size = sjcl.keyexchange.srp._knownGroupSizes[i].toString();
+ group = sjcl.keyexchange.srp._knownGroups[size];
group.N = new sjcl.bn(group.N);
group.g = new sjcl.bn(group.g);
}
- this._didInitKnownGroups = true;
+ sjcl.keyexchange.srp._didInitKnownGroups = true;
},
- _knownGroupSizes: [1024, 1536, 2048],
+ _knownGroupSizes: [1024, 1536, 2048, 3072, 4096, 6144, 8192],
_knownGroups: {
1024: {
N: "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C" +
@@ -106,8 +106,121 @@ sjcl.keyexchange.srp = {
"94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F" +
"9E4AFF73",
g: 2
+ },
+
+ 3072: {
+ N: "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
+ "E0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",
+ g: 5
+ },
+
+ 4096: {
+ N: "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" +
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" +
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" +
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" +
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" +
+ "FFFFFFFFFFFFFFFF",
+ g: 5
+ },
+
+ 6144: {
+ N: "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" +
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" +
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" +
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" +
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" +
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" +
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" +
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" +
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" +
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" +
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" +
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" +
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" +
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" +
+ "6DCC4024FFFFFFFFFFFFFFFF",
+ g: 5
+ },
+
+ 8192: {
+ N:"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" +
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" +
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" +
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" +
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" +
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" +
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" +
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" +
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" +
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" +
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" +
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" +
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" +
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" +
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" +
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" +
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" +
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" +
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" +
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" +
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" +
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" +
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" +
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" +
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" +
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" +
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" +
+ "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA" +
+ "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C" +
+ "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" +
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886" +
+ "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6" +
+ "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5" +
+ "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268" +
+ "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6" +
+ "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" +
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ g: 19
}
}
};
-