text: /** @fileOverview OCB 2.0 implementation
   *
   * @author Emily Stark
   * @author Mike Hamburg
   * @author Dan Boneh
   */
  
  /** @namespace
   * Phil Rogaway's Offset CodeBook mode, version 2.0.
   * May be covered by US and international patents.
   *
   * @author Emily Stark
   * @author Mike Hamburg
   * @author Dan Boneh
   */
  sjcl.mode.ocb2 = {
    /** The name of the mode.
     * @constant
     */
    name: "ocb2",
    
    /** Encrypt in OCB mode, version 2.0.
     * @param {Object} prp The block cipher.  It must have a block size of 16 bytes.
     * @param {bitArray} plaintext The plaintext data.
     * @param {bitArray} iv The initialization value.
     * @param {bitArray} [adata=[]] The authenticated data.
     * @param {Number} [tlen=64] the desired tag length, in bits.
     * @param [false] premac 1 if the authentication data is pre-macced with PMAC.
     * @return The encrypted data, an array of bytes.
     * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits.
     */
    encrypt: function(prp, plaintext, iv, adata, tlen, premac) {
      if (sjcl.bitArray.bitLength(iv) !== 128) {
        throw new sjcl.exception.invalid("ocb iv must be 128 bits");
      }
      var i,
          times2 = sjcl.mode.ocb2._times2,
          w = sjcl.bitArray,
          xor = w._xor4,
          checksum = [0,0,0,0],
          delta = times2(prp.encrypt(iv)),
          bi, bl,
          output = [],
          pad;
          
      adata = adata || [];
      tlen  = tlen || 64;
    
      for (i=0; i+4 < plaintext.length; i+=4) {
        /* Encrypt a non-final block */
        bi = plaintext.slice(i,i+4);
        checksum = xor(checksum, bi);
        output = output.concat(xor(delta,prp.encrypt(xor(delta, bi))));
        delta = times2(delta);
      }
      
      /* Chop out the final block */
      bi = plaintext.slice(i);
      bl = w.bitLength(bi);
      pad = prp.encrypt(xor(delta,[0,0,0,bl]));
      bi = w.clamp(xor(bi.concat([0,0,0]),pad), bl);
      
      /* Checksum the final block, and finalize the checksum */
      checksum = xor(checksum,xor(bi.concat([0,0,0]),pad));
      checksum = prp.encrypt(xor(checksum,xor(delta,times2(delta))));
      
      /* MAC the header */
      if (adata.length) {
        checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
      }
      
      return output.concat(w.concat(bi, w.clamp(checksum, tlen)));
    },
    
    /** Decrypt in OCB mode.
     * @param {Object} prp The block cipher.  It must have a block size of 16 bytes.
     * @param {bitArray} ciphertext The ciphertext data.
     * @param {bitArray} iv The initialization value.
     * @param {bitArray} [adata=[]] The authenticated data.
     * @param {Number} [tlen=64] the desired tag length, in bits.
     * @param {boolean} [premac=false] true if the authentication data is pre-macced with PMAC.
     * @return The decrypted data, an array of bytes.
     * @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits.
     * @throws {sjcl.exception.corrupt} if if the message is corrupt.
     */
    decrypt: function(prp, ciphertext, iv, adata, tlen, premac) {
      if (sjcl.bitArray.bitLength(iv) !== 128) {
        throw new sjcl.exception.invalid("ocb iv must be 128 bits");
      }
      tlen  = tlen || 64;
      var i,
          times2 = sjcl.mode.ocb2._times2,
          w = sjcl.bitArray,
          xor = w._xor4,
          checksum = [0,0,0,0],
          delta = times2(prp.encrypt(iv)),
          bi, bl,
          len = sjcl.bitArray.bitLength(ciphertext) - tlen,
          output = [],
          pad;
          
      adata = adata || [];
    
      for (i=0; i+4 < len/32; i+=4) {
        /* Decrypt a non-final block */
        bi = xor(delta, prp.decrypt(xor(delta, ciphertext.slice(i,i+4))));
        checksum = xor(checksum, bi);
        output = output.concat(bi);
        delta = times2(delta);
      }
      
      /* Chop out and decrypt the final block */
      bl = len-i*32;
      pad = prp.encrypt(xor(delta,[0,0,0,bl]));
      bi = xor(pad, w.clamp(ciphertext.slice(i),bl).concat([0,0,0]));
      
      /* Checksum the final block, and finalize the checksum */
      checksum = xor(checksum, bi);
      checksum = prp.encrypt(xor(checksum, xor(delta, times2(delta))));
      
      /* MAC the header */
      if (adata.length) {
        checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
      }
      
      if (!w.equal(w.clamp(checksum, tlen), w.bitSlice(ciphertext, len))) {
        throw new sjcl.exception.corrupt("ocb: tag doesn't match");
      }
      
      return output.concat(w.clamp(bi,bl));
    },
    
    /** PMAC authentication for OCB associated data.
     * @param {Object} prp The block cipher.  It must have a block size of 16 bytes.
     * @param {bitArray} adata The authenticated data.
     */
    pmac: function(prp, adata) {
      var i,
          times2 = sjcl.mode.ocb2._times2,
          w = sjcl.bitArray,
          xor = w._xor4,
          checksum = [0,0,0,0],
          delta = prp.encrypt([0,0,0,0]),
          bi;
          
      delta = xor(delta,times2(times2(delta)));
   
      for (i=0; i+4<adata.length; i+=4) {
        delta = times2(delta);
        checksum = xor(checksum, prp.encrypt(xor(delta, adata.slice(i,i+4))));
      }
      
      bi = adata.slice(i);
      if (w.bitLength(bi) < 128) {
        delta = xor(delta,times2(delta));
        bi = w.concat(bi,[0x80000000|0,0,0,0]);
      }
      checksum = xor(checksum, bi);
      return prp.encrypt(xor(times2(xor(delta,times2(delta))), checksum));
    },
    
    /** Double a block of words, OCB style.
     * @private
     */
    _times2: function(x) {
      return [x[0]<<1 ^ x[1]>>>31,
              x[1]<<1 ^ x[2]>>>31,
              x[2]<<1 ^ x[3]>>>31,
              x[3]<<1 ^ (x[0]>>>31)*0x87];
    }
  };
  