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