mirror of
https://github.com/moparisthebest/mail
synced 2024-11-25 10:22:18 -05:00
merged master/ecc into nacl
This commit is contained in:
commit
e1c8866afe
@ -24,6 +24,7 @@ app.crypto.Util = function(window, uuid) {
|
||||
window.crypto.getRandomValues(keyBuf);
|
||||
keyBase64 = window.btoa(this.uint8Arr2BinStr(keyBuf));
|
||||
} else {
|
||||
// add an additional peace of entropy to the pot and stir with the sjcl prng
|
||||
sjcl.random.addEntropy((new Date()).valueOf(), 2, "calltime");
|
||||
keyBuf = sjcl.random.randomWords(keySize / 32, 0);
|
||||
keyBase64 = sjcl.codec.base64.fromBits(keyBuf);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/**
|
||||
* @constructor
|
||||
* Constructs a new bignum from another bignum, a number or a hex string.
|
||||
*/
|
||||
sjcl.bn = function(it) {
|
||||
@ -156,12 +157,19 @@ sjcl.bn.prototype = {
|
||||
},
|
||||
|
||||
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)) {
|
||||
@ -340,11 +348,12 @@ sjcl.bn.prototype = {
|
||||
/** 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;
|
||||
},
|
||||
@ -354,13 +363,14 @@ sjcl.bn.prototype = {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/** @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;
|
||||
@ -384,6 +394,7 @@ 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 */
|
||||
function p(it) {
|
||||
this.initWith(it);
|
||||
/*if (this.limbs[this.modOffset]) {
|
||||
@ -415,7 +426,9 @@ 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.
|
||||
* @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;
|
||||
|
||||
@ -439,6 +452,7 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/** @this {sjcl.bn} */
|
||||
ppr._strongReduce = (ppr.fullMask === -1) ? ppr.reduce : function() {
|
||||
var limbs = this.limbs, i = limbs.length - 1, k, l;
|
||||
this.reduce();
|
||||
@ -452,7 +466,9 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
|
||||
}
|
||||
};
|
||||
|
||||
/** mostly constant-time, very expensive full reduction. */
|
||||
/** mostly constant-time, very expensive full reduction.
|
||||
* @this {sjcl.bn}
|
||||
*/
|
||||
ppr.fullReduce = function() {
|
||||
var greater, i;
|
||||
// massively above the modulus, may be negative
|
||||
@ -484,6 +500,8 @@ sjcl.bn.pseudoMersennePrime = function(exponent, coeff) {
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/** @this {sjcl.bn} */
|
||||
ppr.inverse = function() {
|
||||
return (this.power(this.modulus.sub(2)));
|
||||
};
|
||||
|
@ -46,6 +46,10 @@
|
||||
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);
|
||||
@ -101,6 +105,8 @@
|
||||
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);
|
||||
@ -130,7 +136,7 @@
|
||||
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]) {
|
||||
|
@ -296,21 +296,38 @@ sjcl.ecc.curves = {
|
||||
/* Diffie-Hellman-like public-key system */
|
||||
sjcl.ecc._dh = function(cn) {
|
||||
sjcl.ecc[cn] = {
|
||||
/** @constructor */
|
||||
publicKey: function(curve, point) {
|
||||
this._curve = curve;
|
||||
this._curveBitLength = curve.r.bitLength();
|
||||
if (point instanceof Array) {
|
||||
this._point = curve.fromBits(point);
|
||||
} else {
|
||||
this._point = point;
|
||||
}
|
||||
|
||||
this.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 };
|
||||
}
|
||||
},
|
||||
|
||||
/** @constructor */
|
||||
secretKey: function(curve, exponent) {
|
||||
this._curve = curve;
|
||||
this._curveBitLength = curve.r.bitLength();
|
||||
this._exponent = exponent;
|
||||
|
||||
this.get = function() {
|
||||
return this._exponent.toBits();
|
||||
}
|
||||
},
|
||||
|
||||
generateKeys: function(curve, paranoia) {
|
||||
/** @constructor */
|
||||
generateKeys: function(curve, paranoia, sec) {
|
||||
if (curve === undefined) {
|
||||
curve = 256;
|
||||
}
|
||||
@ -320,7 +337,10 @@ sjcl.ecc._dh = function(cn) {
|
||||
throw new sjcl.exception.invalid("no such curve");
|
||||
}
|
||||
}
|
||||
var sec = sjcl.bn.random(curve.r, paranoia), pub = curve.G.mult(sec);
|
||||
if (sec === undefined) {
|
||||
var 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) };
|
||||
}
|
||||
@ -351,29 +371,41 @@ sjcl.ecc.elGamal.secretKey.prototype = {
|
||||
sjcl.ecc._dh("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);
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
sjcl.ecc.ecdsa.publicKey.prototype = {
|
||||
verify: function(hash, rs) {
|
||||
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;
|
||||
}
|
||||
|
@ -5,7 +5,8 @@
|
||||
* @author Dan Boneh
|
||||
*/
|
||||
|
||||
/** @namespace Random number generator
|
||||
/** @constructor
|
||||
* @class Random number generator
|
||||
*
|
||||
* @description
|
||||
* <p>
|
||||
@ -39,7 +40,43 @@
|
||||
* look for improvements in future versions.
|
||||
* </p>
|
||||
*/
|
||||
sjcl.random = {
|
||||
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
|
||||
* @param {Number} nwords The number of words to generate.
|
||||
*/
|
||||
@ -255,39 +292,6 @@ sjcl.random = {
|
||||
}
|
||||
},
|
||||
|
||||
/* 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,
|
||||
|
||||
_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
|
||||
*/
|
||||
@ -388,6 +392,8 @@ sjcl.random = {
|
||||
}
|
||||
};
|
||||
|
||||
sjcl.random = new sjcl.prng(6);
|
||||
|
||||
(function(){
|
||||
try {
|
||||
// get cryptographically strong entropy in Webkit
|
||||
|
@ -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;
|
||||
|
@ -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,13 +63,13 @@ 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],
|
||||
|
@ -1,21 +0,0 @@
|
||||
module("ECC Crypto");
|
||||
|
||||
var keys,
|
||||
ciphertext,
|
||||
plaintext = 'Hello, World!';
|
||||
|
||||
test("Generate Keys", function() {
|
||||
// generate keypair
|
||||
keys = sjcl.ecc.elGamal.generateKeys(384, 1);
|
||||
ok(keys);
|
||||
});
|
||||
|
||||
test("Encrypt", function() {
|
||||
ciphertext = sjcl.encrypt(keys.pub, plaintext);
|
||||
ok(ciphertext);
|
||||
});
|
||||
|
||||
test("Decrypt", function() {
|
||||
var decrypted = sjcl.decrypt(keys.sec, ciphertext);
|
||||
equal(plaintext, decrypted);
|
||||
});
|
34
src/test/unit/ecc-test.js
Normal file
34
src/test/unit/ecc-test.js
Normal file
@ -0,0 +1,34 @@
|
||||
module("ECC Crypto");
|
||||
|
||||
var ecc_test = {
|
||||
keySize: 384,
|
||||
plaintext: 'Hello, World!'
|
||||
};
|
||||
|
||||
test("Generate Keys", function() {
|
||||
// generate keypair
|
||||
ecc_test.keys = sjcl.ecc.elGamal.generateKeys(ecc_test.keySize, 0);
|
||||
ok(ecc_test.keys);
|
||||
});
|
||||
|
||||
test("Encrypt", function() {
|
||||
// var tmp = ecc_test.keys.pub.kem(0);
|
||||
|
||||
// var password = tmp.key.slice(0, ecc_test.keySize / 32);
|
||||
// var prp = new sjcl.cipher.ecc(password);
|
||||
|
||||
|
||||
// var iv = aes_test.util.random(ecc_test.keySize);
|
||||
// var ivWords = sjcl.codec.base64.toBits(iv);
|
||||
|
||||
// sjcl.mode.ecc.encrypt(prp, ecc_test.plaintext, ivWords);
|
||||
|
||||
sjcl.random.setDefaultParanoia(0);
|
||||
ecc_test.ciphertext = sjcl.encrypt(ecc_test.keys.pub, ecc_test.plaintext);
|
||||
ok(ecc_test.ciphertext);
|
||||
});
|
||||
|
||||
test("Decrypt", function() {
|
||||
var decrypted = sjcl.decrypt(ecc_test.keys.sec, ecc_test.ciphertext);
|
||||
equal(ecc_test.plaintext, decrypted);
|
||||
});
|
@ -40,6 +40,9 @@
|
||||
<script src="../../lib/sjcl/codecString.js"></script>
|
||||
<script src="../../lib/sjcl/aes.js"></script>
|
||||
<script src="../../lib/sjcl/ccm.js"></script>
|
||||
<script src="../../lib/sjcl/bn.js"></script>
|
||||
<script src="../../lib/sjcl/ecc.js"></script>
|
||||
<script src="../../lib/sjcl/convenience.js"></script>
|
||||
|
||||
<script src="../../lib/nacl.js"></script>
|
||||
|
||||
@ -75,6 +78,7 @@
|
||||
<script src="util-test.js"></script>
|
||||
<script src="aes-test.js"></script>
|
||||
<script src="nacl-crypto-test.js"></script>
|
||||
<script src="ecc-test.js"></script>
|
||||
<script src="crypto-test.js"></script>
|
||||
<script src="localstorage-dao-test.js"></script>
|
||||
<script src="lawnchair-dao-test.js"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user