CTSBlockCipher.java revision 5db505e1f6a68c8d5dfdb0fed0b8607dea7bed96
1ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.orgpackage org.bouncycastle.crypto.modes;
2ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
3ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.orgimport org.bouncycastle.crypto.BlockCipher;
4ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.orgimport org.bouncycastle.crypto.BufferedBlockCipher;
5ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.orgimport org.bouncycastle.crypto.DataLengthException;
6ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.orgimport org.bouncycastle.crypto.InvalidCipherTextException;
7ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
8ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org/**
9ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
10ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org * be used to produce cipher text which is the same length as the plain text.
11ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org */
12ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.orgpublic class CTSBlockCipher
13ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    extends BufferedBlockCipher
14ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org{
15ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    private int     blockSize;
16ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
17ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    /**
18ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * Create a buffered block cipher that uses Cipher Text Stealing
19ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     *
20ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param cipher the underlying block cipher this buffering object wraps.
21ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     */
22ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    public CTSBlockCipher(
23ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        BlockCipher     cipher)
24d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org    {
25d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher) || (cipher instanceof SICBlockCipher))
26d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        {
27d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org            // TODO: This is broken - need to introduce marker interface to differentiate block cipher primitive from mode?
28d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org            throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
29d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        }
30ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
31d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        this.cipher = cipher;
32d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org
33d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        blockSize = cipher.getBlockSize();
34d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org
35d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        buf = new byte[blockSize * 2];
36d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        bufOff = 0;
37d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org    }
38d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org
39d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org    /**
40d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org     * return the size of the output buffer required for an update
41d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org     * an input of len bytes.
42d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org     *
43ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param len the length of the input.
44d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org     * @return the space required to accommodate a call to update
45d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org     * with len bytes of input.
46ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     */
47d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org    public int getUpdateOutputSize(
48d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        int len)
49d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org    {
50d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        int total       = len + bufOff;
51d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org        int leftOver    = total % buf.length;
52d851b91d14ef0bd71acdce7b90c9a8f1af1181adjohannkoenig@chromium.org
53ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        if (leftOver == 0)
54ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
55ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            return total - buf.length;
5641294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org        }
5741294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org
58ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        return total - leftOver;
59ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    }
6041294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org
6141294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org    /**
6241294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     * return the size of the output buffer required for an update plus a
6341294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     * doFinal with an input of len bytes.
6441294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     *
6541294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     * @param len the length of the input.
6641294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     * @return the space required to accommodate a call to update and doFinal
67ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * with len bytes of input.
6841294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     */
69ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    public int getOutputSize(
7041294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org        int len)
71ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    {
72e2064011d36b2008099446503f28e64d445060ecjohannkoenig@chromium.org        return len + bufOff;
73e2064011d36b2008099446503f28e64d445060ecjohannkoenig@chromium.org    }
74ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
75ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    /**
76ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * process a single byte, producing an output block if necessary.
7741294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     *
78ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * @param in the input byte.
79ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * @param out the space for any output that might be produced.
80ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * @param outOff the offset from which the output will be copied.
81ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * @return the number of output bytes copied to out.
8241294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org     * @exception DataLengthException if there isn't enough space in out.
83ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * @exception IllegalStateException if the cipher isn't initialised.
84ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     */
85ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    public int processByte(
86ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        byte        in,
87ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        byte[]      out,
88ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        int         outOff)
89ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        throws DataLengthException, IllegalStateException
90ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    {
9141294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org        int         resultLen = 0;
9241294d96d7dbf9bc215b09832a8336c5fb158f0bjohannkoenig@chromium.org
93ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        if (bufOff == buf.length)
94ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        {
95ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org            resultLen = cipher.processBlock(buf, 0, out, outOff);
96ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org            System.arraycopy(buf, blockSize, buf, 0, blockSize);
97ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
98ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org            bufOff = blockSize;
99ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        }
100ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
101ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        buf[bufOff++] = in;
102ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
103ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        return resultLen;
104ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    }
105ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
106ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    /**
107ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * process an array of bytes, producing output if necessary.
108ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     *
109ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param in the input byte array.
110ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param inOff the offset at which the input data starts.
111ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param len the number of bytes to be copied out of the input array.
112ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param out the space for any output that might be produced.
113ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param outOff the offset from which the output will be copied.
114ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @return the number of output bytes copied to out.
115ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @exception DataLengthException if there isn't enough space in out.
116ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @exception IllegalStateException if the cipher isn't initialised.
117ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     */
118ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    public int processBytes(
119ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        byte[]      in,
120ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int         inOff,
121ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int         len,
122ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        byte[]      out,
123ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int         outOff)
124ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        throws DataLengthException, IllegalStateException
125ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    {
126ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        if (len < 0)
127ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
128ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            throw new IllegalArgumentException("Can't have a negative input length!");
129ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        }
130ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
131ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int blockSize   = getBlockSize();
132ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int length      = getUpdateOutputSize(len);
133ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
134ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        if (length > 0)
135ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
136ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            if ((outOff + length) > out.length)
137ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            {
138ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                throw new DataLengthException("output buffer too short");
139ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            }
140ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        }
141ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
142ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int resultLen = 0;
143ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int gapLen = buf.length - bufOff;
144ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
145ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        if (len > gapLen)
146ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
147ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            System.arraycopy(in, inOff, buf, bufOff, gapLen);
148ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
149ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            resultLen += cipher.processBlock(buf, 0, out, outOff);
150ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            System.arraycopy(buf, blockSize, buf, 0, blockSize);
151ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
152ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            bufOff = blockSize;
153ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
154ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            len -= gapLen;
155ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            inOff += gapLen;
156ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
157ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            while (len > blockSize)
158ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            {
159ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                System.arraycopy(in, inOff, buf, bufOff, blockSize);
160ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
161ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                System.arraycopy(buf, blockSize, buf, 0, blockSize);
162ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
163ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                len -= blockSize;
164ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                inOff += blockSize;
165ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            }
166ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        }
167ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
168ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        System.arraycopy(in, inOff, buf, bufOff, len);
169ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
170ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        bufOff += len;
171ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
172ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org        return resultLen;
173ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org    }
174ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
175ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    /**
176ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * Process the last block in the buffer.
177ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     *
178ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @param out the array the block currently being held is copied into.
17910a9a0d835561a7f2300c561c514efcf374554d6fgalligan@chromium.org     * @param outOff the offset at which the copying starts.
180ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @return the number of output bytes copied to out.
181ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @exception DataLengthException if there is insufficient space in out for
182ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * the output.
183ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @exception IllegalStateException if the underlying cipher is not
184ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * initialised.
185ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     * @exception InvalidCipherTextException if cipher text decrypts wrongly (in
186ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org     * case the exception will never get thrown).
187ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org     */
188ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    public int doFinal(
189ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        byte[]  out,
190ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int     outOff)
191ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        throws DataLengthException, IllegalStateException, InvalidCipherTextException
192ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org    {
193ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        if (bufOff + outOff > out.length)
194ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
195ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            throw new DataLengthException("output buffer to small in doFinal");
196ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        }
197ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
198ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int     blockSize = cipher.getBlockSize();
199ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        int     len = bufOff - blockSize;
200ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        byte[]  block = new byte[blockSize];
201ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
202ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        if (forEncryption)
203ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
204ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            if (bufOff < blockSize)
205ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            {
206ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                throw new DataLengthException("need at least one block of input for CTS");
207ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            }
208ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
209ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            cipher.processBlock(buf, 0, block, 0);
210ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
211ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            if (bufOff > blockSize)
212ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            {
213ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                for (int i = bufOff; i != buf.length; i++)
214ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org                {
215ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org                    buf[i] = block[i - blockSize];
216ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org                }
217ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org
218ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org                for (int i = blockSize; i != bufOff; i++)
219ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org                {
220ac4e313c19203132648a2a271703b6ee76fe4284johannkoenig@chromium.org                    buf[i] ^= block[i - blockSize];
221ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                }
222ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
223ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                if (cipher instanceof CBCBlockCipher)
224ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                {
225ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
226ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
227ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                    c.processBlock(buf, blockSize, out, outOff);
228ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                }
229ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                else
230ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                {
231ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                    cipher.processBlock(buf, blockSize, out, outOff);
232ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                }
233ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
234ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                System.arraycopy(block, 0, out, outOff + blockSize, len);
235ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            }
236ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            else
237ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            {
238ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org                System.arraycopy(block, 0, out, outOff, blockSize);
239ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            }
240ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        }
241ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        else
242ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org        {
243411971f94253c85e1866c281860d6344f6aa0c78fgalligan@chromium.org            if (bufOff < blockSize)
244ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            {
245411971f94253c85e1866c281860d6344f6aa0c78fgalligan@chromium.org                throw new DataLengthException("need at least one block of input for CTS");
246ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            }
247ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
248ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            byte[]  lastBlock = new byte[blockSize];
249ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org
250ed759d81a39febed3a8a395386639d54307504aagrunell@chromium.org            if (bufOff > blockSize)
251            {
252                if (cipher instanceof CBCBlockCipher)
253                {
254                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
255
256                    c.processBlock(buf, 0, block, 0);
257                }
258                else
259                {
260                    cipher.processBlock(buf, 0, block, 0);
261                }
262
263                for (int i = blockSize; i != bufOff; i++)
264                {
265                    lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
266                }
267
268                System.arraycopy(buf, blockSize, block, 0, len);
269
270                cipher.processBlock(block, 0, out, outOff);
271                System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
272            }
273            else
274            {
275                cipher.processBlock(buf, 0, block, 0);
276
277                System.arraycopy(block, 0, out, outOff, blockSize);
278            }
279        }
280
281        int offset = bufOff;
282
283        reset();
284
285        return offset;
286    }
287}
288