1package org.bouncycastle.crypto.modes;
2
3import org.bouncycastle.crypto.BlockCipher;
4import org.bouncycastle.crypto.BufferedBlockCipher;
5import org.bouncycastle.crypto.DataLengthException;
6import org.bouncycastle.crypto.InvalidCipherTextException;
7
8/**
9 * A wrapper class that allows block ciphers to be used to process data in
10 * a piecemeal fashion with PKCS5/PKCS7 padding. The PaddedBlockCipher
11 * outputs a block only when the buffer is full and more data is being added,
12 * or on a doFinal (unless the current block in the buffer is a pad block).
13 * The padding mechanism used is the one outlined in PKCS5/PKCS7.
14 *
15 * @deprecated use org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher instead.
16 */
17public class PaddedBlockCipher
18    extends BufferedBlockCipher
19{
20    /**
21     * Create a buffered block cipher with, or without, padding.
22     *
23     * @param cipher the underlying block cipher this buffering object wraps.
24     */
25    public PaddedBlockCipher(
26        BlockCipher     cipher)
27    {
28        this.cipher = cipher;
29
30        buf = new byte[cipher.getBlockSize()];
31        bufOff = 0;
32    }
33
34    /**
35     * return the size of the output buffer required for an update plus a
36     * doFinal with an input of len bytes.
37     *
38     * @param len the length of the input.
39     * @return the space required to accommodate a call to update and doFinal
40     * with len bytes of input.
41     */
42    public int getOutputSize(
43        int len)
44    {
45        int total       = len + bufOff;
46        int leftOver    = total % buf.length;
47
48        if (leftOver == 0)
49        {
50            if (forEncryption)
51            {
52                return total + buf.length;
53            }
54
55            return total;
56        }
57
58        return total - leftOver + buf.length;
59    }
60
61    /**
62     * return the size of the output buffer required for an update
63     * an input of len bytes.
64     *
65     * @param len the length of the input.
66     * @return the space required to accommodate a call to update
67     * with len bytes of input.
68     */
69    public int getUpdateOutputSize(
70        int len)
71    {
72        int total       = len + bufOff;
73        int leftOver    = total % buf.length;
74
75        if (leftOver == 0)
76        {
77            return total - buf.length;
78        }
79
80        return total - leftOver;
81    }
82
83    /**
84     * process a single byte, producing an output block if neccessary.
85     *
86     * @param in the input byte.
87     * @param out the space for any output that might be produced.
88     * @param outOff the offset from which the output will be copied.
89     * @exception DataLengthException if there isn't enough space in out.
90     * @exception IllegalStateException if the cipher isn't initialised.
91     */
92    public int processByte(
93        byte        in,
94        byte[]      out,
95        int         outOff)
96        throws DataLengthException, IllegalStateException
97    {
98        int         resultLen = 0;
99
100        if (bufOff == buf.length)
101        {
102            resultLen = cipher.processBlock(buf, 0, out, outOff);
103            bufOff = 0;
104        }
105
106        buf[bufOff++] = in;
107
108        return resultLen;
109    }
110
111    /**
112     * process an array of bytes, producing output if necessary.
113     *
114     * @param in the input byte array.
115     * @param inOff the offset at which the input data starts.
116     * @param len the number of bytes to be copied out of the input array.
117     * @param out the space for any output that might be produced.
118     * @param outOff the offset from which the output will be copied.
119     * @exception DataLengthException if there isn't enough space in out.
120     * @exception IllegalStateException if the cipher isn't initialised.
121     */
122    public int processBytes(
123        byte[]      in,
124        int         inOff,
125        int         len,
126        byte[]      out,
127        int         outOff)
128        throws DataLengthException, IllegalStateException
129    {
130        if (len < 0)
131        {
132            throw new IllegalArgumentException("Can't have a negative input length!");
133        }
134
135        int blockSize   = getBlockSize();
136        int length      = getUpdateOutputSize(len);
137
138        if (length > 0)
139        {
140            if ((outOff + length) > out.length)
141            {
142                throw new DataLengthException("output buffer too short");
143            }
144        }
145
146        int resultLen = 0;
147        int gapLen = buf.length - bufOff;
148
149        if (len > gapLen)
150        {
151            System.arraycopy(in, inOff, buf, bufOff, gapLen);
152
153            resultLen += cipher.processBlock(buf, 0, out, outOff);
154
155            bufOff = 0;
156            len -= gapLen;
157            inOff += gapLen;
158
159            while (len > buf.length)
160            {
161                resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
162
163                len -= blockSize;
164                inOff += blockSize;
165            }
166        }
167
168        System.arraycopy(in, inOff, buf, bufOff, len);
169
170        bufOff += len;
171
172        return resultLen;
173    }
174
175    /**
176     * Process the last block in the buffer. If the buffer is currently
177     * full and padding needs to be added a call to doFinal will produce
178     * 2 * getBlockSize() bytes.
179     *
180     * @param out the array the block currently being held is copied into.
181     * @param outOff the offset at which the copying starts.
182     * @exception DataLengthException if there is insufficient space in out for
183     * the output or we are decrypting and the input is not block size aligned.
184     * @exception IllegalStateException if the underlying cipher is not
185     * initialised.
186     * @exception InvalidCipherTextException if padding is expected and not found.
187     */
188    public int doFinal(
189        byte[]  out,
190        int     outOff)
191        throws DataLengthException, IllegalStateException, InvalidCipherTextException
192    {
193        int blockSize = cipher.getBlockSize();
194        int resultLen = 0;
195
196        if (forEncryption)
197        {
198            if (bufOff == blockSize)
199            {
200                if ((outOff + 2 * blockSize) > out.length)
201                {
202                    throw new DataLengthException("output buffer too short");
203                }
204
205                resultLen = cipher.processBlock(buf, 0, out, outOff);
206                bufOff = 0;
207            }
208
209            //
210            // add PKCS7 padding
211            //
212            byte code = (byte)(blockSize - bufOff);
213
214            while (bufOff < blockSize)
215            {
216                buf[bufOff] = code;
217                bufOff++;
218            }
219
220            resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
221        }
222        else
223        {
224            if (bufOff == blockSize)
225            {
226                resultLen = cipher.processBlock(buf, 0, buf, 0);
227                bufOff = 0;
228            }
229            else
230            {
231                throw new DataLengthException("last block incomplete in decryption");
232            }
233
234            //
235            // remove PKCS7 padding
236            //
237            int count = buf[blockSize - 1] & 0xff;
238
239            if ((count < 0) || (count > blockSize))
240            {
241                throw new InvalidCipherTextException("pad block corrupted");
242            }
243
244            resultLen -= count;
245
246            System.arraycopy(buf, 0, out, outOff, resultLen);
247        }
248
249        reset();
250
251        return resultLen;
252    }
253}
254