001: package org.bouncycastle.crypto.paddings;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.BufferedBlockCipher;
005: import org.bouncycastle.crypto.CipherParameters;
006: import org.bouncycastle.crypto.DataLengthException;
007: import org.bouncycastle.crypto.InvalidCipherTextException;
008: import org.bouncycastle.crypto.params.ParametersWithRandom;
009:
010: /**
011: * A wrapper class that allows block ciphers to be used to process data in
012: * a piecemeal fashion with padding. The PaddedBufferedBlockCipher
013: * outputs a block only when the buffer is full and more data is being added,
014: * or on a doFinal (unless the current block in the buffer is a pad block).
015: * The default padding mechanism used is the one outlined in PKCS5/PKCS7.
016: */
017: public class PaddedBufferedBlockCipher extends BufferedBlockCipher {
018: BlockCipherPadding padding;
019:
020: /**
021: * Create a buffered block cipher with the desired padding.
022: *
023: * @param cipher the underlying block cipher this buffering object wraps.
024: * @param padding the padding type.
025: */
026: public PaddedBufferedBlockCipher(BlockCipher cipher,
027: BlockCipherPadding padding) {
028: this .cipher = cipher;
029: this .padding = padding;
030:
031: buf = new byte[cipher.getBlockSize()];
032: bufOff = 0;
033: }
034:
035: /**
036: * Create a buffered block cipher PKCS7 padding
037: *
038: * @param cipher the underlying block cipher this buffering object wraps.
039: */
040: public PaddedBufferedBlockCipher(BlockCipher cipher) {
041: this (cipher, new PKCS7Padding());
042: }
043:
044: /**
045: * initialise the cipher.
046: *
047: * @param forEncryption 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 forEncryption, CipherParameters params)
054: throws IllegalArgumentException {
055: this .forEncryption = forEncryption;
056:
057: reset();
058:
059: if (params instanceof ParametersWithRandom) {
060: ParametersWithRandom p = (ParametersWithRandom) params;
061:
062: padding.init(p.getRandom());
063:
064: cipher.init(forEncryption, p.getParameters());
065: } else {
066: padding.init(null);
067:
068: cipher.init(forEncryption, params);
069: }
070: }
071:
072: /**
073: * return the minimum size of the output buffer required for an update
074: * plus a doFinal with an input of len bytes.
075: *
076: * @param len the length of the input.
077: * @return the space required to accommodate a call to update and doFinal
078: * with len bytes of input.
079: */
080: public int getOutputSize(int len) {
081: int total = len + bufOff;
082: int leftOver = total % buf.length;
083:
084: if (leftOver == 0) {
085: if (forEncryption) {
086: return total + buf.length;
087: }
088:
089: return total;
090: }
091:
092: return total - leftOver + buf.length;
093: }
094:
095: /**
096: * return the size of the output buffer required for an update
097: * an input of len bytes.
098: *
099: * @param len the length of the input.
100: * @return the space required to accommodate a call to update
101: * with len bytes of input.
102: */
103: public int getUpdateOutputSize(int len) {
104: int total = len + bufOff;
105: int leftOver = total % buf.length;
106:
107: if (leftOver == 0) {
108: return total - buf.length;
109: }
110:
111: return total - leftOver;
112: }
113:
114: /**
115: * process a single byte, producing an output block if neccessary.
116: *
117: * @param in the input byte.
118: * @param out the space for any output that might be produced.
119: * @param outOff the offset from which the output will be copied.
120: * @return the number of output bytes copied to out.
121: * @exception DataLengthException if there isn't enough space in out.
122: * @exception IllegalStateException if the cipher isn't initialised.
123: */
124: public int processByte(byte in, byte[] out, int outOff)
125: throws DataLengthException, IllegalStateException {
126: int resultLen = 0;
127:
128: if (bufOff == buf.length) {
129: resultLen = cipher.processBlock(buf, 0, out, outOff);
130: bufOff = 0;
131: }
132:
133: buf[bufOff++] = in;
134:
135: return resultLen;
136: }
137:
138: /**
139: * process an array of bytes, producing output if necessary.
140: *
141: * @param in the input byte array.
142: * @param inOff the offset at which the input data starts.
143: * @param len the number of bytes to be copied out of the input array.
144: * @param out the space for any output that might be produced.
145: * @param outOff the offset from which the output will be copied.
146: * @return the number of output bytes copied to out.
147: * @exception DataLengthException if there isn't enough space in out.
148: * @exception IllegalStateException if the cipher isn't initialised.
149: */
150: public int processBytes(byte[] in, int inOff, int len, byte[] out,
151: int outOff) throws DataLengthException,
152: IllegalStateException {
153: if (len < 0) {
154: throw new IllegalArgumentException(
155: "Can't have a negative input length!");
156: }
157:
158: int blockSize = getBlockSize();
159: int length = getUpdateOutputSize(len);
160:
161: if (length > 0) {
162: if ((outOff + length) > out.length) {
163: throw new DataLengthException("output buffer too short");
164: }
165: }
166:
167: int resultLen = 0;
168: int gapLen = buf.length - bufOff;
169:
170: if (len > gapLen) {
171: System.arraycopy(in, inOff, buf, bufOff, gapLen);
172:
173: resultLen += cipher.processBlock(buf, 0, out, outOff);
174:
175: bufOff = 0;
176: len -= gapLen;
177: inOff += gapLen;
178:
179: while (len > buf.length) {
180: resultLen += cipher.processBlock(in, inOff, out, outOff
181: + resultLen);
182:
183: len -= blockSize;
184: inOff += blockSize;
185: }
186: }
187:
188: System.arraycopy(in, inOff, buf, bufOff, len);
189:
190: bufOff += len;
191:
192: return resultLen;
193: }
194:
195: /**
196: * Process the last block in the buffer. If the buffer is currently
197: * full and padding needs to be added a call to doFinal will produce
198: * 2 * getBlockSize() bytes.
199: *
200: * @param out the array the block currently being held is copied into.
201: * @param outOff the offset at which the copying starts.
202: * @return the number of output bytes copied to out.
203: * @exception DataLengthException if there is insufficient space in out for
204: * the output or we are decrypting and the input is not block size aligned.
205: * @exception IllegalStateException if the underlying cipher is not
206: * initialised.
207: * @exception InvalidCipherTextException if padding is expected and not found.
208: */
209: public int doFinal(byte[] out, int outOff)
210: throws DataLengthException, IllegalStateException,
211: InvalidCipherTextException {
212: int blockSize = cipher.getBlockSize();
213: int resultLen = 0;
214:
215: if (forEncryption) {
216: if (bufOff == blockSize) {
217: if ((outOff + 2 * blockSize) > out.length) {
218: reset();
219:
220: throw new DataLengthException(
221: "output buffer too short");
222: }
223:
224: resultLen = cipher.processBlock(buf, 0, out, outOff);
225: bufOff = 0;
226: }
227:
228: padding.addPadding(buf, bufOff);
229:
230: resultLen += cipher.processBlock(buf, 0, out, outOff
231: + resultLen);
232:
233: reset();
234: } else {
235: if (bufOff == blockSize) {
236: resultLen = cipher.processBlock(buf, 0, buf, 0);
237: bufOff = 0;
238: } else {
239: reset();
240:
241: throw new DataLengthException(
242: "last block incomplete in decryption");
243: }
244:
245: try {
246: resultLen -= padding.padCount(buf);
247:
248: System.arraycopy(buf, 0, out, outOff, resultLen);
249: } finally {
250: reset();
251: }
252: }
253:
254: return resultLen;
255: }
256: }
|