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 cipher.init(true, ivParam.getParameters()); 88 } 89 else 90 { 91 reset(); 92 93 cipher.init(true, params); 94 } 95 } 96 97 /** 98 * return the algorithm name and mode. 99 * 100 * @return the name of the underlying algorithm followed by "/OFB" 101 * and the block size in bits 102 */ 103 public String getAlgorithmName() 104 { 105 return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8); 106 } 107 108 109 /** 110 * return the block size we are operating at (in bytes). 111 * 112 * @return the block size we are operating at (in bytes). 113 */ 114 public int getBlockSize() 115 { 116 return blockSize; 117 } 118 119 /** 120 * Process one block of input from the array in and write it to 121 * the out array. 122 * 123 * @param in the array containing the input data. 124 * @param inOff offset into the in array the data starts at. 125 * @param out the array the output data will be copied into. 126 * @param outOff the offset into the out array the output will start at. 127 * @exception DataLengthException if there isn't enough data in in, or 128 * space in out. 129 * @exception IllegalStateException if the cipher isn't initialised. 130 * @return the number of bytes processed and produced. 131 */ 132 public int processBlock( 133 byte[] in, 134 int inOff, 135 byte[] out, 136 int outOff) 137 throws DataLengthException, IllegalStateException 138 { 139 if ((inOff + blockSize) > in.length) 140 { 141 throw new DataLengthException("input buffer too short"); 142 } 143 144 if ((outOff + blockSize) > out.length) 145 { 146 throw new DataLengthException("output buffer too short"); 147 } 148 149 cipher.processBlock(ofbV, 0, ofbOutV, 0); 150 151 // 152 // XOR the ofbV with the plaintext producing the cipher text (and 153 // the next input block). 154 // 155 for (int i = 0; i < blockSize; i++) 156 { 157 out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]); 158 } 159 160 // 161 // change over the input block. 162 // 163 System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize); 164 System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize); 165 166 return blockSize; 167 } 168 169 /** 170 * reset the feedback vector back to the IV and reset the underlying 171 * cipher. 172 */ 173 public void reset() 174 { 175 System.arraycopy(IV, 0, ofbV, 0, IV.length); 176 177 cipher.reset(); 178 } 179} 180