PaddedBufferedBlockCipher.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.crypto.paddings; 2 3import org.bouncycastle.crypto.BlockCipher; 4import org.bouncycastle.crypto.BufferedBlockCipher; 5import org.bouncycastle.crypto.CipherParameters; 6import org.bouncycastle.crypto.DataLengthException; 7import org.bouncycastle.crypto.InvalidCipherTextException; 8import org.bouncycastle.crypto.OutputLengthException; 9import org.bouncycastle.crypto.params.ParametersWithRandom; 10 11/** 12 * A wrapper class that allows block ciphers to be used to process data in 13 * a piecemeal fashion with padding. The PaddedBufferedBlockCipher 14 * outputs a block only when the buffer is full and more data is being added, 15 * or on a doFinal (unless the current block in the buffer is a pad block). 16 * The default padding mechanism used is the one outlined in PKCS5/PKCS7. 17 */ 18public class PaddedBufferedBlockCipher 19 extends BufferedBlockCipher 20{ 21 BlockCipherPadding padding; 22 23 /** 24 * Create a buffered block cipher with the desired padding. 25 * 26 * @param cipher the underlying block cipher this buffering object wraps. 27 * @param padding the padding type. 28 */ 29 public PaddedBufferedBlockCipher( 30 BlockCipher cipher, 31 BlockCipherPadding padding) 32 { 33 this.cipher = cipher; 34 this.padding = padding; 35 36 buf = new byte[cipher.getBlockSize()]; 37 bufOff = 0; 38 } 39 40 /** 41 * Create a buffered block cipher PKCS7 padding 42 * 43 * @param cipher the underlying block cipher this buffering object wraps. 44 */ 45 public PaddedBufferedBlockCipher( 46 BlockCipher cipher) 47 { 48 this(cipher, new PKCS7Padding()); 49 } 50 51 /** 52 * initialise the cipher. 53 * 54 * @param forEncryption if true the cipher is initialised for 55 * encryption, if false for decryption. 56 * @param params the key and other data required by the cipher. 57 * @exception IllegalArgumentException if the params argument is 58 * inappropriate. 59 */ 60 public void init( 61 boolean forEncryption, 62 CipherParameters params) 63 throws IllegalArgumentException 64 { 65 this.forEncryption = forEncryption; 66 67 reset(); 68 69 if (params instanceof ParametersWithRandom) 70 { 71 ParametersWithRandom p = (ParametersWithRandom)params; 72 73 padding.init(p.getRandom()); 74 75 cipher.init(forEncryption, p.getParameters()); 76 } 77 else 78 { 79 padding.init(null); 80 81 cipher.init(forEncryption, params); 82 } 83 } 84 85 /** 86 * return the minimum size of the output buffer required for an update 87 * plus a doFinal with an input of len bytes. 88 * 89 * @param len the length of the input. 90 * @return the space required to accommodate a call to update and doFinal 91 * with len bytes of input. 92 */ 93 public int getOutputSize( 94 int len) 95 { 96 int total = len + bufOff; 97 int leftOver = total % buf.length; 98 99 if (leftOver == 0) 100 { 101 if (forEncryption) 102 { 103 return total + buf.length; 104 } 105 106 return total; 107 } 108 109 return total - leftOver + buf.length; 110 } 111 112 /** 113 * return the size of the output buffer required for an update 114 * an input of len bytes. 115 * 116 * @param len the length of the input. 117 * @return the space required to accommodate a call to update 118 * with len bytes of input. 119 */ 120 public int getUpdateOutputSize( 121 int len) 122 { 123 int total = len + bufOff; 124 int leftOver = total % buf.length; 125 126 if (leftOver == 0) 127 { 128 return total - buf.length; 129 } 130 131 return total - leftOver; 132 } 133 134 /** 135 * process a single byte, producing an output block if neccessary. 136 * 137 * @param in the input byte. 138 * @param out the space for any output that might be produced. 139 * @param outOff the offset from which the output will be copied. 140 * @return the number of output bytes copied to out. 141 * @exception DataLengthException if there isn't enough space in out. 142 * @exception IllegalStateException if the cipher isn't initialised. 143 */ 144 public int processByte( 145 byte in, 146 byte[] out, 147 int outOff) 148 throws DataLengthException, IllegalStateException 149 { 150 int resultLen = 0; 151 152 if (bufOff == buf.length) 153 { 154 resultLen = cipher.processBlock(buf, 0, out, outOff); 155 bufOff = 0; 156 } 157 158 buf[bufOff++] = in; 159 160 return resultLen; 161 } 162 163 /** 164 * process an array of bytes, producing output if necessary. 165 * 166 * @param in the input byte array. 167 * @param inOff the offset at which the input data starts. 168 * @param len the number of bytes to be copied out of the input array. 169 * @param out the space for any output that might be produced. 170 * @param outOff the offset from which the output will be copied. 171 * @return the number of output bytes copied to out. 172 * @exception DataLengthException if there isn't enough space in out. 173 * @exception IllegalStateException if the cipher isn't initialised. 174 */ 175 public int processBytes( 176 byte[] in, 177 int inOff, 178 int len, 179 byte[] out, 180 int outOff) 181 throws DataLengthException, IllegalStateException 182 { 183 if (len < 0) 184 { 185 throw new IllegalArgumentException("Can't have a negative input length!"); 186 } 187 188 int blockSize = getBlockSize(); 189 int length = getUpdateOutputSize(len); 190 191 if (length > 0) 192 { 193 if ((outOff + length) > out.length) 194 { 195 throw new OutputLengthException("output buffer too short"); 196 } 197 } 198 199 int resultLen = 0; 200 int gapLen = buf.length - bufOff; 201 202 if (len > gapLen) 203 { 204 System.arraycopy(in, inOff, buf, bufOff, gapLen); 205 206 resultLen += cipher.processBlock(buf, 0, out, outOff); 207 208 bufOff = 0; 209 len -= gapLen; 210 inOff += gapLen; 211 212 while (len > buf.length) 213 { 214 resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen); 215 216 len -= blockSize; 217 inOff += blockSize; 218 } 219 } 220 221 System.arraycopy(in, inOff, buf, bufOff, len); 222 223 bufOff += len; 224 225 return resultLen; 226 } 227 228 /** 229 * Process the last block in the buffer. If the buffer is currently 230 * full and padding needs to be added a call to doFinal will produce 231 * 2 * getBlockSize() bytes. 232 * 233 * @param out the array the block currently being held is copied into. 234 * @param outOff the offset at which the copying starts. 235 * @return the number of output bytes copied to out. 236 * @exception DataLengthException if there is insufficient space in out for 237 * the output or we are decrypting and the input is not block size aligned. 238 * @exception IllegalStateException if the underlying cipher is not 239 * initialised. 240 * @exception InvalidCipherTextException if padding is expected and not found. 241 */ 242 public int doFinal( 243 byte[] out, 244 int outOff) 245 throws DataLengthException, IllegalStateException, InvalidCipherTextException 246 { 247 int blockSize = cipher.getBlockSize(); 248 int resultLen = 0; 249 250 if (forEncryption) 251 { 252 if (bufOff == blockSize) 253 { 254 if ((outOff + 2 * blockSize) > out.length) 255 { 256 reset(); 257 258 throw new OutputLengthException("output buffer too short"); 259 } 260 261 resultLen = cipher.processBlock(buf, 0, out, outOff); 262 bufOff = 0; 263 } 264 265 padding.addPadding(buf, bufOff); 266 267 resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen); 268 269 reset(); 270 } 271 else 272 { 273 if (bufOff == blockSize) 274 { 275 resultLen = cipher.processBlock(buf, 0, buf, 0); 276 bufOff = 0; 277 } 278 else 279 { 280 reset(); 281 282 throw new DataLengthException("last block incomplete in decryption"); 283 } 284 285 try 286 { 287 resultLen -= padding.padCount(buf); 288 289 System.arraycopy(buf, 0, out, outOff, resultLen); 290 } 291 finally 292 { 293 reset(); 294 } 295 } 296 297 return resultLen; 298 } 299} 300