Click here to Skip to main content
15,892,927 members
Articles / Desktop Programming / MFC

A Simple, Portable, Rinjdael (AES) Based Stream Cipher Class

Rate me:
Please Sign up or sign in to vote.
4.45/5 (18 votes)
5 Jul 2005CDDL6 min read 136K   3.4K   52  
A file encryption/decryption class with built in MD5 string and file hashing.

//////////////////////////////////////////////
// Implementation file for DCipher class
//////////////////////////////////////////////


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


#include "stdafx.h"
#include "DCipher.h"

/////////////////////////////////////
// Constructors
/////////////////////////////////////

DCipher::DCipher()
{
	//Default Constructor for GetMD5Hash function ONLY
	mp_s8ReadBuffer = 0;
}

DCipher::DCipher(CString szSrc, int head[19])
{
    mp_s8ReadBuffer = 0;

	FileSize=CurPosition=0;
	SrcFile = szSrc;
	szSrc += ".enc";
	OutFile=szSrc;

	for(int i=0; i<19; i++)
	{
		header[i] = head[i];
	}
}

///////////////////////////////////////////
// Destructor
///////////////////////////////////////////
DCipher::~DCipher() 
{
	    FreeBuffer();
};


/////////////////////////////////////
// Implementation
////////////////////////////////////

int DCipher::DecryptFile()
{
	FILE *r, *w;
	int blah = 0;
	int g = 0;
	unsigned int ch;
	GetFileLength();

	if( CheckIfEncrypted() != ENCRYPTED_FILE )
	{
		return ERROR_CANT_DEC;
	}

	if((r = fopen( (LPCTSTR)SrcFile, "rb")) == NULL)
	{
		return ERROR_SRC_FILE;
	}

	if((w = fopen( (LPCTSTR)OutFile, "wb")) == NULL)
	{
		return ERROR_DST_FILE;
	}

	char zzz[25];	//fixed invalid pointer - DKeesler
	fread(zzz, 19, 1, r); // Skip 19 bytes from the file.
	CurPosition=0;

	BuildKey(FALSE);

	/***************************************
	Decryption algorithm 
	***************************************/
	for(long int blk_num=0;;blk_num++)
	{
		for(g=0; g<64; g++)
		{
			CurPosition++;

			if((ch = fgetc(r)) == EOF)
			{
				blah = 1;
				break;
			}

			ch = ~((ch^(blk_num^~g))^(int)password[g]);

			if((ch%2)==0)
			{
				ch-=3;
			}
			else
			{
				ch-=1;
			}

			fputc(ch, w);

		}
			if(blah == 1)
			{
				break;
			}
	}

	fclose(r);
	fclose(w);


	remove((LPCTSTR)SrcFile);
	rename((LPCTSTR)OutFile, (LPCTSTR)SrcFile);

	return NO_ERRORS_DONE;
}

int DCipher::EncryptFile()
{

	FILE *r, *w;
	int blah=0;
	int g=0;
	unsigned int ch;
	GetFileLength();

	if(CheckIfEncrypted() != NORMAL_FILE)
	{
		return ERROR_CANT_ENC;
	}

	if((r = fopen((LPCTSTR)SrcFile, "rb")) == NULL)
	{
		return ERROR_SRC_FILE;
	}

	if((w = fopen((LPCTSTR)OutFile, "wb")) == NULL)
	{
		return ERROR_DST_FILE;
	}

	MarkTheFile(w); // Mark the file.
	CurPosition=0;

	BuildKey(TRUE);

	/***************************************
	Encryption algorithm 
	***************************************/
	for(long int blk_num=0;;blk_num++)
	{
		for(g=0; g<64; g++)
		{
			CurPosition++;
			if((ch=fgetc(r))==EOF)
			{
				blah = 1;
				break;
			}

			if((ch%2)==0)
			{
				ch+=1;
			}
			else
			{
				ch+=3;
			}

			ch =(~ch^(int)password[g])^(blk_num^~g);
			fputc(ch, w);

		}
			if(blah == 1)
			{
				break;
			}
	}
	/***************************************/
	fclose(r);
	fclose(w);

	remove((LPCTSTR)SrcFile);
	rename((LPCTSTR)OutFile, (LPCTSTR)SrcFile);

	return NO_ERRORS_DONE;
}

int DCipher::GetFileLength()
{
	FILE *z = fopen((LPCTSTR)SrcFile, "r");
	long int cur;
	fseek(z, 0L, SEEK_END);
	cur = ftell(z);
	fclose(z);
	FileSize = cur;
	return 0;
}

int DCipher::MarkTheFile(FILE *f)
{
	for(int d=0; d<19; d++)
	fputc(header[d], f);
	return 0;
}


int DCipher::CheckIfEncrypted()
{
	int m[19];
	FILE *h = fopen((LPCTSTR)SrcFile, "rb");
	for(int d=0; d<19; d++)
	m[d] = fgetc(h);
	fclose(h);

	int i = 0;
	do
	{
		if(m[i] != header[i])
			return NORMAL_FILE;
		else
			i++;
	}while(i < 19);

	return ENCRYPTED_FILE;
}



void DCipher::BuildKey(BOOL bEncrypt) //TRUE=encrypt
{

	CString szKey="";
	long int fl = FileSize;
	
	if(!bEncrypt)
		fl -= 19;	//don't count validation bytes if decrypting

	char buf[20];
	_ltoa(fl, buf, 10);
	szKey = buf;

	int sv = szKey.GetLength() / 2;

	_lrotl(fl, sv);

	int mv = atoi(szKey.Right(sv));
	fl *= mv;

	_ltoa(fl, buf, 10);

	CString tmp = buf;

	szKey = CalcMD5FromString((LPCTSTR)tmp, (sv+1)*7);
	
		int l = szKey.GetLength();
		int m = l / 2;
		int j = m+1;
		int d = l-j;

		CString key1;
		CString key2;
		CString key3;
		CString key4;

		key1 = szKey.Left(j);
		key2 = szKey.Right(j);
		key3 = szKey.Mid(d,m);
		key4 = szKey.Right(d);

		szKey = CalcMD5FromString((LPCTSTR)key3, l*3);
		szKey+= CalcMD5FromString((LPCTSTR)key1, l*4);
		szKey+= CalcMD5FromString((LPCTSTR)key4, j*4);
		szKey+= CalcMD5FromString((LPCTSTR)key2, l+m*2);

		strcpy(password, (LPCTSTR)szKey);
}

CString DCipher::GetMD5Hash(CString szKey, int nLen)
{
	CString tmp = szKey;
	szKey = CalcMD5FromString((LPCTSTR)tmp, 20);

		int l = szKey.GetLength();
		int m = l / 2;
		int j = m+1;
		int d = l-j;

		CString key1 = szKey.Right(j);
		CString key2 = szKey.Mid(m-1, m);
		CString key3 = szKey.Left(m);
		CString key4 = szKey.Left(j);

		szKey = CalcMD5FromString((LPCTSTR)key3, l);
	switch (nLen)
	{
	case 256:
		szKey+= CalcMD5FromString((LPCTSTR)key1, l+5);
		break;
	case 512:
		szKey+= CalcMD5FromString((LPCTSTR)key4, l*2);
		szKey+= CalcMD5FromString((LPCTSTR)key2, l+12);
		break;
	}

	return szKey;
}



//////////////////////////////////////////////////////////////////////////
//  BEGIN MD5 HASH SECTION
/////////////////////////////////////////////////////////////////////////

void DCipher::FreeBuffer()
{
    if (mp_s8ReadBuffer) delete mp_s8ReadBuffer;
}

/*********************************************************************
//
//         calculate MD5 from a file of any size (also size = 0)
//         returns "" on file error
//
/********************************************************************/

char* DCipher::CalcMD5FromFile(const char *s8_Path)
{
    if (!mp_s8ReadBuffer) mp_s8ReadBuffer = new char[_ReadBufSize];

    MD5Init();

    // ++++++++++++ Read file block by block +++++++++++

    HANDLE h_File = CreateFile(s8_Path, GENERIC_READ, FILE_SHARE_READ, 0, 
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
        
    if (h_File == INVALID_HANDLE_VALUE)
        return "";

    int s32_Size = GetFileSize(h_File, 0);

    while (s32_Size > 0)
    {
        unsigned long u32_Read = 0;
        if (!ReadFile(h_File, mp_s8ReadBuffer, _ReadBufSize, &u32_Read, 0))
        {
            CloseHandle(h_File);
            return "";
        }

        MD5Update((unsigned char*)mp_s8ReadBuffer, u32_Read);

        s32_Size -= u32_Read;
    };

    CloseHandle(h_File);

    // ++++++++++++ Signature --> String +++++++++++

    return MD5FinalToString();
}


/*********************************************************************
//
//                calculate MD5 from a string
//
/********************************************************************/


char* DCipher::CalcMD5FromString(const char *s8_Input, int nSeedGen)
{
    MD5Init();
    MD5Update((unsigned char*)s8_Input, strlen(s8_Input));

	return MD5ToEncryptionKey(nSeedGen);
}


/*********************************************************************
//
//                           Calculation functions
//
/*********************************************************************
/*
 * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 * initialization constants.
 */
void DCipher::MD5Init()
{
    ctx.buf[0] = 0x67452301;
    ctx.buf[1] = 0xefcdab89;
    ctx.buf[2] = 0x98badcfe;
    ctx.buf[3] = 0x10325476;

    ctx.bits[0] = 0;
    ctx.bits[1] = 0;
}

/*
 * Update context to reflect the concatenation of another buffer full
 * of bytes.
 */
void DCipher::MD5Update(unsigned char *buf, unsigned len)
{
    unsigned long t;

    /* Update bitcount */

    t = ctx.bits[0];
    if ((ctx.bits[0] = t + ((unsigned long) len << 3)) < t)
    ctx.bits[1]++;  /* Carry from low to high */
    ctx.bits[1] += len >> 29;

    t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */

    /* Handle any leading odd-sized chunks */

    if (t) {
    unsigned char *p = (unsigned char *) ctx.in + t;

    t = 64 - t;
    if (len < t) {
        memcpy(p, buf, len);
        return;
    }
    memcpy(p, buf, t);
    DCipher::byteReverse(ctx.in, 16);
    MD5Transform(ctx.buf, (unsigned long *) ctx.in);
    buf += t;
    len -= t;
    }
    /* Process data in 64-byte chunks */

    while (len >= 64) {
    memcpy(ctx.in, buf, 64);
    DCipher::byteReverse(ctx.in, 16);
    MD5Transform(ctx.buf, (unsigned long *) ctx.in);
    buf += 64;
    len -= 64;
    }

    /* Handle any remaining bytes of data. */

    memcpy(ctx.in, buf, len);
}


/*
 * Convert signature to CString [from File]
 */
char* DCipher::MD5FinalToString()
{
    unsigned char signature[16];
    MD5Final(signature);

    ms8_MD5[0] = 0;
    char s8_Temp[5];
    for (int i=0; i<16; i++) 
    {
		sprintf(s8_Temp, "%02X", signature[i]);
        strcat(ms8_MD5, s8_Temp);
    }

    return ms8_MD5;
}


/*
 * Convert signature to CString [from String]
 */
char* DCipher::MD5ToEncryptionKey(int nSeed)
{
    unsigned char signature[16];
    MD5Final(signature);

	srand(nSeed);	/////////////// comment lines signify modification to original code
	int val;	///////////////

    ms8_MD5[0] = 0;
    char s8_Temp[5];
    for (int i=0; i<16; i++) 
    {
		val = rand() % 2;	//////////////////////////////////////

		if(val < 1)	//////////////////////////////////////////////
			sprintf(s8_Temp, "%02X", signature[i]);
		else	//////////////////////////////////////////////////
			sprintf(s8_Temp, "%02x", signature[i]);	//////////////

        strcat(ms8_MD5, s8_Temp);
    }

    return ms8_MD5;
}


/*
 * Final wrapup - pad to 64-byte boundary with the bit pattern 
 * 1 0* (64-bit count of bits processed, MSB-first)
 */
void DCipher::MD5Final(unsigned char digest[16])
{
    unsigned count;
    unsigned char *p;

    /* Compute number of bytes mod 64 */
    count = (ctx.bits[0] >> 3) & 0x3F;

    /* Set the first char of padding to 0x80.  This is safe since there is
       always at least one byte free */
    p = ctx.in + count;
    *p++ = 0x80;

    /* Bytes of padding needed to make 64 bytes */
    count = 64 - 1 - count;

    /* Pad out to 56 mod 64 */
    if (count < 8) {
    /* Two lots of padding:  Pad the first block to 64 bytes */
    memset(p, 0, count);
    DCipher::byteReverse(ctx.in, 16);
    MD5Transform(ctx.buf, (unsigned long *) ctx.in);

    /* Now fill the next block with 56 bytes */
    memset(ctx.in, 0, 56);
    } else {
    /* Pad block to 56 bytes */
    memset(p, 0, count - 8);
    }
    DCipher::byteReverse(ctx.in, 14);

    /* Append length in bits and transform */
    ((unsigned long *) ctx.in)[14] = ctx.bits[0];
    ((unsigned long *) ctx.in)[15] = ctx.bits[1];

    MD5Transform(ctx.buf, (unsigned long *) ctx.in);
    DCipher::byteReverse((unsigned char *) ctx.buf, 4);
    memcpy(digest, ctx.buf, 16);
    memset(&ctx, 0, sizeof(MD5Context));        /* In case it's sensitive */
}


/* The four core functions - F1 is optimized somewhat */

/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))

/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
    ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )

/*
 * The core of the MD5 algorithm, this alters an existing MD5 hash to
 * reflect the addition of 16 longwords of new data.  MD5Update blocks
 * the data and converts bytes into longwords for this routine.
 */
void DCipher::MD5Transform(unsigned long buf[4], unsigned long in[16])
{
    register unsigned long a, b, c, d;

    a = buf[0];
    b = buf[1];
    c = buf[2];
    d = buf[3];

    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);

    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);

    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);

    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);

    buf[0] += a;
    buf[1] += b;
    buf[2] += c;
    buf[3] += d;
}

#ifdef sgi
#define HIGHFIRST
#endif

#ifdef sun
#define HIGHFIRST
#endif


#ifndef HIGHFIRST
    void DCipher::byteReverse(unsigned char *buf, unsigned longs)  
    {
        // Nothing
    }
#else
    // Note: this code is harmless on little-endian machines.
    void DCipher::byteReverse(unsigned char *buf, unsigned longs)
    {
        unsigned long t;
        do {
        t = (unsigned long) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
            ((unsigned) buf[1] << 8 | buf[0]);
        *(unsigned long *) buf = t;
        buf += 4;
        } while (--longs);
    }
#endif



By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
United States United States
I have no biography, but then, I don't have an obituary yet either -- Thank God!!

Comments and Discussions