/* API_examples.c  */

/*
This source code carries out a series of tests on the 
functions in CryptoSys API (diCryptoSys.dll).

The tests in themselves are pretty boring. Use the examples
as the basis for your hopefully-more-useful code.

It is not meant to be representative of good security coding.

There is minimal error checking here - we use assert as a blunt instrument -
and we make little or no effort to clean up passwords etc afterwards.

Use in conjunction with diCryptoSys.lib and diCryptoSys.dll (Version 3.2 or later)

Copyright (C) 2001-6 DI Management Services Pty Limited. All rights reserved.
    Last updated:
    $Date: 2006-07-15 19:48:00 $
    $Revision: 3.2.0 $
*/


#if _MSC_VER >= 1100
    /* Detect memory leaks in MSVC++ */ 
    #define _CRTDBG_MAP_ALLOC
    #include <stdlib.h>
    #include <crtdbg.h>
#else
    #include <stdlib.h>
#endif

#include "diCryptoSys.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

// Compiler-specific explicit link to library
// This works in MSVC and Borland for LIB in same dir - you may need to do otherwise
#pragma comment(lib, ".\\diCryptoSys.lib")

/* SYSTEM-SPECIFIC DIR FNS:
 * Used in `make_new_test_dir' and `remove_test_dir' only.
 */
#ifdef _MSC_VER
    /* MSVC functions */
    #include <direct.h>
    #define MKDIR(d) _mkdir(d)
    #define CHDIR(d) _chdir(d)
    #define RMDIR(d) _rmdir(d)
    #define GETCWD(od, n) _getcwd(od, n)
#elif __BORLANDC__
    /* Borland functions */
    #include <dir.h>
    #define MKDIR(d) mkdir(d)
    #define CHDIR(d) chdir(d)
    #define RMDIR(d) rmdir(d)
    #define GETCWD(od, n) getcwd(od, n)
#elif unix
    /* Linux functions */
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #define MKDIR(d) mkdir(d, 0777)
    #define CHDIR(d) chdir(d)
    #define RMDIR(d) rmdir(d)
    #define GETCWD(od, n) getcwd(od, n)
#else
    /* Take a punt the compiler has inbuilt fns or die!
       -- replace with your own here if necessary */
    #define MKDIR(d) mkdir(d)
    #define CHDIR(d) chdir(d)
    #define RMDIR(d) rmdir(d)
    #define GETCWD(od, n) getcwd(od, n)
#endif

#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif


/* FUNCTIONS TO CREATE AND REMOVE A TEST DIRECTORY */

/* Global variables */
char testdir[FILENAME_MAX];
char old_cwd[FILENAME_MAX];
char new_cwd[FILENAME_MAX];

int make_new_test_dir(void)
{
    /* 
    (1) Try and create a sub-dir in current working dir
    (2) If that fails, use _tempnam
    */

    char hex[9];
    char *tempname;

    /* Use RNG to generate a 4-byte/8-hex char random address */
    RNG_NonceDataHex(hex, sizeof(hex)-1, 4);

    /* Create a test directory */
    sprintf(testdir, ".\\apitest.%s", hex);
    printf("Trying to create test directory '%s'...\n", testdir);
    
    /* Use MS-system-specific fns to create and set as default dir */
    if (MKDIR(testdir) != 0)
#ifdef _MSC_VER
    {   /* Check in case we run this where we don't have permission */
        printf("Unable to create test directory '%s'. Trying with _tempnam...\n", testdir);
        /* Now try using _tempnam (making a copy) */
        tempname = tempnam("\\", "api");
        strncpy(testdir, tempname, FILENAME_MAX-1);
        free(tempname); /* _tempnam uses malloc */
        if (MKDIR(testdir) != 0)
        {
            fprintf(stderr, "ERROR: unable to create a temp directory.");
            exit(EXIT_FAILURE);
        }
    }
#else   /* If not MSVC, we have just failed */
    {
        fprintf(stderr, "ERROR: unable to create a temp directory.");
        exit(EXIT_FAILURE);
    }
#endif
    printf("Created test directory '%s' OK.\n", testdir);

    /* Remember current working directory */
    GETCWD(old_cwd, FILENAME_MAX-1);

    /* Change CWD to our new temp dir */
    CHDIR(testdir);

    /* And check */
    GETCWD(new_cwd, FILENAME_MAX-1);
    printf("Current dir is '%s'\n", new_cwd);

    return 0;
}

void remove_test_dir(char *dirname, char *old_cwd)
{
    /* Use system commands to do the business 
       --see DELCMD macro above (NB strings concatenate) */
    /* CAUTION: Use this carefully */
    int res;

    CHDIR(dirname);

    if (!old_cwd)
        CHDIR("..");
    else
        CHDIR(old_cwd);
    res = RMDIR(dirname);
    if (res == 0)
        printf("Removed test directory OK.\n");
    else
        printf("ERROR: (%d) failed to remove test directory.\n", res);
}


/* UTILITIES USED IN THESE TESTS */

int create_hello_file(char *hello_file)
/* Create a 13-byte text file "hello world" plus CR-LF */
{
    FILE *fp;

    fp = fopen(hello_file, "wb");
    assert (fp != NULL);
    fprintf(fp, "hello world\r\n");
    fclose(fp);
    printf("Created 'hello.txt' as %s\n", hello_file);

    return 0;
}

int create_nowis_file(char *filename)
/* Create a 32-byte text file without a CR-LF at end */
{
    FILE *fp;

    fp = fopen(filename, "wb");
    assert (fp != NULL);
    fprintf(fp, "Now is the time for all good men");
    fclose(fp);
    printf("Created 'nowis.txt' as %s\n", filename);

    return 0;
}

int create_bin_file(char *bin_file)
/* Create a 512-byte binary file (0x00,0x01,0x02,...,0xFF)*2 */
{
    int i, k;
    FILE *fp;

    fp = fopen(bin_file, "wb");
    assert (fp != NULL);
    for (k = 0; k < 2; k++)
        for (i = 0; i < 256; i++)
            fputc(i, fp);
    fclose(fp);
    printf("Created 'test.bin' as %s\n", bin_file);

    return 0;
}

int file_exists(char *fname)
/* Returns true (1) if file exists or false (0) if it doesn't */
{
    FILE *fp;

    fp = fopen(fname, "rb");
    if (fp == NULL)
        return FALSE;

    fclose(fp);
    return TRUE;
}


int cmp_files(char *file1, char *file2)
/* Compares two binary files: returns 0 if identical 
   or 1 if not identical or -1 if file error */
{
    FILE *fp1, *fp2;
    int c1, c2;
    int result = 0; /* Innocent until proven guilty */

    fp1 = fopen(file1, "rb");
    if (fp1 == NULL) 
        return -1;
    fp2 = fopen(file2, "rb");
    if (fp2 == NULL)
    {
        fclose(fp1);
        return -1;
    }

    while ((c1 = fgetc(fp1)) != EOF)
    {
        c2 = fgetc(fp2);
        if (c2 == EOF)
        {   /* File 2 is shorter than file 1 */
            result = 1;
            break;
        }
        if (c1 != c2)
        {   /* Found a mis-match */
            result = 1;
            break;
        }
    }

    if (feof(fp1))
    {   /* Make sure file2 is same length */
        if ((c2 = fgetc(fp2)) != EOF)
            result = 1;
    }

    fclose(fp1);
    fclose(fp2);

    return result;
}

int cmp_file_with_hex(char *file, char *hex_ok)
/* Returns zero if file contains exactly the bytes in hex_ok */
{
    unsigned char *correct, *fbuf, *cp;
    int i, n, x, result;
    FILE *fp;
    char hex[3];

    /* Convert correct hex string to bytes */
    n = strlen(hex_ok) / 2;
    correct = malloc(n);
    assert (correct != NULL);
    fbuf = malloc(n);
    assert (fbuf != NULL);

    for (cp = hex_ok, i = 0; i < n; i++)
    {
        hex[0] = *cp++;
        hex[1] = *cp++;
        hex[2] = 0;
        sscanf(hex, "%x", &x);
        correct[i] = x;
    }

    /* Read in file to buffer */
    fp = fopen(file, "rb");
    assert (fp != NULL);
    fread(fbuf, 1, n, fp);
    /* Make sure we are end of file */
    x = fgetc(fp);

    /* Do we have a match? */
    result = memcmp(fbuf, correct, n);

    /* Clean up */
    fclose(fp);
    free(correct);
    free(fbuf);

    if (x != EOF)
        return 1;

    return result;
}

static int convert_hex_to_bytes(unsigned char bytes[], int maxbytes, const char *hexstr)
/* Converts null-terminated string of hex chars to an array of bytes up to maxbytes long
   Returns # of bytes converted or -1 if error
*/
{
    int i;
    int len = strlen(hexstr) / 2;
    if (maxbytes < len) len = maxbytes;
    for (i = 0; i < len; i++) 
    {
        int t, v;

        t = *hexstr++;
        if ((t >= '0') && (t <= '9')) v = (t - '0') << 4;
        else if ((t >= 'a') && (t <= 'f')) v = (t - 'a' + 10) << 4;
        else if ((t >= 'A') && (t <= 'F')) v = (t - 'A' + 10) << 4;
        else return -1;
        
        t = *hexstr++;
        if ((t >= '0') && (t <= '9')) v ^= (t - '0');
        else if ((t >= 'a') && (t <= 'f')) v ^= (t - 'a' + 10);
        else if ((t >= 'A') && (t <= 'F')) v ^= (t - 'A' + 10);
        else return -1;
        
        bytes[i] = (unsigned char)v;
    }
    return i;
}


static void pr_hexbytes(unsigned char *bytes, int nbytes)
/* Print bytes in hex format + newline */
{
    int i;

    for (i = 0; i < nbytes; i++)
        printf("%02X", bytes[i]);
    printf("\n");
}

static char *lookup_error(int errcode)
/* Looks up description of error msg and returns
   ptr to static string
*/
{
    static char errmsg[128];

    errmsg[0] = '\0';
    API_ErrorLookup(errmsg, sizeof(errmsg), errcode);

    return errmsg;
}

/* DES TESTS */

void test_DES_Hex(void)
{
    char *testfn = "DES_Hex()";
    char sHexKey[] = "0123456789abcdef";
    /* "Now is t" in hex */
    char sInput[] = "4E6F772069732074";
    char sCorrect[] = "3FA40E8A984D4815";
    char sOutput[sizeof(sInput)+1];
    long lngRet;

    printf("Testing %s...\n", testfn);

    printf("KY=%s\n", sHexKey);
    printf("PT=%s\n", sInput);

    // Encrypt in one-off process
    lngRet = DES_Hex(sOutput, sInput, sHexKey, ENCRYPT);
    assert (lngRet == 0);
    /* Check */
    printf("CT=%s %d\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sCorrect, sOutput) == 0);

    // Now decrypt back to plain text using same buffer
    lngRet = DES_Hex(sOutput, sOutput, sHexKey, DECRYPT);
    assert (lngRet == 0);
    printf("P'=%s %d\n", sOutput, lngRet);
    assert (strcmp(sInput, sOutput) == 0);

    printf("...%s tested OK\n", testfn);
}

void test_DES_HexMode(void)
{
    char *testfn = "DES_HexMode()";
    long lngRet;
    char sHexKey[] = "0123456789abcdef";
    char sHexIV[] = "1234567890abcdef";
    // "Now is the time for all good men"
    char sInput[] = "4E6F77206973207468652074696D6520666F7220616C6C20";
    char sCorrect[] = "E5C7CDDE872BF27C43E934008C389C0F683788499A7C05F6";
    // Set sOutput to be same length as sInput
    char sOutput[sizeof(sInput)+1];

    printf("Testing %s...\n", testfn);

    printf("KY=%s\n", sHexKey);
    printf("IV=%s\n", sHexIV);
    printf("PT=%s\n", sInput);

    // Encrypt in one-off process
    lngRet = DES_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %d\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

    // Now decrypt back to plain text
    lngRet = DES_HexMode(sOutput, sOutput, sHexKey, DECRYPT, "cbc", sHexIV);
    assert (lngRet == 0);
    printf("P'=%s %d\n", sOutput, lngRet);
    assert (strcmp(sOutput, sInput) == 0);

    printf("...%s tested OK\n", testfn);
}


void test_DES_UpdateHex(void)
{
    long hContext;
    long result;
    char sKey[] = "0123456789ABCDEF";
    char sInitV[] = "1234567890abcdef";
    char sHexString[33];
    char *correct;

    printf("Testing DES_UpdateHex() in CBC mode ...\n");
    hContext = DES_InitHex(sKey, ENCRYPT, "CBC", sInitV);
    if (hContext == 0)
        printf("DES_InitError=%ld\n", DES_InitError());
    assert (hContext != 0);

    /* First part: "Now is t" in hex (8 chars) */
    strcpy(sHexString, "4e6f772069732074");
    result = DES_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "E5C7CDDE872BF27C";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    /* Second part: "he time for all " in hex (16 chars) */
    strcpy(sHexString, "68652074696d6520666f7220616c6c20");
    result = DES_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "43E934008C389C0F683788499A7C05F6";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    result = DES_Final(hContext);
    assert (result == 0);

    /* Now decrypt */
    hContext = DES_InitHex(sKey, DECRYPT, "CBC", sInitV);
    if (hContext == 0)
        printf("DES_InitError=%ld\n", DES_InitError());
    assert (hContext != 0);

    strcpy(sHexString, "E5C7CDDE872BF27C43E934008C389C0F");
    result = DES_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "4E6F77206973207468652074696D6520";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    strcpy(sHexString, "683788499A7C05F6");
    result = DES_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "666F7220616C6C20";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    result = DES_Final(hContext);
    assert (result == 0);


    printf("...DES_UpdateHex() tested OK\n");
}

void test_DES_FileHex(void)
{
    char *testfn = "DES_FileHex()";
    long lngRet;

    // Construct full path names to files
    char *strFileIn = "now.txt";
    char *strFileOut = "DESnow.enc";
    char *strFileChk = "DESnow.chk";

    // Encrypt plaintext file to cipher
    // WARNING: output file is just clobbered
    char sHexKey[] = "0123456789ABCDEF";

    printf("Testing %s...\n", testfn);

    create_nowis_file(strFileIn);

    lngRet = DES_FileHex(strFileOut, strFileIn, sHexKey, 
        ENCRYPT, "ECB", 0);
    assert (lngRet == 0);

    assert (cmp_file_with_hex(strFileOut, 
        "3FA40E8A984D48156A271787AB8883F9893D51EC4B563B53"
        "73C1ADB2171F7894086F9A1D74C94D4E")
        == 0);

    // Now decrypt it
    lngRet = DES_FileHex(strFileChk, strFileOut, sHexKey, 
        DECRYPT, "ECB", 0);
    assert (lngRet == 0);

    /* and check we got the plaintext we started with */
    assert (cmp_files(strFileChk, strFileIn) == 0);

    remove(strFileIn);
    remove(strFileOut);
    remove(strFileChk);

    printf("...%s tested OK\n", testfn);
}

void test_DES_Bytes_rand(void)
/* Encrypt and decrypt random blocks with random keys */
{
    char *testfn = "DES_Bytes_rand()";

    unsigned char key[8];
    unsigned char plain[1024];
    unsigned char cipher[1024];
    int i, j, n, m;
    long result;

    srand((unsigned)time(NULL));

    printf("Testing DES_Bytes() with random blocks ...\n");
    for (i = 0; i < 10; i++)
    {
        /* Create a random key */
        for (j = 0; j < 8; j++)
            key[j] = rand() & 0xFF;

        /* Create some 'random' plaintext up to 1024 bytes long */
        /* in a multiple of block size */
        m = 1024 / 8;
        n = ((rand() % m) + 1) * 8; 
        assert (n <= sizeof(plain));

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;
    
        /* Encrypt it into ciphertext */
        result = DES_Bytes(cipher, plain, n, key, ENCRYPT);
        assert (result == 0);

        /* Now decipher (use same variable) */
        result = DES_Bytes(cipher, cipher, n, key, DECRYPT);
        assert (result == 0);

        /* Check identical */
        assert (memcmp(plain, cipher, n) == 0);
        printf("%d(%d) ", i+1, n);
    }

    printf("...%s tested OK\n", testfn);
}



void test_DES_CheckKey(void)
{
    char *testfn = "DES_CheckKey()";
    long lngRet;
    char *lpkey;
    
    printf("Testing %s...\n", testfn);
    /* Weak key */
    lpkey = "0101010101010101";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet != 0);

    /* Valid key by one bit */
    lpkey = "0101010101010102";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet == 0);

    /* Another weak key */
    lpkey = "01fe01fe01fe01fe";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet != 0);

    /* Weak double key in 1st half*/
    lpkey = "01010101010101010001112223334455";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet != 0);

    /* Weak triple key in 3rd part */
    lpkey = "000111222333444555666777888999aa0101010101010101";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet != 0);

    /* Valid key */
    lpkey = "000111222333444555666777888999aaabbbcccdddeeefff";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet == 0);

    /* Wrong key length (missing 'f' at end) */
    lpkey = "000111222333444555666777888999aaabbbcccdddeeeff";
    lngRet = DES_CheckKeyHex(lpkey);
    printf("%s is %s (%s)\n", lpkey, (lngRet == 0 ? "OK" : "BAD"), lookup_error(lngRet));
    assert(lngRet != 0);

    printf("...%s tested OK\n", testfn);
}



/* TRIPLE DES (TDEA, 3DES) TESTS */

void test_TDEA_Hex(void)
{
    char *testfn = "TDEA_Hex()";
    char sHexKey[] = "010101010101010101010101010101010101010101010101";
    char sInput[] = "8000000000000000";
    char sCorrect[] = "95F8A5E5DD31D900";
    char sOutput[sizeof(sInput)+1];
    long lngRet;

    printf("Testing %s...\n", testfn);

    printf("KY=%s\n", sHexKey);
    printf("PT=%s\n", sInput);

    // Encrypt in one-off process
    lngRet = TDEA_Hex(sOutput, sInput, sHexKey, ENCRYPT);
    assert (lngRet == 0);
    /* Check */
    printf("CT=%s %d\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sCorrect, sOutput) == 0);

    // Now decrypt back to plain text using same buffer
    lngRet = TDEA_Hex(sOutput, sOutput, sHexKey, DECRYPT);
    assert (lngRet == 0);
    printf("P'=%s %d\n", sOutput, lngRet);
    assert (strcmp(sInput, sOutput) == 0);

    printf("...%s tested OK\n", testfn);
}

void test_TDEA_HexMode(void)
{
    char *testfn = "TDEA_HexMode()";
    long lngRet;
    char sInput[] = "5468697320736F6D652073616D706520636F6E74656E742E0808080808080808";
    char sHexKey[] = "737C791F25EAD0E04629254352F7DC6291E5CB26917ADA32";
    char sHexIV[] = "B36B6BFB6231084E";
    char sCorrect[] = "D76FD1178FBD02F84231F5C1D2A2F74A4159482964F675248254223DAF9AF8E4";
    // Set sOutput to be same length as sInput
    char sOutput[sizeof(sInput)+1];

    printf("Testing %s...\n", testfn);

    printf("KY=%s\n", sHexKey);
    printf("IV=%s\n", sHexIV);
    printf("PT=%s\n", sInput);

    // Encrypt in one-off process
    lngRet = TDEA_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %d\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

    // Now decrypt back to plain text
    lngRet = TDEA_HexMode(sOutput, sOutput, sHexKey, DECRYPT, "cbc", sHexIV);
    assert (lngRet == 0);
    printf("P'=%s %d\n", sOutput, lngRet);
    assert (strcmp(sOutput, sInput) == 0);

    printf("...%s tested OK\n", testfn);
}



void test_TDEA_FileHex(void)
{
    char *testfn = "TDEA_FileHex()";
    long lngRet;

    // Construct full path names to files
    char *strFileIn = "now.txt";
    char *strFileOut = "TDEAnow.enc";
    char *strFileChk = "TDEAnow.chk";

    // Encrypt plaintext file to cipher
    // WARNING: output file is just clobbered
    char sHexKey[] = "fedcba9876543210fedcba9876543210fedcba9876543210";

    printf("Testing %s...\n", testfn);

    create_nowis_file(strFileIn);

    lngRet = TDEA_FileHex(strFileOut, strFileIn, sHexKey, 
        ENCRYPT, "ECB", 0);
    assert (lngRet == 0);

    // Now decrypt it
    lngRet = TDEA_FileHex(strFileChk, strFileOut, sHexKey, 
        DECRYPT, "ECB", 0);
    assert (lngRet == 0);

    /* and check we got the plaintext we started with */
    assert (cmp_files(strFileChk, strFileIn) == 0);

    remove(strFileIn);
    remove(strFileOut);
    remove(strFileChk);

    printf("...%s tested OK\n", testfn);
}

void test_TDEA_Bytes_rand(void)
/* Encrypt and decrypt random blocks with random keys */
{
    char *testfn = "TDEA_Bytes_rand()";

    unsigned char key[24];
    unsigned char plain[1024];
    unsigned char cipher[1024];
    int i, j, n, m;
    long result;

    srand((unsigned)time(NULL));

    printf("Testing %s...\n", testfn);
    for (i = 0; i < 10; i++)
    {
        /* Create a random key */
        for (j = 0; j < 24; j++)
            key[j] = rand() & 0xFF;

        /* Create some 'random' plaintext up to 1024 bytes long */
        /* in a multiple of block size */
        m = 1024 / 8;
        n = ((rand() % m) + 1) * 8; 
        assert (n <= sizeof(plain));

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;
    
        /* Encrypt it into ciphertext */
        result = TDEA_Bytes(cipher, plain, n, key, ENCRYPT);
        assert (result == 0);

        /* Now decipher (use same variable) */
        result = TDEA_Bytes(cipher, cipher, n, key, DECRYPT);
        assert (result == 0);

        /* Check identical */
        assert (memcmp(plain, cipher, n) == 0);
        printf("%d(%d) ", i+1, n);
    }
    printf("\n");

    printf("...%s tested OK\n", testfn);
}

void test_TDEA_BytesMode_rand(void)
/* Encrypt and decrypt random blocks with random keys 
   in random modes 
*/
{
    char *testfn = "TDEA_BytesMode_rand()";

    unsigned char key[24];
    unsigned char plain[1024];
    unsigned char cipher[1024];
    unsigned char iv[8];
    char *modes[] = { "ECB", "CBC" };
    int i, j, n, m, im;
    long result;

    srand((unsigned)time(NULL));

    printf("Testing %s...\n", testfn);
    for (i = 0; i < 10; i++)
    {
        /* Create a random key and IV */
        for (j = 0; j < 24; j++)
            key[j] = rand() & 0xFF;
        for (j = 0; j < 8; j++)
            iv[j] = rand() & 0xFF;

        /* Create some 'random' plaintext up to 1024 bytes long */
        /* in a multiple of block size */
        m = 1024 / 8;
        n = ((rand() % m) + 1) * 8; 
        assert (n <= sizeof(plain));

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;

        /* Select a mode index: 0 or 1 */
        im = rand() & 0x01;
    
        /* Encrypt it into ciphertext */
        result = TDEA_BytesMode(cipher, plain, n, key, ENCRYPT, modes[im], iv);
        assert (result == 0);

        /* Now decipher (use same variable) */
        result = TDEA_BytesMode(cipher, cipher, n, key, DECRYPT, modes[im], iv);
        assert (result == 0);

        /* Check identical */
        assert (memcmp(plain, cipher, n) == 0);
        printf("%d(%d) ", i+1, n);
    }
    printf("\n");

    printf("...%s tested OK\n", testfn);
}



/* HASH TESTS */

void test_SHA1_StringHexHash(void)
{
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrect[] = "a9993e364706816aba3e25717850c26c9cd0d89d";

    printf("Testing SHA1_StringHexHash()...\n");

    result = SHA1_StringHexHash(sDigest, "abc");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...SHA1_StringHexHash() tested OK\n");
}

void test_SHA2_StringHexHash(void)
{
    char *testfn = "SHA2_StringHexHash()";
    long result;
    char sDigest[65];   /* NB 1 extra char */
    char sCorrect[] = 
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";

    printf("Testing %s...\n", testfn);

    result = SHA2_StringHexHash(sDigest, "abc");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_MD5_StringHexHash(void)
{
    long result;
    char sDigest[33];   /* NB 1 extra char */
    char sCorrect[] = "900150983cd24fb0d6963f7d28e17f72";

    printf("Testing MD5_StringHexHash()...\n");

    result = MD5_StringHexHash(sDigest, "abc");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...MD5_StringHexHash() tested OK\n");
}

void test_SHA1_BytesHexHash(void)
{
    char *testfn = "SHA1_BytesHexHash()";
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrect[] = "a9993e364706816aba3e25717850c26c9cd0d89d";
    unsigned char bytes[3];

    printf("Testing %s...\n", testfn);

    bytes[0] = 'a';
    bytes[1] = 'b';
    bytes[2] = 'c';
    result = SHA1_BytesHexHash(sDigest, bytes, 3);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_SHA2_BytesHexHash(void)
{
    char *testfn = "SHA2_BytesHexHash()";
    long result;
    char sDigest[65];   /* NB 1 extra char */
    char sCorrect[] = 
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
    unsigned char bytes[3];

    printf("Testing %s...\n", testfn);

    bytes[0] = 'a';
    bytes[1] = 'b';
    bytes[2] = 'c';
    result = SHA2_BytesHexHash(sDigest, bytes, 3);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_MD5_BytesHexHash(void)
{
    char *testfn = "MD5_BytesHexHash()";
    long result;
    char sDigest[33];   /* NB 1 extra char */
    char sCorrect[] = "900150983cd24fb0d6963f7d28e17f72";
    unsigned char bytes[3];

    printf("Testing %s...\n", testfn);

    bytes[0] = 'a';
    bytes[1] = 'b';
    bytes[2] = 'c';
    result = MD5_BytesHexHash(sDigest, bytes, 3);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_SHA1_BytesHash(void)
{
    char *testfn = "SHA1_BytesHash()";
    long result;
    unsigned char digest[20];   /* NB minimum 20 bytes for SHA1 */
    unsigned char correct[] = {
        0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, 
        0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
    };
    unsigned char bytes[3];

    printf("Testing %s...\n", testfn);

    bytes[0] = 'a';
    bytes[1] = 'b';
    bytes[2] = 'c';
    result = SHA1_BytesHash(digest, bytes, 3);

    assert (result == 0);
    printf("Result =");
    pr_hexbytes(digest, 20);
    printf("Correct=");
    pr_hexbytes(correct, 20);
    assert (memcmp(digest, correct, 20) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_SHA2_BytesHash(void)
{
    char *testfn = "SHA2_BytesHash()";
    long result;
    unsigned char digest[32];   /* NB minimum 32 bytes for SHA256 */
    unsigned char correct[] = {
        0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
        0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
        0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 
        0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
    };
    unsigned char bytes[3];

    printf("Testing %s...\n", testfn);

    bytes[0] = 'a';
    bytes[1] = 'b';
    bytes[2] = 'c';
    result = SHA2_BytesHash(digest, bytes, 3);

    assert (result == 0);
    printf("Result =");
    pr_hexbytes(digest, 32);
    printf("Correct=");
    pr_hexbytes(correct, 32);
    assert (memcmp(digest, correct, 32) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_MD5_BytesHash(void)
{
    char *testfn = "MD5_BytesHash()";
    long result;
    unsigned char digest[16];   /* NB minimum 16 bytes for MD5 */
    unsigned char correct[] = {
        0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96,
        0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72
    };
    unsigned char bytes[3];

    printf("Testing %s...\n", testfn);

    bytes[0] = 'a';
    bytes[1] = 'b';
    bytes[2] = 'c';
    result = MD5_BytesHash(digest, bytes, 3);

    assert (result == 0);
    printf("Result =");
    pr_hexbytes(digest, sizeof(digest));
    printf("Correct=");
    pr_hexbytes(correct, sizeof(digest));
    assert (memcmp(digest, correct, sizeof(digest)) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_SHA1_HexDigest(void)
{
    char *testfn = "SHA1_HexDigest()";
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrect[] = "a9993e364706816aba3e25717850c26c9cd0d89d";
    long hContext;
    unsigned char bytes[2];

    printf("Testing %s...\n", testfn);

    hContext = SHA1_Init();
    assert (hContext != 0);

    /* Combine _AddString and _AddBytes */

    result = SHA1_AddString(hContext, "a");
    assert (result == 0);

    bytes[0] = 'b';
    bytes[1] = 'c';
    result = SHA1_AddBytes(hContext, bytes, 2);
    assert (result == 0);

    result = SHA1_HexDigest(sDigest, hContext);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_SHA2_HexDigest(void)
{
    char *testfn = "SHA2_HexDigest()";
    long result;
    char sDigest[65];   /* NB 1 extra char */
    char sCorrect[] = 
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
    long hContext;
    unsigned char bytes[2];

    printf("Testing %s...\n", testfn);

    hContext = SHA2_Init();
    assert (hContext != 0);

    /* Combine _AddString and _AddBytes */

    result = SHA2_AddString(hContext, "a");
    assert (result == 0);

    bytes[0] = 'b';
    bytes[1] = 'c';
    result = SHA2_AddBytes(hContext, bytes, 2);
    assert (result == 0);

    result = SHA2_HexDigest(sDigest, hContext);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_MD5_HexDigest(void)
{
    char *testfn = "MD5_HexDigest()";
    long result;
    char sDigest[33];   /* NB 1 extra char */
    char sCorrect[] = "900150983cd24fb0d6963f7d28e17f72";
    long hContext;
    unsigned char bytes[2];

    printf("Testing %s...\n", testfn);

    hContext = MD5_Init();
    assert (hContext != 0);

    /* Combine _AddString and _AddBytes */

    result = MD5_AddString(hContext, "a");
    assert (result == 0);

    bytes[0] = 'b';
    bytes[1] = 'c';
    result = MD5_AddBytes(hContext, bytes, 2);
    assert (result == 0);

    result = MD5_HexDigest(sDigest, hContext);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}



void test_SHA1_AddString(void)
{
    char *testfn = "SHA1_AddString()";
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrect[] = "34aa973cd4c4daa4f61eeb2bdbad27316534016f";
    long hContext;
    char sA1000[1001];
    int i;

    printf("Testing %s...\n", testfn);

    hContext = SHA1_Init();
    assert (hContext != 0);

    /* Create a string of 1000 'a's */
    for (i = 0; i < 1000; i++)
        sA1000[i] = 'a';
    sA1000[i] = '\0';

    /* Add 1000 times => one million repetitions of "a" */

    for (i = 0; i < 1000; i++)
    {
        result = SHA1_AddString(hContext, sA1000);
        assert (result == 0);
    }

    /* Create final digest */

    result = SHA1_HexDigest(sDigest, hContext);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_SHA2_AddString(void)
{
    char *testfn = "SHA2_AddString()";
    long result;
    char sDigest[65];   /* NB 1 extra char */
    char sCorrect[] = 
"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0";
    long hContext;
    char sA1000[1001];
    int i;

    printf("Testing %s...\n", testfn);

    hContext = SHA2_Init();
    assert (hContext != 0);

    /* Create a string of 1000 'a's */
    for (i = 0; i < 1000; i++)
        sA1000[i] = 'a';
    sA1000[i] = '\0';

    /* Add 1000 times => one million repetitions of "a" */

    for (i = 0; i < 1000; i++)
    {
        result = SHA2_AddString(hContext, sA1000);
        assert (result == 0);
    }

    /* Create final digest */

    result = SHA2_HexDigest(sDigest, hContext);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}

void test_MD5_AddString(void)
{
    char *testfn = "MD5_AddString()";
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrect[] = "7707d6ae4e027c70eea2a935c2296f21";
    long hContext;
    char sA1000[1001];
    int i;

    printf("Testing %s...\n", testfn);

    hContext = MD5_Init();
    assert (hContext != 0);

    /* Create a string of 1000 'a's */
    for (i = 0; i < 1000; i++)
        sA1000[i] = 'a';
    sA1000[i] = '\0';

    /* Add 1000 times => one million repetitions of "a" */

    for (i = 0; i < 1000; i++)
    {
        result = MD5_AddString(hContext, sA1000);
        assert (result == 0);
    }

    /* Create final digest */

    result = MD5_HexDigest(sDigest, hContext);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);
    printf("...%s tested OK\n", testfn);
}


void test_SHA1_Hmac_KAT(void)
{
    char *testfn = "SHA1_Hmac_KAT()";
    /* Example from Wei Dai's Crypto++ test vectors 
    fipstest.cpp - written and placed in the public domain by Wei Dai
    */
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrect[] = "0922d3405faa3d194f82a45830737d5cc6c75d24";
    unsigned char key[] = {
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 
        0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 
        0x40, 0x41, 0x42, 0x43,
    };
    unsigned char data[] = "Sample #2";
    int key_len, data_len;

    printf("Testing %s...\n", testfn);

/* From http://trolocsis.com/crypto++/fipstest_8cpp-source.html
MAC_KnownAnswerTest<HMAC<SHA> >(
"303132333435363738393a3b3c3d3e3f40414243",
"Sample #2",
"0922d3405faa3d194f82a45830737d5cc6c75d24");
*/
    key_len = sizeof(key);
    data_len = strlen(data);

    result = SHA1_Hmac(sDigest, data, data_len, key, key_len);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);

    printf("...%s tested OK\n", testfn);
}

void test_SHA2_Hmac_KAT(void)
{
    char *testfn = "SHA2_Hmac_KAT()";
    /* Example from Wei Dai's Crypto++ test vectors 
    fipstest.cpp - written and placed in the public domain by Wei Dai
    */
    long result;
    char sDigest[65];   /* NB 1 extra char */
    char sCorrect[] = 
"d28363f335b2dae468793a38680dea9f7fb8be1dceda197cdb3b1cb59a9f6422";
    unsigned char key[] = {
        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 
        0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 
        0x40, 0x41, 0x42, 0x43,
    };
    unsigned char data[] = "abc";
    int key_len, data_len;

    printf("Testing %s...\n", testfn);

/* From http://trolocsis.com/crypto++/fipstest_8cpp-source.html
MAC_KnownAnswerTest<HMAC<SHA256> >(
"303132333435363738393a3b3c3d3e3f40414243",
"abc",
"D28363F335B2DAE468793A38680DEA9F7FB8BE1DCEDA197CDB3B1CB59A9F6422");    
*/
    key_len = sizeof(key);
    data_len = strlen(data);

    result = SHA2_Hmac(sDigest, data, data_len, key, key_len);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect);
    assert (strcmp(sDigest, sCorrect) == 0);

    printf("...%s tested OK\n", testfn);
}


void test_MD5_Hmac(void)
{
    char *testfn = "MD5_Hmac()";
    long result;
    char sDigest[33];   /* NB 1 extra char */
    char sCorrect1[] = "9294727a3638bb1c13f48ef8158bfc9d";
    char sCorrect2[] = "750c783e6ab0b503eaa86e310a5db738";
    char sCorrect3[] = "56be34521d144c88dbb8c733f0e8b3f6";
    int i;
    unsigned char key1[16];
    unsigned char key2[] = "Jefe";
    unsigned char key3[16];
    unsigned char data1[] = "Hi There";
    unsigned char data2[] = "what do ya want for nothing?";
    unsigned char data3[50];
    int key_len, data_len;

    printf("Testing %s...\n", testfn);

    /* Test No 1. from RFC 2104
    key =         0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
    key_len =     16 bytes
    data =        "Hi There"
    data_len =    8  bytes
    digest =      0x9294727a3638bb1c13f48ef8158bfc9d
    */
    key_len = 16;
    for (i = 0; i < key_len; i++)
        key1[i] = 0x0b;

    data_len = 8;

    result = MD5_Hmac(sDigest, data1, data_len, key1, key_len);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect1);
    assert (strcmp(sDigest, sCorrect1) == 0);

    /* Test No 2.
    key =         "Jefe"
    data =        "what do ya want for nothing?"
    data_len =    28 bytes
    digest =      0x750c783e6ab0b503eaa86e310a5db738
    */
    key_len = 4;
    data_len = 28;

    result = MD5_Hmac(sDigest, data2, data_len, key2, key_len);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect2);
    assert (strcmp(sDigest, sCorrect2) == 0);

    /* Test No 3.
    key =         0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    key_len       16 bytes
    data =        0xDDDDDDDDDDDDDDDDDDDD...
                ..DDDDDDDDDDDDDDDDDDDD...
                ..DDDDDDDDDDDDDDDDDDDD...
                ..DDDDDDDDDDDDDDDDDDDD...
                ..DDDDDDDDDDDDDDDDDDDD
    data_len =    50 bytes
    digest =      0x56be34521d144c88dbb8c733f0e8b3f6
    */
    key_len = 16;
    for (i = 0; i < key_len; i++)
        key3[i] = 0xAA;

    data_len = 50;
    for (i = 0; i < data_len; i++)
        data3[i] = 0xDD;

    result = MD5_Hmac(sDigest, data3, data_len, key3, key_len);

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrect3);
    assert (strcmp(sDigest, sCorrect3) == 0);

    printf("...%s tested OK\n", testfn);
}


void test_SHA1_FileHexHash(void)
/* 'hello' and 'bin' are filenames */
{
    char *testfn = "SHA1_FileHexHash";
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrectT[] = "22596363b3de40b06f981fb85d82312e8c0ed511";
    char sCorrectB[] = "88a5b867c3d110207786e66523cd1e4a484da697";
    char sCorrectBIN[] = "dbe649daba340bce7a44b809016d914839b99f10";
    char *hello = "hello$$.txt";
    char *bin = "bin$$.dat";

    printf("Testing %s...\n", testfn);

    create_hello_file(hello);
    create_bin_file(bin);

#ifndef unix
    result = SHA1_FileHexHash(sDigest, hello, "t");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrectT);
    assert (strcmp(sDigest, sCorrectT) == 0);
#endif

    result = SHA1_FileHexHash(sDigest, hello, "b");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrectB);
    assert (strcmp(sDigest, sCorrectB) == 0);

    result = SHA1_FileHexHash(sDigest, bin, "b");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n",  sCorrectBIN);
    assert (strcmp(sDigest, sCorrectBIN) == 0);

    remove(hello);
    remove(bin);
    printf("...%s tested OK\n", testfn);
/*
C:\Test>sha1sum hello.txt
22596363b3de40b06f981fb85d82312e8c0ed511  hello.txt

C:\Test>sha1sum -b hello.txt
88a5b867c3d110207786e66523cd1e4a484da697 *hello.txt

C:\Test>sha1sum -b test.bin
dbe649daba340bce7a44b809016d914839b99f10 *test.bin
*/
}

void test_SHA2_FileHexHash(void)
{
    char *testfn = "SHA2_FileHexHash";
    long result;
    char sDigest[65];   /* NB 1 extra char */
    char sCorrectT[] = 
"a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447";
    char sCorrectB[] = 
"572a95fee9c0f320030789e4883707affe12482fbb1ea04b3ea8267c87a890fb";
    char sCorrectBIN[] = 
"110009dcee21620b166f3abfecb5eff7a873be729d1c2d53822e7acc5f34eb9b";
    char *hello = "hello$$2.txt";
    char *bin = "bin$$2.dat";

    printf("Testing %s...\n", testfn);

    create_hello_file(hello);
    create_bin_file(bin);

#ifndef unix
    result = SHA2_FileHexHash(sDigest, hello, "t");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrectT);
    assert (strcmp(sDigest, sCorrectT) == 0);
#endif

    result = SHA2_FileHexHash(sDigest, hello, "b");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrectB);
    assert (strcmp(sDigest, sCorrectB) == 0);

    result = SHA2_FileHexHash(sDigest, bin, "b");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n",  sCorrectBIN);
    assert (strcmp(sDigest, sCorrectBIN) == 0);

    remove(hello);
    remove(bin);
    printf("...%s tested OK\n", testfn);
}

void test_MD5_FileHexHash(void)
/* 'hello' and 'bin' are filenames */
{
    char *testfn = "MD5_FileHexHash";
    long result;
    char sDigest[41];   /* NB 1 extra char */
    char sCorrectT[] = "6f5902ac237024bdd0c176cb93063dc4";
    char sCorrectB[] = "a0f2a3c1dcd5b1cac71bf0c03f2ff1bd";
    char sCorrectBIN[] = "f5c8e3c31c044bae0e65569560b54332";
    char *hello = "hello$$.txt";
    char *bin = "bin$$.dat";

    printf("Testing %s...\n", testfn);

    create_hello_file(hello);
    create_bin_file(bin);

#ifndef unix
    result = MD5_FileHexHash(sDigest, hello, "t");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrectT);
    assert (strcmp(sDigest, sCorrectT) == 0);
#endif

    result = MD5_FileHexHash(sDigest, hello, "b");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n", sCorrectB);
    assert (strcmp(sDigest, sCorrectB) == 0);

    result = MD5_FileHexHash(sDigest, bin, "b");

    assert (result == 0);
    printf("Result =%s\n", sDigest);
    printf("Correct=%s\n",  sCorrectBIN);
    assert (strcmp(sDigest, sCorrectBIN) == 0);

    remove(hello);
    remove(bin);
    printf("...%s tested OK\n", testfn);
/*
C:\Test>md5sum -t hello.txt
6f5902ac237024bdd0c176cb93063dc4  hello.txt

C:\Test>md5sum -b hello.txt
a0f2a3c1dcd5b1cac71bf0c03f2ff1bd *hello.txt

C:\Test>md5sum -b test.bin
f5c8e3c31c044bae0e65569560b54332 *test.bin
*/
}

void test_PBE_Kdf2(void)
{
    char *testfn = "PBE_Kdf2()";
    /* Use des-ede3-cbc example from test vectors by 
       Dr. Stephen Henson using PBKDF2 defined in PKCS #5 v2.0.
    */
    unsigned char dk[24];
    unsigned char pwd[] = "password";
    unsigned char salt[] = { 0x78, 0x57, 0x8E, 0x5A, 0x5D, 0x63, 0xCB, 0x06 };
    long dkLen, pwdLen, saltLen, count;
    long result;
    unsigned char correct[] = {
        0xBF, 0xDE, 0x6B, 0xE9, 0x4D, 0xF7, 0xE1, 0x1D, 0xD4, 0x09, 0xBC, 0xE2, 
        0x0A, 0x02, 0x55, 0xEC, 0x32, 0x7C, 0xB9, 0x36, 0xFF, 0xE9, 0x36, 0x43
    };

    printf("Testing %s...\n", testfn);

    // Compute the derived key DK given the password, salt and iteration count
    dkLen = sizeof(dk);
    pwdLen = strlen(pwd);
    saltLen = sizeof(salt);
    count = 2048;

    result = PBE_Kdf2(dk, dkLen, pwd, pwdLen, salt, saltLen, count, 0);

    assert (result == 0);
    assert (result == 0);
    printf("Result =");
    pr_hexbytes(dk, dkLen);
    printf("Correct=");
    pr_hexbytes(correct, dkLen);
    assert (memcmp(dk, correct, dkLen) == 0);
    printf("...%s tested OK\n", testfn);

    return;
}


void test_BLF_Hex(void)
{
    long result;
    char sInputHex[] = "0123456789ABCDEF";
    char sKeyHex[] = "FEDCBA9876543210";
    char sCorrectHex[] = "0ACEAB0FC6A0A28D";
    /* NB Output for Hex requires an extra byte */
    char sOutputHex[sizeof(sInputHex)+1];

    printf("Testing BLF_Hex()...\n");
    result = BLF_Hex(sOutputHex, sInputHex, sKeyHex, 1);
    assert (result == 0);
    printf("Result =%s\n", sOutputHex);
    printf("Correct=%s\n", sCorrectHex);
    assert (strcmp(sOutputHex, sCorrectHex) == 0);
    printf("...BLF_Hex() tested OK\n");
}

void test_BLF_HexMode(void)
{
    char *testfn = "BLF_HexMode()";
    long lngRet;
    // "7654321 Now is the time for " padded to 32 bytes with 4 nulls
    char sInput[] = "37363534333231204E6F77206973207468652074696D6520666F722000000000";
    char sCorrect[] = "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC";
    char sHexKey[] = "0123456789ABCDEFF0E1D2C3B4A59687";
    char sHexIV[] = "FEDCBA9876543210";
    // Set sOutput to be same length as sInput
    char sOutput[sizeof(sInput)+1];

    printf("Testing %s...\n", testfn);

    printf("KY=%s\n", sHexKey);
    printf("IV=%s\n", sHexIV);
    printf("PT=%s\n", sInput);

    // Encrypt in one-off process
    lngRet = BLF_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %d\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

    // Now decrypt back to plain text
    lngRet = BLF_HexMode(sOutput, sOutput, sHexKey, DECRYPT, "cbc", sHexIV);
    assert (lngRet == 0);
    printf("P'=%s %d\n", sOutput, lngRet);
    assert (strcmp(sOutput, sInput) == 0);

    printf("...%s tested OK\n", testfn);
}


void test_BLF_UpdateHex(void)
{
    long hContext;
    long result;
    char sKey[] = "0123456789ABCDEF";
    char sHexString[33];
    char *correct;

    printf("Testing BLF_UpdateHex() in ECB mode ...\n");
    hContext = BLF_InitHex(sKey, 1, "ECB", NULL);
    if (hContext == 0)
        printf("BLF_InitError=%ld\n", BLF_InitError());
    assert (hContext != 0);

    /* First part: "Now is t" in hex (8 chars) */
    strcpy(sHexString, "4e6f772069732074");
    result = BLF_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "CB08E682C67E32E2";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    /* Second part: "he time for all " in hex (16 chars) */
    strcpy(sHexString, "68652074696d6520666f7220616c6c20");
    result = BLF_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "8FCB010AC2CE9B1D9C4538762E33B52F";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    result = BLF_Final(hContext);
    assert (result == 0);

    /* Now decrypt */
    hContext = BLF_InitHex(sKey, 0, "ECB", NULL);
    if (hContext == 0)
        printf("BLF_InitError=%ld\n", BLF_InitError());
    assert (hContext != 0);

    strcpy(sHexString, "CB08E682C67E32E2");
    result = BLF_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "4E6F772069732074";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    strcpy(sHexString, "8FCB010AC2CE9B1D9C4538762E33B52F");
    result = BLF_UpdateHex(hContext, sHexString);
    assert (result == 0);
    correct = "68652074696D6520666F7220616C6C20";
    printf("Result =%s\n", sHexString);
    printf("Correct=%s\n", correct);
    assert (strcmp(sHexString, correct) == 0);

    result = BLF_Final(hContext);
    assert (result == 0);


    printf("...BLF_UpdateHex() tested OK\n");
}


void test_BLF_Bytes_rand(void)
/* Encrypt and decrypt random blocks */
{
    unsigned char key[8] = { 
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
    unsigned char plain[512];
    unsigned char cipher[512];
    int i, j, n;
    long result;

    srand((unsigned)time(NULL));

    printf("Testing BLF_Bytes() with random blocks ...\n");
    for (i = 0; i < 10; i++)
    {
        /* Create some 'random' plaintext up to 512 bytes long */
        n = ((rand() & 0x2F) + 1) * 8; /* in multiple of 8 */
        assert (n <= 512);

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;
    
        /* Encrypt it into ciphertext */
        result = BLF_Bytes(cipher, plain, n, key, 8, 1);
        assert (result == 0);

        /* Now decipher (use same variable) */
        result = BLF_Bytes(cipher, cipher, n, key, 8, 0);
        assert (result == 0);

        /* Check identical */
        assert (memcmp(plain, cipher, n) == 0);
        printf("%d(%d) ", i+1, n);
    }
    printf("\n...BLF_Bytes() tested OK\n");
}

void test_BLF_BytesMode_rkeys(void)
/* Encrypt and decrypt with random keys */
{
    unsigned char key[56];
    /* NB we don't want the trailing NUL here! */
    unsigned char plain[32] = "Now is the time for all good men";
    unsigned char cipher[sizeof(plain)];
    unsigned char iv[8];
    int i, j, n;
    long result;

    srand((unsigned)time(NULL));

    printf("Testing BLF_BytesMode() with random keys and IV ...\n");
    for (i = 0; i < 10; i++)
    {
        /* Create some 'random' keys from 1 to 56 bytes long */
        n = (rand() % 56) + 1;
        for (j = 0; j < n; j++)
            key[j] = rand() & 0xFF;
        for (j = 0; j < 8; j++)
            iv[j] = rand() & 0xFF;
    
        /* Encrypt it into ciphertext in CBC mode */
        result = BLF_BytesMode(cipher, plain, sizeof(plain), key, n, 1,
            "CBC", iv);
        assert (result == 0);

        /* Now decipher (use same variable for result) */
        result = BLF_BytesMode(cipher, cipher, sizeof(cipher), key, n, 0,
            "CBC", iv);
        assert (result == 0);

        /* Check identical */
        assert (memcmp(plain, cipher, sizeof(plain)) == 0);
        printf("%d(%d) ", i+1, n);
    }
    printf("\n...BLF_BytesMode() tested OK\n");
}

void test_BLF_BytesMode_rmode(void)
/* Encrypt and decrypt random blocks and modes */
{
    unsigned char key[8] = { 
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
    unsigned char iv[8] = { 
        0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; 
    unsigned char plain[512];
    unsigned char cipher[512];
    int i, j, n;
    long result;
    char *mode[] = { "ECB", "CBC", "CFB", "OFB" };
    int m;

    srand((unsigned)time(NULL));

    printf("Testing BLF_BytesMode() with random modes ...\n");
    for (i = 0; i < 10; i++)
    {
        /* Create some 'random' plaintext up to 512 bytes long */
        n = ((rand() & 0x2F) + 1) * 8; /* in multiple of 8 */
        assert (n <= 512);

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;

        /* And pick a random mode */
        m = rand() & 0x3;
    
        printf("%d-%s(%d) ", i+1, mode[m], n);

        /* Encrypt it into ciphertext */
        result = BLF_BytesMode(cipher, plain, n, key, 8, 1,
            mode[m], iv);
        assert (result == 0);

        /* Now decipher (use same variable) */
        result = BLF_BytesMode(cipher, cipher, n, key, 8, 0,
            mode[m], iv);
        assert (result == 0);

        /* Check identical */
        assert (memcmp(plain, cipher, n) == 0);
    }
    printf("\n...BLF_BytesMode() tested OK\n");
}


void test_BLF_File(void)
{
    char sFileIn[]  = "test$.txt";
    char sFileOut[] = "test$.ecb";
    char sFileChk[] = "test$.chk";
    unsigned char key[8] = { 
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
    unsigned char correct[] = { 
        0x1a, 0xa1, 0x51, 0xb7, 0x7a, 0x5a, 0x33, 0x5c, 
        0x4e, 0x7e, 0xdc, 0x84, 0xa3, 0x86, 0xdc, 0x96 };
    long result;
    FILE *fp;
    char buf[128], *cp;
    int c, n;
    
    /* Create a test file in current dir */
    fp = fopen(sFileIn, "wb");
    assert(fp != NULL);
    fprintf(fp, "hello world\r\n");
    fclose(fp);

    printf("Testing BLF_File()...\n");

    /* Encrypt it and create output file */
    result = BLF_File(sFileOut, sFileIn, key, sizeof(key), 1, "ECB", NULL);
    assert (result == 0);

    /* Read this ciphertext file to a buffer and see if correct */
    fp = fopen(sFileOut, "rb");
    assert (fp != NULL);
    printf("Result =");
    for (n = 0, cp = buf; (c = fgetc(fp)) != EOF && n < sizeof(buf); n++)
    {
        *cp++ =