Thread: blowfish

  1. #1
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838

    blowfish

    anybody have any experience with this?

    i'm currently using Jim Conger's C++ implementation and i just can't quite get it to work right.

    i'm currently reading a file, encrypting it and writing the encrypted data to a new file. reading the encrypted file, then decrypting it ad writing that to a third file.

    for an original input of:

    1234 4321 abcd dcba
    1234 4321 abcd dcba
    1234 4321 abcd dcba
    1234 4321 abcd dcba
    abcdefghijklmnopqrstuvwxyz
    0123456789
    the quick brown fox jumps over the lazy dog

    i get:

    1234 4321 abcd dcba
    1234 4321 abcd dcba
    1234 4321 abcd dcba
    1234 4321 abcd dcba
    abcdefghijklmnopqrstuvwxyz
    0123456789
    the quick brown f>b8Ç'3~²]'uêÍN¦tZQµ{‡š^9žú


    this implementation supposedly has padding to accomodate input blocks that aren't exactly 64bits, but it doesn't quite seem to be doing the job right.

    trying to pad them myself horks the entire decryption process

    i'm also finding some garbage at the end of the file for some reason. a bunch of odd characters that are clearly NOT in the text file. these seem to be part of the problem, but i have no idea what they are or why there are there.

    here's the quick and dirty code i'm using to verify integrity between encryption & decryption...

    Code:
    CBlowFish *bf = new CBlowFish();
            char *key ="thisisthekey";
            bf->Initialize(key,StrLen(key));
            String input;
    
            FILE *file = fopen("C:\\test.txt","r");
            FILE *outfile = fopen("C:\\Enctest.txt","w");
            String output;
            int n = 8;
            int size = 1;
            if(file !=NULL&& outfile !=NULL)
            {
                    fseek(file,0,0);
                    int result = fread(input.c_str(),size,n,file);
                    while(result ==n)
                    {
                            bf->Encode(input.c_str(),output.c_str(),StrLen(inp ut.c_str()));
                            fwrite(output.c_str(),sizeof(output[0]),StrLen(out put.c_str()),outfile);
                            input = "";
                            result = fread(input.c_str(),size,n,file);
                    }
                    if(result>0)
                    {
                            input = input.c_str();
                            input = input.SubString(0,result);               
                            for(int i=0;i<n-result;i++)
                            {
                                    input = input+NULL;
                                    bf->Encode(input.c_str(),output.c_str(),StrLen(inp ut.c_str()));
                                    fwrite(output.c_str(),sizeof(output[0]),StrLen(out put.c_str()),outfile);
                            }
                    }
            }
            fclose(outfile);
            fclose(file);
            file = fopen("C:\\Enctest.txt","r");
            outfile = fopen("C:\\Denctest.txt","w");
            if(file !=NULL&& outfile !=NULL)
            {
                    fseek(file,0,0);
                    int result = fread(input.c_str(),size,n,file);
                    while(result > 0)
                    {
                            bf->Decode(input.c_str(),output.c_str(),StrLen(inp ut.c_str()));
                            fwrite(output.c_str(),sizeof(output[0]),StrLen(out put.c_str()),outfile);
                            input = "";                       
                            result = fread(input.c_str(),size,n,file);
                    }
            }
            fclose(outfile);
            fclose(file);

    halp!

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Try deleting and then recreating the CBlowFish object between encryption and decryption. The implementations might share the same CBC buffer, the IV might be getting messed up (although that should affect the first block, not the last one), etc...

  3. #3
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    Quote Originally Posted by brewbuck View Post
    Try deleting and then recreating the CBlowFish object between encryption and decryption. The implementations might share the same CBC buffer, the IV might be getting messed up (although that should affect the first block, not the last one), etc...
    thanks for the suggestion but i get the exact same result

  4. #4
    The larch
    Join Date
    May 2006
    Posts
    3,573
    I have no idea what this String thing is, but I can't see how you could possibly use the pointer returned by a c_str method for writing to the string. For example here:
    Code:
    int result = fread(input.c_str(),size,n,file);
    So it might be all about out-of-bound accesses to the underlying buffer of Sting.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  5. #5
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    as well as here
    Encode(input.c_str(),output.c_str(),StrLen(inp ut.c_str()));
    c_str() returns const char* and is read-only
    you should pay attention to the const-correctness of your program
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  6. #6
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    Quote Originally Posted by anon View Post
    I have no idea what this String thing is, but I can't see how you could possibly use the pointer returned by a c_str method for writing to the string. For example here:
    Code:
    int result = fread(input.c_str(),size,n,file);
    So it might be all about out-of-bound accesses to the underlying buffer of Sting.
    i use the borland C++ builder compiler.

    String is an implementation of the ANSI standard string class. it's convenient because it has memory allocation built in. it resizes appropriately all on it's own.

    i have used char * of fixed length for input with identical results.

  7. #7
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    Quote Originally Posted by vart View Post
    as well as here
    Encode(input.c_str(),output.c_str(),StrLen(inp ut.c_str()));
    c_str() returns const char* and is read-only
    you should pay attention to the const-correctness of your program
    be that as it may, i can write to the location returned by input. c_str() without any problems...


    edit: lol no i take it back. dammit.
    Last edited by m37h0d; 03-20-2008 at 03:40 PM.

  8. #8
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Well, yes. Obviously, if you go straight for the buffer (for writing), the String class has no idea what you are doing with it, and couldn't possibly resize the buffer automatically. It has probably a small preallocated buffer, and as long as you stay in bounds of that (and use only the c_str method - that is, not actually use the String class itself at all) things appear to work correctly.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  9. #9
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    ok, this discussion of the String class is really ancillary to my problem, so consider this:

    Code:
            CBlowFish *bf = new CBlowFish();
            char *key ="thisisthekey";
            bf->Initialize(key,StrLen(key));
    
    
            FILE *file = fopen("C:\\test.txt","r");
            FILE *outfile = fopen("C:\\Enctest.txt","w");
    
            const int n = 8;
            int size = 1;
            char *output = new char[16];
            char *input = new char[16];
            if(file !=NULL&& outfile !=NULL)
            {
                    fseek(file,0,0);
                    int result = fread(input,size,n,file);
                    while(result ==n)
                    {
                            bf->Encode(input,output,StrLen(input));
                            fwrite(output,sizeof(output[0]),StrLen(output),outfile);
                            delete input;
                            input = new char[16];
                            result = fread(input,size,n,file);
                    }
                    if(result>0)
                    {
                   
                            for(int i=result;i<n-result;i++)
                            {
                                    input[i] = NULL;
                            }
                            bf->Encode(input,output,StrLen(input));
                            fwrite(output,sizeof(output[0]),StrLen(output),outfile);
                            
                    }
            }
            delete bf;
            bf = new CBlowFish();
            bf->Initialize(key,StrLen(key));
    
            fclose(outfile);
            fclose(file);
            file = fopen("C:\\Enctest.txt","r");
            outfile = fopen("C:\\Denctest.txt","w");
            if(file !=NULL&& outfile !=NULL)
            {
                    fseek(file,0,0);
                    int result = fread(input,size,n,file);
                    while(result > 0)
                    {
                            bf->Decode(input,output,StrLen(input));
                            fwrite(output,sizeof(output[0]),StrLen(output),outfile);
                            delete input;
                            input = new char[16];
                            result = fread(input,size,n,file);
                    }
            }
            fclose(outfile);
            fclose(file);
    produces

    1234 4321 abcd dcba
    1234 4321 abcd dcba
    1234 4321 abcd dcba
    1234 4321 abcd dcba
    abcdefghijklmnopqrstuvwx56789
    the quick brown fox jumps over the lazy dog

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Wow... a lot wrong with that code.

    Long story short: You can never know the size of input data unless you check. You can never know the size of the encrypted data unless you check. You must not presume that input or output will be "'null' free". (Using 'StrLen' is unlikely to provide the correct result. I've never studied 'Blowfish', but I seriously doubt it naturally guarantees "'null' free" data, and so unless the source, the programmer, used some form of encoding, like BASE64, the length as determined by searching for the first 'null' will variably be wrong.)

    Obviously, if your sample output is "nearly correct", you have been very lucky regarding the issue. I'd suggest you examine the interface, or better yet, the documentation. In all likelihood the method 'CBlowFish::Encode' will return a value corresponding to the number of bytes you actually need to write. (The 'fread' function should return the number of bytes read--the number of bytes to encode. That is to say, if coded correctly you should only use the return value of 'fread' and probably 'CBlowFish::Encode' in determining the number of bytes to write/read/encode/decode. There should not be a single call to 'StrLen'.)

    Soma

  11. #11
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    Quote Originally Posted by phantomotap View Post
    Wow... a lot wrong with that code.

    Long story short: You can never know the size of input data unless you check. You can never know the size of the encrypted data unless you check. You must not presume that input or output will be "'null' free". (Using 'StrLen' is unlikely to provide the correct result. I've never studied 'Blowfish', but I seriously doubt it naturally guarantees "'null' free" data, and so unless the source, the programmer, used some form of encoding, like BASE64, the length as determined by searching for the first 'null' will variably be wrong.)

    Obviously, if your sample output is "nearly correct", you have been very lucky regarding the issue. I'd suggest you examine the interface, or better yet, the documentation. In all likelihood the method 'CBlowFish::Encode' will return a value corresponding to the number of bytes you actually need to write. (The 'fread' function should return the number of bytes read--the number of bytes to encode. That is to say, if coded correctly you should only use the return value of 'fread' and probably 'CBlowFish::Encode' in determining the number of bytes to write/read/encode/decode. There should not be a single call to 'StrLen'.)

    Soma
    thanks for the insightful post! i have no doubts that there is a lot of bad style and outright errors in the above. i dont' have formal education in C/C++ and i don't have nearly as much familiarity with C as C++.

    my first instinct was to RTFM...but alas there does not seem to be a M to FR. the comments do provide a little help though. you're right about cblowfish::encode returning the number of bytes to be written.

    this works a lot better, but there's still some error; i'm guessing with the block lengths still...

    Code:
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
            CBlowFish *bf = new CBlowFish();
            char *key ="thisisthekey";
            bf->Initialize(key,StrLen(key));
    
    
            FILE *file = fopen("C:\\test.txt","r");
            FILE *outfile = fopen("C:\\Enctest.txt","w");
    
            const int n = 8;
            const int size = 1;
            const int bufLen = n*size;
            unsigned char *output = new char[bufLen];
            unsigned char *input = new char[bufLen];
            if(file !=NULL&& outfile !=NULL)
            {
                    fseek(file,0,0);
                    int result = fread(input,size,n,file);
                    while(result == n)
                    {
                            int outputSize = bf->Encode(input,output,bufLen);
                            fwrite(output,outputSize,size,outfile);
                            result = fread(input,size,n,file);
                    }
                    if(result>0)
                    {
                            for(int i=result;i<n-result;i++)
                            {
                                    input[i] = NULL;
                            }
                            int outputSize = bf->Encode(input,output,bufLen);
                            fwrite(output,outputSize,size,outfile);
                    }
            }
            fclose(outfile);
            fclose(file);
            file = fopen("C:\\Enctest.txt","r");
            outfile = fopen("C:\\Denctest.txt","w");
            if(file !=NULL&& outfile !=NULL)
            {
                    fseek(file,0,0);
                    int result = fread(input,size,n,file);
                    while(result > 0)
                    {
                            bf->Decode(input,output,bufLen);
                            fwrite(output,size,bufLen,outfile);
                            result = fread(input,size,n,file);
                    }
            }
            fclose(outfile);
            fclose(file);        
    }

  12. #12
    Registered User
    Join Date
    Jan 2008
    Posts
    290
    You are performing an action that is meant to operate on binary data, not textual data, therefore you should open your files in binary mode. This is especially important when reading the encoded file.

    You are dynamically allocating the CBlowFish object but never deleting it. I would just declare it on the stack for simplicity.

    I would also prefer to see better error handling with the fopen() calls. You can handle this quite nicely with a helper function (or a macro).

    There is no need to pad your input with null characters. The blowfish code handles this for you. Your responsibility is only to accurately report how much data is in your input buffer (the third parameter to the Encode() method). Even if you did pad your own input, you shouldn't use NULL as a null character. NULL semantically refers to a null pointer. You should use '\0', which refers to a null character.

    There is no need to use strlen on a string literal in your code. Declare your string using array notation and use sizeof() to determine the size at compile time (not really a big deal but I thought I should point it out).

    I don't believe you need the fseek() calls. After a call to fopen(), the internal pointer should already be at the beginning of the file. I may be wrong about this, but I've never had to fseek() to the beginning of a file on any Windows or Linux system I've worked on.

    Lastly, while I was testing this out, I had problems with repeating characters at the end of the decoded file. I realized the decoding process got to the last 2 bytes of the file, it wrote those 2 bytes to the output buffer and left the rest of the buffer the way it was. I had no way of knowing that there were only 2 bytes left (Decode() doesn't tell you how many characters it decoded) so I was writing the whole 8 byte buffer out even though I only needed the first two bytes.

    To combat this bug, I had to keep track of the filesize while reading in the file. Maybe there's a better way to handle this, but keeping track of the filesize works.

    Anyway, less talking and more code. I think its easier to learn if I just show you some code. Notice I use the C versions of the headers. You can use the C++ headers if you want.
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include "blowfish.h"
    
    #define my_fopen(fileptr, filename, mode) \
      fileptr = fopen(filename, mode); \
      if (fileptr == NULL) { \
        fprintf(stderr, "Error: Couldn't open &#37;s.\n", filename); \
        exit(1); \
      }
    
    const char *input_file_name = "test.txt";
    const char *encoded_file_name = "encoded.txt";
    const char *decoded_file_name = "decoded.txt";
    unsigned char key[] = "thisisthekey";
    
    int main(void) {
      FILE *infile, *outfile;
      int result, filesize;
      const int n = 8; // make sure this is a multiple of 8
      const int size = 1;
      unsigned char input[n], output[n];
      
      CBlowFish bf;
      bf.Initialize(key, sizeof(key)-1); // subtract 1 to not count the null terminator
    
      my_fopen(infile, input_file_name, "rb")
      my_fopen(outfile, encoded_file_name, "wb")
      
      filesize = 0;
      while (result = fread(input, size, n, infile)) {
        filesize += result;
        fwrite(output, size, bf.Encode(input, output, result), outfile);
      }
    
      fclose(outfile);
      fclose(infile);
      
      my_fopen(infile, encoded_file_name, "rb")
      my_fopen(outfile, decoded_file_name, "wb")
      
      while (result = fread(input, size, n, infile)) {
        bf.Decode(input, output, result);
        fwrite(output, sizeof(output[0]), filesize < n ? filesize : n, outfile);
        filesize -= n;
      }
      
      fclose(outfile);
      fclose(infile);
      
      return 0;
    }
    [edit] Actually there's a logic error in my code. Maybe you can spot it? [/edit]
    Last edited by arpsmack; 03-21-2008 at 02:31 AM.

  13. #13
    3735928559
    Join Date
    Mar 2008
    Location
    RTP
    Posts
    838
    Quote Originally Posted by arpsmack View Post
    You are performing an action that is meant to operate on binary data, not textual data, therefore you should open your files in binary mode. This is especially important when reading the encoded file.

    You are dynamically allocating the CBlowFish object but never deleting it. I would just declare it on the stack for simplicity.

    I would also prefer to see better error handling with the fopen() calls. You can handle this quite nicely with a helper function (or a macro).

    There is no need to pad your input with null characters. The blowfish code handles this for you. Your responsibility is only to accurately report how much data is in your input buffer (the third parameter to the Encode() method). Even if you did pad your own input, you shouldn't use NULL as a null character. NULL semantically refers to a null pointer. You should use '\0', which refers to a null character.

    There is no need to use strlen on a string literal in your code. Declare your string using array notation and use sizeof() to determine the size at compile time (not really a big deal but I thought I should point it out).

    I don't believe you need the fseek() calls. After a call to fopen(), the internal pointer should already be at the beginning of the file. I may be wrong about this, but I've never had to fseek() to the beginning of a file on any Windows or Linux system I've worked on.

    Lastly, while I was testing this out, I had problems with repeating characters at the end of the decoded file. I realized the decoding process got to the last 2 bytes of the file, it wrote those 2 bytes to the output buffer and left the rest of the buffer the way it was. I had no way of knowing that there were only 2 bytes left (Decode() doesn't tell you how many characters it decoded) so I was writing the whole 8 byte buffer out even though I only needed the first two bytes.

    To combat this bug, I had to keep track of the filesize while reading in the file. Maybe there's a better way to handle this, but keeping track of the filesize works.

    Anyway, less talking and more code. I think its easier to learn if I just show you some code. Notice I use the C versions of the headers. You can use the C++ headers if you want.
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include "blowfish.h"
    
    #define my_fopen(fileptr, filename, mode) \
      fileptr = fopen(filename, mode); \
      if (fileptr == NULL) { \
        fprintf(stderr, "Error: Couldn't open %s.\n", filename); \
        exit(1); \
      }
    
    const char *input_file_name = "test.txt";
    const char *encoded_file_name = "encoded.txt";
    const char *decoded_file_name = "decoded.txt";
    unsigned char key[] = "thisisthekey";
    
    int main(void) {
      FILE *infile, *outfile;
      int result, filesize;
      const int n = 8; // make sure this is a multiple of 8
      const int size = 1;
      unsigned char input[n], output[n];
      
      CBlowFish bf;
      bf.Initialize(key, sizeof(key)-1); // subtract 1 to not count the null terminator
    
      my_fopen(infile, input_file_name, "rb")
      my_fopen(outfile, encoded_file_name, "wb")
      
      filesize = 0;
      while (result = fread(input, size, n, infile)) {
        filesize += result;
        fwrite(output, size, bf.Encode(input, output, result), outfile);
      }
    
      fclose(outfile);
      fclose(infile);
      
      my_fopen(infile, encoded_file_name, "rb")
      my_fopen(outfile, decoded_file_name, "wb")
      
      while (result = fread(input, size, n, infile)) {
        bf.Decode(input, output, result);
        fwrite(output, sizeof(output[0]), filesize < n ? filesize : n, outfile);
        filesize -= n;
      }
      
      fclose(outfile);
      fclose(infile);
      
      return 0;
    }
    [edit] Actually there's a logic error in my code. Maybe you can spot it? [/edit]
    cheers. you, sir, are the man. it is now working flawlessly.

    as far as the logic error, the only thing i can see is that you're subtracting n from filesize, regardless of whether or not n bytes remain in the file or not.

  14. #14
    Registered User
    Join Date
    Jan 2008
    Posts
    290
    Nice spot! In that last while loop there, the code should be this:
    Code:
      while (result = fread(input, size, n, infile)) {
        bf.Decode(input, output, result);
        fwrite(output, sizeof(output[0]), filesize < result ? filesize : result, outfile);
        filesize -= result;
      }
    That won't matter unless you up the value of n to say 16 or 32 since the blowfish algorithm guarantees that the encoded file will have a bytesize of a multiple of 8.

  15. #15
    Registered User Kudose's Avatar
    Join Date
    Jun 2006
    Posts
    92
    Quote Originally Posted by m37h0d View Post
    my first instinct was to RTFM...but alas there does not seem to be a M to FR.
    I've found these to be pretty close.

    http://www.acm.uiuc.edu/webmonkeys/book/c_guide/
    http://www.gnu.org/software/libtool/...ibc/index.html
    http://www.google.com/search?hl=en&q=c+manual

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to use the Blowfish algo?
    By Plasm4 in forum C Programming
    Replies: 12
    Last Post: 08-11-2008, 04:34 AM
  2. Blowfish
    By francoissoft in forum Tech Board
    Replies: 4
    Last Post: 07-21-2007, 11:23 PM
  3. Blowfish Encryption
    By tsy in forum C Programming
    Replies: 1
    Last Post: 08-14-2003, 02:55 AM
  4. relative strength of encryption algorithms (blowfish, des, rinjdael...)
    By duck-billed platypus in forum A Brief History of Cprogramming.com
    Replies: 3
    Last Post: 12-30-2001, 04:20 PM