~brian-thomason/+junk/bouncycastle

« back to all changes in this revision

Viewing changes to src/org/bouncycastle/crypto/BufferedBlockCipher.java

  • Committer: Brian Thomason
  • Date: 2011-12-20 17:20:32 UTC
  • Revision ID: brian.thomason@canonical.com-20111220172032-rdtm13jgdxtksacr
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.bouncycastle.crypto;
 
2
 
 
3
 
 
4
/**
 
5
 * A wrapper class that allows block ciphers to be used to process data in
 
6
 * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
 
7
 * buffer is full and more data is being added, or on a doFinal.
 
8
 * <p>
 
9
 * Note: in the case where the underlying cipher is either a CFB cipher or an
 
10
 * OFB one the last block may not be a multiple of the block size.
 
11
 */
 
12
public class BufferedBlockCipher
 
13
{
 
14
    protected byte[]        buf;
 
15
    protected int           bufOff;
 
16
 
 
17
    protected boolean       forEncryption;
 
18
    protected BlockCipher   cipher;
 
19
 
 
20
    protected boolean       partialBlockOkay;
 
21
    protected boolean       pgpCFB;
 
22
 
 
23
    /**
 
24
     * constructor for subclasses
 
25
     */
 
26
    protected BufferedBlockCipher()
 
27
    {
 
28
    }
 
29
 
 
30
    /**
 
31
     * Create a buffered block cipher without padding.
 
32
     *
 
33
     * @param cipher the underlying block cipher this buffering object wraps.
 
34
     */
 
35
    public BufferedBlockCipher(
 
36
        BlockCipher     cipher)
 
37
    {
 
38
        this.cipher = cipher;
 
39
 
 
40
        buf = new byte[cipher.getBlockSize()];
 
41
        bufOff = 0;
 
42
 
 
43
        //
 
44
        // check if we can handle partial blocks on doFinal.
 
45
        //
 
46
        String  name = cipher.getAlgorithmName();
 
47
        int     idx = name.indexOf('/') + 1;
 
48
 
 
49
        pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
 
50
 
 
51
        if (pgpCFB)
 
52
        {
 
53
            partialBlockOkay = true;
 
54
        }
 
55
        else
 
56
        {
 
57
            partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
 
58
        }
 
59
    }
 
60
 
 
61
    /**
 
62
     * return the cipher this object wraps.
 
63
     *
 
64
     * @return the cipher this object wraps.
 
65
     */
 
66
    public BlockCipher getUnderlyingCipher()
 
67
    {
 
68
        return cipher;
 
69
    }
 
70
 
 
71
    /**
 
72
     * initialise the cipher.
 
73
     *
 
74
     * @param forEncryption if true the cipher is initialised for
 
75
     *  encryption, if false for decryption.
 
76
     * @param params the key and other data required by the cipher.
 
77
     * @exception IllegalArgumentException if the params argument is
 
78
     * inappropriate.
 
79
     */
 
80
    public void init(
 
81
        boolean             forEncryption,
 
82
        CipherParameters    params)
 
83
        throws IllegalArgumentException
 
84
    {
 
85
        this.forEncryption = forEncryption;
 
86
 
 
87
        reset();
 
88
 
 
89
        cipher.init(forEncryption, params);
 
90
    }
 
91
 
 
92
    /**
 
93
     * return the blocksize for the underlying cipher.
 
94
     *
 
95
     * @return the blocksize for the underlying cipher.
 
96
     */
 
97
    public int getBlockSize()
 
98
    {
 
99
        return cipher.getBlockSize();
 
100
    }
 
101
 
 
102
    /**
 
103
     * return the size of the output buffer required for an update 
 
104
     * an input of len bytes.
 
105
     *
 
106
     * @param len the length of the input.
 
107
     * @return the space required to accommodate a call to update
 
108
     * with len bytes of input.
 
109
     */
 
110
    public int getUpdateOutputSize(
 
111
        int len)
 
112
    {
 
113
        int total       = len + bufOff;
 
114
        int leftOver;
 
115
 
 
116
        if (pgpCFB)
 
117
        {
 
118
            leftOver    = total % buf.length - (cipher.getBlockSize() + 2);
 
119
        }
 
120
        else
 
121
        {
 
122
            leftOver    = total % buf.length;
 
123
        }
 
124
 
 
125
        return total - leftOver;
 
126
    }
 
127
 
 
128
    /**
 
129
     * return the size of the output buffer required for an update plus a
 
130
     * doFinal with an input of 'length' bytes.
 
131
     *
 
132
     * @param length the length of the input.
 
133
     * @return the space required to accommodate a call to update and doFinal
 
134
     * with 'length' bytes of input.
 
135
     */
 
136
    public int getOutputSize(
 
137
        int length)
 
138
    {
 
139
        // Note: Can assume partialBlockOkay is true for purposes of this calculation
 
140
        return length + bufOff;
 
141
    }
 
142
 
 
143
    /**
 
144
     * process a single byte, producing an output block if neccessary.
 
145
     *
 
146
     * @param in the input byte.
 
147
     * @param out the space for any output that might be produced.
 
148
     * @param outOff the offset from which the output will be copied.
 
149
     * @return the number of output bytes copied to out.
 
150
     * @exception DataLengthException if there isn't enough space in out.
 
151
     * @exception IllegalStateException if the cipher isn't initialised.
 
152
     */
 
153
    public int processByte(
 
154
        byte        in,
 
155
        byte[]      out,
 
156
        int         outOff)
 
157
        throws DataLengthException, IllegalStateException
 
158
    {
 
159
        int         resultLen = 0;
 
160
 
 
161
        buf[bufOff++] = in;
 
162
 
 
163
        if (bufOff == buf.length)
 
164
        {
 
165
            resultLen = cipher.processBlock(buf, 0, out, outOff);
 
166
            bufOff = 0;
 
167
        }
 
168
 
 
169
        return resultLen;
 
170
    }
 
171
 
 
172
    /**
 
173
     * process an array of bytes, producing output if necessary.
 
174
     *
 
175
     * @param in the input byte array.
 
176
     * @param inOff the offset at which the input data starts.
 
177
     * @param len the number of bytes to be copied out of the input array.
 
178
     * @param out the space for any output that might be produced.
 
179
     * @param outOff the offset from which the output will be copied.
 
180
     * @return the number of output bytes copied to out.
 
181
     * @exception DataLengthException if there isn't enough space in out.
 
182
     * @exception IllegalStateException if the cipher isn't initialised.
 
183
     */
 
184
    public int processBytes(
 
185
        byte[]      in,
 
186
        int         inOff,
 
187
        int         len,
 
188
        byte[]      out,
 
189
        int         outOff)
 
190
        throws DataLengthException, IllegalStateException
 
191
    {
 
192
        if (len < 0)
 
193
        {
 
194
            throw new IllegalArgumentException("Can't have a negative input length!");
 
195
        }
 
196
 
 
197
        int blockSize   = getBlockSize();
 
198
        int length      = getUpdateOutputSize(len);
 
199
        
 
200
        if (length > 0)
 
201
        {
 
202
            if ((outOff + length) > out.length)
 
203
            {
 
204
                throw new DataLengthException("output buffer too short");
 
205
            }
 
206
        }
 
207
 
 
208
        int resultLen = 0;
 
209
        int gapLen = buf.length - bufOff;
 
210
 
 
211
        if (len > gapLen)
 
212
        {
 
213
            System.arraycopy(in, inOff, buf, bufOff, gapLen);
 
214
 
 
215
            resultLen += cipher.processBlock(buf, 0, out, outOff);
 
216
 
 
217
            bufOff = 0;
 
218
            len -= gapLen;
 
219
            inOff += gapLen;
 
220
 
 
221
            while (len > buf.length)
 
222
            {
 
223
                resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
 
224
 
 
225
                len -= blockSize;
 
226
                inOff += blockSize;
 
227
            }
 
228
        }
 
229
 
 
230
        System.arraycopy(in, inOff, buf, bufOff, len);
 
231
 
 
232
        bufOff += len;
 
233
 
 
234
        if (bufOff == buf.length)
 
235
        {
 
236
            resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
 
237
            bufOff = 0;
 
238
        }
 
239
 
 
240
        return resultLen;
 
241
    }
 
242
 
 
243
    /**
 
244
     * Process the last block in the buffer.
 
245
     *
 
246
     * @param out the array the block currently being held is copied into.
 
247
     * @param outOff the offset at which the copying starts.
 
248
     * @return the number of output bytes copied to out.
 
249
     * @exception DataLengthException if there is insufficient space in out for
 
250
     * the output, or the input is not block size aligned and should be.
 
251
     * @exception IllegalStateException if the underlying cipher is not
 
252
     * initialised.
 
253
     * @exception InvalidCipherTextException if padding is expected and not found.
 
254
     * @exception DataLengthException if the input is not block size
 
255
     * aligned.
 
256
     */
 
257
    public int doFinal(
 
258
        byte[]  out,
 
259
        int     outOff)
 
260
        throws DataLengthException, IllegalStateException, InvalidCipherTextException
 
261
    {
 
262
        try
 
263
        {
 
264
            int resultLen = 0;
 
265
 
 
266
            if (outOff + bufOff > out.length)
 
267
            {
 
268
                throw new DataLengthException("output buffer too short for doFinal()");
 
269
            }
 
270
 
 
271
            if (bufOff != 0)
 
272
            {
 
273
                if (!partialBlockOkay)
 
274
                {
 
275
                    throw new DataLengthException("data not block size aligned");
 
276
                }
 
277
 
 
278
                cipher.processBlock(buf, 0, buf, 0);
 
279
                resultLen = bufOff;
 
280
                bufOff = 0;
 
281
                System.arraycopy(buf, 0, out, outOff, resultLen);
 
282
            }
 
283
 
 
284
            return resultLen;
 
285
        }
 
286
        finally
 
287
        {
 
288
            reset();
 
289
        }
 
290
    }
 
291
 
 
292
    /**
 
293
     * Reset the buffer and cipher. After resetting the object is in the same
 
294
     * state as it was after the last init (if there was one).
 
295
     */
 
296
    public void reset()
 
297
    {
 
298
        //
 
299
        // clean the buffer.
 
300
        //
 
301
        for (int i = 0; i < buf.length; i++)
 
302
        {
 
303
            buf[i] = 0;
 
304
        }
 
305
 
 
306
        bufOff = 0;
 
307
 
 
308
        //
 
309
        // reset the underlying cipher.
 
310
        //
 
311
        cipher.reset();
 
312
    }
 
313
}