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 a Cipher-FeedBack (CFB) mode on top of a simple cipher. 10 */ 11public class CFBBlockCipher 12 implements BlockCipher 13{ 14 private byte[] IV; 15 private byte[] cfbV; 16 private byte[] cfbOutV; 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 the 26 * feedback mode. 27 * @param bitBlockSize the block size in bits (note: a multiple of 8) 28 */ 29 public CFBBlockCipher( 30 BlockCipher cipher, 31 int bitBlockSize) 32 { 33 this.cipher = cipher; 34 this.blockSize = bitBlockSize / 8; 35 36 this.IV = new byte[cipher.getBlockSize()]; 37 this.cfbV = new byte[cipher.getBlockSize()]; 38 this.cfbOutV = new byte[cipher.getBlockSize()]; 39 } 40 41 /** 42 * return the underlying block cipher that we are wrapping. 43 * 44 * @return the underlying block cipher that we are wrapping. 45 */ 46 public BlockCipher getUnderlyingCipher() 47 { 48 return cipher; 49 } 50 51 /** 52 * Initialise the cipher and, possibly, the initialisation vector (IV). 53 * If an IV isn't passed as part of the parameter, the IV will be all zeros. 54 * An IV which is too short is handled in FIPS compliant fashion. 55 * 56 * @param encrypting if true the cipher is initialised for 57 * encryption, if false for decryption. 58 * @param params the key and other data required by the cipher. 59 * @exception IllegalArgumentException if the params argument is 60 * inappropriate. 61 */ 62 public void init( 63 boolean encrypting, 64 CipherParameters params) 65 throws IllegalArgumentException 66 { 67 this.encrypting = encrypting; 68 69 if (params instanceof ParametersWithIV) 70 { 71 ParametersWithIV ivParam = (ParametersWithIV)params; 72 byte[] iv = ivParam.getIV(); 73 74 if (iv.length < IV.length) 75 { 76 // prepend the supplied IV with zeros (per FIPS PUB 81) 77 System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length); 78 for (int i = 0; i < IV.length - iv.length; i++) 79 { 80 IV[i] = 0; 81 } 82 } 83 else 84 { 85 System.arraycopy(iv, 0, IV, 0, IV.length); 86 } 87 88 reset(); 89 90 cipher.init(true, ivParam.getParameters()); 91 } 92 else 93 { 94 reset(); 95 96 cipher.init(true, params); 97 } 98 } 99 100 /** 101 * return the algorithm name and mode. 102 * 103 * @return the name of the underlying algorithm followed by "/CFB" 104 * and the block size in bits. 105 */ 106 public String getAlgorithmName() 107 { 108 return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8); 109 } 110 111 /** 112 * return the block size we are operating at. 113 * 114 * @return the block size we are operating at (in bytes). 115 */ 116 public int getBlockSize() 117 { 118 return blockSize; 119 } 120 121 /** 122 * Process one block of input from the array in and write it to 123 * the out array. 124 * 125 * @param in the array containing the input data. 126 * @param inOff offset into the in array the data starts at. 127 * @param out the array the output data will be copied into. 128 * @param outOff the offset into the out array the output will start at. 129 * @exception DataLengthException if there isn't enough data in in, or 130 * space in out. 131 * @exception IllegalStateException if the cipher isn't initialised. 132 * @return the number of bytes processed and produced. 133 */ 134 public int processBlock( 135 byte[] in, 136 int inOff, 137 byte[] out, 138 int outOff) 139 throws DataLengthException, IllegalStateException 140 { 141 return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff); 142 } 143 144 /** 145 * Do the appropriate processing for CFB mode encryption. 146 * 147 * @param in the array containing the data to be encrypted. 148 * @param inOff offset into the in array the data starts at. 149 * @param out the array the encrypted data will be copied into. 150 * @param outOff the offset into the out array the output will start at. 151 * @exception DataLengthException if there isn't enough data in in, or 152 * space in out. 153 * @exception IllegalStateException if the cipher isn't initialised. 154 * @return the number of bytes processed and produced. 155 */ 156 public int encryptBlock( 157 byte[] in, 158 int inOff, 159 byte[] out, 160 int outOff) 161 throws DataLengthException, IllegalStateException 162 { 163 if ((inOff + blockSize) > in.length) 164 { 165 throw new DataLengthException("input buffer too short"); 166 } 167 168 if ((outOff + blockSize) > out.length) 169 { 170 throw new DataLengthException("output buffer too short"); 171 } 172 173 cipher.processBlock(cfbV, 0, cfbOutV, 0); 174 175 // 176 // XOR the cfbV with the plaintext producing the cipher text 177 // 178 for (int i = 0; i < blockSize; i++) 179 { 180 out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]); 181 } 182 183 // 184 // change over the input block. 185 // 186 System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize); 187 System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize); 188 189 return blockSize; 190 } 191 192 /** 193 * Do the appropriate processing for CFB mode decryption. 194 * 195 * @param in the array containing the data to be decrypted. 196 * @param inOff offset into the in array the data starts at. 197 * @param out the array the encrypted data will be copied into. 198 * @param outOff the offset into the out array the output will start at. 199 * @exception DataLengthException if there isn't enough data in in, or 200 * space in out. 201 * @exception IllegalStateException if the cipher isn't initialised. 202 * @return the number of bytes processed and produced. 203 */ 204 public int decryptBlock( 205 byte[] in, 206 int inOff, 207 byte[] out, 208 int outOff) 209 throws DataLengthException, IllegalStateException 210 { 211 if ((inOff + blockSize) > in.length) 212 { 213 throw new DataLengthException("input buffer too short"); 214 } 215 216 if ((outOff + blockSize) > out.length) 217 { 218 throw new DataLengthException("output buffer too short"); 219 } 220 221 cipher.processBlock(cfbV, 0, cfbOutV, 0); 222 223 // 224 // change over the input block. 225 // 226 System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize); 227 System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize, blockSize); 228 229 // 230 // XOR the cfbV with the plaintext producing the plain text 231 // 232 for (int i = 0; i < blockSize; i++) 233 { 234 out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]); 235 } 236 237 return blockSize; 238 } 239 240 /** 241 * reset the chaining vector back to the IV and reset the underlying 242 * cipher. 243 */ 244 public void reset() 245 { 246 System.arraycopy(IV, 0, cfbV, 0, IV.length); 247 248 cipher.reset(); 249 } 250} 251