Bug #18143 my_aes.c : AES_ENCRYPT pads unnecessarily when strlen(source) % 16==0
Submitted: 10 Mar 2006 19:01 Modified: 29 Apr 2006 12:55
Reporter: Rolf Martin-Hoster Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server Severity:S2 (Serious)
Version:4.1 and others? OS:
Assigned to: CPU Architecture:Any

[10 Mar 2006 19:01] Rolf Martin-Hoster
Description:
I found this while finding a php compatible method to encrypt and decrypt via AES. In essence, if the length of the value to be encrypted (source) is equal to a multiple of the block size then an extra block is added. The problem lies in the fact that this greatly decreases the security of the encryption. If one block value is known then this great increases the chances of a brute force or other vulnerability.

How to repeat:
To duplicate :

mysql> select hex(aes_decrypt(concat(aes_encrypt('',''),aes_encrypt('','')),''));
+--------------------------------------------------------------------+
| hex(aes_decrypt(concat(aes_encrypt('',''),aes_encrypt('','')),'')) |
+--------------------------------------------------------------------+
| 10101010101010101010101010101010                                   |
+--------------------------------------------------------------------+

Compared to :

mysql> select hex(aes_decrypt(aes_encrypt('',''),''));
+-----------------------------------------+
| hex(aes_decrypt(aes_encrypt('',''),'')) |
+-----------------------------------------+
|                                         |
+-----------------------------------------+
1 row in set (0.00 sec)

Suggested fix:
  /* Encode the rest. We always have incomplete block */
  pad_len = AES_BLOCK_SIZE - (source_length - AES_BLOCK_SIZE*num_blocks);
  memcpy(block, source, 16 - pad_len);
  bfill(block + AES_BLOCK_SIZE - pad_len, pad_len, pad_len);
  rijndaelEncrypt(aes_key.rk, aes_key.nr, block, (uint8*) dest);
  return AES_BLOCK_SIZE*(num_blocks + 1);

I dont know C well enough but this section from the AES_ENCRYPT function should be coniditional if pad_len!=block size.
[23 Mar 2006 15:36] Valeriy Kravchuk
Thank you for a problem report. Can you provide a test case without concat()?
[6 Apr 2006 15:03] Rolf Martin-Hoster
The first query is what SHOULD be stored, the second+ queries show the extra 16byte block of padding on the end. Notice the second 16 byte block is static regardless of the encrypted value. AFAIK this works with ANY key not just ''.

mysql> select hex(substr(aes_encrypt('4111111111111111',''),1,16));
+------------------------------------------------------+
| hex(substr(aes_encrypt('4111111111111111',''),1,16)) |
+------------------------------------------------------+
| D56CE610B945BDF822B68F79B06E8140                     |
+------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select hex(aes_encrypt('4111111111111111',''));
+------------------------------------------------------------------+
| hex(aes_encrypt('4111111111111111',''))                          |
+------------------------------------------------------------------+
| D56CE610B945BDF822B68F79B06E81400143DB63EE66B0CDFF9F69917680151E |
+------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select hex(aes_encrypt('4111111111111112',''));
+------------------------------------------------------------------+
| hex(aes_encrypt('4111111111111112',''))                          |
+------------------------------------------------------------------+
| 707C4FB24E1942DBC502F18077441A720143DB63EE66B0CDFF9F69917680151E |
+------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select hex(aes_encrypt('4111111111111113',''));
+------------------------------------------------------------------+
| hex(aes_encrypt('4111111111111113',''))                          |
+------------------------------------------------------------------+
| 14A268184D5370AF6C5F7FE4CE0C7E380143DB63EE66B0CDFF9F69917680151E |
+------------------------------------------------------------------+
1 row in set (0.00 sec)
[6 Apr 2006 15:10] Rolf Martin-Hoster
Additionally, as with the queries about I have decrypted the additional block with and without the correct key. Notice the incorrect key returns null where as the correct one returns a null length string.

mysql> select length(aes_decrypt(substr(aes_encrypt('4111111111111111',''),17,16),''));
+--------------------------------------------------------------------------+
| length(aes_decrypt(substr(aes_encrypt('4111111111111111',''),17,16),'')) |
+--------------------------------------------------------------------------+
|                                                                        0 |
+--------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select length(aes_decrypt(substr(aes_encrypt('4111111111111111',''),17,16),'1234'));
+------------------------------------------------------------------------------+
| length(aes_decrypt(substr(aes_encrypt('4111111111111111',''),17,16),'1234')) |
+------------------------------------------------------------------------------+
|                                                                         NULL |
+------------------------------------------------------------------------------+
[29 Apr 2006 12:55] Valeriy Kravchuk
Duplicate of bug #16890.
[1 Apr 2014 9:16] Georgi Kodinov
This extra block is added to implement PKCS#5 padding. See https://www.openssl.org/docs/crypto/EVP_EncryptInit.html#NOTES for more details. 
This padding is an additional safety measure to validate the message.