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.StreamBlockCipher;
      7 import org.bouncycastle.crypto.params.ParametersWithIV;
      8 import org.bouncycastle.util.Arrays;
      9 
     10 /**
     11  * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
     12  */
     13 public class CFBBlockCipher
     14     extends StreamBlockCipher
     15 {
     16     private byte[]          IV;
     17     private byte[]          cfbV;
     18     private byte[]          cfbOutV;
     19     private byte[]          inBuf;
     20 
     21     private int             blockSize;
     22     private BlockCipher     cipher = null;
     23     private boolean         encrypting;
     24     private int             byteCount;
     25 
     26     /**
     27      * Basic constructor.
     28      *
     29      * @param cipher the block cipher to be used as the basis of the
     30      * feedback mode.
     31      * @param bitBlockSize the block size in bits (note: a multiple of 8)
     32      */
     33     public CFBBlockCipher(
     34         BlockCipher cipher,
     35         int         bitBlockSize)
     36     {
     37         super(cipher);
     38 
     39         this.cipher = cipher;
     40         this.blockSize = bitBlockSize / 8;
     41 
     42         this.IV = new byte[cipher.getBlockSize()];
     43         this.cfbV = new byte[cipher.getBlockSize()];
     44         this.cfbOutV = new byte[cipher.getBlockSize()];
     45         this.inBuf = new byte[blockSize];
     46     }
     47 
     48     /**
     49      * Initialise the cipher and, possibly, the initialisation vector (IV).
     50      * If an IV isn't passed as part of the parameter, the IV will be all zeros.
     51      * An IV which is too short is handled in FIPS compliant fashion.
     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 < IV.length)
     72             {
     73                 // prepend the supplied IV with zeros (per FIPS PUB 81)
     74                 System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length);
     75                 for (int i = 0; i < IV.length - iv.length; i++)
     76                 {
     77                     IV[i] = 0;
     78                 }
     79             }
     80             else
     81             {
     82                 System.arraycopy(iv, 0, IV, 0, IV.length);
     83             }
     84 
     85             reset();
     86 
     87             // if null it's an IV changed only.
     88             if (ivParam.getParameters() != null)
     89             {
     90                 cipher.init(true, ivParam.getParameters());
     91             }
     92         }
     93         else
     94         {
     95             reset();
     96 
     97             // if it's null, key is to be reused.
     98             if (params != null)
     99             {
    100                 cipher.init(true, params);
    101             }
    102         }
    103     }
    104 
    105     /**
    106      * return the algorithm name and mode.
    107      *
    108      * @return the name of the underlying algorithm followed by "/CFB"
    109      * and the block size in bits.
    110      */
    111     public String getAlgorithmName()
    112     {
    113         return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
    114     }
    115 
    116     protected byte calculateByte(byte in)
    117           throws DataLengthException, IllegalStateException
    118     {
    119         return (encrypting) ? encryptByte(in) : decryptByte(in);
    120     }
    121 
    122     private byte encryptByte(byte in)
    123     {
    124         if (byteCount == 0)
    125         {
    126             cipher.processBlock(cfbV, 0, cfbOutV, 0);
    127         }
    128 
    129         byte rv = (byte)(cfbOutV[byteCount] ^ in);
    130         inBuf[byteCount++] = rv;
    131 
    132         if (byteCount == blockSize)
    133         {
    134             byteCount = 0;
    135 
    136             System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
    137             System.arraycopy(inBuf, 0, cfbV, cfbV.length - blockSize, blockSize);
    138         }
    139 
    140         return rv;
    141     }
    142 
    143     private byte decryptByte(byte in)
    144     {
    145         if (byteCount == 0)
    146         {
    147             cipher.processBlock(cfbV, 0, cfbOutV, 0);
    148         }
    149 
    150         inBuf[byteCount] = in;
    151         byte rv = (byte)(cfbOutV[byteCount++] ^ in);
    152 
    153         if (byteCount == blockSize)
    154         {
    155             byteCount = 0;
    156 
    157             System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize);
    158             System.arraycopy(inBuf, 0, cfbV, cfbV.length - blockSize, blockSize);
    159         }
    160 
    161         return rv;
    162     }
    163 
    164     /**
    165      * return the block size we are operating at.
    166      *
    167      * @return the block size we are operating at (in bytes).
    168      */
    169     public int getBlockSize()
    170     {
    171         return blockSize;
    172     }
    173 
    174     /**
    175      * Process one block of input from the array in and write it to
    176      * the out array.
    177      *
    178      * @param in the array containing the input data.
    179      * @param inOff offset into the in array the data starts at.
    180      * @param out the array the output data will be copied into.
    181      * @param outOff the offset into the out array the output will start at.
    182      * @exception DataLengthException if there isn't enough data in in, or
    183      * space in out.
    184      * @exception IllegalStateException if the cipher isn't initialised.
    185      * @return the number of bytes processed and produced.
    186      */
    187     public int processBlock(
    188         byte[]      in,
    189         int         inOff,
    190         byte[]      out,
    191         int         outOff)
    192         throws DataLengthException, IllegalStateException
    193     {
    194         processBytes(in, inOff, blockSize, out, outOff);
    195 
    196         return blockSize;
    197     }
    198 
    199     /**
    200      * Do the appropriate processing for CFB mode encryption.
    201      *
    202      * @param in the array containing the data to be encrypted.
    203      * @param inOff offset into the in array the data starts at.
    204      * @param out the array the encrypted data will be copied into.
    205      * @param outOff the offset into the out array the output will start at.
    206      * @exception DataLengthException if there isn't enough data in in, or
    207      * space in out.
    208      * @exception IllegalStateException if the cipher isn't initialised.
    209      * @return the number of bytes processed and produced.
    210      */
    211     public int encryptBlock(
    212         byte[]      in,
    213         int         inOff,
    214         byte[]      out,
    215         int         outOff)
    216         throws DataLengthException, IllegalStateException
    217     {
    218         processBytes(in, inOff, blockSize, out, outOff);
    219 
    220         return blockSize;
    221     }
    222 
    223     /**
    224      * Do the appropriate processing for CFB mode decryption.
    225      *
    226      * @param in the array containing the data to be decrypted.
    227      * @param inOff offset into the in array the data starts at.
    228      * @param out the array the encrypted data will be copied into.
    229      * @param outOff the offset into the out array the output will start at.
    230      * @exception DataLengthException if there isn't enough data in in, or
    231      * space in out.
    232      * @exception IllegalStateException if the cipher isn't initialised.
    233      * @return the number of bytes processed and produced.
    234      */
    235     public int decryptBlock(
    236         byte[]      in,
    237         int         inOff,
    238         byte[]      out,
    239         int         outOff)
    240         throws DataLengthException, IllegalStateException
    241     {
    242         processBytes(in, inOff, blockSize, out, outOff);
    243 
    244         return blockSize;
    245     }
    246 
    247     /**
    248      * Return the current state of the initialisation vector.
    249      *
    250      * @return current IV
    251      */
    252     public byte[] getCurrentIV()
    253     {
    254         return Arrays.clone(cfbV);
    255     }
    256 
    257     /**
    258      * reset the chaining vector back to the IV and reset the underlying
    259      * cipher.
    260      */
    261     public void reset()
    262     {
    263         System.arraycopy(IV, 0, cfbV, 0, IV.length);
    264         Arrays.fill(inBuf, (byte)0);
    265         byteCount = 0;
    266 
    267         cipher.reset();
    268     }
    269 }
    270