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.StreamBlockCipher; 7 import org.bouncycastle.crypto.params.ParametersWithIV; 8 import org.bouncycastle.util.Arrays; 9 10 /** 11 * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. 12 */ 13 public class CFBBlockCipher 14 extends StreamBlockCipher 15 { 16 private byte[] IV; 17 private byte[] cfbV; 18 private byte[] cfbOutV; 19 private byte[] inBuf; 20 21 private int blockSize; 22 private BlockCipher cipher = null; 23 private boolean encrypting; 24 private int byteCount; 25 26 /** 27 * Basic constructor. 28 * 29 * @param cipher the block cipher to be used as the basis of the 30 * feedback mode. 31 * @param bitBlockSize the block size in bits (note: a multiple of 8) 32 */ 33 public CFBBlockCipher( 34 BlockCipher cipher, 35 int bitBlockSize) 36 { 37 super(cipher); 38 39 this.cipher = cipher; 40 this.blockSize = bitBlockSize / 8; 41 42 this.IV = new byte[cipher.getBlockSize()]; 43 this.cfbV = new byte[cipher.getBlockSize()]; 44 this.cfbOutV = new byte[cipher.getBlockSize()]; 45 this.inBuf = new byte[blockSize]; 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 * An IV which is too short is handled in FIPS compliant fashion. 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 */ 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 < IV.length) 72 { 73 // prepend the supplied IV with zeros (per FIPS PUB 81) 74 System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length); 75 for (int i = 0; i < IV.length - iv.length; i++) 76 { 77 IV[i] = 0; 78 } 79 } 80 else 81 { 82 System.arraycopy(iv, 0, IV, 0, IV.length); 83 } 84 85 reset(); 86 87 // if null it's an IV changed only. 88 if (ivParam.getParameters() != null) 89 { 90 cipher.init(true, ivParam.getParameters()); 91 } 92 } 93 else 94 { 95 reset(); 96 97 // if it's null, key is to be reused. 98 if (params != null) 99 { 100 cipher.init(true, params); 101 } 102 } 103 } 104 105 /** 106 * return the algorithm name and mode. 107 * 108 * @return the name of the underlying algorithm followed by "/CFB" 109 * and the block size in bits. 110 */ 111 public String getAlgorithmName() 112 { 113 return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8); 114 } 115 116 protected byte calculateByte(byte in) 117 throws DataLengthException, IllegalStateException 118 { 119 return (encrypting) ? encryptByte(in) : decryptByte(in); 120 } 121 122 private byte encryptByte(byte in) 123 { 124 if (byteCount == 0) 125 { 126 cipher.processBlock(cfbV, 0, cfbOutV, 0); 127 } 128 129 byte rv = (byte)(cfbOutV[byteCount] ^ in); 130 inBuf[byteCount++] = rv; 131 132 if (byteCount == blockSize) 133 { 134 byteCount = 0; 135 136 System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize); 137 System.arraycopy(inBuf, 0, cfbV, cfbV.length - blockSize, blockSize); 138 } 139 140 return rv; 141 } 142 143 private byte decryptByte(byte in) 144 { 145 if (byteCount == 0) 146 { 147 cipher.processBlock(cfbV, 0, cfbOutV, 0); 148 } 149 150 inBuf[byteCount] = in; 151 byte rv = (byte)(cfbOutV[byteCount++] ^ in); 152 153 if (byteCount == blockSize) 154 { 155 byteCount = 0; 156 157 System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize); 158 System.arraycopy(inBuf, 0, cfbV, cfbV.length - blockSize, blockSize); 159 } 160 161 return rv; 162 } 163 164 /** 165 * return the block size we are operating at. 166 * 167 * @return the block size we are operating at (in bytes). 168 */ 169 public int getBlockSize() 170 { 171 return blockSize; 172 } 173 174 /** 175 * Process one block of input from the array in and write it to 176 * the out array. 177 * 178 * @param in the array containing the input data. 179 * @param inOff offset into the in array the data starts at. 180 * @param out the array the output data will be copied into. 181 * @param outOff the offset into the out array the output will start at. 182 * @exception DataLengthException if there isn't enough data in in, or 183 * space in out. 184 * @exception IllegalStateException if the cipher isn't initialised. 185 * @return the number of bytes processed and produced. 186 */ 187 public int processBlock( 188 byte[] in, 189 int inOff, 190 byte[] out, 191 int outOff) 192 throws DataLengthException, IllegalStateException 193 { 194 processBytes(in, inOff, blockSize, out, outOff); 195 196 return blockSize; 197 } 198 199 /** 200 * Do the appropriate processing for CFB mode encryption. 201 * 202 * @param in the array containing the data to be encrypted. 203 * @param inOff offset into the in array the data starts at. 204 * @param out the array the encrypted data will be copied into. 205 * @param outOff the offset into the out array the output will start at. 206 * @exception DataLengthException if there isn't enough data in in, or 207 * space in out. 208 * @exception IllegalStateException if the cipher isn't initialised. 209 * @return the number of bytes processed and produced. 210 */ 211 public int encryptBlock( 212 byte[] in, 213 int inOff, 214 byte[] out, 215 int outOff) 216 throws DataLengthException, IllegalStateException 217 { 218 processBytes(in, inOff, blockSize, out, outOff); 219 220 return blockSize; 221 } 222 223 /** 224 * Do the appropriate processing for CFB mode decryption. 225 * 226 * @param in the array containing the data to be decrypted. 227 * @param inOff offset into the in array the data starts at. 228 * @param out the array the encrypted data will be copied into. 229 * @param outOff the offset into the out array the output will start at. 230 * @exception DataLengthException if there isn't enough data in in, or 231 * space in out. 232 * @exception IllegalStateException if the cipher isn't initialised. 233 * @return the number of bytes processed and produced. 234 */ 235 public int decryptBlock( 236 byte[] in, 237 int inOff, 238 byte[] out, 239 int outOff) 240 throws DataLengthException, IllegalStateException 241 { 242 processBytes(in, inOff, blockSize, out, outOff); 243 244 return blockSize; 245 } 246 247 /** 248 * Return the current state of the initialisation vector. 249 * 250 * @return current IV 251 */ 252 public byte[] getCurrentIV() 253 { 254 return Arrays.clone(cfbV); 255 } 256 257 /** 258 * reset the chaining vector back to the IV and reset the underlying 259 * cipher. 260 */ 261 public void reset() 262 { 263 System.arraycopy(IV, 0, cfbV, 0, IV.length); 264 Arrays.fill(inBuf, (byte)0); 265 byteCount = 0; 266 267 cipher.reset(); 268 } 269 } 270