1package org.bouncycastle.crypto.modes;
2
3import org.bouncycastle.crypto.BlockCipher;
4import org.bouncycastle.crypto.CipherParameters;
5import org.bouncycastle.crypto.DataLengthException;
6import org.bouncycastle.crypto.params.ParametersWithIV;
7
8/**
9 * implements a Output-FeedBack (OFB) mode on top of a simple cipher.
10 */
11public 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