Simple AES encryption using openssl library in C

I want to encrypt a structure containing several lines, and then decrypt it. I tried the following code. The source code is found from the Internet and it works great. I change it to structure. Below is the code.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <openssl/aes.h> #include <openssl/rand.h> typedef struct ticket { /* test field */ int ticketId; char username[20]; char date[20]; } USR_TICKET; // a simple hex-print routine. could be modified to print 16 bytes-per-line static void hex_print(const void* pv, size_t len) { const unsigned char * p = (const unsigned char*)pv; if (NULL == pv) printf("NULL"); else { size_t i = 0; for (; i<len;++i) printf("%02X ", *p++); } printf("\n"); } // main entrypoint int main(int argc, char **argv) { int keylength; printf("Give a key length [only 128 or 192 or 256!]:\n"); scanf("%d", &keylength); /* generate a key with a given length */ unsigned char aes_key[keylength/8]; memset(aes_key, 0, keylength/8); if (!RAND_bytes(aes_key, keylength/8)) exit(-1); /* input struct creation */ size_t inputslength = sizeof(USR_TICKET); USR_TICKET ticket; ticket.ticketId = 1; time_t now = time(NULL); strftime(ticket.date, 20, "%Y-%m-%d", localtime(&now)); strcpy(ticket.username, "ravinda"); printf("Username - %s\n", ticket.username); printf("Ticket Id - %d\n", ticket.ticketId); printf("Date - %s\n", ticket.date); /* init vector */ unsigned char iv_enc[AES_BLOCK_SIZE], iv_dec[AES_BLOCK_SIZE]; RAND_bytes(iv_enc, AES_BLOCK_SIZE); memcpy(iv_dec, iv_enc, AES_BLOCK_SIZE); // buffers for encryption and decryption const size_t encslength = ((inputslength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; unsigned char enc_out[encslength]; unsigned char dec_out[inputslength]; memset(enc_out, 0, sizeof(enc_out)); memset(dec_out, 0, sizeof(dec_out)); // so i can do with this aes-cbc-128 aes-cbc-192 aes-cbc-256 AES_KEY enc_key, dec_key; AES_set_encrypt_key(aes_key, keylength, &enc_key); AES_cbc_encrypt((unsigned char *)&ticket, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT); AES_set_decrypt_key(aes_key, keylength, &dec_key); AES_cbc_encrypt(enc_out, dec_out, encslength, &dec_key, iv_dec, AES_DECRYPT); printf("original:\t"); hex_print((unsigned char *)&ticket, inputslength); printf("encrypt:\t"); hex_print(enc_out, sizeof(enc_out)); printf("decrypt:\t"); hex_print(dec_out, sizeof(dec_out)); USR_TICKET * dyc = (USR_TICKET *)dec_out; printf("Username - %s\n", dyc->username); printf("Ticket Id - %d\n", dyc->ticketId); printf("Date - %s\n", dyc->date); return 0; } 

The only problem is that the first two elements of the structure are decrypted correctly. After that, the data will be updated. What am I doing wrong here?

+6
source share
4 answers

I would almost go so far as to say that this is a problem with OpenSSL. It seems that when the length parameter passed to AES_cbc_encrypt is> AES_BLOCK_SIZE but not its integer multiple (i.e. length mod AES_BLOCK_SIZE != 0 ), then the last block is encrypted using the initial IV, and not the previous block of ciphertext as should be the case for CBC mode.

You can fix this in one of two ways:

Copy your structure into a buffer, the size of which is an integer multiple of AES_BLOCK_SIZE , or encrypt in two parts - full blocks, followed by a separate partial block. The advantage of the latter is that you can avoid the use of additional memory and can be performed as follows:

 size_t fullBlocks = inputslength - (inputslength % AES_BLOCK_SIZE); size_t remainingBlock = inputslength - fullBlocks; AES_cbc_encrypt((unsigned char *)&ticket, enc_out, fullBlocks, &enc_key, iv_enc, AES_ENCRYPT); AES_cbc_encrypt((unsigned char *)&ticket + fullBlocks, enc_out + fullBlocks, remainingBlock, &enc_key, iv_enc, AES_ENCRYPT); 

You can then decrypt, since you are currently no problem. However, it is worth noting that you must declare dec_out as the same size as enc_out , because you currently override the dec_out buffer when decrypting.

Edit:

I raised this as an error in OpenSSL: https://rt.openssl.org/Ticket/Display.html?id=3182&user=guest&pass=guest , and so far there are some arguments as to whether this is actually a mistake or just (undocumented) undefined, the general consensus is that you should use EVP routines instead, rather than these low-level functions.

+7
source

The problem may be with the structure you are using, mainly due to the filling and size of the elements. Try serializing your input and it should work. in a simple way, try to allocate one char buffer and copy the contents of the structure to the buffer one after another and try to encrypt this buffer, and when decrypting, also follow the same method. Send here what you observe after that. We need to be able to comment better.

+2
source

The source code has made a registration for you. The problem is that you passed the wrong plaintext length to the AES function. You must pass the encslength (filled length) to AES_cbc_encrypt . Just change this line

 AES_cbc_encrypt((unsigned char *)&ticket, enc_out, inputslength, &enc_key, iv_enc, AES_ENCRYPT); 

to

 AES_cbc_encrypt((unsigned char *)&ticket, enc_out, encslength, &enc_key, iv_enc, AES_ENCRYPT); 

This should solve your problem.

+2
source

At least I also think that using struct and sizeof(a_struct_type) is not very good here.

The actual binary sequence struct USR_TICKET and the sizeof result will differ depending on the implementation of padding and byte alignment.

Here is the test result.

1) First, I download the latest version of openssl1.0.2c and build it, and also test the code here with this library in OS X 10.10.3, the code works fine.

2) And I tried to run the test 5 times. Every time, even the plain text original changes due to the filling of the struct (on my OS, the last 4 bytes will change every time).

3) So, the same input in the struct created another plain text , which finally produced another encrypted text. The user may be confused by the different ciphertext result when trying to encrypt the same input information (here raving, 1, 2015-6-25 ) with the same key and iv.

4) Also, just change the internal definition of struct , which will cause the same problem in description (3).

 typedef struct ticket { /* test field */ int ticketId; char username[19]; // here try to change from 20 to 19 char date[20]; // here try to change from 20 to other size } USR_TICKET; 

PS. Some results are derived from the description above (2),

Output1:

 Give a key length [only 128 or 192 or 256!]: 128 Username - ravinda Ticket Id - 1 Date - 2015-06-25 original: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 BB 3A 50 encrypt: BA 32 86 CC 71 55 2F 73 ED A1 C9 DE 00 32 1A 20 D9 A5 16 52 8A CD F0 F7 38 04 76 38 5A 47 35 3B A3 07 97 41 C4 C2 05 53 74 93 91 26 7E DE 40 47 decrypt: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 BB 3A 50 Username - ravinda Ticket Id - 1 Date - 2015-06-25 

Output2:

 Give a key length [only 128 or 192 or 256!]: 128 Username - ravinda Ticket Id - 1 Date - 2015-06-25 original: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 0B 10 5A encrypt: BE 60 0F FC 17 A3 42 4A 95 7C 39 DB BF 2C BA 59 42 DC 0C AD B2 20 76 6A 04 E3 DE 11 3E D0 AF 88 A5 B9 D2 25 D4 AE F0 B7 82 9F 13 39 80 39 61 9D decrypt: 01 00 00 00 72 61 76 69 6E 64 61 00 00 00 00 00 00 00 00 00 00 00 00 00 32 30 31 35 2D 30 36 2D 32 35 00 00 00 00 00 00 58 0B 10 5A Username - ravinda Ticket Id - 1 Date - 2015-06-25 
0
source

Source: https://habr.com/ru/post/957770/


All Articles