| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- /** @fileOverview Convenince functions centered around JSON encapsulation.
- *
- * @author Emily Stark
- * @author Mike Hamburg
- * @author Dan Boneh
- */
-
- /** @namespace JSON encapsulation */
- sjcl.json = {
- /** Default values for encryption */
- defaults: { v:1, iter:1000, ks:128, ts:64, mode:"ccm", adata:"", cipher:"aes" },
- /** 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.
- * @throws {sjcl.exception.invalid} if a parameter is invalid.
- */
- 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;
- j._add(p, params);
- 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) ||
- (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) ||
- (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) ||
- (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;
- }
- if (typeof plaintext === "string") {
- plaintext = sjcl.codec.utf8String.toBits(plaintext);
- }
- 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, p.adata, p.tag);
-
- //return j.encode(j._subtract(p, j.defaults));
- return j.encode(p);
- },
-
- /** 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) {
- 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;
- 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) ||
- (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) ||
- (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) ||
- (!p.iv) ||
- (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;
- }
- prp = new sjcl.cipher[p.cipher](password);
-
- /* do the decryption */
- ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, p.adata, p.tag);
-
- /* return the json data */
- j._add(rp, p);
- rp.key = password;
-
- return sjcl.codec.utf8String.fromBits(ct);
- },
-
- /** Encode a flat structure into a JSON string.
- * @param {Object} obj The structure to encode.
- * @return {String} A JSON string.
- * @throws {sjcl.exception.invalid} if obj has a non-alphanumeric property.
- * @throws {sjcl.exception.bug} if a parameter has an unsupported type.
- */
- encode: function (obj) {
- var i, out='{', comma='';
- for (i in obj) {
- if (obj.hasOwnProperty(i)) {
- if (!i.match(/^[a-z0-9]+$/i)) {
- throw new sjcl.exception.invalid("json encode: invalid property name");
- }
- 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");
- }
- }
- }
- 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.
- * @return {Object} The decoded structure.
- * @throws {sjcl.exception.invalid} if str isn't (simple) JSON.
- */
- decode: function (str) {
- str = str.replace(/\s/g,'');
- 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))) {
- throw new sjcl.exception.invalid("json decode: this isn't json!");
- }
- if (m[3]) {
- 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]);
- }
- }
- 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.
- * @param {boolean} [requireSame=false] If true, throw an exception if any field of target differs from corresponding field of src.
- * @return {Object} target.
- * @private
- */
- _add: function (target, src, requireSame) {
- if (target === undefined) { target = {}; }
- if (src === undefined) { return target; }
- var i;
- for (i in src) {
- if (src.hasOwnProperty(i)) {
- if (requireSame && target[i] !== undefined && target[i] !== src[i]) {
- throw new sjcl.exception.invalid("required parameter overridden");
- }
- target[i] = src[i];
- }
- }
- 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
- */
- _filter: function (src, filter) {
- var out = {}, i;
- for (i=0; i<filter.length; i++) {
- if (src[filter[i]] !== undefined) {
- out[filter[i]] = src[filter[i]];
- }
- }
- return out;
- }
- };
- /** Simple encryption function; convenient shorthand for sjcl.json.encrypt.
- * @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.
- */
- sjcl.encrypt = sjcl.json.encrypt;
- /** Simple decryption function; convenient shorthand for sjcl.json.decrypt.
- * @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.
- */
- sjcl.decrypt = sjcl.json.decrypt;
- /** The cache for cachedPbkdf2.
- * @private
- */
- sjcl.misc._pbkdf2Cache = {};
- /** Cached PBKDF2 key derivation.
- * @param {String} The password.
- * @param {Object} 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) };
- };
|