encryptionopensslaesblock-cipher

AES 128 CTR plain text size not multiple of block size


I read that AES 128 CTR mode should work on blocks of 16 bytes (128bit), like CBC mode.

indeed, if I try with openssl to encode 18 bytes plaintext with :

max@jarvis:~$ printf 0123456789abcdefgh | openssl enc -e -nopad -nosalt -aes-128-cbc -K 00000000000000000000000000000000 -iv 00000000000000000000000000000000 | hd
bad decrypt
140670739715200:error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length:../crypto/evp/evp_enc.c:425:
00000000  14 f5 fe 74 69 66 f2 92  65 1c 22 88 bb ff 46 09  |...tif..e."...F.|
00000010
max@jarvis:~$ printf 0123456789abcdefgh | openssl enc -e  -nosalt -aes-128-cbc -K 00000000000000000000000000000000 -iv 00000000000000000000000000000000 | hd
00000000  14 f5 fe 74 69 66 f2 92  65 1c 22 88 bb ff 46 09  |...tif..e."...F.|
00000010  c2 ae b2 99 18 cd 6e ee  55 92 77 d9 e8 f3 1f bf  |......n.U.w.....|
00000020

the ciphertext is 16 or 32 bytes, depending on the presence of -nopad parameter.

but if I try CTR:

max@jarvis:~$ printf 0123456789abcdefgh | openssl enc -e -nopad -nosalt -aes-128-ctr -K 00000000000000000000000000000000 -iv 00000000000000000000000000000000 | hd
00000000  56 d8 79 e7 db bf 1a 0c  b0 75 9b 3b a9 50 4e 48  |V.y......u.;.PNH|
00000010  3f 8a                                             |?.|
00000012
max@jarvis:~$ printf 0123456789abcdefgh | openssl enc -e  -nosalt -aes-128-ctr -K 00000000000000000000000000000000 -iv 00000000000000000000000000000000 | hd
00000000  56 d8 79 e7 db bf 1a 0c  b0 75 9b 3b a9 50 4e 48  |V.y......u.;.PNH|
00000010  3f 8a                                             |?.|
00000012

the ciphertext is 18 bytes, like plaintext, in each case.

I cannot figure out why


Solution

  • CTR mode makes a block cipher (like AES) behave as a stream cipher. The results you are getting are expected - the size of the input data will equal the size of the output data.

    CBC operates block by block, which is what mandates a padding scheme. CTR passes blocks of a counter value to AES, still honoring the block size, but then takes the output block and XORs it with the plaintext.

    In your case above, two full 128-bit blocks will have been passed through AES to produce the key stream, but only 128 + 16 bits (18 bytes) of that key stream are used and XORed with your plaintext, producing a ciphertext of equal length.