docs: function (password, ciphertext, params, rp) {
      params = params || {};
      rp = rp || {};
  
      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) ||
          (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;
      } 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);
  
      /* return the json data */
      j._add(rp, p);
      rp.key = password;
  
      if (params.raw === 1) {
        return ct;
      } else {
        return sjcl.codec.utf8String.fromBits(ct);
      }
    }