OpenSSL and AES - c

i'm working with OpenSSL lib and get very strange effect with AES encrypt/decrypt: if i'll change some byte in encrypted message and decrypt it, i will see the part of original message, that isn't suppose to be. This is source code:
#include <openssl/evp.h>
#include <string.h>
int do_crypt(void)
{
int outlen, inlen;
FILE *in, *out;
in = fopen("in.txt", "r");
out = fopen("out.txt", "w");
unsigned char key[32];
strcpy(key, "10000000000000000000000000000002");
unsigned char iv[8];
unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE];
EVP_CIPHER_CTX ctx;
const EVP_CIPHER * cipher;
EVP_CIPHER_CTX_init(&ctx);
cipher = EVP_aes_256_cfb();
EVP_EncryptInit(&ctx, cipher, key, 0);
while(1) {
inlen = fread(inbuf, 1, BUFSIZE, in);
if(inlen <= 0) break;
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) return 0;
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_EncryptFinal(&ctx, outbuf, &outlen)) return 0;
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
int do_decrypt(char *infile)
{
int outlen, inlen;
FILE *in, *out;
in = fopen("out.txt", "r");
out = fopen("out2.txt", "w");
unsigned char key[32];
strcpy(key, "10000000000000000000000000000002");
unsigned char iv[8];
unsigned char inbuf[BUFSIZE], outbuf[BUFSIZE];
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
EVP_DecryptInit(&ctx, EVP_aes_256_cfb(), key, 0);
while(1) {
inlen = fread(inbuf, 1, BUFSIZE, in);
if(inlen <= 0) break;
if(!EVP_DecryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen)) return 0;
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_DecryptFinal(&ctx, outbuf, &outlen)) return 0;
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}
main(int argc, char **argv){
if(atoi(argv[1]) == 1)
do_crypt(0);
if(atoi(argv[1]) == 2)
do_decrypt(0);
}
What could be wrong?

What's wrong is your expectation that the whole message becomes unreadable because a single byte was changed.
Which parts of the message become unreadable depends on the chosen encryption mode. You're using CFB. This means if you change a single byte in the ciphertext, the corresponding byte and the block after that get corrupted, and the cipher recovers from the error afterwards.
PCBC will corrupt all output after the error. But it still does not detect the error.
I recommend adding authentication (either a MAC, or a mode with integrated authentication such as AES-GCM).

This is exactly what is supposed to be.
Greatly oversimplifying, message is encrypted and decrypted from left to right. Until decryption routine hits changed byte, decrypted text MUST match original message. What happens after changed byte is dependent on cipher mode, but decryption of the left part of the text cannot be affected by this change.

Related

Usage of EVP_MAX_BLOCK_LENGTH with OPENSSL

I'm working with openssl but i have some doubts..
this is the code presented in the openssl doc, to encrypt with AES 128.
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
EVP_CIPHER_CTX *ctx;
/*
* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789abcdeF";
unsigned char iv[] = "1234567887654321";
/* Don't set key or IV right away; we want to check lengths */
ctx = EVP_CIPHER_CTX_new();
EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL,
do_encrypt);
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
/* Now we can set key and IV */
EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt);
for (;;) {
inlen = fread(inbuf, 1, 1024, in);
if (inlen <= 0)
break;
if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if (!EVP_CipherFinal_ex(ctx, outbuf, &outlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
I have 2 question:
Why, for the output buffer, its allocated 1024 + EVP_MAX_BLOCK_LENGTH?
unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
Why don't we just allocate 1024 for both buffers?
what is the purpose of the EVP_CipherFinal_ex? To encrypt the last block? if so, shouldn't it already do the EVP_CipherUpdate() into the for loop?
Thank you all in advice!

different binary when i encrypt and decrypt txt file using AES in C [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm trying to encrypt and decrypt txt file using AES in C.
but I got problem.
I watched binary of plain.txt and decrypted.txt.
so There's not same binary.
I want to make same binary.
How to fix this problem ?
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
void encrypt(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
int fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outLen1 = 0; int outLen2 = 0;
unsigned char *indata = malloc(fsize);
unsigned char *outdata;
unsigned blocksize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, sizeof(unsigned char), fsize, ifp);
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
blocksize = EVP_CIPHER_CTX_block_size(&ctx);
outdata = malloc(fsize * 2 + blocksize + 15);
EVP_EncryptUpdate(&ctx, outdata, &outLen1, indata, fsize);
EVP_EncryptFinal(&ctx, outdata, &outLen2);
fwrite(outdata, sizeof(unsigned char), fsize, ofp);
free(indata);
free(outdata);
}
void decrypt(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
int fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outLen1 = 0; int outLen2 = 0;
unsigned char *indata = malloc(fsize);
unsigned char *outdata;
unsigned blocksize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, sizeof(unsigned char), fsize, ifp);
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
blocksize = EVP_CIPHER_CTX_block_size(&ctx);
outdata = malloc(fsize * 2 + blocksize + 15);
EVP_DecryptUpdate(&ctx, outdata, &outLen1, indata, fsize);
EVP_DecryptFinal(&ctx, outdata, &outLen2);
fwrite(outdata, sizeof(unsigned char), fsize, ofp);
free(indata);
free(outdata);
}
int main(int argc, char *argv[])
{
FILE *fIN;
FILE *fOUT;
errno_t error;
char buf[255 + 1];
if ((error =fopen_s(&fIN,"C://Projects/test_curl/Debug/plain.txt","rb"))!=0 {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
if ((error = fopen_s(&fOUT, "C://Projects/test_curl/Debug/cyphertext.txt", "wb")) != 0) {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
encrypt(fIN, fOUT);
fclose(fIN);
fclose(fOUT);
if ((error = fopen_s(&fIN, "C://Projects/test_curl/Debug/cyphertext.txt", "rb")) != 0) {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
if ((error = fopen_s(&fOUT, "C://Projects/test_curl/Debug/decrypted.txt", "wb")) != 0) {
strerror_s(buf, 255, error);
printf("fopen_s error : \n%s\n", buf);
return 0;
}
decrypt(fIN, fOUT);
fclose(fIN);
fclose(fOUT);
return 0;
}
You're basically not following the letter of the instructions on how to invoke the API's you're calling. Among the things wrong:
The EVP_EncryptUpdate function encrypts the given data, storing the size of the encryption output in its size out param, which you're basically ignoring. The same is true on the decryption side.
You blast over the initial encrypted/decrypted data block with your invoke of EVP_EncryptFinal (and EVP_DecryptFinal), in contradiction to precisely what the libcrypto documentation specifically warns you NOT to do.
Your output size in both functions is wrong. You're writing fsize bytes, which was the input size, not the output size. the latter is provided in the output-size params which you're ignoring (see the first item in this list).
The following functions do what you want. Compare them to what you had (wishful thinking on my part). I took the liberty of not using encrypt as the name, as that is a commonly provided DES function in unistd.h.
void encrypt_data(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
long fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outlen=0;
unsigned char *indata = malloc(fsize);
unsigned char *outdata;
unsigned blocksize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, sizeof(unsigned char), fsize, ifp);
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
// round up to nearest block size, this will add a complete
// block of additional padding if the input size is perfectly
// a factor of the block size
blocksize = EVP_CIPHER_CTX_block_size(&ctx);
outdata = malloc(((fsize + blocksize)/blocksize) * blocksize);
EVP_EncryptUpdate(&ctx, outdata, &outlen, indata, (int)fsize);
fwrite(outdata, 1, outlen, ofp);
EVP_EncryptFinal(&ctx, outdata, &outlen);
fwrite(outdata, 1, outlen, ofp);
free(indata);
free(outdata);
}
void decrypt_data(FILE *ifp, FILE *ofp)
{
fseek(ifp, 0L, SEEK_END);
long fsize = ftell(ifp);
fseek(ifp, 0L, SEEK_SET);
EVP_CIPHER_CTX ctx;
int outlen = 0;
unsigned char *indata = malloc(2 * fsize);
unsigned char *outdata = indata + fsize;
unsigned char ckey[] = "thiskeyisverybadaaaaaaaaaaaaaaab";
unsigned char ivec[] = "dontusethisinputaaaaaaaaaaaaaaab";
fread(indata, 1, fsize, ifp);
EVP_DecryptInit(&ctx, EVP_aes_256_cbc(), ckey, ivec);
EVP_DecryptUpdate(&ctx, outdata, &outlen, indata, (int)fsize);
fwrite(outdata, 1, outlen, ofp);
EVP_DecryptFinal(&ctx, outdata, &outlen);
fwrite(outdata, 1, outlen, ofp);
free(indata);
}
Those will work.
Best of luck.

C output differs from openssl output

Given the following openssl example prog
#include <openssl/evp.h>
int do_crypt(FILE* in, FILE* out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
unsigned char inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
EVP_CIPHER_CTX* ctx;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789abcdeF";
unsigned char iv[] = "1234567887654321";
/* Don't set key or IV right away; we want to check lengths */
ctx = EVP_CIPHER_CTX_new();
EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL, do_encrypt);
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
/* Now we can set key and IV */
EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, do_encrypt);
for (;;) {
inlen = fread(inbuf, 1, 1024, in);
if (inlen <= 0)
break;
if (!EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if (!EVP_CipherFinal_ex(ctx, outbuf, &outlen)) {
/* Error */
EVP_CIPHER_CTX_free(ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
int main(int argc, char* argv[])
{
FILE *fpIn;
FILE *fpOut;
fpIn = fopen("text-in.txt", "rb");
fpOut = fopen("text-out.txt", "wb");
int test = do_crypt(fpIn, fpOut, 1);
fclose(fpIn);
fclose(fpOut);
return 0;
}
I would expect that
openssl aes-128-cbc -in text-in.txt -K 0123456789abcdeF -iv 1234567887654321
would create the same output. But it doesn't. The C prog can decrypt the files it encrypted. But it can't decrypt the files encrypted with openssl.
The encryption key and IV are not actually text but binary data.
When you do the encryption on the command line, the -K and -iv arguments expect their input in hex.
So the key you're passing in is actually:
\x01\x23\x45\x67\x89\xab\xcd\xef\x00\x00\x00\x00\x00\x00\x00\x00
And the IV is:
\x12\x34\x56\x78\x87\x65\x43\x21\x00\x00\x00\x00\x00\x00\x00\x00
Both have tail padding of 0's because the key that was specified was too short.
In order to match the program's output, you need to pass in the hex codes of the characters you specified. For example, 1 is 30, 2 is 31, a is 61, F is 46, and so forth:
openssl aes-128-cbc -in text-in.txt -K 30313233343536373839616263646546 -iv 31323334353637383837363534333231

AES CTR encryption and decryption

So I have this code, which basically, encrypts two plain text messages and then try to decrypt them, and print. The problem is that first message is recovered fine but the second is garbage. I downloaded this code from this tutorial and then modified it to work with strings instead of files as I need it for sending encrypted text over sockets. So length of the plaintext won't be known to other endpoint, is there a way to find the length or will I have to send the length of plaintext along with the cipher somehow?
Now, I think there is a problem in the break condition of decrypt.
Also, is the main() code right conceptually: encrypt messages with updating state and then reset state and decrypt messages with updating state?
And is there a way to find out the actual length of cipher text (not the buffer)?
This is just a dummy program that I was trying out to understand how AES CTR will work.
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
// Code example uses partail code from: http://stackoverflow.com/questions/3141860/aes-ctr-256-encryption-mode-of-operation-on-openssl
// Mostly in the ctr_ state, and init_ctr functions.
struct ctr_state
{
unsigned char ivec[AES_BLOCK_SIZE];
unsigned int num;
unsigned char ecount[AES_BLOCK_SIZE];
};
int init_ctr(struct ctr_state *state, const unsigned char iv[16])
{
/* aes_ctr128_encrypt requires 'num' and 'ecount' set to zero on the
* first call. */
state->num = 0;
memset(state->ecount, 0, AES_BLOCK_SIZE);
/* Initialise counter in 'ivec' to 0 */
memset(state->ivec + 8, 0, 8);
/* Copy IV into 'ivec' */
memcpy(state->ivec, iv, 8);
}
void fencrypt(char* text, char* cipher, const unsigned char* enc_key, struct ctr_state* state)
{
AES_KEY key;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
int offset=0;
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set encryption key.");
exit(1);
}
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
printf("while going\n");
memcpy(indata, text+offset, AES_BLOCK_SIZE);
AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);
memcpy(cipher+offset, outdata, AES_BLOCK_SIZE);
offset=offset+AES_BLOCK_SIZE;
if (offset > strlen(text))
{
break;
}
}
}
void fdecrypt(char* cipher, char* text, const unsigned char* enc_key, struct ctr_state* state)
{
AES_KEY key;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
int offset=0;
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set decryption key.");
exit(1);
}
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
memcpy(indata, cipher+offset, AES_BLOCK_SIZE);
//printf("%i\n", state.num);
AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);
memcpy(text+offset, outdata, AES_BLOCK_SIZE);
offset=offset+AES_BLOCK_SIZE;
if (offset > strlen(cipher))
{
break;
}
}
}
int main(int argc, char *argv[])
{
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
char* plain="quick brown fox jumped over the lazy dog what ";
char* plain2="a dog he is idiot who is the genius ";
char cipher[128];
char cipher2[128];
char recovered[128];
char recovered2[128];
const unsigned char* enc_key="123456789abcdef0";
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
init_ctr(&state, iv); //Counter call
printf("Plain text length:%lu\n",strlen(plain));
// BIO_dump_fp(stdout, plain, strlen(plain));
// printf("Plain text:%s\n",plain);
fencrypt(plain, cipher,enc_key,&state);
fencrypt(plain2, cipher2,enc_key,&state);
// cipher[strlen(plain)]='\0';
// BIO_dump_fp(stdout, cipher, strlen(plain));
init_ctr(&state, iv); //Counter call
fdecrypt(cipher,recovered,enc_key,&state);
fdecrypt(cipher2,recovered2,enc_key,&state);
// printf("Cipher text length:%lu\n",strlen(cipher));
printf("Recovered text:%s\n",recovered);
printf("Recovered text:%s\n",recovered2);
return 0;
}
CTR mode doesn't need separate encrypt and decrypt method. Encryption key can be set once. OpenSSL's AES_ctr128_encrypt takes care of most of the work, so the code can be simplified.
Also we really need vector test. Here we are just testing with random text "quick brown fox...", we get back the same text, but there is NO guarantee that 'cipher' was correct, and wether or not the encryption is AES quality. I'll add a quick vector test later if there is time.
void init_ctr(struct ctr_state *state, const unsigned char iv[16])
{
state->num = 0;
memset(state->ecount, 0, 16);
memcpy(state->ivec, iv, 16);
}
void crypt_message(const u8* src, u8* dst, unsigned int src_len, const AES_KEY* key, const u8* iv)
{
struct ctr_state state;
init_ctr(&state, iv);
AES_ctr128_encrypt(src, dst, src_len, key, state.ivec, state.ecount, &state.num);
}
int main()
{
int len;
char source[128];
char cipher[128];
char recovered[128];
unsigned char iv[AES_BLOCK_SIZE];
const unsigned char* enc_key = (const unsigned char*)"123456789abcdef0";
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
AES_KEY key;
AES_set_encrypt_key(enc_key, 128, &key);
strcpy(source, "quick brown fox jumped over the lazy dog what.");
len = strlen(source);
memset(recovered, 0, sizeof(recovered));
crypt_message((const u8*)source, (u8*)cipher, len, &key, iv);
crypt_message((const u8*)cipher, (u8*)recovered, len, &key, iv);
printf("Recovered text:%s\n", recovered);
strcpy(source, "a dog he is idiot who is the genius.");
len = strlen(source);
memset(recovered, 0, sizeof(recovered));
crypt_message((const u8*)source, (u8*)cipher, len, &key, iv);
crypt_message((const u8*)cipher, (u8*)recovered, len, &key, iv);
printf("Recovered text:%s\n", recovered);
return 0;
}
To encrypt/decrypt files, or send/receive:
void crypt_file(const u8* src_file, const u8* dst_file, const AES_KEY* key, const u8* iv)
{
struct ctr_state state;
init_ctr(&state, iv);
const int buffer_size = 512; //not less than 16
unsigned char buffer_in[buffer_size];
unsigned char buffer_out[buffer_size];
int bytes_read;
//open files and/or socket
//file/message loop
{
//read source, obtain buffer_in and bytes_read
AES_ctr128_encrypt(buffer_in, buffer_out, bytes_read, key, state.ivec, state.ecount, &state.num);
//write buffer_out/bytes_read to destination
}
//close handles
}
In your code, fdecrypt() contains strlen(cipher). However cipher is pure binary data, strlen doesn't work with it. You have to supply the length manually. I added len parameter to fdecrypt. In main I use strlen(plaintext) for simplicity, though it should be the true length of cipher data. Changes are denoted by ##change
void fdecrypt(unsigned int len, char* cipher, char* text, const unsigned char* enc_key, struct ctr_state* state)
{
AES_KEY key;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
int offset=0;
//Initializing the encryption KEY
if (AES_set_encrypt_key(enc_key, 128, &key) < 0)
{
fprintf(stderr, "Could not set decryption key.");
exit(1);
}
//Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext
while(1)
{
memcpy(indata, cipher+offset, AES_BLOCK_SIZE);
//printf("%i\n", state.num);
AES_ctr128_encrypt(indata, outdata, AES_BLOCK_SIZE, &key, state->ivec, state->ecount, &state->num);
memcpy(text+offset, outdata, AES_BLOCK_SIZE);
offset=offset+AES_BLOCK_SIZE;
//if (offset > strlen(cipher))##changed
if (offset > len)
{
break;
}
}
}
int main(int argc, char *argv[])
{
unsigned char iv[AES_BLOCK_SIZE];
struct ctr_state state;
char* plain="quick brown fox jumped over the lazy dog what ";
char* plain2="a dog he is idiot who is the genius ";
char cipher[128];
char cipher2[128];
char recovered[128];
char recovered2[128];
const unsigned char* enc_key=(const unsigned char*)"123456789abcdef0";
if(!RAND_bytes(iv, AES_BLOCK_SIZE))
{
fprintf(stderr, "Could not create random bytes.");
exit(1);
}
init_ctr(&state, iv); //Counter call
printf("Plain text length:%lu\n",strlen(plain));
// BIO_dump_fp(stdout, plain, strlen(plain));
// printf("Plain text:%s\n",plain);
fencrypt(plain, cipher,enc_key,&state);
fencrypt(plain2, cipher2,enc_key,&state);
// cipher[strlen(plain)]='\0';
// BIO_dump_fp(stdout, cipher, strlen(plain));
init_ctr(&state, iv); //Counter call
fdecrypt(strlen(plain), cipher,recovered,enc_key,&state);//##changed
fdecrypt(strlen(plain2), cipher2,recovered2,enc_key,&state);//##changed
// printf("Cipher text length:%lu\n",strlen(cipher));
printf("Recovered text:%s\n",recovered);
printf("Recovered text:%s\n",recovered2);
return 0;
}

Issues with encrypting a file using openssl evp api(aes256cbc)

I am trying to read a file(.txt) in this case and encrypting/decrypting it with AES256CBC using EVP api of openssl.(read(plain.txt)->create(encrypt.txt)->create(decrypt.txt))
# include <stdio.h>
# include <stdlib.h>
# include <openssl/evp.h>
# include <openssl/aes.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <assert.h>
# include <error.h>
# include "debug.h"
# define SIZE 32
char buf[SIZE];
int aes_init(unsigned char* pwd, unsigned int pwd_len, unsigned char * salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
{
int i, rounds =5; /* rounds */
unsigned char key[32], iv[32];
i = EVP_BytesToKey(EVP_aes_256_cbc(),EVP_sha1(),salt,pwd,pwd_len,rounds,key,iv);
if(i != 32)
{
printf("\n Error,Incorrect key size generated:%d:\n",i);
return -1;
}
EVP_CIPHER_CTX_init(e_ctx);
EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
EVP_CIPHER_CTX_init(d_ctx);
EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
return 0;
}
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
*len = ci_len + flen;
return cipher_text;
}
unsigned char* aes_decrypt(EVP_CIPHER_CTX *e, unsigned char * c_text, unsigned int * len)
{
int pi_len = (*len);
int flen = 0;
unsigned char * plain_text = malloc(pi_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plain_text, &pi_len, c_text, *len);
EVP_DecryptFinal_ex(e, plain_text+pi_len, &flen);
(*len) = pi_len + flen;
return plain_text;
}
int main(int argc,char **argv)
{
if(argc != 2)
{
perror("\n Error:\nCorrect Usage: Enter Password to be used");
exit(-1);
}
EVP_CIPHER_CTX en,de; /* The EVP structure which keeps track of all crypt operations see evp.h for details */
int in, out, fd, dec,i =0; /* fd for input and output files and random dev*/
unsigned int pwd_len = strlen((const char *)argv[1]); /* Length of the pwd supplied by the user */
unsigned char *pwd =(unsigned char*) argv[1]; /* Pointer to the pwd supplied by the user */
unsigned int rd= 0;
unsigned char salt[8];
unsigned char * encry = NULL, *decry = NULL;
i =0;
if((in = open("plain.txt",O_RDONLY)) == -1) /* Opening a plain text file for encryption */
{
perror("\n Error,Opening file for reading::");
exit(-1);
}
if((fd = open("/dev/random", O_RDONLY)) == -1)
{
perror("\n Error,Opening /dev/random::");
exit(-1);
}
else
{
if(read(fd,salt,8) == -1)
{
perror("\n Error,reading from /dev/random::");
exit(-1);
}
}
if(aes_init(pwd,pwd_len,(unsigned char*) salt,&en,&de)) /* Generating Key and IV and initializing the EVP struct */
{
perror("\n Error, Cant initialize key and IV:");
return -1;
}
if((out = open("encrypt.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("ENC%d",out);
perror("\n Error,Opening the file to be written::");
exit(-1);
}
rd =0;
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
if((write(out,encry,rd)) != rd)
{
perror("\n Error,Required encrypted bytes not written::");
exit(-1);
}
free(encry);
}
rd =0;
if((dec = open("dec22.txt",O_RDWR|O_CREAT,0400 | 0200)) == -1)
{
dbug_p("dec%d",dec);
perror("\n Error,Opening the decrypting o/p file::");
exit(-1);
}
if((lseek(out,0,SEEK_SET)) != 0) perror("\n Error:setting lseek::");
for(i=0;i<SIZE;i++) buf[i] =0;
while((rd = read(out,dbuf,SIZE)) >0)
{
decry = aes_decrypt(&de,(unsigned char*) dbuf, &rd);
if((write(dec,decry,rd)) != rd)
{
perror("\n Error,Required decrypted bytes not written::");
exit(-1);
}
free(decry);
}
close(in);
close(fd);
EVP_CIPHER_CTX_cleanup(&en);
EVP_CIPHER_CTX_cleanup(&de);
return 0;
}
My problem was that my when i decrypt an encrypted file i get a file which is not properly decrypted (e.g. correct stringgarbagecorrect stringgarbage ...)
abhi#ubuntu:~/mpro/EFF$ cat plain.txt
Today is tuesday
tomorrow is wednesday
then thursday and friday and saturday
finally sunday
Decrypted file
cat dec22.txt
Today is tuesdayw)Q������O-%�A�8���R��.�O���and saturday
finally sunday
What can be the reason for this. Is it reading something else also or i am making any foolish error somewhere.
EDIT: If I just encrypt an array (tried with 36char long) it correctly encrypted and decrypted without printing any garbage.
I guess i am missing(not handling) some *nix file structure details ..??
Or is there any better way to do this encryption on a file.?
Many thanks
I think your analysis is wrong. This loop is problematic:
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::\n",buf);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
dbug_p("\n EN::%s::\n",encry);
decry = aes_decrypt(&de,(unsigned char*) encry,&rd);
dbug_p("\n DE::%s::\n",decry);
free(encry);
free(decry);
}
Firstly because you print using %s which expects a zero terminator. However, the encrypted/decrypted data is not zero terminated. Instead, you should print rd characters using a loop like for (i = 0; i < rd; i++) printf("%02x "); - this is why your analysis of the problem is likely flawed.
Secondly, I assume that in your real problem, you are reading SIZE bytes at a time and sending them to aes_decrypt() separately. This will fail because EVP_DecryptFinal_ex() is getting called too early (before all the encrypted blocks were read). You have two options. Either you send the read bytes through EVP_DecryptUpdate() in each loop iteration, and call EVP_DecryptFinal() after completing the loop (and init accordingly before the loop), or you read the whole file into a buffer first, and then send it through aes_decrypt() in one go.
Or in other words, you need to send the whole data block resulting from aes_encrypt() later to aes_decrypt(). You cannot send them in different chunks, unless you split the functions up and use the EVP "update" functions on the separate chunks.
while((rd = read(in,buf,SIZE)) >0)
{
dbug_p("\nREAD::%s::%d*\n",buf,rd);
encry = aes_encrypt(&en,(unsigned char*) buf, &rd);
and,
unsigned char* aes_encrypt(EVP_CIPHER_CTX *e,unsigned char * plain_text, unsigned int * len ) /* this function encryptes the file:fd is passed as parameter */
{
int ci_len = (*len) + AES_BLOCK_SIZE;
int flen =0;
unsigned char * cipher_text = malloc(ci_len);
EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL); /* allows reusing of e for multiple cipher cycles */
EVP_EncryptUpdate(e, cipher_text, &ci_len, plain_text, *len); /* Update cipher text */
EVP_EncryptFinal_ex(e, cipher_text+ci_len, &flen); /* updates the remaining bytes */
You are calling EVP_EncryptFinal_ex multiple times. It is supposed to be called only once at the end.
Same is true for how you are doing decryption.
Here is a simple example from the man page on how to do encrypt.
Have a similar function for decryption and it should work.
int do_crypt(char *outfile)
{
unsigned char outbuf[1024];
int outlen, tmplen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
unsigned char iv[] = {1,2,3,4,5,6,7,8};
char intext[] = "Some Crypto Text";
EVP_CIPHER_CTX ctx;
FILE *out;
EVP_CIPHER_CTX_init(&ctx);
EVP_EncryptInit_ex(&ctx, EVP_bf_cbc(), NULL, key, iv);
if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen, intext, strlen(intext)))
{
/* Error */
return 0;
}
/* Buffer passed to EVP_EncryptFinal() must be after data just
* encrypted to avoid overwriting it.
*/
if(!EVP_EncryptFinal_ex(&ctx, outbuf + outlen, &tmplen))
{
/* Error */
return 0;
}
outlen += tmplen;
EVP_CIPHER_CTX_cleanup(&ctx);
/* Need binary mode for fopen because encrypted data is
* binary data. Also cannot use strlen() on it because
* it wont be null terminated and may contain embedded
* nulls.
*/
out = fopen(outfile, "wb");
fwrite(outbuf, 1, outlen, out);
fclose(out);
return 1;
}
the following example is reading a file as your case. See how Update (called multiple times) and Final (once at the end) routines are used.
int do_crypt(FILE *in, FILE *out, int do_encrypt)
{
/* Allow enough space in output buffer for additional block */
inbuf[1024], outbuf[1024 + EVP_MAX_BLOCK_LENGTH];
int inlen, outlen;
/* Bogus key and IV: we'd normally set these from
* another source.
*/
unsigned char key[] = "0123456789";
unsigned char iv[] = "12345678";
/* Don't set key or IV because we will modify the parameters */
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_rc2(), NULL, NULL, NULL, do_encrypt);
EVP_CIPHER_CTX_set_key_length(&ctx, 10);
/* We finished modifying parameters so now we can set key and IV */
EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, do_encrypt);
for(;;)
{
inlen = fread(inbuf, 1, 1024, in);
if(inlen <= 0) break;
if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
}
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
fwrite(outbuf, 1, outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
return 1;
}

Resources