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