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 Output-FeedBack (OFB) mode on top of a simple cipher. 10 */ 11public class OFBBlockCipher 12 implements BlockCipher 13{ 14 private byte[] IV; 15 private byte[] ofbV; 16 private byte[] ofbOutV; 17 18 private final int blockSize; 19 private final BlockCipher cipher; 20 21 /** 22 * Basic constructor. 23 * 24 * @param cipher the block cipher to be used as the basis of the 25 * feedback mode. 26 * @param blockSize the block size in bits (note: a multiple of 8) 27 */ 28 public OFBBlockCipher( 29 BlockCipher cipher, 30 int blockSize) 31 { 32 this.cipher = cipher; 33 this.blockSize = blockSize / 8; 34 35 this.IV = new byte[cipher.getBlockSize()]; 36 this.ofbV = new byte[cipher.getBlockSize()]; 37 this.ofbOutV = new byte[cipher.getBlockSize()]; 38 } 39 40 /** 41 * return the underlying block cipher that we are wrapping. 42 * 43 * @return the underlying block cipher that we are wrapping. 44 */ 45 public BlockCipher getUnderlyingCipher() 46 { 47 return cipher; 48 } 49 50 /** 51 * Initialise the cipher and, possibly, the initialisation vector (IV). 52 * If an IV isn't passed as part of the parameter, the IV will be all zeros. 53 * An IV which is too short is handled in FIPS compliant fashion. 54 * 55 * @param encrypting if true the cipher is initialised for 56 * encryption, if false for decryption. 57 * @param params the key and other data required by the cipher. 58 * @exception IllegalArgumentException if the params argument is 59 * inappropriate. 60 */ 61 public void init( 62 boolean encrypting, //ignored by this OFB mode 63 CipherParameters params) 64 throws IllegalArgumentException 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 "/OFB" 109 * and the block size in bits 110 */ 111 public String getAlgorithmName() 112 { 113 return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8); 114 } 115 116 117 /** 118 * return the block size we are operating at (in bytes). 119 * 120 * @return the block size we are operating at (in bytes). 121 */ 122 public int getBlockSize() 123 { 124 return blockSize; 125 } 126 127 /** 128 * Process one block of input from the array in and write it to 129 * the out array. 130 * 131 * @param in the array containing the input data. 132 * @param inOff offset into the in array the data starts at. 133 * @param out the array the output data will be copied into. 134 * @param outOff the offset into the out array the output will start at. 135 * @exception DataLengthException if there isn't enough data in in, or 136 * space in out. 137 * @exception IllegalStateException if the cipher isn't initialised. 138 * @return the number of bytes processed and produced. 139 */ 140 public int processBlock( 141 byte[] in, 142 int inOff, 143 byte[] out, 144 int outOff) 145 throws DataLengthException, IllegalStateException 146 { 147 if ((inOff + blockSize) > in.length) 148 { 149 throw new DataLengthException("input buffer too short"); 150 } 151 152 if ((outOff + blockSize) > out.length) 153 { 154 throw new DataLengthException("output buffer too short"); 155 } 156 157 cipher.processBlock(ofbV, 0, ofbOutV, 0); 158 159 // 160 // XOR the ofbV with the plaintext producing the cipher text (and 161 // the next input block). 162 // 163 for (int i = 0; i < blockSize; i++) 164 { 165 out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]); 166 } 167 168 // 169 // change over the input block. 170 // 171 System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize); 172 System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize); 173 174 return blockSize; 175 } 176 177 /** 178 * reset the feedback vector back to the IV and reset the underlying 179 * cipher. 180 */ 181 public void reset() 182 { 183 System.arraycopy(IV, 0, ofbV, 0, IV.length); 184 185 cipher.reset(); 186 } 187} 188