Score:1

AES output lengths are not always a multiple of 16

ng flag

I have a C# solution that encrypts a bunch of small data chunks using AES.

        //This is how I'm configuring the Aes object
        var aes = Aes.Create();
        aes.Mode = CipherMode.CBC;
        aes.KeySize = 256;
        aes.Padding = PaddingMode.PKCS7;

I then write the raw ciphertext bytes to SQL Server VARBINARY columns.

Querying the length of these VARBINARY ciphertext columns I expected them to always be a multiple of 16 bytes. However that does not appear to be the case here.

I tried reading up about it online and the only questions I found are asking why AES ciphertext is padded up to 16 byte blocks, so I thought I'd ask about the inverse question here.

Notes:

  • I tested decrypting one of these oddly sized ciphertexts and it worked fine, so the ciphertext is not malformed.
  • I noticed it doesn't happen often, in one run it happened 19 times out of 5,828.
  • When it does happen it's always off-by-one (31 instead of 32, 767 instead of 768, etc..)

I had a thought that maybe the AES standard might truncate cipher-bytes that are exactly zero (or some other well known number) from the end of the output since that could just be rebuilt by the decryptor? But would love clarification.

Score:4
my flag

You are correct; standard AES-CBC (without, say, ciphertext stealing) has an output that is always a multiple of 16 bytes in length.

One possibility that occurs to me that if your AES implementation has a glitch were if the last byte happened to be 0x00, then it wouldn't actually output it (and in the decrypt direction, if the ciphertext was short a byte, it'd implicitly add an 0x00).

If this hypothesis were the case, then in your 5,828 test cases, we'd expect to see the last ciphertext byte be 0 (and hence truncated) an expected 22.8 times; 19 times is well within a standard deviation, and so it is plausible...

Maarten Bodewes avatar
in flag
Note that in that case you could have an off-by two once in 65536 (so likely you just didn't encounter it yet), off by 3 once in ~4 billion etc. Just saying because adding a single byte might not fix the issue (if it needs fixing). I've also seen a lot of issues with testing the binary size, such as retrieving the binary and then testing the size using a string-based function (e.g. `strlen` in C). Actually, I'd think that is more likely than an error in the encryption / decryption routines - although the implementations in most DB are terrible as well.
ng flag
I pulled out one of the values that SQL Server claims is 31 bytes, but the value returned is 32 hexpairs long, so I think @MaartenBodewes is right and SQL Server is actually just reporting the length wrong when I use the LEN function. What I find MORE interesting though is the last byte in all of these is not 00, but 20, which I believe is the ASCII space character? Maybe LEN treats it as a string and tries to auto-trim the strings? In any case this is no longer a crypto question I think. EDIT: Just learned about the DATALENGTH function, which is what I should have been using.
Score:0
ng flag

Ok, as others have indicated the standard would always produce a ciphertext with a length that is a multiple of 16 bytes. The issue was when I was measuring the length I had used the T-SQL LEN function which seems to trim 0x20 bytes off the end of binary sequences when it measures the length.

I have now learned that I should be using the DATALENGTH function which does not do this trimming, and when I tried it all values are multiples of 16.

Apologies for the confusion!

Morrolan avatar
ng flag
To clarify - that is even the **expected** (albeit peculiar) behaviour of T-SQL: "LEN excludes trailing spaces" as per e.g. [the official documentation](https://docs.microsoft.com/en-us/sql/t-sql/functions/len-transact-sql?view=sql-server-ver15#remarks)
ng flag
Yeah that makes a lot more sense then. Thanks @Morrolan
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.