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