views:

112

answers:

2

I am doing AES CBC decryption in java using javax.crypto . I am using the following Cipher class methods:

  • public final void init (int opmode, Key key, AlgorithmParameters params) method for initialization,
  • final int update(byte[] input, int inputOffset, int inputLen, byte[] output) method for decrypting the data,
  • and finally I call the final int doFinal(byte[] output, int outputOffset) method to finish the decryption.

My query is this: Can I assume that the size of the data returned to me by the doFinal call would always be less than or equal to the AES Block Size? The documentation describes the doFinal method as:

“Finishes a multi-part transformation (encryption or decryption). Processes any bytes that may have been buffered in previous update calls. The final transformed bytes are stored in the output buffer.”

But it nowhere says that the output buffer would contain at most one block of data. Though I understand that this is the general behaviour of AES APIs, and this is the behaviour my code has exhibited till now, but would this assumption always hold?

A: 

In general (as in, in the context of the Cipher class) I don't believe it would be safe to assume this. As per the javadocs for that doFinal method:

If the output buffer is too small to hold the result, a ShortBufferException is thrown. In this case, repeat this call with a larger output buffer. Use getOutputSize to determine how big the output buffer should be.

So if you're allocating the output buffer "near" the point where you call the doFinal method, then it would make sense to call getOutputSize and allocate an appropriately-sized buffer. Job done.

On the other hand, if you're passing in a buffer from "far away" that was created to be exactly the block size, you might be in more trouble. It would be perfectly legal (at least, according to the public interface of the Java class) for a Cipher implementation to return an output larger than the block size, so long as the getOutputSize method returns the appropriate size.

In fact, if you're doing CBC decryption, doesn't that require you to pass in all of the blocks to the update method? In which case, you should get the full plaintext output back from doFinal, not just a single block?

Andrzej Doyle
A: 

Generally speaking, it is not safe to assume that buffering is for one block only; and when you look at the details, you may see that it depends on the type of padding. With the usual "PKCS#5" padding, at least one byte and at most n bytes (for blocks of size n) are added, so the decryption system may limit itself to n bytes of buffering. Some other types of padding are a bit more complex, e.g. CTS requires 2n bytes of buffering. The Java cryptographic layer does not seem to support CTS right now, but this may be added in a future version.

Cipher.getOutputSize(len) will give you the maximum output size, given len additional input bytes. The returned value may be somewhat larger than what will be actually returned, especially with decryption, since it depends on what padding bytes will be actually found upon decryption.

What is safe to assume is that the total decrypted message length is no longer than the total encrypted message length (symmetric encryption does not involve data compression). So you may maintain two counters, one for the input data bytes (the encrypted blocks) and one for the obtained output data bytes; the difference will be a maximum bound for what can be obtained from the doFinal(). But that's what getOutputSize() does anyway.

Thomas Pornin
The padding used in my case is 'PKCS5Padding' only. Does that mean I can assume maximum output size to be AES Block Size? The reason I can't use 'Cipher.getOutputSize(len)' is because the buffer to store the output of 'doFinal()' is allocated and passed to my module from code far far away which I shouldn't modify. And the size of that allocated buffer is AES Block Size.
vikas
You cannot assume that, but you can check it. If blocks have size _n_, and the encrypted message consists in _k_ blocks, then the total decrypted message length is between _n(k-1)_ and _(nk)-1_ (inclusive). With counters, you can then know (at runtime) whether the remaining buffered data will fit in a buffer of size _n_ when decrypted. You cannot assume that _in advance_ because that's not a guaranteed property of the Java Cryptographic Architecture.
Thomas Pornin