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 Output-FeedBack (OFB) mode on top of a simple cipher.
010: */
011: public class OFBBlockCipher implements BlockCipher {
012: private byte[] IV;
013: private byte[] ofbV;
014: private byte[] ofbOutV;
015:
016: private final int blockSize;
017: private final BlockCipher cipher;
018:
019: /**
020: * Basic constructor.
021: *
022: * @param cipher the block cipher to be used as the basis of the
023: * feedback mode.
024: * @param blockSize the block size in bits (note: a multiple of 8)
025: */
026: public OFBBlockCipher(BlockCipher cipher, int blockSize) {
027: this .cipher = cipher;
028: this .blockSize = blockSize / 8;
029:
030: this .IV = new byte[cipher.getBlockSize()];
031: this .ofbV = new byte[cipher.getBlockSize()];
032: this .ofbOutV = new byte[cipher.getBlockSize()];
033: }
034:
035: /**
036: * return the underlying block cipher that we are wrapping.
037: *
038: * @return the underlying block cipher that we are wrapping.
039: */
040: public BlockCipher getUnderlyingCipher() {
041: return cipher;
042: }
043:
044: /**
045: * Initialise the cipher and, possibly, the initialisation vector (IV).
046: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
047: * An IV which is too short is handled in FIPS compliant fashion.
048: *
049: * @param encrypting if true the cipher is initialised for
050: * encryption, if false for decryption.
051: * @param params the key and other data required by the cipher.
052: * @exception IllegalArgumentException if the params argument is
053: * inappropriate.
054: */
055: public void init(boolean encrypting, //ignored by this OFB mode
056: CipherParameters params) throws IllegalArgumentException {
057: if (params instanceof ParametersWithIV) {
058: ParametersWithIV ivParam = (ParametersWithIV) params;
059: byte[] iv = ivParam.getIV();
060:
061: if (iv.length < IV.length) {
062: // prepend the supplied IV with zeros (per FIPS PUB 81)
063: System.arraycopy(iv, 0, IV, IV.length - iv.length,
064: iv.length);
065: for (int i = 0; i < IV.length - iv.length; i++) {
066: IV[i] = 0;
067: }
068: } else {
069: System.arraycopy(iv, 0, IV, 0, IV.length);
070: }
071:
072: reset();
073:
074: cipher.init(true, ivParam.getParameters());
075: } else {
076: reset();
077:
078: cipher.init(true, params);
079: }
080: }
081:
082: /**
083: * return the algorithm name and mode.
084: *
085: * @return the name of the underlying algorithm followed by "/OFB"
086: * and the block size in bits
087: */
088: public String getAlgorithmName() {
089: return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8);
090: }
091:
092: /**
093: * return the block size we are operating at (in bytes).
094: *
095: * @return the block size we are operating at (in bytes).
096: */
097: public int getBlockSize() {
098: return blockSize;
099: }
100:
101: /**
102: * Process one block of input from the array in and write it to
103: * the out array.
104: *
105: * @param in the array containing the input data.
106: * @param inOff offset into the in array the data starts at.
107: * @param out the array the output data will be copied into.
108: * @param outOff the offset into the out array the output will start at.
109: * @exception DataLengthException if there isn't enough data in in, or
110: * space in out.
111: * @exception IllegalStateException if the cipher isn't initialised.
112: * @return the number of bytes processed and produced.
113: */
114: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
115: throws DataLengthException, IllegalStateException {
116: if ((inOff + blockSize) > in.length) {
117: throw new DataLengthException("input buffer too short");
118: }
119:
120: if ((outOff + blockSize) > out.length) {
121: throw new DataLengthException("output buffer too short");
122: }
123:
124: cipher.processBlock(ofbV, 0, ofbOutV, 0);
125:
126: //
127: // XOR the ofbV with the plaintext producing the cipher text (and
128: // the next input block).
129: //
130: for (int i = 0; i < blockSize; i++) {
131: out[outOff + i] = (byte) (ofbOutV[i] ^ in[inOff + i]);
132: }
133:
134: //
135: // change over the input block.
136: //
137: System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length
138: - blockSize);
139: System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize,
140: blockSize);
141:
142: return blockSize;
143: }
144:
145: /**
146: * reset the feedback vector back to the IV and reset the underlying
147: * cipher.
148: */
149: public void reset() {
150: System.arraycopy(IV, 0, ofbV, 0, IV.length);
151:
152: cipher.reset();
153: }
154: }
|