text: // Copyright 2012 The Closure Library Authors. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS-IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  /**
   * @fileoverview Implementation of CBC mode for block ciphers.  See
   *     http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
   *     #Cipher-block_chaining_.28CBC.29. for description.
   *
   * @author nnaze@google.com (Nathan Naze)
   */
  
  goog.provide('goog.crypt.Cbc');
  
  goog.require('goog.array');
  goog.require('goog.asserts');
  goog.require('goog.crypt');
  
  
  
  /**
   * Implements the CBC mode for block ciphers. See
   * http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation
   * #Cipher-block_chaining_.28CBC.29
   *
   * @param {!goog.crypt.BlockCipher} cipher The block cipher to use.
   * @param {number=} opt_blockSize The block size of the cipher in bytes.
   *     Defaults to 16 bytes.
   * @constructor
   * @final
   * @struct
   */
  goog.crypt.Cbc = function(cipher, opt_blockSize) {
  
    /**
     * Block cipher.
     * @type {!goog.crypt.BlockCipher}
     * @private
     */
    this.cipher_ = cipher;
  
    /**
     * Block size in bytes.
     * @type {number}
     * @private
     */
    this.blockSize_ = opt_blockSize || 16;
  };
  
  
  /**
   * Encrypt a message.
   *
   * @param {!Array<number>|!Uint8Array} plainText Message to encrypt. An array of
   *     bytes. The length should be a multiple of the block size.
   * @param {!Array<number>|!Uint8Array} initialVector Initial vector for the CBC
   *     mode. An array of bytes with the same length as the block size.
   * @return {!Array<number>} Encrypted message.
   */
  goog.crypt.Cbc.prototype.encrypt = function(plainText, initialVector) {
  
    goog.asserts.assert(
        plainText.length % this.blockSize_ == 0,
        'Data\'s length must be multiple of block size.');
  
    goog.asserts.assert(
        initialVector.length == this.blockSize_,
        'Initial vector must be size of one block.');
  
    // Implementation of
    // http://en.wikipedia.org/wiki/File:Cbc_encryption.png
  
    var cipherText = [];
    var vector = initialVector;
  
    // Generate each block of the encrypted cypher text.
    for (var blockStartIndex = 0; blockStartIndex < plainText.length;
         blockStartIndex += this.blockSize_) {
      // Takes one block from the input message.
      var plainTextBlock = goog.array.slice(
          plainText, blockStartIndex, blockStartIndex + this.blockSize_);
  
      var input = goog.crypt.xorByteArray(plainTextBlock, vector);
      var resultBlock = this.cipher_.encrypt(input);
  
      goog.array.extend(cipherText, resultBlock);
      vector = resultBlock;
    }
  
    return cipherText;
  };
  
  
  /**
   * Decrypt a message.
   *
   * @param {!Array<number>|!Uint8Array} cipherText Message to decrypt. An array
   *     of bytes. The length should be a multiple of the block size.
   * @param {!Array<number>|!Uint8Array} initialVector Initial vector for the CBC
   *     mode. An array of bytes with the same length as the block size.
   * @return {!Array<number>} Decrypted message.
   */
  goog.crypt.Cbc.prototype.decrypt = function(cipherText, initialVector) {
  
    goog.asserts.assert(
        cipherText.length % this.blockSize_ == 0,
        'Data\'s length must be multiple of block size.');
  
    goog.asserts.assert(
        initialVector.length == this.blockSize_,
        'Initial vector must be size of one block.');
  
    // Implementation of
    // http://en.wikipedia.org/wiki/File:Cbc_decryption.png
  
    var plainText = [];
    var blockStartIndex = 0;
    var vector = initialVector;
  
    // Generate each block of the decrypted plain text.
    while (blockStartIndex < cipherText.length) {
      // Takes one block.
      var cipherTextBlock = goog.array.slice(
          cipherText, blockStartIndex, blockStartIndex + this.blockSize_);
  
      var resultBlock = this.cipher_.decrypt(cipherTextBlock);
      var plainTextBlock = goog.crypt.xorByteArray(vector, resultBlock);
  
      goog.array.extend(plainText, plainTextBlock);
      vector = cipherTextBlock;
  
      blockStartIndex += this.blockSize_;
    }
  
    return plainText;
  };
  