AES_128_CBC Encryption/Decryption using OpenSSL - c

I am currently trying to develop a C program to implement AES_128_CBC using the OpenSSL library. When I compile and run the program the first time, I get blocks of ciphertext and then my plaintext shows as being decrypted. This seems to be running smoothly. My problem is when I compile and run again. My blocks of ciphertext grow (by about 3x in size), yet my decryption remains the same. I would expect that if I was using the same key and IV, my cipher text would remain the same no matter how many times I compile and run the program. Can anyone see why this may be happening?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#define BUFSIZE 1024
/* AES key for Encryption and Decryption */
const static unsigned char aes_key[]= {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF};
unsigned char iv[] = {0x98,0x76,0x54,0x32,0x10,0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10,0xFE,0xDC,0xBA};
/* 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[BUFSIZE];
strcpy(aes_input, "Testing");
fprintf(stderr, "%s\n", aes_input);
/* Init vector */
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)];
unsigned char *e = enc_out;
/* 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);
fprintf(stderr, "Encrypted:");
while (*e) printf(" [%02x]", *e++);
printf("\n");
/* 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);
fprintf(stderr, "The Plaintext After Decryption is: %s\n", dec_out);
return 0;
}

You have overlooked a few things in your code.
First, you have declared your aes_input array without initializing it:
/* Input data to encrypt */
unsigned char aes_input[BUFSIZE];
Since you have not initialized this array, it may hold any value after this. Different compilers will do different things here. One way to initialize the entire array to a predictable value is by filling it with zeroes, like this:
unsigned char aes_input[BUFSIZE] = {0};
After this, the output should be predictable, no matter how many times you run it.
The contents of this entire array are relevant, because you are asking OpenSSL to encrypt the entire array, since you pass sizeof(aes_input) as the size of the plaintext to be encrypted when invoking the AES_cbc_encrypt() function:
AES_cbc_encrypt(aes_input, enc_out, sizeof(aes_input), &enc_key, iv, AES_ENCRYPT);
Since your input is largely uninitialized (except for the first eight bytes, which contain the string "Testing"), the output of this function can not be predicted.
Now when you print that output as follows:
while (*e) printf(" [%02x]", *e++);
you will only see the bytes up to the first byte that has the value of 0. This is a mistake, because the cipher text could easily (and does) contain bytes with the value of 0. You are treating the ciphertext as if it is a zero-terminated string, which it is not. Instead of stopping when encountering a 0 value, you should loop over all bytes in the ciphertext.

Related

How to store AES_encrypt size cipher with exact size calculation?

I am facing issues with AES encryption text and need a bit help regarding the same ,here is a explanation ,I am using this function to convert a plain text to cipher text.
/*
* Encrypt *len bytes of data
* All data going in & out is considered binary (unsigned char[])
*/
unsigned char *aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext, int *len)
{
/* max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes */
int c_len = *len + AES_BLOCK_SIZE, f_len = 0;
unsigned char *ciphertext = palloc(c_len);
/* allows reusing of 'e' for multiple encryption cycles */
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL);
/* update ciphertext, c_len is filled with the length of ciphertext generated,
*len is the size of plaintext in bytes */
EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len);
/* update ciphertext with the final remaining bytes */
EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len);
*len = c_len + f_len;
return ciphertext;
}
This function works perfectly when we use encryption but when i try to calculate length of cipher-text using strlen i don't get exact length ,
i have google and found a bit information regarding cipher-text printing in for
loop using %c equal to a length of size len.
my query is how would i store cipher text either in a file with the exact length ? as strlen(cipher-text) doesn't give exact length ,what is a way to store cipher text ?
Any help would be much appreciated :) Thanks in advance
The whole point of this interface is that *len now contains the length of the ciphertext (I suppose it starts with the value of the plain text). The EncryptFinal adds padding, so that the lengths are changed. If plain has length 16
the ciphertext will have length 32.
You cannot use strlen to compute the lengths. This can be done in the case of the argv entries, as these are strings by design. The key is more like a password here. It's a pretty encryption weak system (hard coded salt, 5 rounds etc.).
After ciphertext = aes_encrypt(&en, (unsigned char *)input[i], &len);
if you want to write the ciphertext to file, just do something like
FILE *cipher = NULL;
cipherfile=fopen("mycipher.txt", "wb");
if (NULL ==cipherfile){ /* error handling here */
}
if (1 != fwrite(ciphertext, len, 1, cipherfile)){ /* error handling */
}
close(cipherfile);
because the ciphertext length is in len. The original author at GitHub probably intended olen to be used instead (now it's unused). So (better) &olen can be used in both calls (aes_encrypt and fwrite) above, and then len stays the input length, which can be used in decryption checking, etc.

Same short encryption result using AES with different keys

I have a single plain text which is
unsigned char plaintext[] = "Hi, this is trial number one";
For the keys, instead of using something like:
unsigned char key[16] = "azertyuiopqsdfg";
I decided to use tons of them like "dog", "azkier", "jfieifdragon", ...
My code so far looks like this:
unsigned char *aes_encrypt(unsigned char *plaintext, unsigned char *key)
{
EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();
unsigned char iv[16] = "0000000000000000";
int c_len = strlen(plaintext) + AES_BLOCK_SIZE;
int f_len = 0;
unsigned char *ciphertext = malloc(c_len);
EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
EVP_EncryptUpdate(ctx, ciphertext, &c_len, plaintext, strlen(plaintext));
EVP_EncryptFinal_ex(ctx, ciphertext+c_len, &f_len);
EVP_CIPHER_CTX_free(ctx);
return ciphertext;
}
When I compile and run, the output looks something like this:
the key: dog
the plain: Hi, this is trial number one
ciphertext: 157a320
the key: azkier
the plain: Hi, this is trial number one
ciphertext: 157a320
.....
My questions are:
why I always get the same ciphertext even though I'm using different keys?
Also, why the ciphertext is really short? My plaintext is pretty long tho.
Thanks.
Update --> The way I call the aes_encrypt is like this:
unsigned char plaintext[] = "Hi, this is trial number one";
unsigned char *cipher;
cipher = aes_encrypt(plaintext, "dog");
printf("The cipher is: %x\n", cipher);
free(cipher);
unsigned char *cipher;
cipher = aes_encrypt(plaintext, "azkier");
printf("The cipher is: %x\n", cipher);
free(cipher);
In your test code:
printf("The cipher is: %x\n", cipher);
Well, of course that doesn't work -- %x prints the address of cipher as hexadecimal, not its contents. If you want a dump of the contents of cipher, you'll need to loop over each byte yourself.
Additionally, the key parameter to EVP_EncryptInit_ex is a fixed-length buffer, whose size is set based on the cipher you're using. It is not a string. Passing a short string may cause unpredictable behavior, as whatever data happens to be stored after the string ends may be used as part of the key.

AES encryption using openssl

I'm trying to encrypt using openssl AES encryption function.
here's my code:
#include <stdio.h>
#include <openssl/aes.h>
#include <string.h>
const static unsigned char aes_key[]={"passwordpasswor"}; //15 characters + \0
void print_data(const char *tittle, const void* data, int len);
int main() {
unsigned char aes_input[]="#!/bin/bash\necho hello world";
unsigned char iv[AES_BLOCK_SIZE];
memset (iv,0x00,AES_BLOCK_SIZE);
unsigned char enc_out[sizeof(aes_input)];
unsigned char dec_out[sizeof(aes_input)];
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);
//decryption
memset(iv,0x00,AES_BLOCK_SIZE);
AES_set_decrypt_key(aes_key,sizeof(aes_key)*8,&dec_key);
AES_cbc_encrypt(enc_out,dec_out,sizeof(aes_input),&dec_key,iv,AES_DECRYPT);
//verify
printf("original %s\n",aes_input);
printf("encrypted %s\n",enc_out);
printf("decrypted %s\n",dec_out);
return 0;
}
the code produces the following output (with an extra newline between each for clarity):
original #!/bin/bash
echo hello world
encrypted ���jv�.)��$I���b�:dmPvTQޜ�#!/bin/bash
echo hello world
decrypted #!/bin/bash
echo hello world
I've tried other messages, it seems that the encryption message will show the original message if used with printf.
You're trying to dump non-printable data to a terminal device, and specifically doing so using a library call that expects null-termination. The output of an AES encryption can contain bytes of any value (including embedded nullchar values)
You need the following:
Properly size your output buffer size. By default AES_cbc_encrypt uses pkcs padding and will use up to one full additional block for padding data.
Dump your output using an alternative mechanism, such as a trivial hexdump routine.
Both of the above are done below:
#include <stdio.h>
#include <openssl/aes.h>
#include <string.h>
static hex_print(const void *pv, size_t len)
{
static const char alpha[] = "0123456789abcdef";
const unsigned char *beg = pv, *end = beg+len;
for (; beg != end; ++beg)
{
putc(alpha[(*beg >> 4) & 0xF], stdout);
putc(alpha[*beg & 0xF], stdout);
}
putc('\n', stdout);
}
const static unsigned char aes_key[]={"passwordpasswor"}; //15 characters + \0
void print_data(const char *tittle, const void* data, int len);
int main() {
unsigned char aes_input[]="#!/bin/bash\necho hello world";
unsigned char enc_out[AES_BLOCK_SIZE * ((sizeof(aes_input) + AES_BLOCK_SIZE)/AES_BLOCK_SIZE)];
unsigned char dec_out[sizeof(aes_input)];
unsigned char iv[AES_BLOCK_SIZE] = {0};
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);
//decryption
memset(iv,0x00,AES_BLOCK_SIZE);
AES_set_decrypt_key(aes_key,sizeof(aes_key)*8,&dec_key);
AES_cbc_encrypt(enc_out,dec_out,sizeof(aes_input),&dec_key,iv,AES_DECRYPT);
//verify
printf("original %s\n",aes_input);
hex_print(enc_out, sizeof enc_out);
printf("decrypted %s\n",dec_out);
return 0;
}
Output
original #!/bin/bash
echo hello world
e389c96a76d708b42e29b4b4052449f1ffc762db3a646d1650765451de9c1dd0
decrypted #!/bin/bash
echo hello world
Note in particular the last byte of the encryption. It isn't 00, which means the printf call you were incorrectly using was marching beyond that buffer and into the land of undefined behavior. In fact, there are no nullchar bytes in that string (it is a different, yet closely-related problem when there is an embedded 00 in the middle of your data, in which case printf would have stopped prematurely.
In this case, I can speculate (with little value; such is the nature of undefined behavior) that the march took printf into the next automatic variable on the stack, which was the decrypted array.
Modifying the output sequence to use all hex output will demonstrate the difference between plaintext and encrypted data. For example, changing the last three functional lines of your program to:
//verify
hex_print(aes_input, sizeof(aes_input));
hex_print(enc_out, sizeof enc_out);
hex_print(dec_out, sizeof(dec_out));
will deliver the following output:
23212f62696e2f626173680a6563686f2068656c6c6f20776f726c6400
e389c96a76d708b42e29b4b4052449f1ffc762db3a646d1650765451de9c1dd0
23212f62696e2f626173680a6563686f2068656c6c6f20776f726c6400
which makes sense. if you walk the bytes (two digits per) in the original and decrypted strings you can see they're (a) equal, (b) not equal to the cipher text, and (c), a little time in an ascii table will show you they are indeed the original text message.
Best of luck.
You have encrypted the terminating nul of the string by using the argument
sizeof(aes_input)
You are consistent, so it gets decrypted too. Unfortunately the encrypted string no longer has a nul terminator, since it was encrypted too. So I recommend
strlen(aes_input)
for the arguments (but not for the string allocation). You must also terminate the two strings enc_out[] and dec_out[].

Reading in 64 byte chunks from a file in C

I can't seem to wrap my head around this concept of reading in 64byte chunks, then using the blowfish,
BF_cfb64_encrypt(source, dest, sizeof(source), &bf_key, iv, &enc, BF_DECRYPT)
function to encrypt? it. I know how to use the BF function, but reading in 64 bytes, from say a 4096 byte file is my confusion. Any tips or suggestions would be greatly appreciated. My understanding is that 1 char is a byte, so does that mean I simply keep a count and when char count is 8 then that means I have read 64 bytes, hence encrypt, then write to file, and repeat until the entire file is parsed?
First, familiarity with your stream cipher is probably warranted. Blowfish encrypts/decrypts using a block size of 64 bits; not bytes. So long as you understand the 64 "bytes" you're referring to is your requirement and not Blowfishes, and that Blowfish only requires 8-byte blocks.
That said, a loop passing over a file with a size that is a multiple of the algorithm block size, extracting decrypted data one 64 byte frame at a time is certainly doable.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <openssl/blowfish.h>
int main(int argc, char *argv[])
{
if (argc < 2)
return EXIT_FAILURE;
FILE * fp = fopen(argv[1], "rb");
if (fp == NULL)
{
perror(argv[1]);
return EXIT_FAILURE;
}
// your key bytes would be here (obviously).
unsigned char key[16] = "1234567890123456";
int key_len = sizeof(key)-1;
// setup the key schedule.
BF_KEY bf_key;
BF_set_key(&bf_key, key_len, key);
// and setup the initialization vector. normally the IV is
// randomly generated when encrypting, then stored as the
// lead 8 bytes of ciphertext output. this assumes you're
// iv is static (all zeros) similar to SSH
unsigned char iv[8] = {0};
int n = 0;
// finally, begin reading the data in chunks of 64 bytes
// sending it through the blowfish algorithm
unsigned char source[64];
unsigned char dest[64];
while (fread(source, sizeof(source), 1, fp) == 1)
{
BF_cfb64_encrypt(source, dest, sizeof(dest), &bf_key, iv, &n, BF_DECRYPT);
// do something with your dest[] plaintext block
}
fclose(fp);
return 0;
}
That sample is fairly trivial, but it brings up some things about symmetric block algorithms and padding that you may not be considering (or perhaps you have, and it simply has nothing to do with this question).
Symmetric block algorithms like Blowfish operate on a block size. In the case of Blowfish that block size is 64 bits (8 bytes). This means that the encryption/decryption operation always happens in 64-bit-sized chunks. If if you were using the lower level openssl apis you would have to encrypt (and decrypt) the data in blocks no larger than that. The higher-level APIs (such as BF_cfb64_encrypt) are designed to allow "stream mode" meaning you can submit your data in larger chunks, so long as they're sized to be a multiple of the block size. and you retain the iv and n values between chained successive calls to the API.
Finally, I started writing this rather long diatribe about symmetric block algorithms and padding modes, but realized this isn't really appropriate to this question, so I can only suggest you research into them. I've a suspicion you'll need to at some point.
Why donot you use read system call?
Required Include Files
#include <unistd.h>
Function Definition
size_t read(int fildes, void *buf, size_t nbytes);
Parameters
int fildes : The file descriptor of where to read the input. You can either use a file descriptor obtained from the open system call, or you can use 0, 1, or 2, to refer to standard input, standard output, or standard error, respectively.
const void *buf : A character array where the read content will be stored.
size_t nbytes : The number of bytes to read before truncating the data. If the data to be read is smaller than nbytes, all data is saved in the buffer.
return value : Returns the number of bytes that were read. If value is negative, then the system call returned an error.
Sample
#include <unistd.h>
int main()
{
char data[128];
if(read(0, data, 128) < 0)
write(2, "An error occurred in the read.\n", 31);
exit(0);
}
** Pseudo Code **
int no_byte_read = 0; // Will store number of byte read
void *buffer; //temporary storage for read data
FILE *fp; //File pointer of the file to be read
buffer = (void*) malloc(64); //Allocate space to temporary buffer.
fp = open file to be read;
do{
no_byte_read = read(fp, buffer, 64); // read 64 byte from file and store in buffer
if(no_byte_read < 0){
printf("Error occoured in read");
break;
}
}while(no_byte_read == 64) //If this condition is true that means still some bytes
remain in file which must be read.

Encrypted string is not the same as test vector

So I am trying to encrypt some data with the EVP-API from OpenSSL. But I am not recieving the same result as the test vectors.
This is the main function
#include <stdio.h>
#include <windows.h>
#include <openssl\aes.h>
#include <openssl\evp.h>
int main()
{
unsigned char *to = (unsigned char*)malloc(2056);
ZeroMemory(to,2056);
int *tosize;
unsigned char* key = (unsigned char*)"0000000000000000000000000000000000000000000000000000000000000000";
unsigned char* iv = (unsigned char*)"00000000000000000000000000000000";
unsigned char* plain = (unsigned char*)"00000000000000000000000000000000";
to = AESEncrypt(key,iv,plain,strlen((const char*)plain));
if (to != 0)
{
for (int i = 0; i < strlen((const char*)to);i++)
{
printf("%x02", (int*)UCHAR(to[i]));
}
}
}
And this is the function which I am trying to call. No errors recieved. Every call is true (no error).
unsigned char* AESEncrypt(unsigned char* key, unsigned char*iv, unsigned char*plain, size_t plainsize)
{
EVP_CIPHER_CTX *x = (EVP_CIPHER_CTX*) malloc(sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(x);
if (EVP_EncryptInit(x,EVP_aes_256_cbc(),key,iv))
{
unsigned char* to = (unsigned char*) malloc(plainsize + EVP_CIPHER_CTX_block_size(x));
int tosize = 0;
if(EVP_EncryptUpdate(x,to,&tosize,plain,plainsize))
{
if (EVP_EncryptFinal(x,to,&tosize))
{
return to;
}
}
}
return 0;
}
This is the test vector:
KEY = 0000000000000000000000000000000000000000000000000000000000000000
IV = 00000000000000000000000000000000
PLAINTEXT = 80000000000000000000000000000000
CIPHERTEXT = ddc6bf790c15760d8d9aeb6f9a75fd4e
This is what I am recieving:
CIPHERTEXT = 5a0215028e.... and it goes on. As you see it is not correct.
What could I be doing wrong?
CAVS test vectors follow a fairly straight-forward pattern. From test to test they vary to certain degrees, but one thing is eminently consistent:
Hex Strings are BYTE representations; NOT character data
Hence your "test" is entirely wrong. You're using a key, iv, and plain text that is filled with the character '0', not the byte value 0. So obviously no wonder you're getting a different result.
For that specific test your arrays should be:
unsigned char key[64] = {0};
unsigned char iv[16] = {0};
unsigned char plain[16] = {0};
Furthermore, your size sent to your encryption function should be the byte count of your plain text. Finally, your encryption function should ideally take a target buffer AND a modifiable size as the output parameters.
int AESEncrypt256(
unsigned char* key, // key must be 64 bytes wide.
unsigned char *iv, // IV must be 16 bytes wide.
unsigned char *src, // source buffer to encryt
unsigned int src_len, // length of source buffer in bytes
unsigned char *dst, // target buffer to write to
unsigned int *dst_len); // in: size of dst, out: bytes written to dst
And code the function to match those parameters. You'll be glad you did.
CAVS tests vectors are not "text". They are bytes and should be treated as such. You need to get a handle on that now, as the Monte Carlo tests are likely going to cost you some hair if you don't.
Do yourself an enormous favor and write some code now that translates a string of hex digits into an unsigned char byte array. You'll need the same to translate back for result strings. And make these routines solid as you will be using them a lot when writing these tests.
Spoiler Alert
Test the string for an odd number of chars first. If it is odd, the first byte in your translated buffer should be based on 0c, where c is the first char in the input string. From then on (or if the number of chars is even, then from the very start) grab them two-at-a-time when converting the rest of the byte string into real bytes. This means this
123456
results in a byte array of
{ 0x12, 0x34, 0x56 }
while this:
89AB1
should be:
{0x08, 0x9A, 0xB1 }

Resources