javaencryptionaesnoncectr-mode

How to handle the IV/Nonce/Counter for AES CTR?


import javax.crypto.Cipher;

public abstract class Crypto {


    private static final String CIPHER_ALGORITHM = "AES/CTR/NoPadding";
    private String AesKeyString = "ByWelFHCgFqivFZrWs89LQ==";

    private void setKey() throws NoSuchAlgorithmException{
        byte[] keyBytes;
        keyBytes = Base64.getDecoder().decode(AesKeyString);
        aesKey = new SecretKeySpec(keyBytes, "AES");
    }

    protected byte[] execute(int mode, byte[] target, byte[] iv) 
            throws Exception{
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(mode, aesKey, ivSpec);
        return cipher.doFinal(target);
    }

}

According to NIST Recommendation - Appendix B, there are two valid approaches to construct the initial counter blocks (AES is a 128-bit block cipher):

  1. 128-bit nonce XORed with an m-bit counter value (usually 32 bits).
  2. 64-bit nonce prepended to a 64-bit counter.

My question is:


Solution

  • Java simply leaves the choice of the way you construct the counter to you. You simply have to initialize the CTR mode using a 16 byte IV, which is nothing more than the initial counter value.

    Once you start encrypting it will use a counter over the full 128 bits. Then again, you would hardly want it to start over as that would directly compromise the security of the plaintext. The disadvantage is that the 32 bit XOR method is not directly supported (if you start with a a counter of FFFFFFFF the next value will alter the 33rd least significant bit of the counter).

    Then again, I would rather choose a 8-byte nonce and leave the least significant bits set to all zeros. Or choose GCM mode of course.


    Proof:

    Cipher aesCTR = Cipher.getInstance("AES/CTR/NoPadding");
    SecretKey aesKey = new SecretKeySpec(new byte[16], "AES");
    IvParameterSpec lastIV = new IvParameterSpec(Hex.decode("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF"));
    aesCTR.init(Cipher.ENCRYPT_MODE, aesKey, lastIV);
    byte[] twoBlocks = aesCTR.doFinal(new byte[2 * aesCTR.getBlockSize()]);
    byte[] secondBlock = Arrays.copyOfRange(twoBlocks, 16, 32);
    System.out.printf("%s%n", Hex.toHexString(secondBlock));
    
    IvParameterSpec firstIV = new IvParameterSpec(new byte[16]); // all zero IV
    aesCTR.init(Cipher.ENCRYPT_MODE, aesKey, firstIV);
    byte[] oneBlock = aesCTR.doFinal(new byte[aesCTR.getBlockSize()]);
    System.out.printf("%s%n", Hex.toHexString(oneBlock));
    

    Output:

    66e94bd4ef8a2c3b884cfa59ca342b2e
    66e94bd4ef8a2c3b884cfa59ca342b2e