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 
      8 /**
      9  * implements a Output-FeedBack (OFB) mode on top of a simple cipher.
     10  */
     11 public class OFBBlockCipher
     12     implements BlockCipher
     13 {
     14     private byte[]          IV;
     15     private byte[]          ofbV;
     16     private byte[]          ofbOutV;
     17 
     18     private final int             blockSize;
     19     private final BlockCipher     cipher;
     20 
     21     /**
     22      * Basic constructor.
     23      *
     24      * @param cipher the block cipher to be used as the basis of the
     25      * feedback mode.
     26      * @param blockSize the block size in bits (note: a multiple of 8)
     27      */
     28     public OFBBlockCipher(
     29         BlockCipher cipher,
     30         int         blockSize)
     31     {
     32         this.cipher = cipher;
     33         this.blockSize = blockSize / 8;
     34 
     35         this.IV = new byte[cipher.getBlockSize()];
     36         this.ofbV = new byte[cipher.getBlockSize()];
     37         this.ofbOutV = new byte[cipher.getBlockSize()];
     38     }
     39 
     40     /**
     41      * return the underlying block cipher that we are wrapping.
     42      *
     43      * @return the underlying block cipher that we are wrapping.
     44      */
     45     public BlockCipher getUnderlyingCipher()
     46     {
     47         return cipher;
     48     }
     49 
     50     /**
     51      * Initialise the cipher and, possibly, the initialisation vector (IV).
     52      * If an IV isn't passed as part of the parameter, the IV will be all zeros.
     53      * An IV which is too short is handled in FIPS compliant fashion.
     54      *
     55      * @param encrypting if true the cipher is initialised for
     56      *  encryption, if false for decryption.
     57      * @param params the key and other data required by the cipher.
     58      * @exception IllegalArgumentException if the params argument is
     59      * inappropriate.
     60      */
     61     public void init(
     62         boolean             encrypting, //ignored by this OFB mode
     63         CipherParameters    params)
     64         throws IllegalArgumentException
     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 "/OFB"
    109      * and the block size in bits
    110      */
    111     public String getAlgorithmName()
    112     {
    113         return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8);
    114     }
    115 
    116 
    117     /**
    118      * return the block size we are operating at (in bytes).
    119      *
    120      * @return the block size we are operating at (in bytes).
    121      */
    122     public int getBlockSize()
    123     {
    124         return blockSize;
    125     }
    126 
    127     /**
    128      * Process one block of input from the array in and write it to
    129      * the out array.
    130      *
    131      * @param in the array containing the input data.
    132      * @param inOff offset into the in array the data starts at.
    133      * @param out the array the output data will be copied into.
    134      * @param outOff the offset into the out array the output will start at.
    135      * @exception DataLengthException if there isn't enough data in in, or
    136      * space in out.
    137      * @exception IllegalStateException if the cipher isn't initialised.
    138      * @return the number of bytes processed and produced.
    139      */
    140     public int processBlock(
    141         byte[]      in,
    142         int         inOff,
    143         byte[]      out,
    144         int         outOff)
    145         throws DataLengthException, IllegalStateException
    146     {
    147         if ((inOff + blockSize) > in.length)
    148         {
    149             throw new DataLengthException("input buffer too short");
    150         }
    151 
    152         if ((outOff + blockSize) > out.length)
    153         {
    154             throw new DataLengthException("output buffer too short");
    155         }
    156 
    157         cipher.processBlock(ofbV, 0, ofbOutV, 0);
    158 
    159         //
    160         // XOR the ofbV with the plaintext producing the cipher text (and
    161         // the next input block).
    162         //
    163         for (int i = 0; i < blockSize; i++)
    164         {
    165             out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]);
    166         }
    167 
    168         //
    169         // change over the input block.
    170         //
    171         System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize);
    172         System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize);
    173 
    174         return blockSize;
    175     }
    176 
    177     /**
    178      * reset the feedback vector back to the IV and reset the underlying
    179      * cipher.
    180      */
    181     public void reset()
    182     {
    183         System.arraycopy(IV, 0, ofbV, 0, IV.length);
    184 
    185         cipher.reset();
    186     }
    187 }
    188