2

The following example encrypts and decrypts to the same original string properly on a 64-bit Desktop Ubuntu 16.04, but when the same code is compiled and run on Raspberry Pi ( ARM ) ( and also another custom Linux ARM board ) it fails to decrypt to the original string. Both the Raspberry Pi and the other ARM board decrypt to the same, but incorrect, value.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/crypto.h>

/* AES key for Encryption and Decryption */
const static unsigned char aes_key[]={0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};

/* Print Encrypted and Decrypted data packets */
void print_data(const char *tittle, const void* data, int len);

int main( )
{
    /* Input data to encrypt */
    unsigned char aes_input[]={0x0,0x1,0x2,0x3,0x4,0x5};

    fprintf(stderr,"%s\n",SSLeay_version(SSLEAY_VERSION));

    /* Init vector */
    unsigned char iv[AES_BLOCK_SIZE];
    memset(iv, 0x00, AES_BLOCK_SIZE);

    /* Buffers for Encryption and Decryption */
    unsigned char enc_out[sizeof(aes_input)];
    unsigned char dec_out[sizeof(aes_input)];

    /* AES-128 bit CBC Encryption */
    AES_KEY enc_key, dec_key;
    AES_set_encrypt_key(aes_key, sizeof(aes_key)*8, &enc_key);
    AES_cbc_encrypt(aes_input, enc_out, sizeof(aes_input), &enc_key, iv,         AES_ENCRYPT);
    /* AES-128 bit CBC Decryption */
    memset(iv, 0x00, AES_BLOCK_SIZE); // don't forget to set iv vector again, else you can't decrypt data properly
    AES_set_decrypt_key(aes_key, sizeof(aes_key)*8, &dec_key); // Size of key is in bits
    AES_cbc_encrypt(enc_out, dec_out, sizeof(aes_input), &dec_key, iv, AES_DECRYPT);

    /* Printing and Verifying */
    print_data("\n Original ",aes_input, sizeof(aes_input)); // you can not print data as a string, because after Encryption its not ASCII

    print_data("\n Encrypted",enc_out, sizeof(enc_out));

    print_data("\n Decrypted",dec_out, sizeof(dec_out));

    return 0;
}

void print_data(const char *tittle, const void* data, int len)
{
    printf("%s : ",tittle);
    const unsigned char * p = (const unsigned char*)data;
    int i = 0;

    for (; i<len; ++i)
        printf("%02X ", *p++);

    printf("\n");
}

Ubuntu result:

 OpenSSL 1.0.1f 6 Jan 2014

 Original  : 00 01 02 03 04 05 

 Encrypted : D5 40 D0 BB 16 1D 

 Decrypted : 00 01 02 03 04 05 

Raspberry Pi result:

OpenSSL 1.0.2l  25 May 2017

Original  : 00 01 02 03 04 05 

Encrypted : D5 40 D0 BB 16 1D 

Decrypted : D3 87 81 20 2B B9

Custom board result:

OpenSSL 1.1.0f  25 May 2017

Original  : 00 01 02 03 04 05 

Encrypted : D5 40 D0 BB 16 1D 

Decrypted : D3 87 81 20 2B B9 

Custom Board ( Updated OpenSSL to match Ubuntu ):

 OpenSSL 1.0.1f 6 Jan 2014

 Original  : 00 01 02 03 04 05 

 Encrypted : D5 40 D0 BB 16 1D 

 Decrypted : D3 87 81 20 2B B9

Why does open source libcrypto not behave the same on Ubuntu and 2 different ARM machines?

PhilBot
  • 748
  • 18
  • 85
  • 173
  • Are your `openssl` versions the same across platforms? I know there was issues with different `openssl` versions and `libcrypto` not containing the same results in decryption. – Zak Sep 14 '17 at 15:49
  • I just updated the post - I downgraded the custom board's RFS to have the matching version for Ubuntu and it still fails. – PhilBot Sep 14 '17 at 15:50
  • Does this help you? https://stackoverflow.com/questions/39637388/encryption-decryption-doesnt-work-well-between-two-different-openssl-versions – Zak Sep 14 '17 at 16:20
  • 3
    I tbink the output size for AES_cbc_encrypt must be a multiple of AES_BLOCK_SIZE. – n. m. could be an AI Sep 14 '17 at 16:27
  • 1
    Also see https://stackoverflow.com/questions/31226594/does-aes-cbc-encrypt-add-padding – n. m. could be an AI Sep 14 '17 at 16:33
  • 1
    @n.m. thanks for the input. I tried again on the Pi with a 16 byte string to encrypt and it worked. So some versions of libcrypto must pad the data silently and some versions must encrypt without complaining that the data is not a multiple of AES_BLOCK_SIZE. Seems deficient... thanks for the help – PhilBot Sep 14 '17 at 18:54
  • Please post as an answer below and I will mark as answered. – PhilBot Sep 14 '17 at 18:55
  • If you don't pad to the block size, the behaviour is **undefined**. – n. m. could be an AI Sep 14 '17 at 19:14
  • In fact I have no idea why this even works on some platforms. It should not, that's how CBC works. – n. m. could be an AI Sep 14 '17 at 20:12
  • OK I have a pretty good idea now. – n. m. could be an AI Sep 15 '17 at 10:01
  • 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 Sep 16 '17 at 05:48

1 Answers1

2

Normally with the CBC mode, you use a buffer of size which is a multiple of the block size of your cipher. This is the nature of CBC and it is mentioned in every openssl man page on CBC type routines (unfortunately I couldn't find a man page or any documentation on AES_cbc_encrypt).

The wrong buffer size appears to work on some platforms by sheer luck. To see what really happens, allocate one byte more for enc_out and zero out that extra byte after encryption. Decryption will fail.

unsigned char enc_out[sizeof(aes_input) + 1];
...
AES_cbc_encrypt(aes_input, enc_out, sizeof(aes_input), &enc_key, iv, AES_ENCRYPT);
enc_out[sizeof(aes_input)] = 0;

Output of the modified code on my machine:

Original  : 00 01 02 03 04 05 

Encrypted : D5 40 D0 BB 16 1D 00 

Decrypted : 89 FB 06 F4 CD 6A 

Unmodified code produces the "correct" output.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243