2

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

jww
  • 97,681
  • 90
  • 411
  • 885
abhi
  • 3,476
  • 5
  • 41
  • 58
  • [OpenSSL 1.1.0c changed the digest algorithm](http://stackoverflow.com/q/39637388/608639) used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both `EVP_BytesToKey` and commands like `openssl enc`. – jww Jan 26 '17 at 16:31

2 Answers2

4

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.

Daniel Roethlisberger
  • 6,958
  • 2
  • 41
  • 59
  • Thanks again for answering my question. Actually i am not calling the aes_decrypt() in my actual code like that..I am first reading from plain.txt in a loop (32bytes each time) and passing to aes_encrypt() and then writing them in a encrypt.txt file untill EOF and then i open then file lseek it to start and then again read 32bytes/loop iteration from this encrypted file and call aes_decrypt() this time and on return writing the decrypted code to a new txt file. I have updated my code above with full code please check Many thanks – abhi May 01 '12 at 00:06
  • Got what you are saying in your comment i tried calling encrypt final and decrypt final ONCE at the end of there respective read iterations. Now i am not getting any garbage in the decrypted file but some characters are missing from the decrypted file..any idea whay?? many thanks.. – abhi May 01 '12 at 00:15
  • 1
    Well, not sure I understand how your current code looks like, but what you should do is: init, while { read, update, write }, final, write. Both for encryption and for decryption. – Daniel Roethlisberger May 01 '12 at 11:52
  • thanks once again Daniel.. I misunderstood the usage of _update and _final initially.. Now i have updated my code and it is working fine and at the same time cleared my doubts also.. Many thanks once again for answering my third question in a row :) – abhi May 01 '12 at 18:41
3
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;
        }
dpp
  • 1,748
  • 11
  • 7