I'm writing an application server and I've decided to use AES128/CTR/NoPadding to secure connections, as it's considered to be secure enough without having to expand the bytes to the block boundary and I thought it's a good fit to TCP which is logically a seamless stream.
The problem is that Cipher.update() doesn't return the encrypted block until it has a full 16-byte block because CTR is basically based on a block cipher though simulating a stream cipher. I should read data from a tcp socket and process messages as soon as they arrive, but I can't retrieve the most recent block because it's still building up and its size is less than 16 bytes. And I can't just wait because we don't know when the next message would be sent. Of course I could call Cipher.doFinal() to get the leftover but that would mean the end of the stream (connection) and the Cipher object would be reinitialized.
I thought it would be nice if there's a way to peek the carryover. CTR simply XORs the plain text with the keystream so I should be able to get the encrypted data regardless of the rest of the bytes in the block. Would there be a nice workaround to this problem? I'm thinking about writing a wrapper that encrypts fake plain text with zeroes to get the keystream in advance and XORs manually, but I wonder how other people solved this problem.
Update
I'm developing an Android application and it turned out that this is the problem of the Dalvik VM. As Robert and monnand pointed out below, Java SE doesn't have this problem at least with the default provider. I think I'll have to write a wrapper class or change the mode to CFB8 to get around this problem. (CTR8 didn't work) Thanks for all the responses!
At the CTR (Counter) mode of operation, shown in Fig. 5, as an input block to the encryptor (Encrypt), i.e. as an IV, the value of a counter (Counter, Counter + 1,…, Counter + N – 1) is used. It also is a stream encryptor. The counter has the same size as the used block.
Counter (CTR) Like OFB, counter mode turns a block cipher into a stream cipher. It generates the next keystream block by encrypting successive values of a "counter".
3.2. Security strength CTR mode works faster than XTS-AES mode but CTR mode is malleable – an attacker can flip plaintext bits by simply flipping the corresponding ciphertext bits.
I just tested AES in CTR mode using Oracle Java 1.7 and I can not verify your observations:
Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[1]).length); // output: 1
System.out.println(c.update(new byte[20]).length); // output: 20
May be you use a defect third party implementation because "AES128/CTR/NoPadding" is not a known cipher on my system.
I had exactly the same problem today and got it fixed just now.
The problem is your provider, which is probably Bouncy Castle. When you call getInstance()
, simply provide the name of your algorithm (which in my case is "AES/CTR/NoPadding"). DO NOT specify the provider.
Let the code explain itself:
As @Robert said, the following code works correctly:
Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[1]).length); // output: 1
System.out.println(c.update(new byte[20]).length); // output: 20
However, if you specify the provider as "BC" instead, it will be wrong:
Cipher c = Cipher.getInstance("AES/CTR/NoPadding", "BC");
KeyGenerator kg = KeyGenerator.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, kg.generateKey());
System.out.println(c.update(new byte[20]).length); // output: 16
System.out.println(c.update(new byte[1]).length); // null pointer exception
It could be considered as a bug of Bouncy Castle, or a kind of (weird but reasonable) feature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With