2
votes

I'm using the following implementation of blowfish (with some stuff cut out of the middle) to encrypt and decrypt messages. I am attempting to encrypt a message on a big endian machine (SunOS 5.10), and then send it over a socket to a little endian machine (linux). I can encrypt and decrypt on the same machine, and on different linux machines, but I can't get it to work between these two machines.

Because the blowfish implementation seems to break the message up into DWords, each of which contains two 32 bit integers, I have been breaking my encrypted message into 32 bit integers, applying htonl() to each, sending them over the network, applying ntohl() to each, and then attempting to decrypt. The result is not pretty, but interestingly enough, most of the correct characters are present after decryption, but they are garbled and some extra garbage characters appear in some places. I really can't imagine what I might be missing.

/********** blowfish.h **********/

#ifndef ___BLOWFISH_H___
#define ___BLOWFISH_H___


#define NUM_SUBKEYS  18
#define NUM_S_BOXES  4
#define NUM_ENTRIES  256

#define MAX_STRING   256
#define MAX_PASSWD   56  // 448bits

#ifdef BIG_ENDIAN
struct WordByte
{
    unsigned int zero:8;
    unsigned int one:8;
    unsigned int two:8;
    unsigned int three:8;
};
#endif

#ifdef LITTLE_ENDIAN
struct WordByte
{
    unsigned int three:8;
    unsigned int two:8;
    unsigned int one:8;
    unsigned int zero:8;
};
#endif

union Word
{
    unsigned int word;
    WordByte byte;
};

struct DWord
{
    Word word0;
    Word word1;
};


class Blowfish
{
private:
  unsigned int PA[NUM_SUBKEYS];
  unsigned int SB[NUM_S_BOXES][NUM_ENTRIES];

  void Gen_Subkeys(char *);
  inline void BF_En(Word *,Word *);
  inline void BF_De(Word *,Word *);

public:
  Blowfish();
  ~Blowfish();

  void Reset();
  void Set_Passwd(char * = NULL);
  void Encrypt(void *,unsigned int);
  void Decrypt(void *,unsigned int);
};


#endif



/********** blowfish.cc **********/

#include <iostream.h>
#include <string.h>
#include "blowfish.h"


#define F(x)    (((SB[0][x.byte.zero] + SB[1][x.byte.one]) ^ SB[2][x.byte.two]) + SB[3][x.byte.three])


void Blowfish::Gen_Subkeys(char *Passwd)
{
  unsigned int i,j,len=strlen(Passwd);
  Word Work,null0,null1;

  if (len > 0)
  {
    j = 0;
    for (i=0;i<NUM_SUBKEYS;i++)
    {
    Work.byte.zero = Passwd[(j++)%len];
    Work.byte.one = Passwd[(j++)%len];
    Work.byte.two = Passwd[(j++)%len];
            Work.byte.three = Passwd[(j++)%len];
            PA[i] ^= Work.word;
        }

    null0.word = null1.word = 0;

    for (i=0;i<NUM_SUBKEYS;i+=2)
      {
            BF_En(&null0,&null1);
        PA[i] = null0.word;
            PA[i+1] = null1.word;
        }

    for (j=0;j<NUM_S_BOXES;j++)
            for (i=0;i<NUM_ENTRIES;i+=2)
        {
        BF_En(&null0,&null1);
        SB[j][i] = null0.word;
        SB[j][i+1] = null1.word;
        }
   }

   Work.word = null0.word = null1.word = 0;
   Passwd = NULL;
   len = 0;
}

void Blowfish::BF_En(Word *x1,Word *x2)
{
  Word w1=*x1,w2=*x2;

  w1.word ^= PA[0];
  w2.word ^= F(w1)^PA[1];       w1.word ^= F(w2)^PA[2];
  w2.word ^= F(w1)^PA[3];       w1.word ^= F(w2)^PA[4];
  w2.word ^= F(w1)^PA[5];       w1.word ^= F(w2)^PA[6];
  w2.word ^= F(w1)^PA[7];       w1.word ^= F(w2)^PA[8];
  w2.word ^= F(w1)^PA[9];       w1.word ^= F(w2)^PA[10];
  w2.word ^= F(w1)^PA[11];      w1.word ^= F(w2)^PA[12];
  w2.word ^= F(w1)^PA[13];      w1.word ^= F(w2)^PA[14];
  w2.word ^= F(w1)^PA[15];      w1.word ^= F(w2)^PA[16];
  w2.word ^= PA[17];

  *x1 = w2;
  *x2 = w1;
}

void Blowfish::BF_De(Word *x1,Word *x2)
{
  Word w1=*x1,w2=*x2;

  w1.word ^= PA[17];
  w2.word ^= F(w1)^PA[16];      w1.word ^= F(w2)^PA[15];
  w2.word ^= F(w1)^PA[14];      w1.word ^= F(w2)^PA[13];
  w2.word ^= F(w1)^PA[12];      w1.word ^= F(w2)^PA[11];
  w2.word ^= F(w1)^PA[10];      w1.word ^= F(w2)^PA[9];
  w2.word ^= F(w1)^PA[8];       w1.word ^= F(w2)^PA[7];
  w2.word ^= F(w1)^PA[6];       w1.word ^= F(w2)^PA[5];
  w2.word ^= F(w1)^PA[4];       w1.word ^= F(w2)^PA[3];
  w2.word ^= F(w1)^PA[2];       w1.word ^= F(w2)^PA[1];
  w2.word ^= PA[0];

  *x1 = w2;
  *x2 = w1;
}


Blowfish::Blowfish()
{
  Reset();
}

Blowfish::~Blowfish()
{
  Reset();
}


void Blowfish::Reset()
{
  unsigned int i,j;

  unsigned int PA_Init[NUM_SUBKEYS] =
  {
    0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
    0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
    0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
    0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
    0x9216d5d9, 0x8979fb1b
  };

  unsigned int SB_Init[NUM_S_BOXES][NUM_ENTRIES] =
  {
    0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
    0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
    0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
    0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
    ...
    ...
    ...
  };

  for (i=0;i<NUM_SUBKEYS;i++)
    PA[i] = PA_Init[i];

  for (j=0;j<NUM_S_BOXES;j++)
    for (i=0;i<NUM_ENTRIES;i++)
      SB[j][i] = SB_Init[j][i];
}

void Blowfish::Set_Passwd(char *Passwd)
{
  char New_Passwd[MAX_STRING];
  unsigned int i,len;

    if (Passwd == NULL)
    {
    do
    {
    cout << "\aEnter your password: ";
    cin.get(New_Passwd,MAX_STRING,'\n');
    len = strlen(New_Passwd);
    }
    while (len > MAX_PASSWD);
    Passwd = New_Passwd;
  }
  else
    len = strlen(Passwd);

  Reset();
  if (len > 0)
    Gen_Subkeys(Passwd);

  for (i=0;i<MAX_STRING;i++)
    New_Passwd[i] = '\0';
   Passwd = NULL;
   len = 0;
}

void Blowfish::Encrypt(void *Ptr,unsigned int N_Bytes)
{
  unsigned int i;
  DWord *Work;

  if (N_Bytes%8)
  {
    cerr << "\aBlowfish requires the input to be a multiple of 8 bytes (64 bits) to work.\n";
    return;
  }

    N_Bytes /= 8;
  Work = (DWord *)Ptr;

  for (i=0;i<N_Bytes;i++)
  {
    BF_En(&Work->word0,&Work->word1);
    Work++;
  }

  Work = NULL;
}

void Blowfish::Decrypt(void *Ptr,unsigned int N_Bytes)
{
  unsigned int i;
  DWord *Work;

  if (N_Bytes%8)
  {
    cerr << "\aBlowfish requires the input to be a multiple of 8 bytes (64 bits) to work.\n";
    return;
  }

    N_Bytes /= 8;
  Work = (DWord *)Ptr;
  for (i=0;i<N_Bytes;i++)
  {
    BF_De(&Work->word0,&Work->word1);
    Work++;
  }

  Work = NULL;
}

The following is the encrypted string on the big-endian machine:

11010011 11110110 01100110 10001101 01010011 11000110 00010001 11110110 00011010 11001010 00011111 00100001 10110000 10110100 10110000 11110000 11111100 11000010 00110011 00001111 10011110 01100111 00010101 10000101 00001010 01111110 10000101 00011100 10010010 10111010 10010111 11111001 00111101 01011100 00000010 01010011 11100000 11001101 11111010 00111100 00011101 10001001 01010111 01011010 11100001 10101111 10001100 10111001 00011111 10111100 10010101 01101110 10001101 11010100 11101110 11010001 10111011 11111110 10100111 01111000 11010001 01011101 00110100 01000001 01011011 01111100 10010011 00010001 10100011 10011011 10111100 01100000

This is the encrypted string after I have sent it over the network to the little-endian machine:

10001101 01100110 11110110 11010011 11110110 00010001 11000110 01010011 00100001 00011111 11001010 00011010 11110000 10110000 10110100 10110000 00001111 00110011 11000010 11111100 10000101 00010101 01100111 10011110 00011100 10000101 01111110 00001010 11111001 10010111 10111010 10010010 01010011 00000010 01011100 00111101 00111100 11111010 11001101 11100000 01011010 01010111 10001001 00011101 10111001 10001100 10101111 11100001 01101110 10010101 10111100 00011111 11010001 11101110 11010100 10001101 01111000 10100111 11111110 10111011 01000001 00110100 01011101 11010001 00010001 10010011 01111100 01011011 01100000 10111100 10011011 10100011

Finally, this is what the string needs to be, in order for it to be successfully decrypted on the little-endian machine (i.e. this is what I get when I encrypt the original string on the little-endian machine, instead of the big endian machine):

10001101 10111011 10110010 10111000 01110001 11000110 00110110 10101110 11000011 11000100 00001010 10011110 11111101 10101000 01110011 10110110 00011001 10000011 11111000 11010001 01101001 00101100 10110001 00010001 11101101 10001011 01101110 01101011 11110001 01101010 00111010 00010110 11101111 01001111 11001100 00011101 01101010 11110000 00110000 01110100 10111010 01000101 01011001 11111001 11011101 01000011 10000110 01000001 11101110 00100001 00111000 00101001 11110011 00001111 01000011 11000000 10110010 10011101 11110000 00000001 10001011 10110101 10010011 01010010 01110100 11100000 10010011 10111101 00111110 00100001 10101111 11010000

I'm currently trying to compare the strings, but I can't find a pattern.

1
Your WordByte structures are obviously an attempt to handle endianness, yet you say you're using htonl/ntohl too - might they not then conflict? Also, you might want to check both programs are being compiled 32 bit, as e.g. your using of unsigned in Word is broken otherwise.Tony Delroy
A side comment: you seem to be using Blowfish in ECB mode, which is not recommended practice.jschultz410
@TonyD The decrypted message is much worse (i.e. contains pseudo-random characters) when I don't use htonl/ntohl. I've continued to use them because I figured the odds of getting most of the original message's characters back after decryption is low if the decryption is done incorrectly. Therefore I must be moving in the right direction?? Also, I've changed all "unsigned int" instances to "uint32_t" and the problem persists.ztforster
@ztforster: I think I see a problem. When you call Encrypt() it takes the same byte string on both platforms. While you did correct for the fact that word.zero is different on the different platforms, you didn't correct the initial input string. That is, when you initially call BF_En() w1.word is a different number on the big endian machine and the little endian machine. So the first ^= is going to result in different numbers on the different architectures. Try htonl() on word0 and word1 before you call BF_En() on the little endian machine and see if the result matches better.jschultz410
@ztforster: You can easily check if when you call BF_En() on the two platforms with the same input block if w1.word and w2.word are the same numbers or not. Just printf("0x%08x 0x%08x\n", w1.word, w2.word); at the top of BF_En(). If they don't match, then that's your problem.jschultz410

1 Answers

1
votes

I think your problem is that you try to use a byte string input without somehow endian correcting it so that the same mathematical calculations are being done on different endian platforms:

void Blowfish::Encrypt(void *Ptr,unsigned int N_Bytes)
{
  unsigned int i;
  DWord *Work;

  if (N_Bytes%8)
  {
    cerr << "\aBlowfish requires the input to be a multiple of 8 bytes (64 bits) to work.\n";
    return;
  }

  N_Bytes /= 8;
  Work = (DWord *)Ptr;  // NOTE: this won't work on some archs if Ptr isn't properly aligned on a 32b boundary

  for (i=0;i<N_Bytes;i++)
  {
    Work->word0 = ntohl(Work->word0);  // necessary to convert byte string to same number on different archs
    Work->word1 = ntohl(Work->word1);
    BF_En(&Work->word0, &Work->word1);
    Work->word0 = htonl(Work->word0);  // leave all encryptions in network byte order
    Work->word1 = htonl(Work->word1);
    Work++;
  }

  Work = NULL;
}

void Blowfish::Decrypt(void *Ptr,unsigned int N_Bytes)
{
  unsigned int i;
  DWord *Work;

  if (N_Bytes%8)
  {
    cerr << "\aBlowfish requires the input to be a multiple of 8 bytes (64 bits) to work.\n";
    return;
  }

  N_Bytes /= 8;
  Work = (DWord *)Ptr;  // NOTE: this won't work on some archs if Ptr isn't properly aligned on a 32b boundary

  for (i=0;i<N_Bytes;i++)
  {
    Work->word0 = ntohl(Work->word0);  // necessary to convert byte string to same number on different archs
    Work->word1 = ntohl(Work->word1);
    BF_De(&Work->word0, &Work->word1);
    Work->word0 = htonl(Work->word0);  // leave all decryptions in network byte order
    Work->word1 = htonl(Work->word0);
    Work++;
  }

  Work = NULL;
}

Note that with the above code, you wouldn't need to worry about the endianness of your data at the application level anymore. The same input byte string should encrypt/decrypt to the same output byte string on both platforms.