001: package org.bouncycastle.crypto.modes;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.BufferedBlockCipher;
005: import org.bouncycastle.crypto.DataLengthException;
006: import org.bouncycastle.crypto.InvalidCipherTextException;
007:
008: /**
009: * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
010: * be used to produce cipher text which is the same length as the plain text.
011: */
012: public class CTSBlockCipher extends BufferedBlockCipher {
013: private int blockSize;
014:
015: /**
016: * Create a buffered block cipher that uses Cipher Text Stealing
017: *
018: * @param cipher the underlying block cipher this buffering object wraps.
019: */
020: public CTSBlockCipher(BlockCipher cipher) {
021: if ((cipher instanceof OFBBlockCipher)
022: || (cipher instanceof CFBBlockCipher)) {
023: throw new IllegalArgumentException(
024: "CTSBlockCipher can only accept ECB, or CBC ciphers");
025: }
026:
027: this .cipher = cipher;
028:
029: blockSize = cipher.getBlockSize();
030:
031: buf = new byte[blockSize * 2];
032: bufOff = 0;
033: }
034:
035: /**
036: * return the size of the output buffer required for an update
037: * an input of len bytes.
038: *
039: * @param len the length of the input.
040: * @return the space required to accommodate a call to update
041: * with len bytes of input.
042: */
043: public int getUpdateOutputSize(int len) {
044: int total = len + bufOff;
045: int leftOver = total % buf.length;
046:
047: if (leftOver == 0) {
048: return total - buf.length;
049: }
050:
051: return total - leftOver;
052: }
053:
054: /**
055: * return the size of the output buffer required for an update plus a
056: * doFinal with an input of len bytes.
057: *
058: * @param len the length of the input.
059: * @return the space required to accommodate a call to update and doFinal
060: * with len bytes of input.
061: */
062: public int getOutputSize(int len) {
063: return len + bufOff;
064: }
065:
066: /**
067: * process a single byte, producing an output block if neccessary.
068: *
069: * @param in the input byte.
070: * @param out the space for any output that might be produced.
071: * @param outOff the offset from which the output will be copied.
072: * @return the number of output bytes copied to out.
073: * @exception DataLengthException if there isn't enough space in out.
074: * @exception IllegalStateException if the cipher isn't initialised.
075: */
076: public int processByte(byte in, byte[] out, int outOff)
077: throws DataLengthException, IllegalStateException {
078: int resultLen = 0;
079:
080: if (bufOff == buf.length) {
081: resultLen = cipher.processBlock(buf, 0, out, outOff);
082: System.arraycopy(buf, blockSize, buf, 0, blockSize);
083:
084: bufOff = blockSize;
085: }
086:
087: buf[bufOff++] = in;
088:
089: return resultLen;
090: }
091:
092: /**
093: * process an array of bytes, producing output if necessary.
094: *
095: * @param in the input byte array.
096: * @param inOff the offset at which the input data starts.
097: * @param len the number of bytes to be copied out of the input array.
098: * @param out the space for any output that might be produced.
099: * @param outOff the offset from which the output will be copied.
100: * @return the number of output bytes copied to out.
101: * @exception DataLengthException if there isn't enough space in out.
102: * @exception IllegalStateException if the cipher isn't initialised.
103: */
104: public int processBytes(byte[] in, int inOff, int len, byte[] out,
105: int outOff) throws DataLengthException,
106: IllegalStateException {
107: if (len < 0) {
108: throw new IllegalArgumentException(
109: "Can't have a negative input length!");
110: }
111:
112: int blockSize = getBlockSize();
113: int length = getUpdateOutputSize(len);
114:
115: if (length > 0) {
116: if ((outOff + length) > out.length) {
117: throw new DataLengthException("output buffer too short");
118: }
119: }
120:
121: int resultLen = 0;
122: int gapLen = buf.length - bufOff;
123:
124: if (len > gapLen) {
125: System.arraycopy(in, inOff, buf, bufOff, gapLen);
126:
127: resultLen += cipher.processBlock(buf, 0, out, outOff);
128: System.arraycopy(buf, blockSize, buf, 0, blockSize);
129:
130: bufOff = blockSize;
131:
132: len -= gapLen;
133: inOff += gapLen;
134:
135: while (len > blockSize) {
136: System.arraycopy(in, inOff, buf, bufOff, blockSize);
137: resultLen += cipher.processBlock(buf, 0, out, outOff
138: + resultLen);
139: System.arraycopy(buf, blockSize, buf, 0, blockSize);
140:
141: len -= blockSize;
142: inOff += blockSize;
143: }
144: }
145:
146: System.arraycopy(in, inOff, buf, bufOff, len);
147:
148: bufOff += len;
149:
150: return resultLen;
151: }
152:
153: /**
154: * Process the last block in the buffer.
155: *
156: * @param out the array the block currently being held is copied into.
157: * @param outOff the offset at which the copying starts.
158: * @return the number of output bytes copied to out.
159: * @exception DataLengthException if there is insufficient space in out for
160: * the output.
161: * @exception IllegalStateException if the underlying cipher is not
162: * initialised.
163: * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
164: * case the exception will never get thrown).
165: */
166: public int doFinal(byte[] out, int outOff)
167: throws DataLengthException, IllegalStateException,
168: InvalidCipherTextException {
169: if (bufOff + outOff > out.length) {
170: throw new DataLengthException(
171: "output buffer to small in doFinal");
172: }
173:
174: int blockSize = cipher.getBlockSize();
175: int len = bufOff - blockSize;
176: byte[] block = new byte[blockSize];
177:
178: if (forEncryption) {
179: cipher.processBlock(buf, 0, block, 0);
180:
181: if (bufOff < blockSize) {
182: throw new DataLengthException(
183: "need at least one block of input for CTS");
184: }
185:
186: for (int i = bufOff; i != buf.length; i++) {
187: buf[i] = block[i - blockSize];
188: }
189:
190: for (int i = blockSize; i != bufOff; i++) {
191: buf[i] ^= block[i - blockSize];
192: }
193:
194: if (cipher instanceof CBCBlockCipher) {
195: BlockCipher c = ((CBCBlockCipher) cipher)
196: .getUnderlyingCipher();
197:
198: c.processBlock(buf, blockSize, out, outOff);
199: } else {
200: cipher.processBlock(buf, blockSize, out, outOff);
201: }
202:
203: System.arraycopy(block, 0, out, outOff + blockSize, len);
204: } else {
205: byte[] lastBlock = new byte[blockSize];
206:
207: if (cipher instanceof CBCBlockCipher) {
208: BlockCipher c = ((CBCBlockCipher) cipher)
209: .getUnderlyingCipher();
210:
211: c.processBlock(buf, 0, block, 0);
212: } else {
213: cipher.processBlock(buf, 0, block, 0);
214: }
215:
216: for (int i = blockSize; i != bufOff; i++) {
217: lastBlock[i - blockSize] = (byte) (block[i - blockSize] ^ buf[i]);
218: }
219:
220: System.arraycopy(buf, blockSize, block, 0, len);
221:
222: cipher.processBlock(block, 0, out, outOff);
223: System
224: .arraycopy(lastBlock, 0, out, outOff + blockSize,
225: len);
226: }
227:
228: int offset = bufOff;
229:
230: reset();
231:
232: return offset;
233: }
234: }
|