text: /*
  CryptoJS v3.1.2
  code.google.com/p/crypto-js
  (c) 2009-2013 by Jeff Mott. All rights reserved.
  code.google.com/p/crypto-js/wiki/License
  */
  /**
   * Cipher core components.
   */
  CryptoJS.lib.Cipher || (function (undefined) {
      // Shortcuts
      var C = CryptoJS;
      var C_lib = C.lib;
      var Base = C_lib.Base;
      var WordArray = C_lib.WordArray;
      var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm;
      var C_enc = C.enc;
      var Utf8 = C_enc.Utf8;
      var Base64 = C_enc.Base64;
      var C_algo = C.algo;
      var EvpKDF = C_algo.EvpKDF;
  
      /**
       * Abstract base cipher template.
       *
       * @property {number} keySize This cipher's key size. Default: 4 (128 bits)
       * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits)
       * @property {number} _ENC_XFORM_MODE A constant representing encryption mode.
       * @property {number} _DEC_XFORM_MODE A constant representing decryption mode.
       */
      var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({
          /**
           * Configuration options.
           *
           * @property {WordArray} iv The IV to use for this operation.
           */
          cfg: Base.extend(),
  
          /**
           * Creates this cipher in encryption mode.
           *
           * @param {WordArray} key The key.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @return {Cipher} A cipher instance.
           *
           * @static
           *
           * @example
           *
           *     var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray });
           */
          createEncryptor: function (key, cfg) {
              return this.create(this._ENC_XFORM_MODE, key, cfg);
          },
  
          /**
           * Creates this cipher in decryption mode.
           *
           * @param {WordArray} key The key.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @return {Cipher} A cipher instance.
           *
           * @static
           *
           * @example
           *
           *     var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray });
           */
          createDecryptor: function (key, cfg) {
              return this.create(this._DEC_XFORM_MODE, key, cfg);
          },
  
          /**
           * Initializes a newly created cipher.
           *
           * @param {number} xformMode Either the encryption or decryption transormation mode constant.
           * @param {WordArray} key The key.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @example
           *
           *     var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray });
           */
          init: function (xformMode, key, cfg) {
              // Apply config defaults
              this.cfg = this.cfg.extend(cfg);
  
              // Store transform mode and key
              this._xformMode = xformMode;
              this._key = key;
  
              // Set initial values
              this.reset();
          },
  
          /**
           * Resets this cipher to its initial state.
           *
           * @example
           *
           *     cipher.reset();
           */
          reset: function () {
              // Reset data buffer
              BufferedBlockAlgorithm.reset.call(this);
  
              // Perform concrete-cipher logic
              this._doReset();
          },
  
          /**
           * Adds data to be encrypted or decrypted.
           *
           * @param {WordArray|string} dataUpdate The data to encrypt or decrypt.
           *
           * @return {WordArray} The data after processing.
           *
           * @example
           *
           *     var encrypted = cipher.process('data');
           *     var encrypted = cipher.process(wordArray);
           */
          process: function (dataUpdate) {
              // Append
              this._append(dataUpdate);
  
              // Process available blocks
              return this._process();
          },
  
          /**
           * Finalizes the encryption or decryption process.
           * Note that the finalize operation is effectively a destructive, read-once operation.
           *
           * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt.
           *
           * @return {WordArray} The data after final processing.
           *
           * @example
           *
           *     var encrypted = cipher.finalize();
           *     var encrypted = cipher.finalize('data');
           *     var encrypted = cipher.finalize(wordArray);
           */
          finalize: function (dataUpdate) {
              // Final data update
              if (dataUpdate) {
                  this._append(dataUpdate);
              }
  
              // Perform concrete-cipher logic
              var finalProcessedData = this._doFinalize();
  
              return finalProcessedData;
          },
  
          keySize: 128/32,
  
          ivSize: 128/32,
  
          _ENC_XFORM_MODE: 1,
  
          _DEC_XFORM_MODE: 2,
  
          /**
           * Creates shortcut functions to a cipher's object interface.
           *
           * @param {Cipher} cipher The cipher to create a helper for.
           *
           * @return {Object} An object with encrypt and decrypt shortcut functions.
           *
           * @static
           *
           * @example
           *
           *     var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES);
           */
          _createHelper: (function () {
              function selectCipherStrategy(key) {
                  if (typeof key == 'string') {
                      return PasswordBasedCipher;
                  } else {
                      return SerializableCipher;
                  }
              }
  
              return function (cipher) {
                  return {
                      encrypt: function (message, key, cfg) {
                          return selectCipherStrategy(key).encrypt(cipher, message, key, cfg);
                      },
  
                      decrypt: function (ciphertext, key, cfg) {
                          return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg);
                      }
                  };
              };
          }())
      });
  
      /**
       * Abstract base stream cipher template.
       *
       * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits)
       */
      var StreamCipher = C_lib.StreamCipher = Cipher.extend({
          _doFinalize: function () {
              // Process partial blocks
              var finalProcessedBlocks = this._process(!!'flush');
  
              return finalProcessedBlocks;
          },
  
          blockSize: 1
      });
  
      /**
       * Mode namespace.
       */
      var C_mode = C.mode = {};
  
      /**
       * Abstract base block cipher mode template.
       */
      var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({
          /**
           * Creates this mode for encryption.
           *
           * @param {Cipher} cipher A block cipher instance.
           * @param {Array} iv The IV words.
           *
           * @static
           *
           * @example
           *
           *     var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words);
           */
          createEncryptor: function (cipher, iv) {
              return this.Encryptor.create(cipher, iv);
          },
  
          /**
           * Creates this mode for decryption.
           *
           * @param {Cipher} cipher A block cipher instance.
           * @param {Array} iv The IV words.
           *
           * @static
           *
           * @example
           *
           *     var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words);
           */
          createDecryptor: function (cipher, iv) {
              return this.Decryptor.create(cipher, iv);
          },
  
          /**
           * Initializes a newly created mode.
           *
           * @param {Cipher} cipher A block cipher instance.
           * @param {Array} iv The IV words.
           *
           * @example
           *
           *     var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words);
           */
          init: function (cipher, iv) {
              this._cipher = cipher;
              this._iv = iv;
          }
      });
  
      /**
       * Cipher Block Chaining mode.
       */
      var CBC = C_mode.CBC = (function () {
          /**
           * Abstract base CBC mode.
           */
          var CBC = BlockCipherMode.extend();
  
          /**
           * CBC encryptor.
           */
          CBC.Encryptor = CBC.extend({
              /**
               * Processes the data block at offset.
               *
               * @param {Array} words The data words to operate on.
               * @param {number} offset The offset where the block starts.
               *
               * @example
               *
               *     mode.processBlock(data.words, offset);
               */
              processBlock: function (words, offset) {
                  // Shortcuts
                  var cipher = this._cipher;
                  var blockSize = cipher.blockSize;
  
                  // XOR and encrypt
                  xorBlock.call(this, words, offset, blockSize);
                  cipher.encryptBlock(words, offset);
  
                  // Remember this block to use with next block
                  this._prevBlock = words.slice(offset, offset + blockSize);
              }
          });
  
          /**
           * CBC decryptor.
           */
          CBC.Decryptor = CBC.extend({
              /**
               * Processes the data block at offset.
               *
               * @param {Array} words The data words to operate on.
               * @param {number} offset The offset where the block starts.
               *
               * @example
               *
               *     mode.processBlock(data.words, offset);
               */
              processBlock: function (words, offset) {
                  // Shortcuts
                  var cipher = this._cipher;
                  var blockSize = cipher.blockSize;
  
                  // Remember this block to use with next block
                  var thisBlock = words.slice(offset, offset + blockSize);
  
                  // Decrypt and XOR
                  cipher.decryptBlock(words, offset);
                  xorBlock.call(this, words, offset, blockSize);
  
                  // This block becomes the previous block
                  this._prevBlock = thisBlock;
              }
          });
  
          function xorBlock(words, offset, blockSize) {
              // Shortcut
              var iv = this._iv;
  
              // Choose mixing block
              if (iv) {
                  var block = iv;
  
                  // Remove IV for subsequent blocks
                  this._iv = undefined;
              } else {
                  var block = this._prevBlock;
              }
  
              // XOR blocks
              for (var i = 0; i < blockSize; i++) {
                  words[offset + i] ^= block[i];
              }
          }
  
          return CBC;
      }());
  
      /**
       * Padding namespace.
       */
      var C_pad = C.pad = {};
  
      /**
       * PKCS #5/7 padding strategy.
       */
      var Pkcs7 = C_pad.Pkcs7 = {
          /**
           * Pads data using the algorithm defined in PKCS #5/7.
           *
           * @param {WordArray} data The data to pad.
           * @param {number} blockSize The multiple that the data should be padded to.
           *
           * @static
           *
           * @example
           *
           *     CryptoJS.pad.Pkcs7.pad(wordArray, 4);
           */
          pad: function (data, blockSize) {
              // Shortcut
              var blockSizeBytes = blockSize * 4;
  
              // Count padding bytes
              var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes;
  
              // Create padding word
              var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes;
  
              // Create padding
              var paddingWords = [];
              for (var i = 0; i < nPaddingBytes; i += 4) {
                  paddingWords.push(paddingWord);
              }
              var padding = WordArray.create(paddingWords, nPaddingBytes);
  
              // Add padding
              data.concat(padding);
          },
  
          /**
           * Unpads data that had been padded using the algorithm defined in PKCS #5/7.
           *
           * @param {WordArray} data The data to unpad.
           *
           * @static
           *
           * @example
           *
           *     CryptoJS.pad.Pkcs7.unpad(wordArray);
           */
          unpad: function (data) {
              // Get number of padding bytes from last byte
              var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
  
              // Remove padding
              data.sigBytes -= nPaddingBytes;
          }
      };
  
      /**
       * Abstract base block cipher template.
       *
       * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits)
       */
      var BlockCipher = C_lib.BlockCipher = Cipher.extend({
          /**
           * Configuration options.
           *
           * @property {Mode} mode The block mode to use. Default: CBC
           * @property {Padding} padding The padding strategy to use. Default: Pkcs7
           */
          cfg: Cipher.cfg.extend({
              mode: CBC,
              padding: Pkcs7
          }),
  
          reset: function () {
              // Reset cipher
              Cipher.reset.call(this);
  
              // Shortcuts
              var cfg = this.cfg;
              var iv = cfg.iv;
              var mode = cfg.mode;
  
              // Reset block mode
              if (this._xformMode == this._ENC_XFORM_MODE) {
                  var modeCreator = mode.createEncryptor;
              } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ {
                  var modeCreator = mode.createDecryptor;
  
                  // Keep at least one block in the buffer for unpadding
                  this._minBufferSize = 1;
              }
              this._mode = modeCreator.call(mode, this, iv && iv.words);
          },
  
          _doProcessBlock: function (words, offset) {
              this._mode.processBlock(words, offset);
          },
  
          _doFinalize: function () {
              // Shortcut
              var padding = this.cfg.padding;
  
              // Finalize
              if (this._xformMode == this._ENC_XFORM_MODE) {
                  // Pad data
                  padding.pad(this._data, this.blockSize);
  
                  // Process final blocks
                  var finalProcessedBlocks = this._process(!!'flush');
              } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ {
                  // Process final blocks
                  var finalProcessedBlocks = this._process(!!'flush');
  
                  // Unpad data
                  padding.unpad(finalProcessedBlocks);
              }
  
              return finalProcessedBlocks;
          },
  
          blockSize: 128/32
      });
  
      /**
       * A collection of cipher parameters.
       *
       * @property {WordArray} ciphertext The raw ciphertext.
       * @property {WordArray} key The key to this ciphertext.
       * @property {WordArray} iv The IV used in the ciphering operation.
       * @property {WordArray} salt The salt used with a key derivation function.
       * @property {Cipher} algorithm The cipher algorithm.
       * @property {Mode} mode The block mode used in the ciphering operation.
       * @property {Padding} padding The padding scheme used in the ciphering operation.
       * @property {number} blockSize The block size of the cipher.
       * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string.
       */
      var CipherParams = C_lib.CipherParams = Base.extend({
          /**
           * Initializes a newly created cipher params object.
           *
           * @param {Object} cipherParams An object with any of the possible cipher parameters.
           *
           * @example
           *
           *     var cipherParams = CryptoJS.lib.CipherParams.create({
           *         ciphertext: ciphertextWordArray,
           *         key: keyWordArray,
           *         iv: ivWordArray,
           *         salt: saltWordArray,
           *         algorithm: CryptoJS.algo.AES,
           *         mode: CryptoJS.mode.CBC,
           *         padding: CryptoJS.pad.PKCS7,
           *         blockSize: 4,
           *         formatter: CryptoJS.format.OpenSSL
           *     });
           */
          init: function (cipherParams) {
              this.mixIn(cipherParams);
          },
  
          /**
           * Converts this cipher params object to a string.
           *
           * @param {Format} formatter (Optional) The formatting strategy to use.
           *
           * @return {string} The stringified cipher params.
           *
           * @throws Error If neither the formatter nor the default formatter is set.
           *
           * @example
           *
           *     var string = cipherParams + '';
           *     var string = cipherParams.toString();
           *     var string = cipherParams.toString(CryptoJS.format.OpenSSL);
           */
          toString: function (formatter) {
              return (formatter || this.formatter).stringify(this);
          }
      });
  
      /**
       * Format namespace.
       */
      var C_format = C.format = {};
  
      /**
       * OpenSSL formatting strategy.
       */
      var OpenSSLFormatter = C_format.OpenSSL = {
          /**
           * Converts a cipher params object to an OpenSSL-compatible string.
           *
           * @param {CipherParams} cipherParams The cipher params object.
           *
           * @return {string} The OpenSSL-compatible string.
           *
           * @static
           *
           * @example
           *
           *     var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams);
           */
          stringify: function (cipherParams) {
              // Shortcuts
              var ciphertext = cipherParams.ciphertext;
              var salt = cipherParams.salt;
  
              // Format
              if (salt) {
                  var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext);
              } else {
                  var wordArray = ciphertext;
              }
  
              return wordArray.toString(Base64);
          },
  
          /**
           * Converts an OpenSSL-compatible string to a cipher params object.
           *
           * @param {string} openSSLStr The OpenSSL-compatible string.
           *
           * @return {CipherParams} The cipher params object.
           *
           * @static
           *
           * @example
           *
           *     var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString);
           */
          parse: function (openSSLStr) {
              // Parse base64
              var ciphertext = Base64.parse(openSSLStr);
  
              // Shortcut
              var ciphertextWords = ciphertext.words;
  
              // Test for salt
              if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) {
                  // Extract salt
                  var salt = WordArray.create(ciphertextWords.slice(2, 4));
  
                  // Remove salt from ciphertext
                  ciphertextWords.splice(0, 4);
                  ciphertext.sigBytes -= 16;
              }
  
              return CipherParams.create({ ciphertext: ciphertext, salt: salt });
          }
      };
  
      /**
       * A cipher wrapper that returns ciphertext as a serializable cipher params object.
       */
      var SerializableCipher = C_lib.SerializableCipher = Base.extend({
          /**
           * Configuration options.
           *
           * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL
           */
          cfg: Base.extend({
              format: OpenSSLFormatter
          }),
  
          /**
           * Encrypts a message.
           *
           * @param {Cipher} cipher The cipher algorithm to use.
           * @param {WordArray|string} message The message to encrypt.
           * @param {WordArray} key The key.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @return {CipherParams} A cipher params object.
           *
           * @static
           *
           * @example
           *
           *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key);
           *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv });
           *     var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL });
           */
          encrypt: function (cipher, message, key, cfg) {
              // Apply config defaults
              cfg = this.cfg.extend(cfg);
  
              // Encrypt
              var encryptor = cipher.createEncryptor(key, cfg);
              var ciphertext = encryptor.finalize(message);
  
              // Shortcut
              var cipherCfg = encryptor.cfg;
  
              // Create and return serializable cipher params
              return CipherParams.create({
                  ciphertext: ciphertext,
                  key: key,
                  iv: cipherCfg.iv,
                  algorithm: cipher,
                  mode: cipherCfg.mode,
                  padding: cipherCfg.padding,
                  blockSize: cipher.blockSize,
                  formatter: cfg.format
              });
          },
  
          /**
           * Decrypts serialized ciphertext.
           *
           * @param {Cipher} cipher The cipher algorithm to use.
           * @param {CipherParams|string} ciphertext The ciphertext to decrypt.
           * @param {WordArray} key The key.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @return {WordArray} The plaintext.
           *
           * @static
           *
           * @example
           *
           *     var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL });
           *     var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL });
           */
          decrypt: function (cipher, ciphertext, key, cfg) {
              // Apply config defaults
              cfg = this.cfg.extend(cfg);
  
              // Convert string to CipherParams
              ciphertext = this._parse(ciphertext, cfg.format);
  
              // Decrypt
              var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext);
  
              return plaintext;
          },
  
          /**
           * Converts serialized ciphertext to CipherParams,
           * else assumed CipherParams already and returns ciphertext unchanged.
           *
           * @param {CipherParams|string} ciphertext The ciphertext.
           * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext.
           *
           * @return {CipherParams} The unserialized ciphertext.
           *
           * @static
           *
           * @example
           *
           *     var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format);
           */
          _parse: function (ciphertext, format) {
              if (typeof ciphertext == 'string') {
                  return format.parse(ciphertext, this);
              } else {
                  return ciphertext;
              }
          }
      });
  
      /**
       * Key derivation function namespace.
       */
      var C_kdf = C.kdf = {};
  
      /**
       * OpenSSL key derivation function.
       */
      var OpenSSLKdf = C_kdf.OpenSSL = {
          /**
           * Derives a key and IV from a password.
           *
           * @param {string} password The password to derive from.
           * @param {number} keySize The size in words of the key to generate.
           * @param {number} ivSize The size in words of the IV to generate.
           * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly.
           *
           * @return {CipherParams} A cipher params object with the key, IV, and salt.
           *
           * @static
           *
           * @example
           *
           *     var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
           *     var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
           */
          execute: function (password, keySize, ivSize, salt) {
              // Generate random salt
              if (!salt) {
                  salt = WordArray.random(64/8);
              }
  
              // Derive key and IV
              var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
  
              // Separate key and IV
              var iv = WordArray.create(key.words.slice(keySize), ivSize * 4);
              key.sigBytes = keySize * 4;
  
              // Return params
              return CipherParams.create({ key: key, iv: iv, salt: salt });
          }
      };
  
      /**
       * A serializable cipher wrapper that derives the key from a password,
       * and returns ciphertext as a serializable cipher params object.
       */
      var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({
          /**
           * Configuration options.
           *
           * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL
           */
          cfg: SerializableCipher.cfg.extend({
              kdf: OpenSSLKdf
          }),
  
          /**
           * Encrypts a message using a password.
           *
           * @param {Cipher} cipher The cipher algorithm to use.
           * @param {WordArray|string} message The message to encrypt.
           * @param {string} password The password.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @return {CipherParams} A cipher params object.
           *
           * @static
           *
           * @example
           *
           *     var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password');
           *     var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL });
           */
          encrypt: function (cipher, message, password, cfg) {
              // Apply config defaults
              cfg = this.cfg.extend(cfg);
  
              // Derive key and other params
              var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize);
  
              // Add IV to config
              cfg.iv = derivedParams.iv;
  
              // Encrypt
              var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg);
  
              // Mix in derived params
              ciphertext.mixIn(derivedParams);
  
              return ciphertext;
          },
  
          /**
           * Decrypts serialized ciphertext using a password.
           *
           * @param {Cipher} cipher The cipher algorithm to use.
           * @param {CipherParams|string} ciphertext The ciphertext to decrypt.
           * @param {string} password The password.
           * @param {Object} cfg (Optional) The configuration options to use for this operation.
           *
           * @return {WordArray} The plaintext.
           *
           * @static
           *
           * @example
           *
           *     var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL });
           *     var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL });
           */
          decrypt: function (cipher, ciphertext, password, cfg) {
              // Apply config defaults
              cfg = this.cfg.extend(cfg);
  
              // Convert string to CipherParams
              ciphertext = this._parse(ciphertext, cfg.format);
  
              // Derive key and other params
              var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt);
  
              // Add IV to config
              cfg.iv = derivedParams.iv;
  
              // Decrypt
              var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg);
  
              return plaintext;
          }
      });
  }());
  