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 Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
010: */
011: public class CBCBlockCipher implements BlockCipher {
012: private byte[] IV;
013: private byte[] cbcV;
014: private byte[] cbcNextV;
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 chaining.
024: */
025: public CBCBlockCipher(BlockCipher cipher) {
026: this .cipher = cipher;
027: this .blockSize = cipher.getBlockSize();
028:
029: this .IV = new byte[blockSize];
030: this .cbcV = new byte[blockSize];
031: this .cbcNextV = new byte[blockSize];
032: }
033:
034: /**
035: * return the underlying block cipher that we are wrapping.
036: *
037: * @return the underlying block cipher that we are wrapping.
038: */
039: public BlockCipher getUnderlyingCipher() {
040: return cipher;
041: }
042:
043: /**
044: * Initialise the cipher and, possibly, the initialisation vector (IV).
045: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
046: *
047: * @param encrypting if true the cipher is initialised for
048: * encryption, if false for decryption.
049: * @param params the key and other data required by the cipher.
050: * @exception IllegalArgumentException if the params argument is
051: * inappropriate.
052: */
053: public void init(boolean encrypting, CipherParameters params)
054: throws IllegalArgumentException {
055: this .encrypting = encrypting;
056:
057: if (params instanceof ParametersWithIV) {
058: ParametersWithIV ivParam = (ParametersWithIV) params;
059: byte[] iv = ivParam.getIV();
060:
061: if (iv.length != blockSize) {
062: throw new IllegalArgumentException(
063: "initialisation vector must be the same length as block size");
064: }
065:
066: System.arraycopy(iv, 0, IV, 0, iv.length);
067:
068: reset();
069:
070: cipher.init(encrypting, ivParam.getParameters());
071: } else {
072: reset();
073:
074: cipher.init(encrypting, params);
075: }
076: }
077:
078: /**
079: * return the algorithm name and mode.
080: *
081: * @return the name of the underlying algorithm followed by "/CBC".
082: */
083: public String getAlgorithmName() {
084: return cipher.getAlgorithmName() + "/CBC";
085: }
086:
087: /**
088: * return the block size of the underlying cipher.
089: *
090: * @return the block size of the underlying cipher.
091: */
092: public int getBlockSize() {
093: return cipher.getBlockSize();
094: }
095:
096: /**
097: * Process one block of input from the array in and write it to
098: * the out array.
099: *
100: * @param in the array containing the input data.
101: * @param inOff offset into the in array the data starts at.
102: * @param out the array the output data will be copied into.
103: * @param outOff the offset into the out array the output will start at.
104: * @exception DataLengthException if there isn't enough data in in, or
105: * space in out.
106: * @exception IllegalStateException if the cipher isn't initialised.
107: * @return the number of bytes processed and produced.
108: */
109: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
110: throws DataLengthException, IllegalStateException {
111: return (encrypting) ? encryptBlock(in, inOff, out, outOff)
112: : decryptBlock(in, inOff, out, outOff);
113: }
114:
115: /**
116: * reset the chaining vector back to the IV and reset the underlying
117: * cipher.
118: */
119: public void reset() {
120: System.arraycopy(IV, 0, cbcV, 0, IV.length);
121:
122: cipher.reset();
123: }
124:
125: /**
126: * Do the appropriate chaining step for CBC mode encryption.
127: *
128: * @param in the array containing the data to be encrypted.
129: * @param inOff offset into the in array the data starts at.
130: * @param out the array the encrypted data will be copied into.
131: * @param outOff the offset into the out array the output will start at.
132: * @exception DataLengthException if there isn't enough data in in, or
133: * space in out.
134: * @exception IllegalStateException if the cipher isn't initialised.
135: * @return the number of bytes processed and produced.
136: */
137: private int encryptBlock(byte[] in, int inOff, byte[] out,
138: int outOff) throws DataLengthException,
139: IllegalStateException {
140: if ((inOff + blockSize) > in.length) {
141: throw new DataLengthException("input buffer too short");
142: }
143:
144: /*
145: * XOR the cbcV and the input,
146: * then encrypt the cbcV
147: */
148: for (int i = 0; i < blockSize; i++) {
149: cbcV[i] ^= in[inOff + i];
150: }
151:
152: int length = cipher.processBlock(cbcV, 0, out, outOff);
153:
154: /*
155: * copy ciphertext to cbcV
156: */
157: System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
158:
159: return length;
160: }
161:
162: /**
163: * Do the appropriate chaining step for CBC mode decryption.
164: *
165: * @param in the array containing the data to be decrypted.
166: * @param inOff offset into the in array the data starts at.
167: * @param out the array the decrypted data will be copied into.
168: * @param outOff the offset into the out array the output will start at.
169: * @exception DataLengthException if there isn't enough data in in, or
170: * space in out.
171: * @exception IllegalStateException if the cipher isn't initialised.
172: * @return the number of bytes processed and produced.
173: */
174: private int decryptBlock(byte[] in, int inOff, byte[] out,
175: int outOff) throws DataLengthException,
176: IllegalStateException {
177: if ((inOff + blockSize) > in.length) {
178: throw new DataLengthException("input buffer too short");
179: }
180:
181: System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
182:
183: int length = cipher.processBlock(in, inOff, out, outOff);
184:
185: /*
186: * XOR the cbcV and the output
187: */
188: for (int i = 0; i < blockSize; i++) {
189: out[outOff + i] ^= cbcV[i];
190: }
191:
192: /*
193: * swap the back up buffer into next position
194: */
195: byte[] tmp;
196:
197: tmp = cbcV;
198: cbcV = cbcNextV;
199: cbcNextV = tmp;
200:
201: return length;
202: }
203: }
|