Related
So I am writing an archiver program to also encrypt-decrypt files sing block cipher. When decrpting from a archive file, and I have checked, in situations where the remaining btes of the fle before EOF is smaller than the read buffer some garbage values are written to the file. I am using OpenSSL's EVP API with C language. Any ideas what is wrong? You can find the code for AES 256 bit decryption below.
int TARIM_decrypt_aes256(FILE* infile, FILE* outfile, unsigned char* key, unsigned char* iv, unsigned long long int fileLoc, unsigned long int fileSize)
{
if (infile == NULL || outfile == NULL || key == NULL || iv == NULL)
{
printf("(ERROR) decrypt_aes256: One/more parameters are NULL pointers\n");
return 1;
}
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
{
printf("(ERROR) decrypt_aes256: Failed to generate cipher context. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
return 1;
}
EVP_CIPHER* cipher = EVP_CIPHER_fetch(NULL, "AES-256-CBC", NULL);
if (cipher == NULL)
{
printf("(ERROR) decrypt_aes256: Failed to fetch cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// Explicitly Enable Padding
EVP_CIPHER_CTX_set_padding(ctx, 1);
int num_read = 0;
int block_size = EVP_CIPHER_block_size(cipher);
int inLen = 1024;
int outLen = inLen + block_size;
unsigned char inbuffer[inLen], outbuffer[outLen]; // Allow space for additional block in outbuffer
if (!EVP_CipherInit_ex2(ctx, cipher, NULL, NULL, 0, NULL))
{
printf("(ERROR) decrypt_aes256: Failed to Initiate Cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// Assert size of key and iv
OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 32);
OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
// Set key and iv
if (!EVP_CipherInit_ex2(ctx, NULL, key, iv, 0, NULL))
{
printf("(ERROR) decrypt_aes256: Failed to set key and iv. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
// Seek to File Location
fseek(infile, fileLoc, SEEK_SET);
// Loop until bytes read
unsigned long long int b_count = 0;
while (1)
{
num_read = fread(inbuffer, sizeof(unsigned char), inLen, infile);
b_count += num_read;
if (!EVP_CipherUpdate(ctx, outbuffer, &outLen, inbuffer, num_read))
{
printf("(ERROR) decrypt_aes256: Failed to pass bytes to the cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
fwrite(outbuffer, sizeof(unsigned char), outLen, outfile);
// EOF
if (b_count >= fileSize || num_read < inLen)
{ break; }
}
// Cipher Final block with padding
if (!EVP_CipherFinal_ex(ctx, outbuffer, &outLen))
{
printf("(ERROR) decrypt_aes256: Failed to pass bytes from final block to cipher. OpenSSL: %s\n", ERR_error_string(ERR_get_error(), NULL));
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 1;
}
fwrite(outbuffer, sizeof(unsigned char), outLen, outfile);
// Clean up
EVP_CIPHER_free(cipher);
EVP_CIPHER_CTX_free(ctx);
return 0;
}
I tried changing the inLen variable to block_size but that gives me OpenSSL's bad decrypt error.
int inLen = block_size;
The Error comes out like this dring decryption:
(ERROR) decrypt_aes256: Failed to pass bytes from final block to cipher. OpenSSL: error:1C800064:Provider routines::bad decrypt
[Edit]
I did find a weird behavior of my program. So the program can encrypt files together into a single file. The files are not padded to differentiate to save space, instead I keep the record of the file size and figure out the start byte position in the code using their sizes and extract them. The only files that give me trouble when using inLen = block_size were the files who were a multiple for 'block_size' and only when they were clustered in a single file. Here is the code that figures out the start byte of a file:
// Get Relative Location
unsigned long long int relativeLoc = 0;
for (long long int itr = 0; itr < option_num; itr++) {
if (block_size == 0 || fArray[itr].fsize == 0) {
relativeLoc += fArray[itr].fsize;
} else {
// Adjust for padding
relativeLoc += ((fArray[itr].fsize / block_size) + 1) * block_size;
}
}
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!
This question already has answers here:
Simple AES encryption decryption with openssl library in C
(4 answers)
Closed 7 years ago.
Its not duplicate since I was not asking how to encrypt, but I was asking whats wrong in my encryption. Dont like this Question you are free to delete it. I dont care.
I am trying to encrypt one simple hello text file.
Here is my code to encrypt the data.
int encrypt(EVP_CIPHER_CTX *ctx, FILE *ifp,FILE *ofp)
{
int bytes_read, bytes_written, enc_bytes,tlen;
unsigned char indata[AES_BLOCK_SIZE];
unsigned char encdata[AES_BLOCK_SIZE];
unsigned char outdata[AES_BLOCK_SIZE];
while (1) {
bytes_read = fread(indata, 1, AES_BLOCK_SIZE, ifp);
if (EVP_EncryptUpdate (ctx, encdata, &enc_bytes, indata, bytes_read) != 1)
{
printf ("error in encrypt update\n");
return -1;
}
printf ("INPUT\n");
print_memory(indata, bytes_read);
if (EVP_EncryptFinal (ctx, encdata + enc_bytes, &tlen) != 1)
{
printf ("error in encrypt final\n");
return -1;
}
printf ("OUTPUT\n");
print_memory(encdata,enc_bytes+tlen);
bytes_written = fwrite(encdata, 1,enc_bytes + tlen, ofp);
if (bytes_read < AES_BLOCK_SIZE)
break;
}
Here is key and ivec, I have used for initialization
unsigned char ckey[] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF};
unsigned char ivec[] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF};
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (&ctx);
EVP_EncryptInit (&ctx, EVP_bf_cbc (), ckey, ivec);
Here is the output encrypted data in hex 24 47 50 58 93 0B 04 9C D5 54 65 93 D1 6B AD 5A
but when i try to decode the data using openSSL cmd
I get following error
anshul:~/> openssl aes-128-cbc -d -in otext -K 000102030405060708090A0B0C0D0E0F -iv 000102030405060708090A0B0C0D0E0F -nosalt
bad decrypt
3075450556:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:529:
This is the same error I get if I use wrong ivec or key.
So I do have wild guess that there is something different in c program key or ivec and openssl cmd key or ivek
I am trying to encrypt one simple hello text file...
I took code from http://openssl.org/docs/crypto/EVP_EncryptInit.html#EXAMPLES...
Here's the example from the OpenSSL wiki on EVP Authenticated Encryption and Decryption. It uses GCM mode because you often want/need confidentiality and authenticity assurances, and not just confidentiality.
OpenSSL routines work on byte strings in memory. So you will have to read the file and present a byte string to the OpenSSL functions.
int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *aad,
int aad_len, unsigned char *key, unsigned char *iv,
unsigned char *ciphertext, unsigned char *tag)
{
EVP_CIPHER_CTX *ctx;
int len, ciphertext_len;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the encryption operation. */
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
handleErrors();
/* Set IV length if default 12 bytes (96 bits) is not appropriate */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
handleErrors();
/* Initialise key and IV */
if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
/* Provide any AAD data. This can be called zero or more times as
* required
*/
if(1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary
*/
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;
/* Finalise the encryption. Normally ciphertext bytes may be written at
* this stage, but this does not occur in GCM mode
*/
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;
/* Get the tag */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
handleErrors();
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ciphertext_len;
}
And the decryption routine:
int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *aad,
int aad_len, unsigned char *tag, unsigned char *key, unsigned char *iv,
unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len, plaintext_len, ret;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the decryption operation. */
if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
handleErrors();
/* Set IV length. Not necessary if this is 12 bytes (96 bits) */
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
handleErrors();
/* Initialise key and IV */
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
/* Provide any AAD data. This can be called zero or more times as
* required
*/
if(!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;
/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
handleErrors();
/* Finalise the decryption. A positive return value indicates success,
* anything else is a failure - the plaintext is not trustworthy.
*/
ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
if(ret > 0)
{
/* Success */
plaintext_len += len;
return plaintext_len;
}
else
{
/* Verify failed */
return -1;
}
}
anshul:~/> openssl aes-128-cbc -d -in ...
For GCM mode, this won't work. GCM has not been cut-in for the OpenSSL encrypt and decrypt subcommands. See AES-GCM failing from Command Line Interface on the OpenSSL mailing list.
And in case you are wondering, that's not a valid reason to use a different mode, like CBC or OFB. Use GCM mode.
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;
}
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.