Modes
*****

Cipher modes operate on the next level up from the underlying block cipher. They transform the blocks going in and out of the cipher in ways to give them desirable properties in certain circumstances. The cipher modes implemented by GNU Crypto, which is contained in the gnu.crypto.mode package and are referenced herein by their three-letter abbreviations described below, are:

  * Cipher block chaining mode. The ``CBC'' mode makes every block of the ciphertext depend upon all previous blocks by adding feedback to the transformation. This is done by XORing the plaintext with the previous ciphertext (or, with the first block, an initialization vector) before it is transformed. That is, encryption looks like: C[i] = ENCRYPT(k, P_i ^ C[i-1]); and decryption is P[i] = C[i-1] ^ DECRYPT(C[i]).

  * Counter mode. Counter mode, referred to as ``CTR'' mode, is one of a class of sequenced cipher modes that turn the underlying cipher into a keystream. Counter mode relys on a simple counter register that is updated for every block processed. For plaintexts P[1] ... P[n], ciphertexts C[1] ... C[n], counter elements T[1] ... T[n], and an encryption function ENCRYPT(k, ...), encryption is defined as C[i] = P[i] ^ ENCRYPT(k, T[i]) and decryption as P[i] = C[i] ^ ENCRYPT(k, T[i]).

  * Electronic codebook mode. Or ``ECB'' mode, is the most obvious cipher mode: the cipher block is the direct output of the forward function, and the plain block is the direct output of the inverse function. That is, encryption is C_i = E_k(P_i) and decryption is P_i = E_k^\bgroup-1\egroup(C_i).

  * Integer counter mode. ``ICM'' mode has features in common with counter mode described above. The counter, T_i, is computed by T_i = (T_0 + i) \bmod 256^b, where b is the cipher's block size. T_0 is initialized to the integer representation of some initialization vector. The keystream bytes are then E_k(T_i). Encryption and decryption are then C_i = P_i \oplus E_k(T_i) and P_i = C_i \oplus E_k(T_i), respectively.

  * Output feeback mode. ``OFB'' mode creates a keystream by repeatedly iterating the underlying block cipher over an initialization vector. That is, the ith keystream block is X_i = E(X_\bgroup i-1\egroup) for 1 < i \leq n, and X_1 = IV. Like the other stream modes, the input block i is transformed by the exclusive-or of the block with X_i.

The IMode Interface
===================

The IMode interface is similar to the IBlockCipher interface, except modes have a state associated with them, e.g. whether the instance is used for encryption or decryption. The IMode interface is usually the one that is used when encrypting or decrypting; IBlockCipher is used when the lowest level---the cipher function itself---needs to be accessed. IMode extends IBlockCipher interface, and thus all methods specified in that interface are implemented in modes, and have the same meaning. The properties passed to the init method of IBlockCipher may also be passed to the init mehtod of IMode, along with the following property names.

Figure 2: Modes class diagram

 -- Variable: java.lang.String STATE
     The property name for the mode's state, as passed to the init method. Values for this property are an java.lang.Integer containing either the ENCRYPTION constant or the DECRYPTION constant.

 -- Variable: int ENCRYPTION
     The value passed for the STATE property, wrapped in a java.lang.Integer, which indicates that the instance is to be used for encryption.

 -- Variable: int DECRYPTION
     The value passed for the STATE property, wrapped in a java.lang.Integer, which indicates that the instance is to be used for decryption.

 -- Variable: java.lang.String MODE_BLOCK_SIZE
     The property name for the block size of this mode. The value for this propery should be a java.lang.Integer of the block size. If omitted, the underlying cipher's block size is used.

 -- Variable: java.lang.String IV
     The property name for the initialization vector to initialize this mode with, if required. The value should be a byte array equal in size to the MODE_BLOCK_SIZE property. If omitted a byte array consisting of zeros is used.

 -- Function: void update (byte[] IN, int INOFFSET, byte[] OUT, int OUTOFFSET) throws java.lang.IllegalStateException
     Transforms the block in IN starting at INOFFSET into the block in OUT starting at OUTOFFSET. Encryption or decryption is performed depending upon the value passed along with the state property given to the init method. A java.lang.IllegalStateException is thrown if this instance has not been initialized, and it is up to the programmer to ensure that there is one full block in IN starting at INOFFSET, and enough space for one full block in OUT starting at OUTOFFSET. Since modes can have states, and may require that the be used in a particular sequence, using this method is preferred over the encryptBlock and decryptBlock methods of IBlockCipher.

The ModeFactory Class
=====================

The preferred way to get mode instances is through the ModeFactory class, from one of the following methods:

 -- Function: static IMode getInstance (java.lang.String MODE, java.lang.String CIPHER, int CIPHERBLOCKSIZE)
     Returns an instance of CIPHER wrapped in an instance of MODE, initialized to a block size of CIPHERBLOCKSIZE, or returns null if no appropriate cipher or mode is available. The MODE argument is one of the names described above, and CIPHER is one of the names described in the Ciphers chapter.

 -- Function: static IMode getInstance (java.lang.String MODE, IBlockCipher CIPHER, int CIPHERBLOCKSIZE)
     Returns an instance of MODE using the already-initialized CIPHER, initializing the mode with a block size of CIPHERBLOCKSIZE, or returns null if no appropriate mode is available.

Additionally the following method is defined:

 -- Function: static java.util.Set getNames ()
     This method returns a java.util.Set of the names (each element of type java.lang.String) of all supported modes.

Example
=======

The following example encrypts and decrypts a byte array with the AES in CFB mode. See the next chapter on padding for instances where the input is not a multiple of the cipher or mode's block size.

     IMode mode = ModeFactory.getInstance("CFB", "AES", 16);
     Map attributes = new HashMap();

     // These attributes are defined in gnu.crypto.cipher.IBlockCipher.
     attributes.put(IMode.KEY_MATERIAL, key_bytes);
     attributes.put(IMode.CIPHER_BLOCK_SIZE, new Integer(16));

     // These attributes are defined in IMode.
     attributes.put(IMode.STATE, new Integer(IMode.ENCRYPTION));
     attributes.put(IMode.IV, iv_bytes);
     mode.init(attributes);
     int bs = mode.currentBlockSize();

     for (int i = 0; i + bs < pt.length; i += bs)
        {
           mode.update(pt, i, ct, i);
        }

     mode.reset();
     attributes.put(IMode.STATE, new Integer(IMode.DECRYPTION);
     mode.init(attributes);

     for (int i = 0; i + bs < ct.length; i += bs)
        {
           mode.update(ct, i, cpt, i);
        }