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 OpenPGP's rather strange version of Cipher-FeedBack (CFB) mode on top of a simple cipher. For further info see <a href="http://www.ietf.org/rfc/rfc2440.html">RFC 2440</a>.
010: */
011: public class PGPCFBBlockCipher implements BlockCipher {
012: private byte[] IV;
013: private byte[] FR;
014: private byte[] FRE;
015: private byte[] tmp;
016:
017: private BlockCipher cipher;
018:
019: private int count;
020: private int blockSize;
021: private boolean forEncryption;
022:
023: private boolean inlineIv; // if false we don't need to prepend an IV
024:
025: /**
026: * Basic constructor.
027: *
028: * @param cipher the block cipher to be used as the basis of the
029: * feedback mode.
030: * @param inlineIv if true this is for PGP CFB with a prepended iv.
031: */
032: public PGPCFBBlockCipher(BlockCipher cipher, boolean inlineIv) {
033: this .cipher = cipher;
034: this .inlineIv = inlineIv;
035:
036: this .blockSize = cipher.getBlockSize();
037: this .IV = new byte[blockSize];
038: this .FR = new byte[blockSize];
039: this .FRE = new byte[blockSize];
040: this .tmp = new byte[blockSize];
041: }
042:
043: /**
044: * return the underlying block cipher that we are wrapping.
045: *
046: * @return the underlying block cipher that we are wrapping.
047: */
048: public BlockCipher getUnderlyingCipher() {
049: return cipher;
050: }
051:
052: /**
053: * return the algorithm name and mode.
054: *
055: * @return the name of the underlying algorithm followed by "/PGPCFB"
056: * and the block size in bits.
057: */
058: public String getAlgorithmName() {
059: if (inlineIv) {
060: return cipher.getAlgorithmName() + "/PGPCFBwithIV";
061: } else {
062: return cipher.getAlgorithmName() + "/PGPCFB";
063: }
064: }
065:
066: /**
067: * return the block size we are operating at.
068: *
069: * @return the block size we are operating at (in bytes).
070: */
071: public int getBlockSize() {
072: return cipher.getBlockSize();
073: }
074:
075: /**
076: * Process one block of input from the array in and write it to
077: * the out array.
078: *
079: * @param in the array containing the input data.
080: * @param inOff offset into the in array the data starts at.
081: * @param out the array the output data will be copied into.
082: * @param outOff the offset into the out array the output will start at.
083: * @exception DataLengthException if there isn't enough data in in, or
084: * space in out.
085: * @exception IllegalStateException if the cipher isn't initialised.
086: * @return the number of bytes processed and produced.
087: */
088: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
089: throws DataLengthException, IllegalStateException {
090: if (inlineIv) {
091: return (forEncryption) ? encryptBlockWithIV(in, inOff, out,
092: outOff)
093: : decryptBlockWithIV(in, inOff, out, outOff);
094: } else {
095: return (forEncryption) ? encryptBlock(in, inOff, out,
096: outOff) : decryptBlock(in, inOff, out, outOff);
097: }
098: }
099:
100: /**
101: * reset the chaining vector back to the IV and reset the underlying
102: * cipher.
103: */
104: public void reset() {
105: count = 0;
106:
107: for (int i = 0; i != FR.length; i++) {
108: if (inlineIv) {
109: FR[i] = 0;
110: } else {
111: FR[i] = IV[i]; // if simple mode, key is IV (even if this is zero)
112: }
113: }
114:
115: cipher.reset();
116: }
117:
118: /**
119: * Initialise the cipher and, possibly, the initialisation vector (IV).
120: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
121: * An IV which is too short is handled in FIPS compliant fashion.
122: *
123: * @param forEncryption if true the cipher is initialised for
124: * encryption, if false for decryption.
125: * @param params the key and other data required by the cipher.
126: * @exception IllegalArgumentException if the params argument is
127: * inappropriate.
128: */
129: public void init(boolean forEncryption, CipherParameters params)
130: throws IllegalArgumentException {
131: this .forEncryption = forEncryption;
132:
133: if (params instanceof ParametersWithIV) {
134: ParametersWithIV ivParam = (ParametersWithIV) params;
135: byte[] iv = ivParam.getIV();
136:
137: if (iv.length < IV.length) {
138: // prepend the supplied IV with zeros (per FIPS PUB 81)
139: System.arraycopy(iv, 0, IV, IV.length - iv.length,
140: iv.length);
141: for (int i = 0; i < IV.length - iv.length; i++) {
142: IV[i] = 0;
143: }
144: } else {
145: System.arraycopy(iv, 0, IV, 0, IV.length);
146: }
147:
148: reset();
149:
150: cipher.init(true, ivParam.getParameters());
151: } else {
152: reset();
153:
154: cipher.init(true, params);
155: }
156: }
157:
158: /**
159: * Encrypt one byte of data according to CFB mode.
160: * @param data the byte to encrypt
161: * @param where am i in the current block, determines when to resync the block
162: * @returns the encrypted byte
163: */
164: private byte encryptByte(byte data, int blockOff) {
165: return (byte) (FRE[blockOff] ^ data);
166: }
167:
168: /**
169: * Do the appropriate processing for CFB IV mode encryption.
170: *
171: * @param in the array containing the data to be encrypted.
172: * @param inOff offset into the in array the data starts at.
173: * @param out the array the encrypted data will be copied into.
174: * @param outOff the offset into the out array the output will start at.
175: * @exception DataLengthException if there isn't enough data in in, or
176: * space in out.
177: * @exception IllegalStateException if the cipher isn't initialised.
178: * @return the number of bytes processed and produced.
179: */
180: private int encryptBlockWithIV(byte[] in, int inOff, byte[] out,
181: int outOff) throws DataLengthException,
182: IllegalStateException {
183: if ((inOff + blockSize) > in.length) {
184: throw new DataLengthException("input buffer too short");
185: }
186:
187: if ((outOff + blockSize) > out.length) {
188: throw new DataLengthException("output buffer too short");
189: }
190:
191: if (count == 0) {
192: cipher.processBlock(FR, 0, FRE, 0);
193:
194: for (int n = 0; n < blockSize; n++) {
195: out[outOff + n] = encryptByte(IV[n], n);
196: }
197:
198: System.arraycopy(out, outOff, FR, 0, blockSize);
199:
200: cipher.processBlock(FR, 0, FRE, 0);
201:
202: out[outOff + blockSize] = encryptByte(IV[blockSize - 2], 0);
203: out[outOff + blockSize + 1] = encryptByte(
204: IV[blockSize - 1], 1);
205:
206: System.arraycopy(out, outOff + 2, FR, 0, blockSize);
207:
208: cipher.processBlock(FR, 0, FRE, 0);
209:
210: for (int n = 0; n < blockSize; n++) {
211: out[outOff + blockSize + 2 + n] = encryptByte(in[inOff
212: + n], n);
213: }
214:
215: System.arraycopy(out, outOff + blockSize + 2, FR, 0,
216: blockSize);
217:
218: count += 2 * blockSize + 2;
219:
220: return 2 * blockSize + 2;
221: } else if (count >= blockSize + 2) {
222: cipher.processBlock(FR, 0, FRE, 0);
223:
224: for (int n = 0; n < blockSize; n++) {
225: out[outOff + n] = encryptByte(in[inOff + n], n);
226: }
227:
228: System.arraycopy(out, outOff, FR, 0, blockSize);
229: }
230:
231: return blockSize;
232: }
233:
234: /**
235: * Do the appropriate processing for CFB IV mode decryption.
236: *
237: * @param in the array containing the data to be decrypted.
238: * @param inOff offset into the in array the data starts at.
239: * @param out the array the encrypted data will be copied into.
240: * @param outOff the offset into the out array the output will start at.
241: * @exception DataLengthException if there isn't enough data in in, or
242: * space in out.
243: * @exception IllegalStateException if the cipher isn't initialised.
244: * @return the number of bytes processed and produced.
245: */
246: private int decryptBlockWithIV(byte[] in, int inOff, byte[] out,
247: int outOff) throws DataLengthException,
248: IllegalStateException {
249: if ((inOff + blockSize) > in.length) {
250: throw new DataLengthException("input buffer too short");
251: }
252:
253: if ((outOff + blockSize) > out.length) {
254: throw new DataLengthException("output buffer too short");
255: }
256:
257: if (count == 0) {
258: for (int n = 0; n < blockSize; n++) {
259: FR[n] = in[inOff + n];
260: }
261:
262: cipher.processBlock(FR, 0, FRE, 0);
263:
264: count += blockSize;
265:
266: return 0;
267: } else if (count == blockSize) {
268: // copy in buffer so that this mode works if in and out are the same
269: System.arraycopy(in, inOff, tmp, 0, blockSize);
270:
271: System.arraycopy(FR, 2, FR, 0, blockSize - 2);
272:
273: FR[blockSize - 2] = tmp[0];
274: FR[blockSize - 1] = tmp[1];
275:
276: cipher.processBlock(FR, 0, FRE, 0);
277:
278: for (int n = 0; n < blockSize - 2; n++) {
279: out[outOff + n] = encryptByte(tmp[n + 2], n);
280: }
281:
282: System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
283:
284: count += 2;
285:
286: return blockSize - 2;
287: } else if (count >= blockSize + 2) {
288: // copy in buffer so that this mode works if in and out are the same
289: System.arraycopy(in, inOff, tmp, 0, blockSize);
290:
291: out[outOff + 0] = encryptByte(tmp[0], blockSize - 2);
292: out[outOff + 1] = encryptByte(tmp[1], blockSize - 1);
293:
294: System.arraycopy(tmp, 0, FR, blockSize - 2, 2);
295:
296: cipher.processBlock(FR, 0, FRE, 0);
297:
298: for (int n = 0; n < blockSize - 2; n++) {
299: out[outOff + n + 2] = encryptByte(tmp[n + 2], n);
300: }
301:
302: System.arraycopy(tmp, 2, FR, 0, blockSize - 2);
303:
304: }
305:
306: return blockSize;
307: }
308:
309: /**
310: * Do the appropriate processing for CFB mode encryption.
311: *
312: * @param in the array containing the data to be encrypted.
313: * @param inOff offset into the in array the data starts at.
314: * @param out the array the encrypted data will be copied into.
315: * @param outOff the offset into the out array the output will start at.
316: * @exception DataLengthException if there isn't enough data in in, or
317: * space in out.
318: * @exception IllegalStateException if the cipher isn't initialised.
319: * @return the number of bytes processed and produced.
320: */
321: private int encryptBlock(byte[] in, int inOff, byte[] out,
322: int outOff) throws DataLengthException,
323: IllegalStateException {
324: if ((inOff + blockSize) > in.length) {
325: throw new DataLengthException("input buffer too short");
326: }
327:
328: if ((outOff + blockSize) > out.length) {
329: throw new DataLengthException("output buffer too short");
330: }
331:
332: cipher.processBlock(FR, 0, FRE, 0);
333: for (int n = 0; n < blockSize; n++) {
334: out[outOff + n] = encryptByte(in[inOff + n], n);
335: }
336:
337: for (int n = 0; n < blockSize; n++) {
338: FR[n] = out[outOff + n];
339: }
340:
341: return blockSize;
342:
343: }
344:
345: /**
346: * Do the appropriate processing for CFB mode decryption.
347: *
348: * @param in the array containing the data to be decrypted.
349: * @param inOff offset into the in array the data starts at.
350: * @param out the array the encrypted data will be copied into.
351: * @param outOff the offset into the out array the output will start at.
352: * @exception DataLengthException if there isn't enough data in in, or
353: * space in out.
354: * @exception IllegalStateException if the cipher isn't initialised.
355: * @return the number of bytes processed and produced.
356: */
357: private int decryptBlock(byte[] in, int inOff, byte[] out,
358: int outOff) throws DataLengthException,
359: IllegalStateException {
360: if ((inOff + blockSize) > in.length) {
361: throw new DataLengthException("input buffer too short");
362: }
363:
364: if ((outOff + blockSize) > out.length) {
365: throw new DataLengthException("output buffer too short");
366: }
367:
368: cipher.processBlock(FR, 0, FRE, 0);
369: for (int n = 0; n < blockSize; n++) {
370: out[outOff + n] = encryptByte(in[inOff + n], n);
371: }
372:
373: for (int n = 0; n < blockSize; n++) {
374: FR[n] = in[inOff + n];
375: }
376:
377: return blockSize;
378:
379: }
380: }
|