Encryption Algorithm

There are a number of secure encryption algorithms available in today's market place which are tried and tested. Some of these algorithms are designed to meet certain criteria such as low memory usage and fast in operation.

In choosing an appropriate algorithm for this program I had to consider:

All encryption algorithms have weaknesses. Some have less weaknesses than others. This program must be able to deal with very sensitive documents. And these documents must be kept from being viewed by those it was not intended for.

To ensure the safety of the encryption I have used three encryption algorithms.

Blowfish is considered secure and is fast. It is a well known algorithm and as a result it has probably been well studied by the security agencies world wide. The security agencies may have found a way to crack documents encrypted with blowfish that we don't know about.

RC4 is secure and is an XOR stream cypher algorithm. I am using a variation of RC4. For ease of discussion I will call my encryption JC4. The simplicity of the algorithm also enables the non cryptanalyst to easily recognise the security of the algorithm. Some algorithms are so complex that it is easy to make a coding mistake which renders the encryption algorithm less secure. JC4 does not have this problem.

It has a major weakness. That is that if two different items of plain text have been encrypted with the same password then it is possible to do some manipulation with both plain texts and extract either of the plain texts without having to know the password.

JC4 eliminates this weakness. It does this by the following methods: a: JC4 mixes the positions of the text after the plain text has been XOR ed. You now cannot XOR two different items of plain text and obtain the original plain text.

The values obtained from A to E are combined in a string and a SHA Hash is taken of the string. This gives us 20 characters. We want 10 characters. We get 10 characters by XORing every two characters and placing the result of each XOR in another string;

The 10 generated characters are stored with the encrypted plain text. This has no effect on the security of the encryption as the security depends on the password used by the user.

Security of Password length

The advent of computers with very fast processors and large amounts of memory will improve the speed of this program greatly. If a hacker has a very efficient form of this program running on a very fast computer then what is the security of the algorithms. It can be assumed that the only way a hacker can crack an encrypted message is by brute force. If the time to crack a two password message by brute force (trying every character in the password in turn is 1x10-10 seconds then we can create the following table for the use of a password with the potential use of 250 characters.

Number of Letters seconds Minutes Hours Days years Centuries
2 1x10-10
3 200x10-10=2x10-8
4 200x 2x10-8=4x10-6
5 200x4x10-6=8x10-4
6 200x8x10-4=16x10-2
7 200x16x10-2=32
8 32x200=64x102 102
9 200x 102 =2x104 2x102 8
10 200x8=16x102 5
11 5x200=1000 10
12 2000
13 200,000
14 40,000,000

The above assumes a totally random password. In the case of this encryption program it is possible because the passwords that are used to encrypt messages do not need to be remembered. The password used to encrypt the password list file is more vulnerable to attack. This is because this password has to be remembered and so would have some order to it which can be exploited by an attacker. This will reduce the time taken to crack the password used to decode the password list file data. The figures used are very approximate. But they do show the advantages of a long password.

The password file can be kept in a safe place out of reach of a potential hacker i.e on a floppy disk or in a safe. This enables the password of the password list file to be not so secure. Only passwords in the password list file are used to encrypt messages.

The long passwords of random characters in the password list box makes it unfeasible to try and crack messages or data encrypted with this technique. The passwords in the password list file can have two passwords of 50 characters each. I have not used this for the password list file password. I have done this for safety reasons. If the password to the password list file is lost the data in the password list file will be lost. To reduce confusion in remembering passwords I have only allowed 50 characters for the password of the password list box.

Digital Signatures

Digital Signatures identify plain text in computer format. An ideal digital signature will be unique for every item of plain text and it will not be possible to identify what the plain text will be from the digital signature. SHA meets this criteria. It is also in the Public Domain and not copyrighted. SHA was proposed by NIST who are experts in the field of encryption. SHA is also fast when used.

SHA consists of a string of 20 characters (bytes). These characters can be any ASCII value including zero. To aid readability the SHA character set can be converted into five long integers. A long integer consists of 4 bytes. The integers can be separated by a non number such as *. And this is what I have done. See Digital Signatures of Records for examples of this.

Large File Digital Signature

For very large files a digital signature of the entire file is not practicable because of memory considerations. It is possible to combine a number of partial digital signatures. This is what I have done. I divide the file into 500k chunks and take an SHA hash of each chunk. When all the chunks are SHA hashed I then take an SHA hash of all the SHA's to obtain another SHA. The result of this I use as the Digital Signature for the large File.

Compression.

Compression is useful when storing documents on disk as they take up less space. In Chapter 10 of "Applied Cryptography" by Bruce Schneirr the following is said:

Using a data compression algorithm together with an encryption algorithm makes sense for two reasons:

There are several compression methods available PKZIP being widely known.

The algorithm I use for encryption uses both XORing of characters and mixing. I found that if I use a compression routine with a small header of 4 Bytes then security of the encryption is enhanced. But there is a problem with small files. If the file is small then the mixing part of encryption is non effective. But if there is a header then the file becomes larger and as such the mixing part of the encryption becomes more effective. It is for this reason I decided use the pkzip type of encryption despite the problem of its known header.

Pkzip has a known header and thus there is a sample of known plain text with which to attack my encryption. There are techniques by which known text can be turned into unknown text.

Turning Pkzip header into unknown text

The Plain text is compressed using pkzip compression. The pkzip header information is placed at the beginning of the compressed text. Blowfish is then used on the compressed text including the pkzip header. The compressed and encrypted plain text with pkzip header is then XOR encrypted. After this we start from the end of the file and XOR each character with its adjacent character from the end of the file to the beginning of the file. This changes a known bit of encrypted plain text to unknown encrypted text. The positions of the encrypted text are changed. This results in the known pkzip header being totally disguised and being impossible to pick out from the rest of the text.

Long Passwords

Email Addresses can be forged and it is possible for forgers to send an email from your email address. This creates problems of security. Who has sent me the email and is from the person in the address. You can identify the person by the password used. The password is used in communication only between two parties and is never used for communication with another party. This is why long passwords have their use. A long password which a communicator generates randomly has extreme unlikelihood to be randomly generated by someone else.

Malware

There is a practice among persons who wish to do mischief to send files and programs which damage the computer's operating system. On a number of occasion's this malware gets onto the system via a forged email address. The program will enable the email receiver to be aware of the senders forgery if communication is carried out using encrypted dated.

File Structures and Encryption Technique

File Structures and De-cryption Technique

Decryption is a simple process and involves several steps.

The code for the encryption/decryption follows:

BOOL FAR PASCAL encrypt(unsigned char *fileBuffer,
	unsigned long fileBufferLength)
{
    lVar.progress = 0;
    Initialise10ByteVector(Vector10Byte);    // obtain initialisation vector
    if (Fvar.hGmem) //    ensure original plain text is completely removed from memory
    {
        memset(Fvar.hpAddress, 0, Fvar.GmemSize);
        HeapDestroy(Fvar.hGmem);
        Fvar.hGmem = NULL;
        Fvar.GmemSize = 0;
    }
    
    ProcessEncrypt(fileBuffer, fileBufferLength, lVar.password); // password 1

    if (lVar.password2[0] != 0)    //    use second password if it exists
    {
        ProcessEncrypt(fileBuffer, fileBufferLength, lVar.password2);
    }
         //    inform user something is happening
    ProgressBarMessage("getting Digital Signature of Encrypted Data", "");
    if (!SHA(Fvar.BufferJee, fileBuffer, fileBufferLength)) // get digital signature of
                                     //     encrypted file
    {
        ErrBox("Cannot get Hash of encrypted file");
        return FALSE;
    }

    //    attach initialisation vector to encrypted data
    lVar.a = &fileBuffer[fileBufferLength];
    lVar.b = Vector10Byte;
    for (lVar.i=0; lVar.i < 10; lVar.i++)
    {
        *lVar.a = *lVar.b;
        lVar.a++;
        lVar.b++;
    }
    // attach SHA of encrypted file to encrypted data after initialisation vector
    
    lVar.b = (unsigned char *) Fvar.BufferJee;  // SHA
    
    for (lVar.i=0; lVar.i < sizeof(long)*5; lVar.i++)
    {
        *lVar.a = *lVar.b;
        lVar.a++;
        lVar.b++;
    }
    
    Fvar.AmountWrite +=  (unsigned long) (sizeof(long)*5+10);
    return TRUE;
}

BOOL FAR PASCAL ProcessEncrypt(unsigned char *fileBuffer,
    unsigned long fileBufferLength, char *passphrase)
{
    AppendToPassphrase(passphrase, Vector10Byte, key);
    memset(passphrase, 0, strlen(passphrase));    // remove passphrase from memory    
    DoBlowfishEncrypt(fileBuffer,
        fileBufferLength, TRUE);    //    encrypt with blowfish

    InitialiseStateArray(state, key);
    memset(key, 0, sizeof(KEYLENGTH+10));     //    remove key from memory
    ProgressBarMessage("XOR Encrypt", "");    // notify user something is happening
    rc4(fileBuffer,
        fileBufferLength, state, TRUE); // this is not exactly RC4 but similar
    ProgressBarMessage("XORing neighbour", ""); // notify user something is happening
    XORfromTop(fileBuffer, fileBufferLength);
    ProgressBarMessage("Position Mix", ""); // notify user something is happening
    rc4Mix(fileBuffer, fileBufferLength, TRUE); // mix character positions
    memset(state, 0, sizeof(state));    // remove state array from memory    
    return TRUE;
}

void FAR PASCAL rc4Mix(unsigned char *fileBuffer,
    unsigned long fileBufferLength, BOOL ENCRYPT)
{    
    lVar.nRet = 2;
    if (fileBufferLength < 50000) lVar.nRet = 5;
    if (fileBufferLength < 10000) lVar.nRet = 10;
    if (fileBufferLength < 500) lVar.nRet = 20;
    lVar.progress = 0;
    lVar.k = 0;

    unsigned char *a, *b;
    a = fileBuffer;
    b = fileBuffer;
    
    if (ENCRYPT)
    {  
        
        for (lVar.i=0; lVar.i < lVar.nRet; lVar.i++)
        { 
            
            lVar.MOD = 2;
            for (lVar.l = 3; lVar.l < fileBufferLength-1; lVar.l++)
            {
                 unsigned char temp;
                    lVar.MOD++;
                if (lVar.MOD == 256) lVar.MOD = 0;

                lVar.j = lVar.MOD;

            //     the above equals this lVar.j = (lVar.l+1) % 256;
                lVar.k = (state[lVar.j] + lVar.k);

                if (lVar.k >= 256)
                {
                    lVar.k -= 256;
                    if (lVar.k >= 256) lVar.k -= 256;
                }
                 lVar.ck = (lVar.k + lVar.l);
                lVar.ci = lVar.l;
            
                //    we are going to swap array aroung
                if (lVar.ci > fileBufferLength - DIFF ||
                    lVar.ck > fileBufferLength - DIFF)
                {
                    if (lVar.ci > fileBufferLength - DIFF)
                    {
                        while (lVar.ci > fileBufferLength - DIFF)
                        {
                            lVar.ci -= (fileBufferLength-DIFF);
                        }
                        if (lVar.ci < 3) lVar.ci = 3;
                    }
                    if (lVar.ck > fileBufferLength - DIFF)
                    {
                        while (lVar.ck > fileBufferLength - DIFF)
                        {
                            lVar.ck -= (fileBufferLength-DIFF);
                        }
                    }
                    if (lVar.ck < 3) lVar.ck = 3;
                }
                
                temp = *(fileBuffer+lVar.ck);
                *(fileBuffer+lVar.ck) = *(fileBuffer+lVar.ci);
                *(fileBuffer+lVar.ci) = temp;
        
                temp = state[lVar.j];
                state[lVar.j] = state[lVar.k];
                state[lVar.k] = temp;
                lVar.progress ++;
                if (lVar.progress > PROGRESSCOUNTMAX)
                {
                    //    inform user something is happening
                    SendMessage(hProgBar, PBM_STEPIT, 0, 0);
                    lVar.progress = 0;
                }
            
            }
        }
    }
    else
    {
        //    create array of position mixing characters
        Fvar.h3 = Fvar.hpAddress3;
                
        for (lVar.i=0; lVar.i < lVar.nRet; lVar.i++) 
        {
            lVar.MOD = 2;
            for (lVar.l = 3; lVar.l < fileBufferLength-1; lVar.l++)
            {
                 //    range 2 to fileBufferLength-2
                 unsigned char temp;
                lVar.MOD++;
                if (lVar.MOD == 256) lVar.MOD = 0;
        
                lVar.j = lVar.MOD;
            
                lVar.k = (state[lVar.j] + lVar.k);
                
                if (lVar.k >= 256)
                {
                    lVar.k -= 256;
                    if (lVar.k >= 256) lVar.k -= 256;
                }
                        *Fvar.h3 = lVar.k;
                 Fvar.h3++;
                 
        
                temp = state[lVar.j];
                state[lVar.j] = state[lVar.k];
                state[lVar.k] = temp;
                lVar.progress ++;
                if (lVar.progress > PROGRESSCOUNTMAX)
                {
                    SendMessage(hProgBar, PBM_STEPIT, 0, 0);
                    lVar.progress = 0;
                }
            
            }
        }  
        
        //    have array of position moving characters 
        //    will work from end of array back to beginning

        //    to unmix characters

        Fvar.h3--;
        for (lVar.i=0; lVar.i < lVar.nRet; lVar.i++)
        {
            
            for (lVar.l = fileBufferLength-2; lVar.l > 2; lVar.l--)
            {
                 //    range 2 to fileBufferLength-2
                 unsigned char temp;
                 
                 lVar.ck = (*Fvar.h3 + lVar.l);
        
                lVar.ci = lVar.l;
            
                //    we are going to swap array aroung
                if (lVar.ci > fileBufferLength - DIFF ||
                    lVar.ck > fileBufferLength - DIFF)
                {
                    if (lVar.ci > fileBufferLength - DIFF)
                    {
                        while (lVar.ci > fileBufferLength - DIFF)
                        {
                            lVar.ci -= fileBufferLength-DIFF;
                        }
                        if (lVar.ci < 3) lVar.ci = 3;

                    }
                    if (lVar.ck > fileBufferLength - DIFF)
                    {
                        while (lVar.ck > fileBufferLength - DIFF)
                        {
                            lVar.ck -= fileBufferLength-DIFF;
                        }
                    }
                    if (lVar.ck < 3) lVar.ck = 3;
                    
                }
                    
                temp = *(fileBuffer+lVar.ck);
                *(fileBuffer+lVar.ck) = *(fileBuffer+lVar.ci);
                *(fileBuffer+lVar.ci) = temp;
                lVar.progress ++;
                if (lVar.progress > PROGRESSCOUNTMAX)
                {
                    //    inform user something is happening
                    SendMessage(hProgBar, PBM_STEPIT, 0, 0);
                    lVar.progress = 0;
                }
                *Fvar.h3 = '\0';    //    clear mixing position from memory
                            //     memory
                Fvar.h3--;
            }
        }
    }
}



BOOL FAR PASCAL decrypt(unsigned char *fileBuffer,
    unsigned long fileBufferLength)
{
    //    check file size 
    if (fileBufferLength <= (unsigned long) (sizeof(long)*5+10))
    {
        ErrBox("File Size is too small");
        return FALSE;
    }
    
    fileBufferLength -= (unsigned long) (sizeof(long)*5+10);
    
    //    get initialisation vector from file

    lVar.a = &fileBuffer[fileBufferLength];
    lVar.b = Vector10Byte;
    for (lVar.i=0; lVar.i < 10; lVar.i++)
    {
        *lVar.b = *lVar.a;
        lVar.a++;
        lVar.b++;
    }
    
    //    get SHA Digital signature from file
    lVar.b = (unsigned char *) Fvar.BufferJee;
    for (lVar.i=0; lVar.i < sizeof(long)*5; lVar.i++)
    {
        *lVar.b = *lVar.a;
        lVar.a++;
        lVar.b++;
    }
    
    Fvar.AmountWrite = fileBufferLength;
    
    //    check saved SHA with SHA of encrypted file
    if (!SHA(Gvar.astring, fileBuffer, fileBufferLength))
    {
        ErrBox("Cannot get Hash of encrypted file");
        return FALSE;
    }
    if (!CompareHashes(Gvar.astring, Fvar.BufferJee))
    {
        ErrBox("Recorded Hash of Encrypted File does not match Hash");
        return FALSE;
    }
    
    //Initialise10ByteVector(Vector10Byte);
    initArrays(fileBufferLength); //    create array for character positions
    
    //    recover positions
    if (lVar.password2[0] != 0) // if second password
    {
        ProcessDecrypt(fileBuffer, fileBufferLength, lVar.password2);
    }

    ProcessDecrypt(fileBuffer, fileBufferLength, lVar.password);

    if (Fvar.hGmem3)    //    destroy character positions array
    {
        memset(Fvar.h3, 0, Fvar.Gmem3Size);
        HeapDestroy(Fvar.hGmem3);
        Fvar.hGmem3 = NULL;
        Fvar.Gmem3Size = 0;
    }
    ProgressBarMessage("","");
    return TRUE;
}

BOOL FAR PASCAL ProcessDecrypt(unsigned char *fileBuffer,
    unsigned long fileBufferLength, char *passphrase)
{

    //    get to state mixed position
    AppendToPassphrase(passphrase,
        Vector10Byte, key);
    memset(passphrase, 0, strlen(passphrase)); // remove password from memory
    InitialiseStateArray(state, key);

    
    
    ProgressBarMessage("Indexing Character Positions", "");     // inform user something
                                    // happening    
    
    rc4(fileBuffer,
        fileBufferLength, state, FALSE);            //    get state array at 
                                    //    beginning of unmixing position
    
    ProgressBarMessage("Unmixing Character Positions", ""); // inform user something
                                    //  happening
    rc4Mix(fileBuffer,
        fileBufferLength, FALSE);    //    unmix character positions

    ProgressBarMessage("Moving Characters", "");     // inform user something
                                //  happening
    
    ProgressBarMessage("UnXORing Neighbour", "");    // inform user something
                                //  happening
    XORfromBottom(fileBuffer, fileBufferLength);     // unXOR with neighbour

    InitialiseStateArray(state, key);            // reset state array
    ProgressBarMessage("XORing Character", "");     // inform user something
                                //  happening
    rc4(fileBuffer,
        fileBufferLength, state, TRUE);    //    XOR to get back to previous state

    memset(state, 0, sizeof(state));    //    remove state array contents from memory
    DoBlowfishEncrypt(fileBuffer,
        fileBufferLength, FALSE);    //    blowfish decryption

    memset(key, 0, sizeof(KEYLENGTH+10));    //    remove key from memory

    return TRUE;
}





void FAR PASCAL XORfromBottom(unsigned char *fileBuffer,
    unsigned long fileBufferLength)
{
    lVar.a = &fileBuffer[1];
    lVar.b = &fileBuffer[2];
    
    for (lVar.l = 2; lVar.l < fileBufferLength-2; lVar.l++)
    {
        *lVar.a ^= *lVar.b;    //    moving from beginning to end
        lVar.b++;
        lVar.a++;
        lVar.progress ++;
        if (lVar.progress > PROGRESSCOUNTMAX)
        {
            SendMessage(hProgBar, PBM_STEPIT, 0, 0);
            lVar.progress = 0;
        }
    }
}


void FAR PASCAL XORfromTop(unsigned char *fileBuffer,
    unsigned long fileBufferLength)
{
    lVar.a = &fileBuffer[1];
    lVar.b = &fileBuffer[2];
    
    
    lVar.b += (fileBufferLength-5);
    lVar.a += (fileBufferLength-5);
    
    
    while (lVar.a != &fileBuffer[1])
    {
        *lVar.a ^= *lVar.b;    //    moving from end to beginning
        lVar.b--;
        lVar.a--;
        lVar.progress ++;
        if (lVar.progress > PROGRESSCOUNTMAX)
        {
            SendMessage(hProgBar, PBM_STEPIT, 0, 0);
            lVar.progress = 0;
        }
    }
    *lVar.a ^= *lVar.b;
}

BOOL FAR PASCAL DoBlowfishEncrypt(unsigned char *fileBuffer,
	unsigned long fileBufferLength, BOOL ENCRYPT)
{
    BLOWFISH_KEY bfKey;
    int m, j;
    unsigned char BlowfishBuffer[8], *a;

    if( blowfishKeyInit( &bfKey, key, (KEYLENGTH+10) ) != TRUE )
    {
        return FALSE;
    }

    j = fileBufferLength/8;
    j--;
    a = fileBuffer + fileBufferLength -8;
    if (ENCRYPT)
    {
        ProgressBarMessage("Blowfish Encrypt", ""); // inform user of progress
        for (m = 0; m < j; m++)
        {
            memcpy( BlowfishBuffer, a, 8 );
            blowfishEncrypt( &bfKey, BlowfishBuffer );
            memcpy(a, BlowfishBuffer, 8);
            a -= 8;
        }
    }
    else
    {
        ProgressBarMessage("Blowfish Decrypt", ""); // inform user of progress
        for (m = 0; m < j; m++)
        {
            memcpy( BlowfishBuffer, a, 8 );
            blowfishDecrypt( &bfKey, BlowfishBuffer );
            memcpy(a, BlowfishBuffer, 8);
            a -= 8;
        }
    }
    memset(&bfKey, 0, sizeof(BLOWFISH_KEY)); // remove blowfish key from memory
    return TRUE;
}

<\pre>

Large File Hashing using SHA

The SHA algorithm relies on obtaining the whole file. This is not possible for large files. There is not enough memory on the computer. The nature of this algorithm does enable one to break the file into Chunks that an SHA hash of each of these chunks and then take an SHA hash of the combined chunks.

The program splits large files into 1 MB chunks. Thus it would not be wise to use 1MB as my chunk size to SHA. I choose .5 MB. My code for SHA hashing a large file is as follows:

BOOL FAR PASCAL GetHashLargeFile(HWND hWnd, char *filename)
{
    //    we have to get digital signature of large file
    //    we can do this by getting SHA of large file
    #define HASHSIZE 500000
    HANDLE hashfile;
    long Fsize;
    RemoveFileBuffers();
    ProgressBar(TRUE);        //    show progress bar
    ProgressBarMessage("Obtaining SHA Hash Large File", filename); // inform user of
                                        // progress
    if (!createDoubleMemory(HASHSIZE, TRUE))    //    create memory
    {
        return FALSE;
    }

    hashfile = CreateFile(filename, GENERIC_READ, 0, NULL,
        OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
    
    if (hashfile == INVALID_HANDLE_VALUE)
    {
        ErrBox("The File %s could not be found.", filename);
        return FALSE; 
    }
    
    Fsize = GetFileSize(hashfile, NULL) ; //    SHA hash size
    
    int i, j, k;
    i = Fsize / HASHSIZE;

    k = 1;
    SendMessage(hProgBar, PBM_SETSTEP, 20, 0); // alter rate of screen display    
    for (j = 0; j < i; j++)
        {
        
            Fvar.h1 = Fvar.hpAddress + 5*sizeof(long);
        SendMessage(hProgBar, PBM_STEPIT, 0, 0); // inform user something is
                                    //  happening
        sprintf(Gvar.tstring, "Obtaining SHA Hash No %d of %d",
            k, i+1);
        ProgressBarMessage(Gvar.tstring, filename);
        k++;
        
        if (Fsize >= FILESIZE)
        { 
            
            ReadFile(hashfile, Fvar.h1, HASHSIZE,
                &Fvar.AmountRead, NULL);
            if (!SHA(Fvar.BufferJee, Fvar.h1, HASHSIZE))
            {
                ErrBox("The File %s could not be hashed.", filename);
                CloseHandle(hashfile);
                hashfile = 0;
                SendMessage(hProgBar, PBM_SETSTEP, 5, 0);
                return FALSE;
            }
        }
        CopyMemory (Fvar.h2, Fvar.BufferJee, 5*sizeof(long));
        Fvar.h2 +=  5*sizeof(long);
    }
    Fsize -= i*HASHSIZE;
    Fvar.h1 = Fvar.hpAddress + 5*sizeof(long);

    if (Fsize > 0)    
    {
        SendMessage(hProgBar, PBM_STEPIT, 0, 0); // inform user something is
                                    //  happening
        sprintf(Gvar.tstring, "Obtaining SHA Hash No %d of %d",
                k, i+1);
        ProgressBarMessage(Gvar.tstring, filename);
        
        ReadFile(hashfile, Fvar.h1, Fsize,
            &Fvar.AmountRead, NULL);
        if (!SHA(Fvar.BufferJee, Fvar.h1, Fsize))
        {
            ErrBox("The File %s could not be hashed.", filename);
            CloseHandle(hashfile);
            hashfile = 0;
            SendMessage(hProgBar, PBM_SETSTEP, 5, 0);
            return FALSE;
        }
        CopyMemory (Fvar.h2, Fvar.BufferJee, 5*sizeof(long));
        Fvar.h2 +=  5*sizeof(long);
    }
    SendMessage(hProgBar, PBM_SETSTEP, 5, 0);    
    
    CloseHandle(hashfile);
    hashfile = 0;
    Fsize = Fvar.h2 - Fvar.hpAddress2;
    Fvar.h2 = Fvar.hpAddress2;
    if (!SHA((char *) Fvar.BufferJee, Fvar.h2, Fsize))
    {
        ErrBox("The File %s could not be hashed.", filename);
        return FALSE;
    }
    RemoveFileBuffers();    // remove arrays from memory
    return TRUE;
    
}

Exiting the Program

When the program exits as much memory is cleared as is possible. This is done by the following code.

void FAR PASCAL clearmeory(int TOTAL)
{
	//	temp

	#define MEMNO 25000
	HANDLE hGm;
	
	struct MemVariables
	{
		HANDLE hM;
		char *M;
		unsigned int HandleSize;
	};

	struct MemVariables *TestMem;

	MEMORYSTATUS mSize;
	mSize.dwLength = sizeof(MEMORYSTATUS);
	GlobalMemoryStatus(&mSize);
	
	Fvar.TotalFileSize = mSize.dwAvailPhys;
	Fvar.TotalFileSize1 = mSize.dwTotalPageFile;
	Fvar.TotalFileSize2 = Fvar.TotalFileSize+Fvar.TotalFileSize1;

	ConvertFileSizeToComma((__int64) Fvar.TotalFileSize,
		Gvar.astring, &Gvar.tstring[100]);
	ConvertFileSizeToComma((__int64) Fvar.TotalFileSize1,
		&Gvar.astring[50], &Gvar.tstring[100]);
	ConvertFileSizeToComma((__int64) Fvar.TotalFileSize2,
		&Gvar.astring[100], &Gvar.tstring[100]);

	sprintf(&Gvar.astring[150], 
"\r\nPhysical Memory = %s\r\nVirtual Memory = %s\r\nTotal memory = %s\r\n",
		Gvar.astring, &Gvar.astring[50], &Gvar.astring[100]);

	ProgressBar(TRUE);
	if (Fvar.h1)
	{
		SetWindowText(hDlgModeless, "Randomising Memory Values");
	}
	else
	{
		SetWindowText(hDlgModeless, "Clearing Memory");
	}

	
//	Assign Memory
	lVar.l = (long) sizeof(struct MemVariables);
	lVar.l *= (long) (MEMNO);
	unsigned int HandleSize;
	hGm = HeapCreate(0, lVar.l, 0);
	if (!hGm)
	{
		ErrBox(errstring);	//	"Not Enough Memory for Program");
		return;
	}

	TestMem = (struct MemVariables *) HeapAlloc(hGm,
									 HEAP_ZERO_MEMORY, lVar.l);
	if (!TestMem)
	{
		if (hGm)
		{
			HeapDestroy(TestMem[lVar.i].hM);
		}
		ErrBox(errstring);	//	"Not Enough Memory for Program");
		return;
	}

	
	HandleSize = 1000000;
	for (lVar.i = 0; lVar.i < MEMNO; lVar.i++)
	{
		
		TestMem[lVar.i].hM = HeapCreate(0, HandleSize, 0);

		if (!TestMem[lVar.i].hM)
		{
			TestMem[lVar.i].hM = 0;
			if (HandleSize < 101) break;
			if (HandleSize == 200) HandleSize = 100;
			if (HandleSize == 500) HandleSize = 200;
			if (HandleSize == 10000) HandleSize = 500;
			if (HandleSize == 1000000) HandleSize = 10000;
			lVar.i--;
			continue;
		}
			
		TestMem[lVar.i].M = (char *) HeapAlloc(TestMem[lVar.i].hM,
									 0 , HandleSize);
		
		if (!TestMem[lVar.i].M)
		{
			HeapDestroy(TestMem[lVar.i].hM);
			TestMem[lVar.i].hM = 0;
			if (HandleSize < 101) break;
			if (HandleSize == 200) HandleSize = 100;
			if (HandleSize == 500) HandleSize = 200;
			if (HandleSize == 10000) HandleSize = 500;
			if (HandleSize == 1000000) HandleSize = 10000;
			lVar.i--;
			continue;
		}
		TestMem[lVar.i].HandleSize = HandleSize;
//		ZeroMemory(TestMem[lVar.i].M, lVar.j);

	}
	//	memory reserved

	//	we need to release some memory in order to function
	HeapDestroy(TestMem[0].hM);
	TestMem[0].hM = 0;
	HandleSize = 995000;
	TestMem[0].hM = HeapCreate(0, HandleSize, 0);

	if (!TestMem[0].hM)
	{
		TestMem[0].hM = 0;
	}
	else
	{
		TestMem[0].M = (char *) HeapAlloc(TestMem[0].hM,
									 0 , HandleSize);
		if (TestMem[0].M)
		{
			TestMem[0].HandleSize = HandleSize;
		}
		else TestMem[0].HandleSize = 0;
	}

	Fvar.TotalFileSize1 = 0;

	for (lVar.j = 0; lVar.j < MEMNO; lVar.j++)
	{
		if (!TestMem[lVar.j].hM)
		{
			break;
		}
		HandleSize = TestMem[lVar.j].HandleSize;
		Fvar.TotalFileSize1 += (__int64) HandleSize;
		ConvertFileSizeToComma(Fvar.TotalFileSize1,
			Gvar.tstring, Gvar.astring);
		
		if (Fvar.h1)
		{
			SetDlgItemText1(hDlgModeless, IDC_MESSAGE1, "create %s Random Values",
							Gvar.tstring);
		}
		else
		{
			SetDlgItemText1(hDlgModeless, IDC_MESSAGE1, "create %s zero",
							Gvar.tstring);
		}
		
		for (Fvar.i = 0; Fvar.i < (int) HandleSize; Fvar.i++)
		{
			if (!Fvar.h1)
			{
				TestMem[lVar.j].M[Fvar.i] = 0;
			}
			else
			{
				TestMem[lVar.j].M[Fvar.i] = *Fvar.h1;
				Fvar.h1++;
				if (Fvar.h1 > Fvar.h2)
				{
					ModifyhpAddress1000000(FALSE);
				}	
			}
		}
	}

	if (Fvar.h1)
	{
		ModifyhpAddress1000000(FALSE);
	}

	//	we are 5000 short
	if (lVar.j < MEMNO)
	{
		HandleSize = 5000;
		TestMem[lVar.j].hM = HeapCreate(0, HandleSize, 0);

		if (!TestMem[lVar.j].hM)
		{
			TestMem[lVar.j].hM = 0;
		}	
		else
		{
			TestMem[lVar.j].M = (char *) HeapAlloc(TestMem[0].hM,
										 0 , HandleSize);
			if (TestMem[lVar.j].M)
			{
				TestMem[lVar.j].HandleSize = HandleSize;
			}
			else TestMem[lVar.j].HandleSize = 0;
		}

		if (TestMem[lVar.j].HandleSize)
		{
			for (Fvar.i = 0; Fvar.i < (int) HandleSize; Fvar.i++)
			{
				if (!Fvar.h1)
				{
					TestMem[lVar.j].M[Fvar.i] = 0;
				}
				else
				{
					TestMem[lVar.j].M[Fvar.i] = *Fvar.h1;
					Fvar.h1++;
						
				}
			}
		}
		Fvar.TotalFileSize1 += (__int64) HandleSize;
	}
		
	MEMORYSTATUS MS;
	MS.dwLength = sizeof(MEMORYSTATUS);
	GlobalMemoryStatus(&MS);



	//	Deallocate Memory
	for (lVar.i = 0; lVar.i < MEMNO; lVar.i++)
	{
		if (!TestMem[lVar.i].hM)
		{
			break;
		}
		HeapDestroy(TestMem[lVar.i].hM);
	}

	if (Fvar.h1)
	{
		MoveMemory(TestMem, Fvar.h1, sizeof(HANDLE)*MEMNO);
		MoveMemory(&mSize, Fvar.h1+sizeof(HANDLE)*MEMNO, sizeof(MEMORYSTATUS));
		//	Fvar.hpAddress removed when zero()
	}
	else
	{
		ZeroMemory(TestMem, sizeof(HANDLE)*MEMNO);
		ZeroMemory(&mSize, sizeof(MEMORYSTATUS));
	}

	
	HeapDestroy(hGm);
	hGm = 0;


	//	memory deallocated

	ProgressBar(FALSE);
	//	Fvar.TotalFileSize = memory set
	//	Fvar.TotalFileSize1 = memory allocated

	//Fvar.TotalFileSize = mSize.dwAvailPhys;
	//Fvar.TotalFileSize2 = Fvar.TotalFileSize+Fvar.TotalFileSize1;
	
	ConvertFileSizeToComma((__int64) Fvar.TotalFileSize1,
		Gvar.tstring, &Gvar.tstring[100]);

	Fvar.TotalFileSize1 = Fvar.TotalFileSize2 - Fvar.TotalFileSize1;
	ConvertFileSizeToComma(Fvar.TotalFileSize1,
		&Gvar.astring[350], &Gvar.tstring[100]);

	//	&Gvar.astring[150]
	if (TOTAL < 2)
	{
		if (Fvar.h1)
		{
			strcpy(Gvar.astring, "Random Bytes");
		}
		else
		{
			strcpy(Gvar.astring, "zero");
		}
		
		MBox(MB_ICONINFORMATION | MB_OK, "Memory Over Write",
			"%s overwritten with %s%s\r\nDifference = %s",
			Gvar.tstring, Gvar.astring, 
			&Gvar.astring[150], &Gvar.astring[350]);
	}
}

Weaknesses

In the normal process of encryption methods of error checking are put in after the data has been encrypted and this has been done here. But in this case error checking has been put in to identify the file after it has been encrypted . This is not usual for encrypted files but is important in the usage that this program is going to be put to. This program is designed to be used for files that have legal significance and could be used in a court of law. It is of the utmost impost importance that the document can be identified as the original data before it was encrypted. This weakens the encryption and requires that the algorithms used in encryption are much stronger than what normally would be the case in order to eliminate the weaknesses thus produced by the legal requirements.
A digital signature of the file is attached to it. This is then compressed using the pkzip algorithm.

This produces a header of known plain text. This provides a means by which the encrypted file can be attacked. There are a number of compression algorithms which are better for encryption purposes but are not as fast as pkzip and on average does not provide as good a compression as pkzip. These compression algorithms do not provide a header but the pkzip algorithm does.

The pkzip header is a weakness as it is has known text. This weakness is removed by turning it into unknown text. This done by encrypting with Blowfish then XORing it with an JC4 algorithm. This is followed by XORing from the end of the encrypted text down to the beginning. (See XORfromTop code). The text is now disguised and unrecognisable. The position of this text is then altered. Without knowing the original text values it is not possible to unmix the character positions.
For safety I do not have the first two characters mixed. This leaves an identifiable character which gives no cues as to either the content of the message or the key. The first character is not encrypted..
The potential weakness of the XOR stream cypher used after Blowfish is removed by XORing the neighbouring characters and mixing the text.
To decrypt the mixed characters requires memory resources. This restricts large files to programs with large amounts of available memory. This is a weakness. A one MB file requires 2MB for decryption. So 3 MB is required possible 4. A 4MB file will require a total of 16MB to decrypt.

But most files of this size will be split anyway into smaller sizes using the Large File decrypt facility

Mixing of characters
What follows is part of a table of how the position of characters can be altered during the running of JC4. For space reasons the numbers are grouped in columns of three and not all of the table is reproduced. The first column is the original position (start). The second column is the position after JC4 character mixing has been applied first time (first password). The third column is the position after JC4 character mixing has been applied second time (second password).

Character mixing as the sole form of encryption is known to be weak. Use character mixing following encryption by RC4 and blowfish makes the encryption many magnitudes stronger. The files cannot be cracked by XORing together. The character mixing has the added bonus that it makes the algoritm proof against quantum computing. There is also an added bonus that an encrypted file can be rendered uncrackable by adding a single character to it - provided of course the decryptor knows nothing about this.

The table below shows that a file of Bytes will be well mixed

index new position new position index new position new position index new position new position
2000 chars 1st round 2rd Round 100 chars 1st round 2rd Round 50 chars 1st round 2rd Round
0 0 0 0 0 0 0 0 0
1 751 1946 1 88 64 1 14 37
2 769 1429 2 51 4 2 11 33
3 1177 680 3 42 84 3 16 42
4 1201 340 4 8 14 4 15 28
5 1144 1562 5 3 42 5 27 40
6 82 114 6 18 2 6 44 43
7 1029 95 7 31 32 7 34 10
8 1688 673 8 14 61 8 45 17
9 1064 491 9 93 23 9 26 23
10 465 783 10 59 44 10 39 35
11 202 237 11 49 17 11 33 46
12 932 1750 12 1 88 12 2 11
13 1955 1474 13 40 77 13 5 27
14 1675 1741 14 61 83 14 37 3
15 902 1914 15 6 18 15 28 41
16 655 846 16 87 89 16 42 1
17 1876 1343 17 54 63 17 24 13
18 801 466 18 2 51 18 18 18
19 1687 1352 19 73 56 19 36 29
20 442 1130 20 47 24 20 38 4
21 1440 353 21 74 95 21 30 6
22 1381 1618 22 60 27 22 22 22
23 1838 217 23 98 86 23 25 21
24 505 61 24 7 31 24 13 5
25 1306 504 25 97 22 25 21 30
26 510 163 26 20 47 26 23 25
27 1699 15 27 75 29 27 40 47
28 1972 847 28 36 30 28 41 31
29 1282 6 29 25 97 29 8 45
1981 1645 194 81 82 46 31 19 36
1982 325 1409 82 46 62 32 7 34
1983 714 1869 83 80 50 33 46 49
1984 1524 331 84 34 79 34 10 39
1985 749 1962 85 41 92 35 48 20
1986 1717 127 86 19 73 36 29 8
1987 1511 720 87 89 26 37 3 16
1988 513 873 88 64 39 38 4 15
1989 1172 26 89 26 20 39 35 48
1990 208 243 90 69 67 40 47 32
1991 1014 1297 91 11 49 41 31 19
1992 1083 1036 92 55 28 42 1 14
1993 173 1057 93 23 98 43 9 26
1994 190 185 94 48 13 44 43 9
1995 59 808 95 57 10 45 17 24
1996 1079 1308 96 81 82 46 49 12
1997 1046 1590 97 22 60 47 32 7
1998 357 1927 98 86 19 48 20 38
1999 1875 820 99 37 45 49 12 2

Digital Signatures of Records

Computer records enable easy forgery of records because of the software available to alter file dates and file data. If a record is kept of digital signatures on a regular basis at least once a day and a record is kept of all the daily digital signatures then if a record is altered then the records can produce evidence of this.

A digital signature record kept securely should be able to provide evidence that a record has not been altered on certain dates.

The digital signature can ensure that when a file is altered it can be detected. If data is entered in the file in a particular manner then it is possible to restore the file to match particular digital signatures. This ability to do so would be useful to match particular legal situations.

An example of digital signatures in Hex for files follows:

File Name File Size Digital Signature
ACCOUNT.APS 12,744 567E28CACD73344072EB7CE9060E7B131C307B2D
A.C 1,197 17C9B97707E8923027C07452581F89AD0A720560
ACCOUNT.CPP 6,698 345E7D71D0F8F1DCEC0A8A341D1BF7CB28516216
ACCOUNT.DSP 4,743 841D1BAD60EC577E953D8AF3D36D4DF1CD31F01E
Osbourne.wpd 4,077 E3A2FE248C0AEB22FC30B8CD026D4ABB18B2E781
register.wpd 6,853 756A3C177A7C9EBABE00D76208824DD139707435
today.dat 556 6F2D5FD59415A51CCBC81CB4A749A332C5E10984

Diagram of Encryption Procedure pre 5.99a (pdf download){short description of image}