Home | History | Annotate | Download | only in modes
      1 package org.bouncycastle.crypto.modes;
      2 
      3 import org.bouncycastle.crypto.BlockCipher;
      4 import org.bouncycastle.crypto.CipherParameters;
      5 import org.bouncycastle.crypto.DataLengthException;
      6 import org.bouncycastle.crypto.params.ParametersWithIV;
      7 import org.bouncycastle.util.Arrays;
      8 
      9 /**
     10  * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
     11  */
     12 public class CBCBlockCipher
     13     implements BlockCipher
     14 {
     15     private byte[]          IV;
     16     private byte[]          cbcV;
     17     private byte[]          cbcNextV;
     18 
     19     private int             blockSize;
     20     private BlockCipher     cipher = null;
     21     private boolean         encrypting;
     22 
     23     /**
     24      * Basic constructor.
     25      *
     26      * @param cipher the block cipher to be used as the basis of chaining.
     27      */
     28     public CBCBlockCipher(
     29         BlockCipher cipher)
     30     {
     31         this.cipher = cipher;
     32         this.blockSize = cipher.getBlockSize();
     33 
     34         this.IV = new byte[blockSize];
     35         this.cbcV = new byte[blockSize];
     36         this.cbcNextV = new byte[blockSize];
     37     }
     38 
     39     /**
     40      * return the underlying block cipher that we are wrapping.
     41      *
     42      * @return the underlying block cipher that we are wrapping.
     43      */
     44     public BlockCipher getUnderlyingCipher()
     45     {
     46         return cipher;
     47     }
     48 
     49     /**
     50      * Initialise the cipher and, possibly, the initialisation vector (IV).
     51      * If an IV isn't passed as part of the parameter, the IV will be all zeros.
     52      *
     53      * @param encrypting if true the cipher is initialised for
     54      *  encryption, if false for decryption.
     55      * @param params the key and other data required by the cipher.
     56      * @exception IllegalArgumentException if the params argument is
     57      * inappropriate.
     58      */
     59     public void init(
     60         boolean             encrypting,
     61         CipherParameters    params)
     62         throws IllegalArgumentException
     63     {
     64         this.encrypting = encrypting;
     65 
     66         if (params instanceof ParametersWithIV)
     67         {
     68                 ParametersWithIV ivParam = (ParametersWithIV)params;
     69                 byte[]      iv = ivParam.getIV();
     70 
     71                 if (iv.length != blockSize)
     72                 {
     73                     throw new IllegalArgumentException("initialisation vector must be the same length as block size");
     74                 }
     75 
     76                 System.arraycopy(iv, 0, IV, 0, iv.length);
     77 
     78                 reset();
     79 
     80                 cipher.init(encrypting, ivParam.getParameters());
     81         }
     82         else
     83         {
     84                 reset();
     85 
     86                 cipher.init(encrypting, params);
     87         }
     88     }
     89 
     90     /**
     91      * return the algorithm name and mode.
     92      *
     93      * @return the name of the underlying algorithm followed by "/CBC".
     94      */
     95     public String getAlgorithmName()
     96     {
     97         return cipher.getAlgorithmName() + "/CBC";
     98     }
     99 
    100     /**
    101      * return the block size of the underlying cipher.
    102      *
    103      * @return the block size of the underlying cipher.
    104      */
    105     public int getBlockSize()
    106     {
    107         return cipher.getBlockSize();
    108     }
    109 
    110     /**
    111      * Process one block of input from the array in and write it to
    112      * the out array.
    113      *
    114      * @param in the array containing the input data.
    115      * @param inOff offset into the in array the data starts at.
    116      * @param out the array the output data will be copied into.
    117      * @param outOff the offset into the out array the output will start at.
    118      * @exception DataLengthException if there isn't enough data in in, or
    119      * space in out.
    120      * @exception IllegalStateException if the cipher isn't initialised.
    121      * @return the number of bytes processed and produced.
    122      */
    123     public int processBlock(
    124         byte[]      in,
    125         int         inOff,
    126         byte[]      out,
    127         int         outOff)
    128         throws DataLengthException, IllegalStateException
    129     {
    130         return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
    131     }
    132 
    133     /**
    134      * reset the chaining vector back to the IV and reset the underlying
    135      * cipher.
    136      */
    137     public void reset()
    138     {
    139         System.arraycopy(IV, 0, cbcV, 0, IV.length);
    140         Arrays.fill(cbcNextV, (byte)0);
    141 
    142         cipher.reset();
    143     }
    144 
    145     /**
    146      * Do the appropriate chaining step for CBC mode encryption.
    147      *
    148      * @param in the array containing the data to be encrypted.
    149      * @param inOff offset into the in array the data starts at.
    150      * @param out the array the encrypted data will be copied into.
    151      * @param outOff the offset into the out array the output will start at.
    152      * @exception DataLengthException if there isn't enough data in in, or
    153      * space in out.
    154      * @exception IllegalStateException if the cipher isn't initialised.
    155      * @return the number of bytes processed and produced.
    156      */
    157     private int encryptBlock(
    158         byte[]      in,
    159         int         inOff,
    160         byte[]      out,
    161         int         outOff)
    162         throws DataLengthException, IllegalStateException
    163     {
    164         if ((inOff + blockSize) > in.length)
    165         {
    166             throw new DataLengthException("input buffer too short");
    167         }
    168 
    169         /*
    170          * XOR the cbcV and the input,
    171          * then encrypt the cbcV
    172          */
    173         for (int i = 0; i < blockSize; i++)
    174         {
    175             cbcV[i] ^= in[inOff + i];
    176         }
    177 
    178         int length = cipher.processBlock(cbcV, 0, out, outOff);
    179 
    180         /*
    181          * copy ciphertext to cbcV
    182          */
    183         System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
    184 
    185         return length;
    186     }
    187 
    188     /**
    189      * Do the appropriate chaining step for CBC mode decryption.
    190      *
    191      * @param in the array containing the data to be decrypted.
    192      * @param inOff offset into the in array the data starts at.
    193      * @param out the array the decrypted data will be copied into.
    194      * @param outOff the offset into the out array the output will start at.
    195      * @exception DataLengthException if there isn't enough data in in, or
    196      * space in out.
    197      * @exception IllegalStateException if the cipher isn't initialised.
    198      * @return the number of bytes processed and produced.
    199      */
    200     private int decryptBlock(
    201         byte[]      in,
    202         int         inOff,
    203         byte[]      out,
    204         int         outOff)
    205         throws DataLengthException, IllegalStateException
    206     {
    207         if ((inOff + blockSize) > in.length)
    208         {
    209             throw new DataLengthException("input buffer too short");
    210         }
    211 
    212         System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
    213 
    214         int length = cipher.processBlock(in, inOff, out, outOff);
    215 
    216         /*
    217          * XOR the cbcV and the output
    218          */
    219         for (int i = 0; i < blockSize; i++)
    220         {
    221             out[outOff + i] ^= cbcV[i];
    222         }
    223 
    224         /*
    225          * swap the back up buffer into next position
    226          */
    227         byte[]  tmp;
    228 
    229         tmp = cbcV;
    230         cbcV = cbcNextV;
    231         cbcNextV = tmp;
    232 
    233         return length;
    234     }
    235 }
    236