Search code examples
javacryptographybouncycastle

How to use bouncycastle's PaddedBufferedBlockCipher correctly?


I've been poking around in the java version of BouncyCastle's encryption APIs. I'm a little bit confused by the PaddedBufferedBlockCipher class. I understand (perhaps mistakenly?) that it's purpose is for processing encryption incrementally, ie, as data is read from a file.

When I call processBytes() once, providing all the data to encrypt at once, and then doFinal() it works as expected. However, when I make several calls to processBytes, using the appropriate offsets/lengths, with the intention of processing in smaller chunks, followed by doFinal(), it fails. Can anyone explain why the following code fails? Values are hard-coded with the assumption that the clearText is 64 bytes long, and the encryption is performed in 4 x 16-byte blocks.

byte[] key= Hex.decode("a4ea1a7227e032c37f5635e70ba1bd38a4ea1a7227e032c37f5635e70ba1bd38");
byte[] iv= Hex.decode("a4ea1a7227e032c37f5635e70ba1bd38");
byte[] clearText= Hex.decode("28d3966905e33c063e7b74c7c7dcb2d688d48d53101d2a6901d365146faf0c9cde3da9ef37664a5e32e4e468f9b52f587d76b78caaf9d9823dd9eb2c1d700d7c");

BlockCipher bc  = new AESFastEngine();
CBCBlockCipher cipher   = new CBCBlockCipher(bc);
PaddedBufferedBlockCipher mode  = new PaddedBufferedBlockCipher(cipher);
KeyParameter kp = new KeyParameter(key);
CipherParameters ivAndKey= new ParametersWithIV(kp, iv);
mode.init(true, ivAndKey);

byte[] outBuf   = new byte[mode.getOutputSize(clearText.length)];

int blockSize    = mode.getBlockSize();

// process the 64 bytes of clearText, in 4 x 16-byte chunks 
for (int i = 0; i < 4; i++) {

    mode.processBytes(clearText, i * blockSize, blockSize, outBuf, i * blockSize);
}

try {
    // call doFinal on the last block (bytes 48->64)
    mode.doFinal(outBuf, 48);
} catch (Exception e){}

System.out.println(new String(Hex.encode(outBuf))); 

The expected output is: fe391ed41650f3b344ed3d200ffa3c55e71b1d97dbd91c0fbdd0989c3a48822e7e5230e9d6d073bc0752436d9bea9f26328a11586a290e712fbbf874ddfd4ba14e28600e55c7d6ac69467693320a82d3

But the result I'm getting is: 00000000000000000000000000000000fe391ed41650f3b344ed3d200ffa3c55e71b1d97dbd91c0fbdd0989c3a48822e328a11586a290e712fbbf874ddfd4ba14e28600e55c7d6ac69467693320a82d3

EDIT

Revised code as per PeterDettman's suggestion:

int bytesProcessed = 0;

for (int i = 0; i < 4; i++) {
            // Fixed to be '+='
    bytesProcessed += mode.processBytes(clearText, i * blockSize, blockSize, outBuf, bytesProcessed);
}

mode.doFinal(outBuf, bytesProcessed);

Solution

  • As to how to fix the code: the processBytes method returns the actual amount that it wrote to the output, so you need to track the position in outBuf and add the returned length to it for each call (and likewise for doFinal). Note that you shouldn't assume any particular buffer size internally, or whether 'full' buffers are flushed immediately or lazily.

    As to the why: in the case of decryption, a padded mode can't output a block until it knows if there are more blocks to come (because it will remove padding from the final block). So it's forced to be 'lazy' about outputting blocks. For encryption, that's not the case, but it's done the same way for, I guess, simplicity/consistency of the code for processBytes.