views:

39

answers:

1

I need to encrypt and send data over TCP (from a few 100 bytes to a few 100 megabytes per message) in chunks from Java to a C++ program, and need to send the size of the data ahead of time so the recipient knows when to stop reading the current message and process it, then wait for the next message (the connection stays open so there's no other way to indicate end of message; as the data can be binary, I can't use a flag to indicate message end due to the possibility the encrypted bytes might randomly happen to be identical to any flag I choose at some point).

My issue is calculating the encrypted message size before encrypting it, which will in general be different than the input length due to padding etc.

Say I have initialized as follows:

AlgorithmParameterSpec paramSpec = new IvParameterSpec(initv);
encipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mac = Mac.getInstance("HmacSHA512");
encipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
mac.init(key);
buf = new byte[encipher.getOutputSize(blockSize)];

Then I send the data as such (and also have an analogous function that uses a stream for input instead of byte[]):

public void writeBytes(DataOutputStream out, byte[] input) {
 try {
  //mac.reset(); // Needed ?
  int left = input.length;
  int offset = 0;
  while (left > 0)
  {
   int chunk = Math.min(left, blockSize);
   int ctLength = encipher.update(input, offset, chunk, buf, 0);
   mac.update(input, offset, chunk);
   out.write(buf, 0, ctLength);
   left -= chunk;
   offset += chunk;
  }
  out.write(encipher.doFinal(mac.doFinal());
  out.flush();
 } catch (Exception e) {
  e.printStackTrace();
 }
}

But how to precalculate the output size that will be sent to the receiving computer? Basically, I want to out.writeInt(messageSize) before the loop. But how to calculate messageSize? The documentation for Cipher's getOutputSize() says that "This call takes into account any unprocessed (buffered) data from a previous update call, and padding." So this seems to imply that the value might change for the same function argument over multiple calls to update() or doFinal()... Can I assume that if blockSize is a multiple of the AES CBC block size to avoid padding, I should have a constant value for each block? That is, simply check that _blockSize % encipher.getOutputSize(1) != 0 and then in the write function,

int messageSize = (input.length / blockSize) * encipher.getOutputSize(blockSize) +                     
                  encipher.getOutputSize(input.length % blockSize + mac.getMacLength());

??

If not, what alternatives do I have?

+1  A: 

When using PKCS5 padding, the size of the message after padding will be:

padded_size = original_size + BLOCKSIZE - (original_size % BLOCKSIZE);

The above gives the complete size of the entire message (up to the doFinal() call), given the complete size of the input message. It occurs to me that you actually want to know just the length of the final portion - all you need to do is store the output byte array of the doFinal() call, and use the .length() method on that array.

caf
Hi caf, I assume BLOCKSIZE is whatever Cipher.getBlocksize() returns?Thanks
Prune
Yes (this should be 16 for AES).
caf