• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import org.bouncycastle.util.Arrays;
8 
9 /**
10  * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
11  */
12 public class CBCBlockCipher
13     implements BlockCipher
14 {
15     private byte[]          IV;
16     private byte[]          cbcV;
17     private byte[]          cbcNextV;
18 
19     private int             blockSize;
20     private BlockCipher     cipher = null;
21     private boolean         encrypting;
22 
23     /**
24      * Basic constructor.
25      *
26      * @param cipher the block cipher to be used as the basis of chaining.
27      */
CBCBlockCipher( BlockCipher cipher)28     public CBCBlockCipher(
29         BlockCipher cipher)
30     {
31         this.cipher = cipher;
32         this.blockSize = cipher.getBlockSize();
33 
34         this.IV = new byte[blockSize];
35         this.cbcV = new byte[blockSize];
36         this.cbcNextV = new byte[blockSize];
37     }
38 
39     /**
40      * return the underlying block cipher that we are wrapping.
41      *
42      * @return the underlying block cipher that we are wrapping.
43      */
getUnderlyingCipher()44     public BlockCipher getUnderlyingCipher()
45     {
46         return cipher;
47     }
48 
49     /**
50      * Initialise the cipher and, possibly, the initialisation vector (IV).
51      * If an IV isn't passed as part of the parameter, the IV will be all zeros.
52      *
53      * @param encrypting 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 encrypting, CipherParameters params)59     public void init(
60         boolean             encrypting,
61         CipherParameters    params)
62         throws IllegalArgumentException
63     {
64         boolean oldEncrypting = this.encrypting;
65 
66         this.encrypting = encrypting;
67 
68         if (params instanceof ParametersWithIV)
69         {
70             ParametersWithIV ivParam = (ParametersWithIV)params;
71             byte[] iv = ivParam.getIV();
72 
73             if (iv.length != blockSize)
74             {
75                 throw new IllegalArgumentException("initialisation vector must be the same length as block size");
76             }
77 
78             System.arraycopy(iv, 0, IV, 0, iv.length);
79 
80             reset();
81 
82             // if null it's an IV changed only.
83             if (ivParam.getParameters() != null)
84             {
85                 cipher.init(encrypting, ivParam.getParameters());
86             }
87             else if (oldEncrypting != encrypting)
88             {
89                 throw new IllegalArgumentException("cannot change encrypting state without providing key.");
90             }
91         }
92         else
93         {
94             reset();
95 
96             // if it's null, key is to be reused.
97             if (params != null)
98             {
99                 cipher.init(encrypting, params);
100             }
101             else if (oldEncrypting != encrypting)
102             {
103                 throw new IllegalArgumentException("cannot change encrypting state without providing key.");
104             }
105         }
106     }
107 
108     /**
109      * return the algorithm name and mode.
110      *
111      * @return the name of the underlying algorithm followed by "/CBC".
112      */
getAlgorithmName()113     public String getAlgorithmName()
114     {
115         return cipher.getAlgorithmName() + "/CBC";
116     }
117 
118     /**
119      * return the block size of the underlying cipher.
120      *
121      * @return the block size of the underlying cipher.
122      */
getBlockSize()123     public int getBlockSize()
124     {
125         return cipher.getBlockSize();
126     }
127 
128     /**
129      * Process one block of input from the array in and write it to
130      * the out array.
131      *
132      * @param in the array containing the input data.
133      * @param inOff offset into the in array the data starts at.
134      * @param out the array the output data will be copied into.
135      * @param outOff the offset into the out array the output will start at.
136      * @exception DataLengthException if there isn't enough data in in, or
137      * space in out.
138      * @exception IllegalStateException if the cipher isn't initialised.
139      * @return the number of bytes processed and produced.
140      */
processBlock( byte[] in, int inOff, byte[] out, int outOff)141     public int processBlock(
142         byte[]      in,
143         int         inOff,
144         byte[]      out,
145         int         outOff)
146         throws DataLengthException, IllegalStateException
147     {
148         return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
149     }
150 
151     /**
152      * reset the chaining vector back to the IV and reset the underlying
153      * cipher.
154      */
reset()155     public void reset()
156     {
157         System.arraycopy(IV, 0, cbcV, 0, IV.length);
158         Arrays.fill(cbcNextV, (byte)0);
159 
160         cipher.reset();
161     }
162 
163     /**
164      * Do the appropriate chaining step for CBC mode encryption.
165      *
166      * @param in the array containing the data to be encrypted.
167      * @param inOff offset into the in array the data starts at.
168      * @param out the array the encrypted data will be copied into.
169      * @param outOff the offset into the out array the output will start at.
170      * @exception DataLengthException if there isn't enough data in in, or
171      * space in out.
172      * @exception IllegalStateException if the cipher isn't initialised.
173      * @return the number of bytes processed and produced.
174      */
encryptBlock( byte[] in, int inOff, byte[] out, int outOff)175     private int encryptBlock(
176         byte[]      in,
177         int         inOff,
178         byte[]      out,
179         int         outOff)
180         throws DataLengthException, IllegalStateException
181     {
182         if ((inOff + blockSize) > in.length)
183         {
184             throw new DataLengthException("input buffer too short");
185         }
186 
187         /*
188          * XOR the cbcV and the input,
189          * then encrypt the cbcV
190          */
191         for (int i = 0; i < blockSize; i++)
192         {
193             cbcV[i] ^= in[inOff + i];
194         }
195 
196         int length = cipher.processBlock(cbcV, 0, out, outOff);
197 
198         /*
199          * copy ciphertext to cbcV
200          */
201         System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
202 
203         return length;
204     }
205 
206     /**
207      * Do the appropriate chaining step for CBC mode decryption.
208      *
209      * @param in the array containing the data to be decrypted.
210      * @param inOff offset into the in array the data starts at.
211      * @param out the array the decrypted data will be copied into.
212      * @param outOff the offset into the out array the output will start at.
213      * @exception DataLengthException if there isn't enough data in in, or
214      * space in out.
215      * @exception IllegalStateException if the cipher isn't initialised.
216      * @return the number of bytes processed and produced.
217      */
decryptBlock( byte[] in, int inOff, byte[] out, int outOff)218     private int decryptBlock(
219         byte[]      in,
220         int         inOff,
221         byte[]      out,
222         int         outOff)
223         throws DataLengthException, IllegalStateException
224     {
225         if ((inOff + blockSize) > in.length)
226         {
227             throw new DataLengthException("input buffer too short");
228         }
229 
230         System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
231 
232         int length = cipher.processBlock(in, inOff, out, outOff);
233 
234         /*
235          * XOR the cbcV and the output
236          */
237         for (int i = 0; i < blockSize; i++)
238         {
239             out[outOff + i] ^= cbcV[i];
240         }
241 
242         /*
243          * swap the back up buffer into next position
244          */
245         byte[]  tmp;
246 
247         tmp = cbcV;
248         cbcV = cbcNextV;
249         cbcNextV = tmp;
250 
251         return length;
252     }
253 }
254