views:

922

answers:

2

If you have a good example of simply encrypting a file using openssl that is better than this one that I am having issues with I would be very grateful.

Update: Myabe the author was correct. Using memset on something I did not allocate reminds me of strtok choking on non-stack variables.

Update2: Got the core dump to stop by using malloc. Updated the code. The control Hs are still appearing. Updating the code to reflect this.

Update3: The looping structure does not appear correct in the example. I am unsure how subsequent reads are occuring greater than the size of the initial read.

Update4: I think I found it. decrypt loop has a olen += tlen and the buffer should discard that set of bits. :( not it.

Update99988: I have given up all hope here. I think I need to throw away this example and start with something from the Openssl book instead. Upon decrypting the middle layer buffers are preappended with ^H's but since a pointer is passed in I am beginning to suspect alignment issues.

I think I may be in the boat of starting with a bad example is worse than starting from scratch. i had to make some corrections (the original is annotated in the code below). The original author had some issues with passing addresses incorrectly. My mind is being kicked around by the different sized buffers the author is using 1024 and 1032 but I think it is related to the 8 bit seed and chained encryption calls.

I am getting garbage characters in (control Hs) and a core dump like the decryption is corrupting the stack. I am fairly new to encryption, openssl, and I am not all that familiar with gdb.

I have made every attempt to simplify this down

  1. gcc --version reports 4.3.2
  2. Open SUSE 11

compile.sh

gcc -g -o blowfish blowfish.c -lcrypto

run.sh

ulimit -c unlimited
./blowfish example.txt encrypted_example decrypted_example
echo diff example.txt decrypted_example
diff example.txt decrypted_example

clean.sh

rm -rf blowfish encrypted_example decrypted_example core

example.txt

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 
fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 
culpa qui officia deserunt mollit anim id est laborum.

Hydrogen H
Helium H
Lithium L
Beryllium B
Boron B
Carbon C
Nitrogen N
Oxygen O
Fluorine F
Neon N
Sodium N
Magnesium M
Aluminium A
Silicon S
Phosphorus P
Sulfur S
Chlorine C
Argon A
Potassium K
Calcium C
Scandium S
Titanium T
Vanadium V
Chromium C
Manganese M
Iron F
Cobalt C
Nickel N
Copper C
Zinc Z
Gallium G
Germanium G
Arsenic A
Selenium S
Bromine B
Krypton K
Rubidium R
Strontium S
Yttrium Y
Zirconium Z
Niobium N
Molybdenum M
Technetium T
Ruthenium R
Rhodium R
Palladium P
Silver A
Cadmium C
Indium I
Tin S
Antimony S
Tellurium T
Iodine I
Xenon X
Caesium C
Barium B
Lanthanum L
Cerium C
Praseodymium P
Neodymium N
Promethium P
Samarium S
Europium E
Gadolinium G
Terbium T
Dysprosium D
Holmium H
Erbium E
Thulium T
Ytterbium Y
Lutetium L
Hafnium H
Tantalum T
Tungsten W
Rhenium R
Osmium O
Iridium I
Platinum P
Gold A
Mercury H
Thallium T
Lead P
Bismuth B
Polonium P
Astatine A
Radon R
Francium F
Radium R
Actinium A
Thorium T
Protactinium P
Uranium U
Neptunium N
Plutonium P
Americium A
Curium C
Berkelium B
Californium C
Einsteinium E
Fermium F
Mendelevium M
Nobelium N
Lawrencium L
Rutherfordium R
Dubnium D
Seaborgium S
Bohrium B
Hassium H
Meitnerium M
Darmstadtium D
Roentgenium R
Ununbium U
Ununtrium U
Ununquadium U
Ununpentium U
Ununhexium U
Ununseptium U
Ununoctium U

BAD CODE WARNING BAD CODE USE CODE FROM SELECTED ANSWER blowfish.c

/* 
This code is a modificaiton of work cieted below.  It 
was modified by ojblass June 16, 2009.

The original can be found at http://tldp.org/LDP/LG/issue87/vinayak.html

TERMS OF THE Open Publication License (OPL)

Copyright © 2003, Vinayak Hegde. Copying license 
http://www.linuxgazette.com/copying.html
Published in Issue 87 of Linux Gazette, February 2003

1. The modified version must be labeled as such.
2. The person making the modifications must be identified and the modifications 
   dated.
3. Acknowledgement of the original author and publisher if applicable must be 
   retained according to normal academic citation practices.
4. The location of the original unmodified document must be identified.
5. The original author's (or authors') name(s) may not be used to assert or 
   imply endorsement of the resulting document without the original author's (or
   authors') permission. 
*/

#include <openssl/blowfish.h>
#include <openssl/evp.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#define IP_SIZE 1024
#define OP_SIZE 1032
unsigned char key[16];
unsigned char iv[8];

int
generate_key ()
{
  int i, j, fd;
  if ((fd = open ("/dev/random", O_RDONLY)) == -1)
    perror ("open error");

  if ((read (fd, key, 16)) == -1)
    perror ("read key error");

  if ((read (fd, iv, 8)) == -1)
    perror ("read iv error");

  printf ("128 bit key:\n");
  for (i = 0; i < 16; i++)
    printf ("%4d ", key[i]);

  printf ("\nInitialization vector\n");
  for (i = 0; i < 8; i++)
    printf ("%4d ", iv[i]);
  printf ("\n");


  close (fd);
  return 0;
}

int
decrypt (int infd, int outfd)
{
  char *inbuff, *outbuf;
  int olen, tlen, n;
  EVP_CIPHER_CTX ctx;
  EVP_CIPHER_CTX_init (&ctx);
  EVP_DecryptInit (&ctx, EVP_bf_cbc (), key, iv);

  outbuf = (unsigned char *) malloc ( sizeof(unsigned char) * IP_SIZE );
  inbuff = (unsigned char *) malloc ( sizeof(unsigned char) * OP_SIZE );

  /* keep reading until a break */
  for (;;)
    {
      memset(inbuff,'\0', OP_SIZE);
      if ((n = read (infd, inbuff, OP_SIZE)) == -1)
    {
      perror ("read error");
      break;
    }
      else if (n == 0)
    break;

      memset(outbuf,'\0', IP_SIZE);

      if (EVP_DecryptUpdate (&ctx, outbuf, &olen, inbuff, n) != 1)
    {
      printf ("error in decrypt update\n");
      return 0;
    }

      if (EVP_DecryptFinal (&ctx, outbuf + olen, &tlen) != 1)
    {
      printf ("error in decrypt final\n");
      return 0;
    }
      olen += tlen;
      if ((n = write (outfd, outbuf, olen)) == -1)
    perror ("write error");
    }

  EVP_CIPHER_CTX_cleanup (&ctx);
  return 1;
}

int
encrypt (int infd, int outfd)
{
  char *inbuff, *outbuf;

  int olen, tlen, n;
  EVP_CIPHER_CTX ctx;
  EVP_CIPHER_CTX_init (&ctx);
  EVP_EncryptInit (&ctx, EVP_bf_cbc (), key, iv);

  outbuf = (unsigned char *) malloc ( sizeof(unsigned char) * OP_SIZE );
  inbuff = (unsigned char *) malloc ( sizeof(unsigned char) * IP_SIZE );

  for (;;)
    {
      memset(inbuff,'\0', IP_SIZE);

      if ((n = read (infd, inbuff, IP_SIZE)) == -1)
    {
      perror ("read error");
      break;
    }
      else if (n == 0)
    break;

      if (EVP_EncryptUpdate (&ctx, outbuf, &olen, inbuff, n) != 1)
    {
      printf ("error in encrypt update\n");
      return 0;
    }

      if (EVP_EncryptFinal (&ctx, outbuf + olen, &tlen) != 1)
    {
      printf ("error in encrypt final\n");
      return 0;
    }
      olen += tlen;
      if ((n = write (outfd, outbuf, olen)) == -1)
    perror ("write error");
    }
  EVP_CIPHER_CTX_cleanup (&ctx);
  return 1;
}

int
main (int argc, char *argv[])
{
  int flags1 = 0, flags2 = 0, outfd, infd, decfd;
  mode_t mode;
  char choice, temp;
  int done = 0, n, olen;

  memset(key,'\0', 16);
  memset(iv,'\0', 8);
  memset(&mode, '\0', sizeof(mode));

  flags1 = flags1 | O_RDONLY;
  flags2 = flags2 | O_RDONLY;
  flags2 = flags2 | O_WRONLY;
  flags2 = flags2 | O_CREAT;

  mode = mode | S_IRUSR;
  mode = mode | S_IWUSR;

  generate_key ();

  if ((infd = open (argv[1], flags1, mode)) == -1)
    perror ("open input file error");

  if ((outfd = open (argv[2], flags2, mode)) == -1)
    perror ("open output file error");

  encrypt (infd, outfd);

  close (infd);
  close (outfd);

  if ((outfd = open (argv[2], flags1, mode)) == -1)
    perror ("open output file error");

  if ((decfd = open (argv[3], flags2, mode)) == -1)
    perror ("open output file error");

  /* After much head scratching reusing the out as an in is correct here */
  decrypt (outfd, decfd);

  close (outfd);
  fsync (decfd);
  close (decfd);

  return 0;
}
+1  A: 

Since you say the error seems to be coming at the decryption stage, I would look with suspicion on these lines:

if (EVP_DecryptUpdate (&ctx, outbuf, &olen, inbuff, n) != 1)
...

if (EVP_DecryptFinal (&ctx, outbuf + olen, &tlen) != 1)
...

How big is the memory allocated for outbuf in relation to what the decryption functions will be putting in them? Are you sure outbuf + olen is not going to take you past the end of the buffer?

1800 INFORMATION
Its very hard to tell if it got corrupted at encrypt or decrypt time. #define IP_SIZE 1024#define OP_SIZE 1032char outbuf[OP_SIZE];char inbuff[IP_SIZE];
ojblass
Am I allowed to do a memset on something I did not allocate?
ojblass
ojblass
You can do memset on any valid area of memory that you can write to - I suspect that you or the decryption function is writing past the end of the array
1800 INFORMATION
Unfortunately I don't have Purify here at home. What is the open source equivalent for that?
ojblass
Valgrind I guess http://en.wikipedia.org/wiki/Valgrind. Haven't used it myself
1800 INFORMATION
+4  A: 

The error is in the way the EVP_DecryptFinal and EVP_EncryptFinal are called. These functions should be called in the end of the for cycle, also the final part where to olen is added tlen and written again was duplicating output. Bellow is the final working version:

/*
 * This code is a modificaiton of work cieted below.  It was modified by
 * ojblass June 16, 2009.
 * 
 * The original can be found at http://tldp.org/LDP/LG/issue87/vinayak.html
 * 
 * TERMS OF THE Open Publication License (OPL)
 * 
 * Copyright © 2003, Vinayak Hegde. Copying license
 * http://www.linuxgazette.com/copying.html Published in Issue 87 of Linux
 * Gazette, February 2003
 * 
 * 1. The modified version must be labeled as such. 2. The person making the
 * modifications must be identified and the modifications dated. 3.
 * Acknowledgement of the original author and publisher if applicable must be
 * retained according to normal academic citation practices. 4. The location
 * of the original unmodified document must be identified. 5. The original
 * author's (or authors') name(s) may not be used to assert or imply
 * endorsement of the resulting document without the original author's (or
 * authors') permission.
 */

#include <openssl/blowfish.h>
#include <openssl/evp.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#define IP_SIZE 1024
#define OP_SIZE 1024 + EVP_MAX_BLOCK_LENGTH
unsigned char   key[16];
unsigned char   iv[8];

int
generate_key()
{
 int             i, fd;
 if ((fd = open("/dev/random", O_RDONLY)) == -1)
  perror("open error");

 if ((read(fd, key, 16)) == -1)
  perror("read key error");

 if ((read(fd, iv, 8)) == -1)
  perror("read iv error");

 printf("128 bit key:\n");
 for (i = 0; i < 16; i++)
  printf("%4d ", key[i]);

 printf("\nInitialization vector\n");
 for (i = 0; i < 8; i++)
  printf("%4d ", iv[i]);
 printf("\n");


 close(fd);
 return 0;
}

int
do_decrypt(int infd, int outfd)
{
 unsigned char           *inbuff, *outbuf;
 int             olen=0, tlen=0, n=0;
 EVP_CIPHER_CTX  ctx;
 EVP_CIPHER_CTX_init(&ctx);
 EVP_DecryptInit(&ctx, EVP_bf_cbc(), key, iv);

 outbuf = (unsigned char *) malloc(sizeof(unsigned char) * OP_SIZE);
 inbuff = (unsigned char *) malloc(sizeof(unsigned char) * IP_SIZE);

 /* keep reading until a break */
 for (;;) {
  memset(inbuff, 0, IP_SIZE);
  if ((n = read(infd, inbuff, IP_SIZE)) == -1) {
   perror("read error");
   break;
  } else if (n == 0)
   break;

  memset(outbuf, 0, OP_SIZE);

  if (EVP_DecryptUpdate(&ctx, outbuf, &olen, inbuff, n) != 1) {
   printf("error in decrypt update\n");
   return 0;
  }
  if ((n = write(outfd, outbuf, olen)) == -1)
   perror("write error");
 }

 tlen=0;
 if (EVP_DecryptFinal(&ctx, outbuf + olen, &tlen) != 1) {
  perror("error in decrypt final");
  return 0;
 }

 if ((n = write(outfd, outbuf+olen, tlen)) == -1)
  perror("write error");

 EVP_CIPHER_CTX_cleanup(&ctx);
 return 1;
}

int
do_encrypt(int infd, int outfd)
{
 unsigned char           *inbuff, *outbuf;

 int             olen=0, tlen=0, n=0;
 EVP_CIPHER_CTX  ctx;
 EVP_CIPHER_CTX_init(&ctx);
 EVP_EncryptInit(&ctx, EVP_bf_cbc(), key, iv);

 outbuf = (unsigned char *) malloc(sizeof(unsigned char) * OP_SIZE);
 inbuff = (unsigned char *) malloc(sizeof(unsigned char) * IP_SIZE);

 for (;;) {
  memset(inbuff, 0, IP_SIZE);

  if ((n = read(infd, inbuff, IP_SIZE)) == -1) {
   perror("read error");
   break;
  } else if (n == 0)
   break;

  if (EVP_EncryptUpdate(&ctx, outbuf, &olen, inbuff, n) != 1) {
   printf("error in encrypt update\n");
   return 0;
  }

  if ((n = write(outfd, outbuf, olen)) == -1)
   perror("write error");
 }
 tlen=0;
 if (EVP_EncryptFinal(&ctx, outbuf + olen, &tlen) != 1) {
  printf("error in encrypt final\n");
  return 0;
 }

 if ((n = write(outfd, outbuf+olen, tlen)) == -1)
  perror("write error");

 EVP_CIPHER_CTX_cleanup(&ctx);
 return 1;
}

int
main(int argc, char *argv[])
{
 int             flags1 = 0, flags2 = 0, outfd, infd;
 mode_t          mode;

 memset(key, 0, 16);
 memset(iv, 0, 8);
 memset(&mode, 0, sizeof(mode));

 flags1 = flags1 | O_RDONLY;
 flags2 = flags2 | O_RDONLY;
 flags2 = flags2 | O_WRONLY;
 flags2 = flags2 | O_CREAT;

 mode = mode | S_IRUSR;
 mode = mode | S_IWUSR;


 generate_key();


 if ((infd = open(argv[1], flags1, mode)) == -1)
  perror("open input file error");

 if ((outfd = open(argv[2], flags2, mode)) == -1)
  perror("open output file error");

 do_encrypt(infd, outfd);

 close(infd);
 fsync(outfd);
 close(outfd);

 if ((infd = open(argv[2], flags1, mode)) == -1)
  perror("open output file error");

 if ((outfd = open(argv[3], flags2, mode)) == -1)
  perror("open output file error");

 do_decrypt(infd, outfd);

 close(infd);
 fsync(infd);
 close(outfd);

 return 0;
}
rsarro
I seriously have my doubts about that. If this were true the last number of bits would we written out twice.
ojblass
From my understanding of the block cyphers this makes sense. The *Final functions are used for the last bits of data that are not multiple of the block size. Besides, the fact that you are doing a *Final function call inside the loop is the cause of the garbage characters in the decrypted file. In my test this solved the problem
rsarro
my previous answer was not completely right. The last edit is a complete working version, based on the code in your question (with the fixes and some cleanups).
rsarro
Looking now. I needed a break from this for a days.
ojblass
Cheers! Enjoy the rep... If I could buy you a beer it would be yours....
ojblass
I don't quite get how the partial buffer is left alone for the final to handle at the decrypt stage unless the regular decrypt acts funny if it does not have a full buffer.
ojblass