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 9 /** 10 * implements a Output-FeedBack (OFB) mode on top of a simple cipher. 11 */ 12 public class OFBBlockCipher 13 extends StreamBlockCipher 14 { 15 private int byteCount; 16 private byte[] IV; 17 private byte[] ofbV; 18 private byte[] ofbOutV; 19 20 private final int blockSize; 21 private final BlockCipher cipher; 22 23 /** 24 * Basic constructor. 25 * 26 * @param cipher the block cipher to be used as the basis of the 27 * feedback mode. 28 * @param blockSize the block size in bits (note: a multiple of 8) 29 */ OFBBlockCipher( BlockCipher cipher, int blockSize)30 public OFBBlockCipher( 31 BlockCipher cipher, 32 int blockSize) 33 { 34 super(cipher); 35 36 this.cipher = cipher; 37 this.blockSize = blockSize / 8; 38 39 this.IV = new byte[cipher.getBlockSize()]; 40 this.ofbV = new byte[cipher.getBlockSize()]; 41 this.ofbOutV = new byte[cipher.getBlockSize()]; 42 } 43 44 /** 45 * Initialise the cipher and, possibly, the initialisation vector (IV). 46 * If an IV isn't passed as part of the parameter, the IV will be all zeros. 47 * An IV which is too short is handled in FIPS compliant fashion. 48 * 49 * @param encrypting if true the cipher is initialised for 50 * encryption, if false for decryption. 51 * @param params the key and other data required by the cipher. 52 * @exception IllegalArgumentException if the params argument is 53 * inappropriate. 54 */ init( boolean encrypting, CipherParameters params)55 public void init( 56 boolean encrypting, //ignored by this OFB mode 57 CipherParameters params) 58 throws IllegalArgumentException 59 { 60 if (params instanceof ParametersWithIV) 61 { 62 ParametersWithIV ivParam = (ParametersWithIV)params; 63 byte[] iv = ivParam.getIV(); 64 65 if (iv.length < IV.length) 66 { 67 // prepend the supplied IV with zeros (per FIPS PUB 81) 68 System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length); 69 for (int i = 0; i < IV.length - iv.length; i++) 70 { 71 IV[i] = 0; 72 } 73 } 74 else 75 { 76 System.arraycopy(iv, 0, IV, 0, IV.length); 77 } 78 79 reset(); 80 81 // if null it's an IV changed only. 82 if (ivParam.getParameters() != null) 83 { 84 cipher.init(true, ivParam.getParameters()); 85 } 86 } 87 else 88 { 89 reset(); 90 91 // if it's null, key is to be reused. 92 if (params != null) 93 { 94 cipher.init(true, params); 95 } 96 } 97 } 98 99 /** 100 * return the algorithm name and mode. 101 * 102 * @return the name of the underlying algorithm followed by "/OFB" 103 * and the block size in bits 104 */ getAlgorithmName()105 public String getAlgorithmName() 106 { 107 return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8); 108 } 109 110 111 /** 112 * return the block size we are operating at (in bytes). 113 * 114 * @return the block size we are operating at (in bytes). 115 */ getBlockSize()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 */ processBlock( byte[] in, int inOff, byte[] out, int outOff)134 public int processBlock( 135 byte[] in, 136 int inOff, 137 byte[] out, 138 int outOff) 139 throws DataLengthException, IllegalStateException 140 { 141 processBytes(in, inOff, blockSize, out, outOff); 142 143 return blockSize; 144 } 145 146 /** 147 * reset the feedback vector back to the IV and reset the underlying 148 * cipher. 149 */ reset()150 public void reset() 151 { 152 System.arraycopy(IV, 0, ofbV, 0, IV.length); 153 byteCount = 0; 154 155 cipher.reset(); 156 } 157 calculateByte(byte in)158 protected byte calculateByte(byte in) 159 throws DataLengthException, IllegalStateException 160 { 161 if (byteCount == 0) 162 { 163 cipher.processBlock(ofbV, 0, ofbOutV, 0); 164 } 165 166 byte rv = (byte)(ofbOutV[byteCount++] ^ in); 167 168 if (byteCount == blockSize) 169 { 170 byteCount = 0; 171 172 System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize); 173 System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize); 174 } 175 176 return rv; 177 } 178 } 179