4

Consider the following snippet of C++ code:

#include <iostream>
#include <openssl/aes.h>

#define AES_KEY_LENGTH 32

using namespace std;

int main()
{
    AES_KEY encryption_key;
    AES_KEY decryption_key;

    unsigned char key[AES_KEY_LENGTH] = {'t', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't'};

    unsigned char iv[AES_BLOCK_SIZE] = {'t', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't', 't', 'e', 's', 't'};

    unsigned char iv_enc[AES_BLOCK_SIZE];
    unsigned char iv_dec[AES_BLOCK_SIZE];

    memcpy(iv_enc, iv, AES_BLOCK_SIZE);
    memcpy(iv_dec, iv, AES_BLOCK_SIZE);

    AES_set_encrypt_key(key, AES_KEY_LENGTH * 8, &(encryption_key));
    AES_set_decrypt_key(key, AES_KEY_LENGTH * 8, &(decryption_key));

    char message[] = "Attack at dawn! Attack.";

    unsigned char * encryption_output = new unsigned char[32];
    encryption_output[31] = 3;

    AES_cbc_encrypt((unsigned char *) message, encryption_output, sizeof(message), &encryption_key, iv_enc, AES_ENCRYPT);

    unsigned char * decryption_output = new unsigned char[32];

    AES_cbc_encrypt(encryption_output, decryption_output, 32, &decryption_key, iv_dec, AES_DECRYPT);
}

What I do here is encrypt and then decrypt a message using openssl aes library. What I am concerned about is the length encryption_output. As far as my understanding goes, since AES encrypts in blocks of size AES_BLOCK_SIZE (aka 16 bytes) the number of output bytes should be equal to the size of the message, rounded up to the closest multiple of AES_BLOCK_SIZE. Is this correct? In particular, what happens if I extend the message to be exactly 32 bytes long? Will this still work, or will 16 empty padding bytes be added thus causing a segmentation fault when trying to write bytes 32 to 47 in encryption_output?

Matteo Monti
  • 8,362
  • 19
  • 68
  • 114
  • Padding creates oracles. The latest secure designs use authenticated encryption modes, in part, to remove the oracles. So you should be using a mode like AES/GCM. And you should be using the high level, [`EVP_*` functions](http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption). `AES_*` and friends suffer endianess issues on occasion, and they will ***never*** use hardware, like AES-NI, because its a software-only implementation. – jww Jul 06 '15 at 17:42
  • I am really not an expert. My whole point is that I need to transfer data of a fixed length. Usually, this length is multiple of the block size. So say that I need to always transfer 16 bytes at a time: since I already know that I need 16 bytes, I really would like to avoid an additional 16 bytes of "useless" padding every time: that's a 100% overhead on my network! – Matteo Monti Jul 07 '15 at 13:52
  • What would the best strategy be to just encrypt fixed length data in a secure way? Is there a significant difference if the length is multiple of the block size or not? – Matteo Monti Jul 07 '15 at 13:54
  • You should *not* use `AES_encrypt` and friends. That's a software-only implementation, so you will not enjoy hardware support, like AES-NI. You should be using `EVP_*` functions. See [EVP Symmetric Encryption and Decryption](http://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption) on the OpenSSL wiki. In fact, you should probably be using authenticated encryption because it provides *both* confidentiality and authenticity. See [EVP Authenticated Encryption and Decryption](http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption) on the OpenSSL wiki. – jww Aug 12 '16 at 21:48

1 Answers1

6

Proper PKCS#7 padding:

  • rounds the length up to a multiple of the blocksize if it wasn´t a multiple before
  • and it adds a whole block otherwise

Else, when decrypting, you couldn´t possibly know if the last ciperhtext block is "real" or only padding. (The actual byte values to pad with are specified too, but your real last block could contain these => again not possible to recognize it).

There are other schemes than PKCS#7, but this is not relevant here.

However, with AES_cbc_encrypt, you´ll have to implement this yourself, ie. pad before encrypting and remove the padding after decrypting. The encrypting itself will work with non-multiple lengths, but the used "padding" has the problem mentioned above. To answer your original question, AES_cbc_encrypt won´t add blocks, rounding up the length is the only thing it does.

For functions with proper padding (and without several other disadvantages of AES_cbc_encrypt, like missing AESNI support etc.etc.), look into the EVP part of OpenSSL. AES_cbc_encrypt is a more lowlevel part, depending on the situation it´s used by the highlevel function too.

Btw., something about C++: If you don´t get a segmentation fault,
it doesn´t mean that the code is correct.

deviantfan
  • 11,268
  • 3
  • 32
  • 49
  • Couldn't ask for a more complete answer, thank you. I was asking because I am exchanging fixed length messages between two endpoints. I already know what the length of the message is, so the whole padding thing is completely useless. In particular, I am sending and receiving those messages over the network and they are almost always multiples in length of the block size: I would have always been wasting those 16 bytes. Thanks again! – Matteo Monti Jul 05 '15 at 10:24
  • 1
    Segfault comment: Yes! Even more, just because your program does what you expect (at the moment) does not mean it is correct. – Johannes Overmann Apr 07 '16 at 15:08