001: package org.bouncycastle.crypto.modes;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.DataLengthException;
006: import org.bouncycastle.crypto.params.ParametersWithIV;
007:
008: /**
009: * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
010: */
011: public class CFBBlockCipher implements BlockCipher {
012: private byte[] IV;
013: private byte[] cfbV;
014: private byte[] cfbOutV;
015:
016: private int blockSize;
017: private BlockCipher cipher = null;
018: private boolean encrypting;
019:
020: /**
021: * Basic constructor.
022: *
023: * @param cipher the block cipher to be used as the basis of the
024: * feedback mode.
025: * @param bitBlockSize the block size in bits (note: a multiple of 8)
026: */
027: public CFBBlockCipher(BlockCipher cipher, int bitBlockSize) {
028: this .cipher = cipher;
029: this .blockSize = bitBlockSize / 8;
030:
031: this .IV = new byte[cipher.getBlockSize()];
032: this .cfbV = new byte[cipher.getBlockSize()];
033: this .cfbOutV = new byte[cipher.getBlockSize()];
034: }
035:
036: /**
037: * return the underlying block cipher that we are wrapping.
038: *
039: * @return the underlying block cipher that we are wrapping.
040: */
041: public BlockCipher getUnderlyingCipher() {
042: return cipher;
043: }
044:
045: /**
046: * Initialise the cipher and, possibly, the initialisation vector (IV).
047: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
048: * An IV which is too short is handled in FIPS compliant fashion.
049: *
050: * @param encrypting if true the cipher is initialised for
051: * encryption, if false for decryption.
052: * @param params the key and other data required by the cipher.
053: * @exception IllegalArgumentException if the params argument is
054: * inappropriate.
055: */
056: public void init(boolean encrypting, CipherParameters params)
057: throws IllegalArgumentException {
058: this .encrypting = encrypting;
059:
060: if (params instanceof ParametersWithIV) {
061: ParametersWithIV ivParam = (ParametersWithIV) params;
062: byte[] iv = ivParam.getIV();
063:
064: if (iv.length < IV.length) {
065: // prepend the supplied IV with zeros (per FIPS PUB 81)
066: System.arraycopy(iv, 0, IV, IV.length - iv.length,
067: iv.length);
068: for (int i = 0; i < IV.length - iv.length; i++) {
069: IV[i] = 0;
070: }
071: } else {
072: System.arraycopy(iv, 0, IV, 0, IV.length);
073: }
074:
075: reset();
076:
077: cipher.init(true, ivParam.getParameters());
078: } else {
079: reset();
080:
081: cipher.init(true, params);
082: }
083: }
084:
085: /**
086: * return the algorithm name and mode.
087: *
088: * @return the name of the underlying algorithm followed by "/CFB"
089: * and the block size in bits.
090: */
091: public String getAlgorithmName() {
092: return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8);
093: }
094:
095: /**
096: * return the block size we are operating at.
097: *
098: * @return the block size we are operating at (in bytes).
099: */
100: public int getBlockSize() {
101: return blockSize;
102: }
103:
104: /**
105: * Process one block of input from the array in and write it to
106: * the out array.
107: *
108: * @param in the array containing the input data.
109: * @param inOff offset into the in array the data starts at.
110: * @param out the array the output data will be copied into.
111: * @param outOff the offset into the out array the output will start at.
112: * @exception DataLengthException if there isn't enough data in in, or
113: * space in out.
114: * @exception IllegalStateException if the cipher isn't initialised.
115: * @return the number of bytes processed and produced.
116: */
117: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
118: throws DataLengthException, IllegalStateException {
119: return (encrypting) ? encryptBlock(in, inOff, out, outOff)
120: : decryptBlock(in, inOff, out, outOff);
121: }
122:
123: /**
124: * Do the appropriate processing for CFB mode encryption.
125: *
126: * @param in the array containing the data to be encrypted.
127: * @param inOff offset into the in array the data starts at.
128: * @param out the array the encrypted data will be copied into.
129: * @param outOff the offset into the out array the output will start at.
130: * @exception DataLengthException if there isn't enough data in in, or
131: * space in out.
132: * @exception IllegalStateException if the cipher isn't initialised.
133: * @return the number of bytes processed and produced.
134: */
135: public int encryptBlock(byte[] in, int inOff, byte[] out, int outOff)
136: throws DataLengthException, IllegalStateException {
137: if ((inOff + blockSize) > in.length) {
138: throw new DataLengthException("input buffer too short");
139: }
140:
141: if ((outOff + blockSize) > out.length) {
142: throw new DataLengthException("output buffer too short");
143: }
144:
145: cipher.processBlock(cfbV, 0, cfbOutV, 0);
146:
147: //
148: // XOR the cfbV with the plaintext producing the cipher text
149: //
150: for (int i = 0; i < blockSize; i++) {
151: out[outOff + i] = (byte) (cfbOutV[i] ^ in[inOff + i]);
152: }
153:
154: //
155: // change over the input block.
156: //
157: System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length
158: - blockSize);
159: System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize,
160: blockSize);
161:
162: return blockSize;
163: }
164:
165: /**
166: * Do the appropriate processing for CFB mode decryption.
167: *
168: * @param in the array containing the data to be decrypted.
169: * @param inOff offset into the in array the data starts at.
170: * @param out the array the encrypted data will be copied into.
171: * @param outOff the offset into the out array the output will start at.
172: * @exception DataLengthException if there isn't enough data in in, or
173: * space in out.
174: * @exception IllegalStateException if the cipher isn't initialised.
175: * @return the number of bytes processed and produced.
176: */
177: public int decryptBlock(byte[] in, int inOff, byte[] out, int outOff)
178: throws DataLengthException, IllegalStateException {
179: if ((inOff + blockSize) > in.length) {
180: throw new DataLengthException("input buffer too short");
181: }
182:
183: if ((outOff + blockSize) > out.length) {
184: throw new DataLengthException("output buffer too short");
185: }
186:
187: cipher.processBlock(cfbV, 0, cfbOutV, 0);
188:
189: //
190: // change over the input block.
191: //
192: System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length
193: - blockSize);
194: System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize,
195: blockSize);
196:
197: //
198: // XOR the cfbV with the plaintext producing the plain text
199: //
200: for (int i = 0; i < blockSize; i++) {
201: out[outOff + i] = (byte) (cfbOutV[i] ^ in[inOff + i]);
202: }
203:
204: return blockSize;
205: }
206:
207: /**
208: * reset the chaining vector back to the IV and reset the underlying
209: * cipher.
210: */
211: public void reset() {
212: System.arraycopy(IV, 0, cfbV, 0, IV.length);
213:
214: cipher.reset();
215: }
216: }
|