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