summaryrefslogtreecommitdiffstats
path: root/core/convenience.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/convenience.js')
-rw-r--r--core/convenience.js163
1 files changed, 104 insertions, 59 deletions
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) };
};
-
-