• 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         this.encrypting = encrypting;
65 
66         if (params instanceof ParametersWithIV)
67         {
68                 ParametersWithIV ivParam = (ParametersWithIV)params;
69                 byte[]      iv = ivParam.getIV();
70 
71                 if (iv.length != blockSize)
72                 {
73                     throw new IllegalArgumentException("initialisation vector must be the same length as block size");
74                 }
75 
76                 System.arraycopy(iv, 0, IV, 0, iv.length);
77 
78                 reset();
79 
80                 cipher.init(encrypting, ivParam.getParameters());
81         }
82         else
83         {
84                 reset();
85 
86                 cipher.init(encrypting, params);
87         }
88     }
89 
90     /**
91      * return the algorithm name and mode.
92      *
93      * @return the name of the underlying algorithm followed by "/CBC".
94      */
getAlgorithmName()95     public String getAlgorithmName()
96     {
97         return cipher.getAlgorithmName() + "/CBC";
98     }
99 
100     /**
101      * return the block size of the underlying cipher.
102      *
103      * @return the block size of the underlying cipher.
104      */
getBlockSize()105     public int getBlockSize()
106     {
107         return cipher.getBlockSize();
108     }
109 
110     /**
111      * Process one block of input from the array in and write it to
112      * the out array.
113      *
114      * @param in the array containing the input data.
115      * @param inOff offset into the in array the data starts at.
116      * @param out the array the output data will be copied into.
117      * @param outOff the offset into the out array the output will start at.
118      * @exception DataLengthException if there isn't enough data in in, or
119      * space in out.
120      * @exception IllegalStateException if the cipher isn't initialised.
121      * @return the number of bytes processed and produced.
122      */
processBlock( byte[] in, int inOff, byte[] out, int outOff)123     public int processBlock(
124         byte[]      in,
125         int         inOff,
126         byte[]      out,
127         int         outOff)
128         throws DataLengthException, IllegalStateException
129     {
130         return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
131     }
132 
133     /**
134      * reset the chaining vector back to the IV and reset the underlying
135      * cipher.
136      */
reset()137     public void reset()
138     {
139         System.arraycopy(IV, 0, cbcV, 0, IV.length);
140         Arrays.fill(cbcNextV, (byte)0);
141 
142         cipher.reset();
143     }
144 
145     /**
146      * Do the appropriate chaining step for CBC mode encryption.
147      *
148      * @param in the array containing the data to be encrypted.
149      * @param inOff offset into the in array the data starts at.
150      * @param out the array the encrypted data will be copied into.
151      * @param outOff the offset into the out array the output will start at.
152      * @exception DataLengthException if there isn't enough data in in, or
153      * space in out.
154      * @exception IllegalStateException if the cipher isn't initialised.
155      * @return the number of bytes processed and produced.
156      */
encryptBlock( byte[] in, int inOff, byte[] out, int outOff)157     private int encryptBlock(
158         byte[]      in,
159         int         inOff,
160         byte[]      out,
161         int         outOff)
162         throws DataLengthException, IllegalStateException
163     {
164         if ((inOff + blockSize) > in.length)
165         {
166             throw new DataLengthException("input buffer too short");
167         }
168 
169         /*
170          * XOR the cbcV and the input,
171          * then encrypt the cbcV
172          */
173         for (int i = 0; i < blockSize; i++)
174         {
175             cbcV[i] ^= in[inOff + i];
176         }
177 
178         int length = cipher.processBlock(cbcV, 0, out, outOff);
179 
180         /*
181          * copy ciphertext to cbcV
182          */
183         System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
184 
185         return length;
186     }
187 
188     /**
189      * Do the appropriate chaining step for CBC mode decryption.
190      *
191      * @param in the array containing the data to be decrypted.
192      * @param inOff offset into the in array the data starts at.
193      * @param out the array the decrypted data will be copied into.
194      * @param outOff the offset into the out array the output will start at.
195      * @exception DataLengthException if there isn't enough data in in, or
196      * space in out.
197      * @exception IllegalStateException if the cipher isn't initialised.
198      * @return the number of bytes processed and produced.
199      */
decryptBlock( byte[] in, int inOff, byte[] out, int outOff)200     private int decryptBlock(
201         byte[]      in,
202         int         inOff,
203         byte[]      out,
204         int         outOff)
205         throws DataLengthException, IllegalStateException
206     {
207         if ((inOff + blockSize) > in.length)
208         {
209             throw new DataLengthException("input buffer too short");
210         }
211 
212         System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
213 
214         int length = cipher.processBlock(in, inOff, out, outOff);
215 
216         /*
217          * XOR the cbcV and the output
218          */
219         for (int i = 0; i < blockSize; i++)
220         {
221             out[outOff + i] ^= cbcV[i];
222         }
223 
224         /*
225          * swap the back up buffer into next position
226          */
227         byte[]  tmp;
228 
229         tmp = cbcV;
230         cbcV = cbcNextV;
231         cbcNextV = tmp;
232 
233         return length;
234     }
235 }
236