/* 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 4.2 or later)

Copyright (C) 2001-9 DI Management Services Pty Limited. All rights reserved.
    Last updated:
    $Date: 2009-03-18 17:57:00 $
    $Revision: 4.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

#ifdef NDEBUG
    /* Make sure assertion testing is turned on */
    #undef NDEBUG
#endif
#include <assert.h>

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

/* Compiler-specific explicit link to library.
   This works in old MSVC and Borland for LIB in same dir - you may need to do otherwise
*/
#if !defined(unix) && (_MSC_VER < 1400)
#pragma comment(lib, ".\\diCryptoSys.lib")
#endif

/* 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, "%s", tempname);    /* Fudge to avoid compiler warning */
        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(const char *file1, const char *file2)
/* Compares two binary files: returns 0 if identical 
   or 1 if not identical or -1 if file error 
   [2006-06-20] modified to check lengths first. */
{
    FILE *fp1, *fp2;
    int c1, c2;
    long len1, len2;
    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;
    }

    /* Compare lengths */
    fseek(fp1, 0, SEEK_END);
    len1 = ftell(fp1);
    fseek(fp2, 0, SEEK_END);
    len2 = ftell(fp2);
    if (len1 != len2)
    {
        fclose(fp1);
        fclose(fp2);
        return 1;
    }

    rewind(fp1);
    rewind(fp2);

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

    fclose(fp1);
    fclose(fp2);

    return result;
}

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

    /* Convert correct hex string to bytes */
    n = (long)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, "%lx", &x);
        correct[i] = (unsigned char)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 = (long)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;
}

/* Various versions that print a byte array in hex format */

static void pr_hexbytes(const 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 void pr_bytesmsg(const char *msg, const unsigned char *bytes, long nbytes)
/* Ditto but print an optional message beforehand */
{
    if (msg)
        printf("%s", msg);
    pr_hexbytes(bytes, nbytes);
}

static void pr_hexdump(const char *pre, const void *bytes, size_t nbytes, const char *post)
/* Print bytes as hex in blocks of 64 chars with optional "pre" and "post" strings */
{
    size_t i;
    const unsigned char *pb = (const unsigned char *)bytes;
    if (pre)
        printf("%s", pre);
    for (i = 0; i < nbytes; i++)
    {
        if (i && (i % 32) == 0)
            printf("\n");
        printf("%02x", *pb++);
    }
    if (post)
        printf("%s", post);
}

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 %ld\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 %ld\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 %ld\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 %ld\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 %ld\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 %ld\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 %ld\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 %ld\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 */

/* Correct message digest test vectors */
/* Hash('abc') */
#define OK_MD5_ABC "900150983cd24fb0d6963f7d28e17f72"
#define OK_MD2_ABC "da853b0d3f88d99b30283a69e6ded6bb"
#define OK_SHA1_ABC "a9993e364706816aba3e25717850c26c9cd0d89d"
#define OK_SHA224_ABC "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"
#define OK_SHA256_ABC "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
#define OK_SHA384_ABC "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"
#define OK_SHA512_ABC "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
#define OK_RMD160_ABC "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"
/* Hash(empty-string) */
#define OK_SHA1_EMPTY "da39a3ee5e6b4b0d3255bfef95601890afd80709"
#define OK_SHA512_EMPTY "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"


void test_HASH_HexFromHex(void)
{
    char *testfn = "HASH_HexFromHex()";

    long result;
    char szDigest[API_MAX_HASH_CHARS+1]; /* NB extra one for terminating null character */
    char *szMsgHex = "616263";  /* = "abc" */

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

    /* Compute default SHA-1 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, 0);
    assert(result > 0);
    printf("SHA1('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA1_ABC) == 0);

    /* Compute MD5 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_MD5);
    assert(result > 0);
    printf("MD5('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_MD5_ABC) == 0);

    /* Compute MD2 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_MD2);
    assert(result > 0);
    printf("MD2('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_MD2_ABC) == 0);

    /* Compute SHA-224 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_SHA224);
    assert(result > 0);
    printf("SHA224('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA224_ABC) == 0);

    /* Compute SHA-256 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_SHA256);
    assert(result > 0);
    printf("SHA256('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA256_ABC) == 0);

    /* Compute SHA-384 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_SHA384);
    assert(result > 0);
    printf("SHA384('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA384_ABC) == 0);

    /* Compute SHA-512 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_SHA512);
    assert(result > 0);
    printf("SHA512('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA512_ABC) == 0);

    /* Compute RIPEMD-160 digest */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, API_HASH_RMD160);
    assert(result > 0);
    printf("RMD160('abc')=%s\n", szDigest);
    assert(strcmp(szDigest, OK_RMD160_ABC) == 0);

    /* Compute digest of empty string */
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, "", API_HASH_SHA1);
    assert(result > 0);
    printf("SHA1(e)=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA1_EMPTY) == 0);
    
    result = HASH_HexFromHex(szDigest, sizeof(szDigest)-1, "", API_HASH_SHA512);
    assert(result > 0);
    printf("SHA512(e)=%s\n", szDigest);
    assert(strcmp(szDigest, OK_SHA512_EMPTY) == 0);
    
    printf("...%s tested OK\n", testfn);
}

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,
    };
    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 = (long)strlen(data);

    result = SHA1_Hmac(sDigest, (unsigned char*)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,
    };
    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 = (long)strlen(data);

    result = SHA2_Hmac(sDigest, (unsigned char*)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);
#else
    result = (long)sCorrectT[0];    /* fudge to avoid compiler warning */
#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);
#else
    result = (long)sCorrectT[0];    /* fudge to avoid compiler warning */
#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);
#else
    result = (long)sCorrectT[0];    /* fudge to avoid compiler warning */
#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_MAC_HexFromBytes(void)
{
    char *testfn = "MAC_HexFromBytes()";

    long result;
    char szDigest[API_MAX_HASH_CHARS+1]; /* NB extra one for terminating null character */
    /* Test Case 2 RFC 2202 and RFC 4231 */
    char *key = "Jefe";
    char *data = "what do ya want for nothing?";

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

    /* Compute default HMAC-SHA-1 */
    result = MAC_HexFromBytes(szDigest, sizeof(szDigest)-1, 
        (unsigned char*)data, (long)strlen(data), (unsigned char*)key, (long)strlen(key), 0);
    assert(result > 0);
    printf("HMAC-SHA-1('Jefe', WDYWFN?)=%s\n", szDigest);
    assert(strcmp(szDigest, "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79") == 0);

    /* Compute HMAC-MD5 */
    result = MAC_HexFromBytes(szDigest, sizeof(szDigest)-1, 
        (unsigned char*)data, (long)strlen(data), (unsigned char*)key, (long)strlen(key), API_HASH_MD5);
    assert(result > 0);
    printf("HMAC-MD5('Jefe', WDYWFN?)=%s\n", szDigest);
    assert(strcmp(szDigest, "750c783e6ab0b503eaa86e310a5db738") == 0);

    /* Compute HMAC-SHA-224 */
    result = MAC_HexFromBytes(szDigest, sizeof(szDigest)-1, 
        (unsigned char*)data, (long)strlen(data), (unsigned char*)key, (long)strlen(key), API_HASH_SHA224);
    assert(result > 0);
    printf("HMAC-SHA-224('Jefe', WDYWFN?)=%s\n", szDigest);
    assert(strcmp(szDigest, 
    "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44") == 0);

    /* Compute HMAC-SHA-256 */
    result = MAC_HexFromBytes(szDigest, sizeof(szDigest)-1, 
        (unsigned char*)data, (long)strlen(data), (unsigned char*)key, (long)strlen(key), API_HASH_SHA256);
    assert(result > 0);
    printf("HMAC-SHA-256('Jefe', WDYWFN?)=%s\n", szDigest);
    assert(strcmp(szDigest, 
    "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843") == 0);

    /* Compute HMAC-SHA-384 */
    result = MAC_HexFromBytes(szDigest, sizeof(szDigest)-1, 
        (unsigned char*)data, (long)strlen(data), (unsigned char*)key, (long)strlen(key), API_HASH_SHA384);
    assert(result > 0);
    printf("HMAC-SHA-384('Jefe', WDYWFN?)=%s\n", szDigest);
    assert(strcmp(szDigest, 
    "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649") == 0);

    /* Compute HMAC-SHA-512 */
    result = MAC_HexFromBytes(szDigest, sizeof(szDigest)-1, 
        (unsigned char*)data, (long)strlen(data), (unsigned char*)key, (long)strlen(key), API_HASH_SHA512);
    assert(result > 0);
    printf("HMAC-SHA-512('Jefe', WDYWFN?)=%s\n", szDigest);
    assert(strcmp(szDigest, 
    "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737") == 0);

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

void test_CMAC_HexFromBytes(void)
{
    char *testfn = "CMAC_HexFromBytes()";

    long r;
    char szOutput[API_MAX_CMAC_CHARS+1];
    unsigned char *data;
    long key_len, data_len;
    unsigned char *key;
    unsigned char key128[] = {
        0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 
        0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C,
    };
    unsigned char key3des[] = {
        0x8A, 0xA8, 0x3B, 0xF8, 0xCB, 0xDA, 0x10, 0x62, 
        0x0B, 0xC1, 0xBF, 0x19, 0xFB, 0xB6, 0xCD, 0x58, 
        0xBC, 0x31, 0x3D, 0x4A, 0x37, 0x1C, 0xA8, 0xB5,
    };
    unsigned char M1[] = {
        0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 
        0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A,
    };

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

    key = key128;
    key_len = sizeof(key128);
    /* Test 1. */
    data = M1;
    data_len = sizeof(M1);
    /* Check required size */
    r = MAC_HexFromBytes(NULL, 0, data, data_len, key, key_len, API_CMAC_AES128);
    printf("MAC_HexFromBytes(NULL, 0,..,AES) returns %ld\n", r);
    assert(r > 0);
    r = MAC_HexFromBytes(szOutput, sizeof(szOutput)-1, data, data_len, key, key_len, API_CMAC_AES128);
    assert(r > 0);
    printf("CMAC-AES-128(Ex1)=%s\n", szOutput);
    assert(strcmp(szOutput, "070a16b46b4d4144f79bdd9dd04a287c") == 0);

    /* Compute CMAC-DES-EDE */
    key = key3des;
    key_len = sizeof(key3des);

    /* Test 0 = empty string */
    data = NULL;
    data_len = 0;
    /* Check required size */
    r = MAC_HexFromBytes(NULL, 0, data, data_len, key, key_len, API_CMAC_TDEA);
    printf("MAC_HexFromBytes(NULL, 0,..,TDEA) returns %ld\n", r);
    assert(r > 0);
    r = MAC_HexFromBytes(szOutput, sizeof(szOutput)-1, data, data_len, key, key_len, API_CMAC_DESEDE);
    assert(r > 0);
    printf("CMAC-DES-EDE(<empty>)=%s\n", szOutput);
    assert(strcmp(szOutput, "b7a688e122ffaf95") == 0);

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

void test_CMAC_HexFromHex(void)
{
    char *testfn = "CMAC_HexFromHex()";

    long r;
    char szDigest[API_MAX_CMAC_CHARS+1]; /* NB extra one for terminating null character */
    /* SP800-38B D.1 AES-128 */
    char *szKeyHex = "2b7e151628aed2a6abf7158809cf4f3c";
    char *szMsgHex = "6bc1bee22e409f96e93d7e117393172a";
    char *lpszOK;

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

    /* Compute CMAC_AES-128 on empty string */
    r = MAC_HexFromHex(szDigest, sizeof(szDigest)-1, "", szKeyHex, API_CMAC_AES128);
    assert(r > 0);
    printf("CMAC-AES-128(K128, e)=%s\n", szDigest);
    lpszOK = "bb1d6929e95937287fa37d129b756746";
    assert(strcmp(szDigest, lpszOK) == 0);

    /* Compute CMAC_AES-128 on Example 2: Mlen = 128 */
    r = MAC_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, szKeyHex, API_CMAC_AES128);
    assert(r > 0);
    printf("CMAC-AES-128(K128, M128)=%s\n", szDigest);
    lpszOK = "070a16b46b4d4144f79bdd9dd04a287c";
    assert(strcmp(szDigest, lpszOK) == 0);

    /* CMAC_AES-256 on Example 12: Mlen = 512 */
    szKeyHex = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
    szMsgHex = "6bc1bee22e409f96e93d7e117393172a"
        "ae2d8a571e03ac9c9eb76fac45af8e51"
        "30c81c46a35ce411e5fbc1191a0a52ef"
        "f69f2445df4f9b17ad2b417be66c3710";
    r = MAC_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, szKeyHex, API_CMAC_AES256);
    assert(r > 0);
    printf("CMAC-AES-256(K256, M512)=%s\n", szDigest);
    lpszOK = "e1992190549f6ed5696a2c056c315410";
    assert(strcmp(szDigest, lpszOK) == 0);

    /* CMAC_TDEA on Example 16: Mlen = 256 */
    szKeyHex = "8aa83bf8cbda10620bc1bf19fbb6cd58bc313d4a371ca8b5";
    szMsgHex = "6bc1bee22e409f96e93d7e117393172a"
        "ae2d8a571e03ac9c9eb76fac45af8e51";
    r = MAC_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, szKeyHex, API_CMAC_DESEDE);
    assert(r > 0);
    printf("CMAC-DES-EDE(K192, M256)=%s\n", szDigest);
    lpszOK = "33e6b1092400eae5";
    assert(strcmp(szDigest, lpszOK) == 0);

    printf("Test with invalid parameters...\n");
    /* CMAC_AES-128 on an invalid hex string */
    r = MAC_HexFromHex(szDigest, sizeof(szDigest)-1, "A", szKeyHex, API_CMAC_AES128);
    printf("CMAC-AES-128(key, 0xA) returns %ld (expected -ve error)\n\t%s\n", r, lookup_error(r));
    assert(r < 0);

    /* CMAC_DES_EDE on an invalid key */
    r = MAC_HexFromHex(szDigest, sizeof(szDigest)-1, szMsgHex, "", API_CMAC_DESEDE);
    printf("CMAC-DES_EDE(BADKEY, M) returns %ld (expected -ve error)\n\t%s\n", r, lookup_error(r));
    assert(r < 0);

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

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];
    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 = (long)strlen(pwd);
    saltLen = sizeof(salt);
    count = 2048;

    result = PBE_Kdf2(dk, dkLen, (unsigned char*)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_PBE_Kdf2_SHA2(void)
{
    char *testfn = "PBE_Kdf2_SHA2()";
    /* Same as above but uses SHA-2 hash functions in HMAC.
    */
    long result;
    char pwd[] = "password";
    char *salthex = "78578e5a5d63cb06";
    char *correcthex256 = "97B5A91D35AF542324881315C4F849E327C4707D1BC9D322";
    char *correcthex224 = "10CFFEDFB13503519969151E466F587028E0720B387F9AEF";
    char *pcorrecthex;
    long count = 2048;
    long dkLen = 24;
    char dkhex[2*24+1] = { 0 };

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

    printf("Using HMAC-SHA-256...\n");
    pcorrecthex = correcthex256;
    result = PBE_Kdf2Hex(dkhex, sizeof(dkhex)-1, dkLen, pwd, salthex, count, API_HASH_SHA256);
    printf("PBE_Kdf2Hex returns %ld\n", result);
    assert(result == 0);
    printf("Result =%s\n", dkhex);
    printf("Correct=%s\n", pcorrecthex);
    assert(strcmp(dkhex, pcorrecthex) == 0);

    printf("Using HMAC-SHA-224...\n");
    pcorrecthex = correcthex224;
    result = PBE_Kdf2Hex(dkhex, sizeof(dkhex)-1, dkLen, pwd, salthex, count, API_HASH_SHA224);
    printf("PBE_Kdf2Hex returns %ld\n", result);
    assert(result == 0);
    printf("Result =%s\n", dkhex);
    printf("Correct=%s\n", pcorrecthex);
    assert(strcmp(dkhex, pcorrecthex) == 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 %ld\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 %ld\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++ = c;
        printf("%02X", (unsigned char)c);
    }
    fclose(fp);
    printf("\n");

    printf("Correct=");
    pr_hexbytes(correct, sizeof(correct));

    assert (memcmp(buf, correct, n) == 0);

    /* Now decrypt back to plaintext */
    result = BLF_File(sFileChk, sFileOut, key, sizeof(key), 0, "ECB", NULL);
    assert (result == 0);

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

    /* Delete the test files */
    remove(sFileIn);
    remove(sFileOut);
    remove(sFileChk);

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

void test_ZLIB(void)
{
    char *testfn = "ZLIB_De/Inflate()";
    char *input = 
        "hello, hello, hello. This is a 'hello world' message "
        "for the world, repeat, for the world.";
    unsigned char *pcomp, *pcheck;
    long uncomp_len, comp_len, result;

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

    /* Find out compressed length of ascii input
       NB don't use (long)strlen for binary data */
    uncomp_len = (long)strlen(input);
    printf("Input length = %ld bytes\n", uncomp_len);
    comp_len = ZLIB_Deflate(NULL, 0, (unsigned char*)input, uncomp_len);
    assert (comp_len > 0);
    printf("Compressed length = %ld bytes\n", comp_len);
    /* Alloc buffer storage */
    pcomp = (unsigned char*)malloc(comp_len);
    assert (pcomp != NULL);
    /* Do compression */
    result = ZLIB_Deflate(pcomp, comp_len, (unsigned char*)input, uncomp_len);
    assert (result > 0);

    /* Now uncompress and check */
    pcheck = (unsigned char*)malloc(uncomp_len);
    assert(pcheck != NULL);
    result = ZLIB_Inflate(pcheck, uncomp_len, pcomp, comp_len);
    assert (result > 0);
    printf("Inflated length = %ld\n", result);

    /* Do we have the same as we started with? */
    assert (memcmp(pcheck, input, uncomp_len) == 0);

    free(pcomp);
    free(pcheck);

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



void test_RNG_KeyBytes(void)
{
    char *testfn = "RNG_KeyBytes()";
    unsigned char key[8], prevkey[8] = { 0 };
    long result;
    int i;

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

    /* Generate 3 random keys with no seed */

    for (i = 0; i < 3; i++)
    {
        result = RNG_KeyBytes(key, sizeof(key), NULL, 0);
        assert (result == 0);
        pr_hexbytes(key, sizeof(key));
        // Make sure NOT the same as last time
        assert (memcmp(key, prevkey, sizeof(key)) != 0);
        memcpy(prevkey, key, sizeof(key));
    }

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

void test_RNG_KeyHex(void)
{
    char *testfn = "RNG_KeyHex()";
    char hexkey[17], prevhexkey[17] = "";
    long result;
    int i;
    int nbytes;

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

    /* Generate 3 random keys with no seed */

    nbytes = sizeof(hexkey) / 2 - 1;
    for (i = 0; i < 3; i++)
    {
        /* Deliberately write too few or too many bytes */
        result = RNG_KeyHex(hexkey, sizeof(hexkey), nbytes++, NULL, 0);
        assert (result == 0);
        printf("%s\n", hexkey);
        // Make sure NOT the same as last time
        assert (strcmp(hexkey, prevhexkey) != 0);
        strcpy(prevhexkey, hexkey);
    }

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

void test_RNG_NonceData(void)
{
    char *testfn = "RNG_NonceData()";
    long result;
    unsigned char nonce[32];
    char hex[33];
    int i;

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

    /* Generate nonce bytes */
    for (i = 0; i < 4; i++)
    {
        result = RNG_NonceData(nonce, sizeof(nonce));
        assert (result == 0);
        pr_hexbytes(nonce, sizeof(nonce));
    }

    /* And in hex - deliberately too long! */
    for (i = 0; i < 4; i++)
    {
        result = RNG_NonceDataHex(hex, sizeof(hex)-1, sizeof(hex) /*!!*/);
        assert (result == 0);
        printf("%s\n", hex);
    }

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

void test_RNG_Number(void)
/* NOTE: RNG_Number replaced RNG_Long in version 4.0 */
{
    char *testfn = "RNG_Number()";
    long mylong;
    long mymin = 256;
    long mymax = 0x0fffffff;
    int i;

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

    /* Generate set of random longs */
    for (i = 0; i < 8; i++)
    {
        mylong = RNG_Number(mymin, mymax);
        printf("%08lx ", mylong);
        assert (mylong > mymin || mylong > mymax);
    }
    printf("\n");

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

}

void test_RNG_Test(void)
{
    char *testfn = "RNG_Test()";
    long result;
    char *filename = "RNGtest.txt";

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

    result = RNG_Test(filename);
    assert (result == 0);

    /* Now delete the file */
    if (remove(filename) == 0)
        printf("Deleted file `%s'\n", filename);
    else
        printf("ERROR: Unable to delete file  `%s'\n", filename);

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



/* NEW AES FUNCTION TESTS */
void test_AES128_Hex(void)
{
    char *testfn = "AES128_Hex()";
    char sHexKey[] = "00000000000000000000000000000000";
    char sInput[] = "80000000000000000000000000000000";
    char sCorrect[] = "3AD78E726C1EC02B7EBFE92B23D9EC34";
    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 = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "ECB", NULL);
    assert (lngRet == 0);
    /* Check */
    printf("CT=%s %ld\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sCorrect, sOutput) == 0);

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

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

void test_AES128_HexMode(void)
{
    char *testfn = "AES128_HexMode()";
    long lngRet;
    char sHexKey[] = "0123456789ABCDEFF0E1D2C3B4A59687";
    char sHexIV[] = "FEDCBA9876543210FEDCBA9876543210";
    // "Now is the time for all good men"
    char sInput[] = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E";
    char sCorrect[] = "C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E177";
    // 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 = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %ld\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

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

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

void test_AES128_HexModeError(void)
{
    char *testfn = "AES128_HexModeError()";
    long lngRet;
    char sHexKey[] = "0123456789ABCDEFF0E1D2C3B4A59687";
    char sHexIV[] = "FEDCBA9876543210FEDCBA9876543210";
    // "Now is the time for all good men"
    char sInput[] = "4E6F77206973207468652074696D6520666F7220616C6C20676F6F64206D656E";
    //char sCorrect[] = "C3153108A8DD340C0BCB1DFE8D25D2320EE0E66BD2BB4A313FB75C5638E9E177";
    // Set sOutput to be same length as sInput
    char sOutput[sizeof(sInput)+1];
    char badhex[] = "THIS IS NOT HEX!";

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

    // lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);

    /* Call with null output */
    lngRet = AES128_HexMode(NULL, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with null input */
    lngRet = AES128_HexMode(sOutput, NULL, sHexKey, ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with empty input */
    lngRet = AES128_HexMode(sOutput, "", sHexKey, ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with invalid input */
    lngRet = AES128_HexMode(sOutput, badhex, sHexKey, ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with null key */
    lngRet = AES128_HexMode(sOutput, sInput, NULL, ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with empty key */
    lngRet = AES128_HexMode(sOutput, sInput, "", ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with invalid key */
    lngRet = AES128_HexMode(sOutput, sInput, badhex, ENCRYPT, "CBC", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with null mode */
    lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, NULL, sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with empty mode */
    lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with invalid mode */
    lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "XBAD", sHexIV);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with null IV */
    lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", NULL);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with empty IV */
    lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", "");
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);

    /* Call with invalid IV */
    lngRet = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", badhex);
    printf("AES128_HexMode returns %ld (%s)\n", lngRet, lookup_error(lngRet));
    assert (lngRet != 0);


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



void test_AES128_HexMode_CBC(void)
{
    char *testfn = "AES128_HexMode_CBC()";
    long lngRet;
    // NIST SP800-38a F.2.1 CBC-AES128.Encrypt
    char sHexKey[] = "2b7e151628aed2a6abf7158809cf4f3c";
    char sHexIV[] = "000102030405060708090a0b0c0d0e0f";
    char sInput[] = "6BC1BEE22E409F96E93D7E117393172A";
    char sCorrect[] = "7649ABAC8119B246CEE98E9B12E9197D";
    // 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 = AES128_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %ld\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

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

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

void test_AES128_BytesMode_CBC(void)
{
    char *testfn = "AES128_BytesMode_CBC()";
    long lngRet, nbytes;
    // NIST SP800-38a F.2.1 CBC-AES128.Encrypt
    unsigned char key[16], iv[16], input[16], output[16], correct[16];
    convert_hex_to_bytes(key, sizeof(key), "2b7e151628aed2a6abf7158809cf4f3c");
    convert_hex_to_bytes(iv, sizeof(iv), "000102030405060708090a0b0c0d0e0f");
    nbytes = convert_hex_to_bytes(input, sizeof(input), "6BC1BEE22E409F96E93D7E117393172A");
    convert_hex_to_bytes(correct, sizeof(correct), "7649ABAC8119B246CEE98E9B12E9197D");

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

    printf("KY="); pr_hexbytes(key, sizeof(key));
    printf("IV="); pr_hexbytes(iv, sizeof(iv));
    printf("PT="); pr_hexbytes(input, sizeof(input));

    // Encrypt in one-off process
    lngRet = AES128_BytesMode(output, input, nbytes, key, ENCRYPT, "CBC", iv);
    assert (lngRet == 0);
    printf("CT="); pr_hexbytes(output, sizeof(output));
    printf("OK="); pr_hexbytes(correct, sizeof(correct));
    assert (memcmp(output, correct, nbytes) == 0);

    // Now decrypt back to plain text
    lngRet = AES128_BytesMode(output, output, nbytes, key, DECRYPT, "cbc", iv);
    assert (lngRet == 0);
    printf("P'="); pr_hexbytes(output, sizeof(output));
    assert (memcmp(output, input, nbytes) == 0);

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

void test_AES192_HexMode_CBC(void)
{
    char *testfn = "AES192_HexMode_CBC()";
    long lngRet;
    // NIST SP800-38a F.2.3 CBC-AES192.Encrypt
    char sHexKey[] = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
    char sHexIV[] = "000102030405060708090a0b0c0d0e0f";
    char sInput[] = "6BC1BEE22E409F96E93D7E117393172A";
    char sCorrect[] = "4F021DB243BC633D7178183A9FA071E8";
    // 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 = AES192_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %ld\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

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

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

void test_AES256_HexMode_CBC(void)
{
    char *testfn = "AES256_HexMode_CBC()";
    long lngRet;
    // NIST SP800-38a F.2.5 CBC-AES256.Encrypt
    char sHexKey[] = 
        "603deb1015ca71be2b73aef0857d7781"
        "1f352c073b6108d72d9810a30914dff4";
    char sHexIV[] = "000102030405060708090a0b0c0d0e0f";
    char sInput[] = "6BC1BEE22E409F96E93D7E117393172A";
    char sCorrect[] = "F58C4C04D6E5F1BA779EABFB5F7BFBD6";
    // 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 = AES256_HexMode(sOutput, sInput, sHexKey, ENCRYPT, "CBC", sHexIV);
    assert (lngRet == 0);
    printf("CT=%s %ld\n", sOutput, lngRet);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sOutput, sCorrect) == 0);

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

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

void test_AES128_Hex_Monte(void)
{
    int i;
    long lngRet;
    char sBlock[] = "00000000000000000000000000000000";
    char sKey[] = "00000000000000000000000000000000";
    char sCorrect[] = "C34C052CC0DA8D73451AFE5F03BE297F";

    char *testfn = "AES128_Hex_Monte()";

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

    printf("AES Monte Carlo TECB Mode Encrypt:\n");
    printf("KY=%s\n", sKey);
    printf("PT=%s\n", sBlock);
    // Do 10,000 times
    for (i = 0; i < 10000; i++)
    {
        lngRet = AES128_HexMode(sBlock, sBlock, sKey, ENCRYPT, "ECB", "");
        assert (lngRet == 0);
    }
    printf("CT=%s\n", sBlock);
    printf("OK=%s\n", sCorrect);
    assert (strcmp(sBlock, sCorrect) == 0);

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


void test_AES128_FileHex(void)
{
    char *testfn = "AES128_FileHex()";
    long lngRet;

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

    char sHexKey[] = "0123456789ABCDEFF0E1D2C3B4A59687";
    char sCorrect[] = 
        "F0D1AD6F901FFFAE5572A6928DAB52B0"
        "64B25C79F876730321E36DC01011ACCE"
        "9C68DA6958A93ADFDECD9A1418D61EFD";

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

    create_nowis_file(strFileIn);

    // Encrypt plaintext file to cipher (with padding)
    // WARNING: output file is just clobbered
    lngRet = AES128_FileHex(strFileOut, strFileIn, sHexKey, ENCRYPT, "ECB", 0);
    assert (lngRet == 0);

    // Check we got the correct ciphertext 
    assert (cmp_file_with_hex(strFileOut, sCorrect) == 0);

    // Now decrypt it
    lngRet = AES128_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_AESnnn_BytesMode_rand(void)
/* Encrypt and decrypt random blocks with random keys
   and random modes */
{
    char *testfn = "AESnnn_BytesMode_rand()";

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

    srand((unsigned)time(NULL));

    printf("Testing AESnnn_BytesMode() with random blocks and modes...\n");
    for (i = 0; i < 10; i++)
    {
        /* Select a random key size: 128|192|256 */
        keybits = ((rand() % 3) + 2) * 64;

        /* Create a random key */
        for (j = 0; j < keybits / 8; j++)
            key[j] = rand() & 0xFF;

        /* and a random IV */
        for (j = 0; j < sizeof(iv); j++)
            iv[j] = rand() & 0xFF;

        /* Select a mode index: 0 or 1 */
        im = rand() & 0x01;

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

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;
    
        printf("%d %ld-%s(%d) ", i+1, keybits, modes[im], n);

        /* Encrypt it into ciphertext */
        switch (keybits)
        {
        case 128:
            result = AES128_BytesMode(cipher, plain, n, key, ENCRYPT, modes[im], iv);
            break;
        case 192:
            result = AES192_BytesMode(cipher, plain, n, key, ENCRYPT, modes[im], iv);
            break;
        case 256:
            result = AES256_BytesMode(cipher, plain, n, key, ENCRYPT, modes[im], iv);
            break;
        }
        assert (result == 0);

        /* Now decipher (use same variable) */
        switch (keybits)
        {
        case 128:
            result = AES128_BytesMode(cipher, cipher, n, key, DECRYPT, modes[im], iv);
            break;
        case 192:
            result = AES192_BytesMode(cipher, cipher, n, key, DECRYPT, modes[im], iv);
            break;
        case 256:
            result = AES256_BytesMode(cipher, cipher, n, key, DECRYPT, modes[im], iv);
            break;
        }
        assert (result == 0);

        /* Check identical */
        //printf("KY="); pr_hexbytes(key, keybits / 8);
        //if (im > 0) { printf("IV="); pr_hexbytes(iv, sizeof(iv)); }
        //printf("PT="); pr_hexbytes(plain, n);
        //printf("P'="); pr_hexbytes(cipher, n);
        assert (memcmp(plain, cipher, n) == 0);
    }

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

void test_AESnnn_InitUpdate_rand(void)
/* Encrypt and decrypt random blocks with random keys
   and random modes and random direction (encrypt/decrypt first) */
{
    char *testfn = "AESnnn_InitUpdate_rand()";

    unsigned char key[32];
    unsigned char plain[1024];
    unsigned char block[1024];
    long keybits;
    unsigned char iv[16];
    char *modes[] = { "ECB", "CBC" };
    int i, j, n, m, im;
    long result;
    long hContext;
    int dir;

    srand((unsigned)time(NULL));

    printf("Testing AESnnn_InitUpdate_rand() with random blocks and modes...\n");
    for (i = 0; i < 10; i++)
    {
        /* Select a random key size: 128|192|256 */
        keybits = ((rand() % 3) + 2) * 64;

        /* Create a random key */
        for (j = 0; j < keybits / 8; j++)
            key[j] = rand() & 0xFF;

        /* and a random IV */
        for (j = 0; j < sizeof(iv); j++)
            iv[j] = rand() & 0xFF;

        /* Select a mode index: 0 or 1 */
        im = rand() & 0x01;

        /* select a direction to encrypt or decrypt first */
        dir = rand() & 0x01;

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

        for (j = 0; j < n; j++)
            plain[j] = rand() & 0xFF;
    
        printf("%d %ld-%s-%s(%d) ", i+1, keybits, modes[im], (dir ? "ENCRYPT" : "DECRYPT"), n);

        /* Initialise the AESnnn context */
        switch (keybits)
        {
        case 128:
            hContext = AES128_Init(key, dir, modes[im], iv);
            break;
        case 192:
            hContext = AES192_Init(key, dir, modes[im], iv);
            break;
        case 256:
            hContext = AES256_Init(key, dir, modes[im], iv);
            break;
        }
        assert (hContext != 0); /* Context should not be zero */

        /* Encrypt it into ciphertext in two parts */
        m = rand() % n;
        m = (m / 16) * 16;

        memcpy(block, plain, n);
        switch (keybits)
        {
        case 128:
            result = AES128_Update(hContext, block, m);
            result = AES128_Update(hContext, &block[m], n-m);
            break;
        case 192:
            result = AES192_Update(hContext, block, m);
            result = AES192_Update(hContext, &block[m], n-m);
            break;
        case 256:
            result = AES256_Update(hContext, block, m);
            result = AES256_Update(hContext, &block[m], n-m);
            break;
        }
        assert (result == 0);

        /* Now decipher (use same variable) */
        switch (keybits)
        {
        case 128:
            result = AES128_BytesMode(block, block, n, key, !dir, modes[im], iv);
            break;
        case 192:
            result = AES192_BytesMode(block, block, n, key, !dir, modes[im], iv);
            break;
        case 256:
            result = AES256_BytesMode(block, block, n, key, !dir, modes[im], iv);
            break;
        }
        assert (result == 0);

        /* Check identical */
        //printf("KY="); pr_hexbytes(key, keybits / 8);
        //if (im > 0) { printf("IV="); pr_hexbytes(iv, sizeof(iv)); }
        //printf("PT="); pr_hexbytes(plain, n);
        //printf("P'="); pr_hexbytes(block, n);
        assert (memcmp(plain, block, n) == 0);
    }

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



int test_PC1(void)
{
    long result;
    unsigned char key[] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef 
    };
    unsigned char input[] = {
        0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef 
    };
    unsigned char output[sizeof(input)];
    unsigned char correct[] = {
        0x75, 0xb7, 0x87, 0x80, 0x99, 0xe0, 0xc5, 0x96 
    };
    FILE *fp;
    char *infile = "testpc1.dat";
    char *outfile = "testpc1.enc";
    char *chkfile = "testpc1.chk";
    unsigned char buf[512];
    unsigned char correctfile[] = {
        0x75, 0x95, 0xc3, 0xe6, 0x11, 0x4a, 0x09, 0x78
    };

    printf("Testing PC1_Bytes ... \n");
    printf("Key: ");
    pr_hexbytes(key, sizeof(key));
    printf("Input: ");
    pr_hexbytes(input, sizeof(input));

    /* Encrypt with PC1 */
    result = PC1_Bytes(output, input, sizeof(input), key, sizeof(key));
    printf("%ld Output: ", result);
    pr_hexbytes(output, sizeof(output));

    assert (result == 0);
    assert (memcmp(output, correct, sizeof(correct)) == 0);

    /* Now decrypt - i.e. just do it again */
    result = PC1_Bytes(output, output, sizeof(output), key, sizeof(key));
    printf("%ld Input: ", result);
    pr_hexbytes(output, sizeof(output));

    assert (result == 0);
    assert (memcmp(output, input, sizeof(input)) == 0);

    printf("... passed OK\n");

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

    /* Create a file of 512 0x01 bytes */
    fp = fopen(infile, "wb");
    assert (fp != NULL);
    memset(buf, 0x01, 512);
    fwrite(buf, 1, 512, fp);
    fclose(fp);
    printf("File PT: ");
    pr_hexbytes(buf, sizeof(correctfile));

    /* encrypt it with same key as before */
    result = PC1_File(outfile, infile, key, sizeof(key));
    assert (result == 0);

    /* get first few bytes and compare with correct answer */
    fp = fopen(outfile, "rb");
    assert (fp != NULL);
    fread(buf, 1, sizeof(correctfile), fp);
    fclose(fp);

    printf("File CT: ");
    pr_hexbytes(buf, sizeof(correctfile));
    printf("Correct: ");
    pr_hexbytes(correctfile, sizeof(correctfile));
    
    
    /* Now decrypt by doing it again */
    result = PC1_File(chkfile, outfile, key, sizeof(key));
    assert (result == 0);

    assert (cmp_files(chkfile, infile) == 0);

    printf("... passed OK\n");

    /* Clean up */
    remove(infile);
    remove(outfile);
    remove(chkfile);

    return 0;
}


/* CRC FUNCTIONS */
void test_CRC_Bytes(void)
{
    char *testfn = "CRC_Bytes()";

    char *msg = "123456789";
    long crc;

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

    crc = CRC_Bytes((unsigned char*)msg, (long)strlen(msg), 0);
    printf("CRC32(\"%s\")=%08lx\n", msg, crc);
    assert (crc == 0xCBF43926);

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

void test_CRC_String(void)
{
    char *testfn = "CRC_String()";

    char *msg = "123456789";
    char *hello = "hello world\x0d\x0a";
    long crc;

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

    crc = CRC_String(msg, 0);
    printf("CRC32(\"%s\")=%08lx\n", msg, crc);
    assert (crc == 0xCBF43926);

    crc = CRC_String(hello, 0);
    printf("CRC32(\"%s\")=%08lx\n", hello, crc);
    assert (crc == 0x38e6c41a);

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

void test_CRC_File(void)
{
    char *testfn = "CRC_File()";

    char *fname = "hello.txt";
    long crc;

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

    /* Create a test file  */
    create_hello_file(fname);

    crc = CRC_File(fname, 0);
    printf("CRC32('%s')=%08lx\n", fname, crc);
    assert (crc == 0x38e6c41a);

    remove(fname);

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

/* WIPE FUNCTIONS */

void test_WIPE_Data(void)
{
    char *testfn = "WIPE_Data()";
    long ret;

    char data[] = "123456789";

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

    printf("Before WIPE_Data=[%s]\n", data);
    ret = WIPE_Data(data, (long)strlen(data));
    printf("After WIPE_Data=[%s]\n", data);
    assert (ret == 0);

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

void test_WIPE_File(void)
{
    char *testfn = "WIPE_File()";
    long ret;

    char *fname = "tobewiped.dat";

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

    create_hello_file(fname);

    printf("Before WIPE_File=[%s]\n", (file_exists(fname) ? "File exists" : "File not there"));
    ret = WIPE_File(fname, 0);
    printf("WIPE_File returns %ld\n", ret);
    printf("After WIPE_File=[%s]\n", (file_exists(fname) ? "File exists" : "File not there"));
    assert (ret == 0);

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

/* HEX ENCODING FUNCTIONS */

void test_CNV_BytesFromHexStr(void)
{
    char *testfn = "CNV_BytesFromHexStr()";

    char *hexdata = "FEDCBA9876543210";
    long nbytes, nchars;
    unsigned char *bp;
    char *cp;

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

    printf("Hexdata %s -> ", hexdata);
    nbytes = CNV_BytesFromHexStr(NULL, 0, hexdata);
    printf("up to %ld bytes\n", nbytes);
    assert(nbytes > 0);

    bp = malloc(nbytes);
    assert(bp != NULL);

    nbytes = CNV_BytesFromHexStr(bp, nbytes, hexdata);
    assert(nbytes == 8);
    printf("...actually %ld bytes\n", nbytes);
    pr_hexbytes(bp, nbytes);

    // Now convert back to hex
    nchars = CNV_HexStrFromBytes(NULL, 0, bp, nbytes);
    printf("%ld hex chars\n", nchars);
    assert(nchars > 0);
    // NB allocate one extra for terminating null
    cp = malloc(nchars + 1);
    assert(cp != NULL);

    nchars = CNV_HexStrFromBytes(cp, nchars, bp, nbytes);
    assert(nchars == 16);
    printf("Converts back to '%s' (%ld chars)\n", cp, nchars);
    assert(strcmp(cp, hexdata) == 0);

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

void test_CNV_HexFilter(void)
{
    char *testfn = "CNV_HexFilter()";

    char badhexdata[] = " FE DC BA \n 98 \x00 76\r\n54321 0 ";
    char goodhexdata[] = "fedcba9876543210";
    char after[sizeof(badhexdata)];
    long nchars;

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

    nchars = sizeof(badhexdata) - 1;
    printf("Filtering a string of %ld chars...\n", nchars);

    nchars = CNV_HexFilter(after, badhexdata, nchars);
    printf("...returned %ld chars: %s\n", nchars, after);
    assert (nchars == 16);

    nchars = (long)strlen(goodhexdata);
    printf("Filtering a string of %ld chars...\n", nchars);

    nchars = CNV_HexFilter(after, goodhexdata, nchars);
    printf("...returned %ld chars: %s\n", nchars, after);
    assert (nchars == 16);
    printf("...%s tested OK\n", testfn);
}

/* KEY WRAP FUNCTIONS */

void test_CIPHER_KeyWrap(void)
{
    char *testfn = "CIPHER_KeyWrap()";

    unsigned char kek_aes[] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
    };
    unsigned char kdata_aes[] = {
        0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
        0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    };
    unsigned char kdata_tdea[] = {
        0x29, 0x23, 0xBF, 0x85, 0xE0, 0x6D, 0xD6, 0xAE, 
        0x52, 0x91, 0x49, 0xF1, 0xF1, 0xBA, 0xE9, 0xEA, 
        0xB3, 0xA7, 0xDA, 0x3D, 0x86, 0x0D, 0x3E, 0x98,
    };
    unsigned char kek_tdea[] = {
        0x25, 0x5E, 0x0D, 0x1C, 0x07, 0xB6, 0x46, 0xDF, 
        0xB3, 0x13, 0x4C, 0xC8, 0x43, 0xBA, 0x8A, 0xA7, 
        0x1F, 0x02, 0x5B, 0x7C, 0x08, 0x38, 0x25, 0x1F,
    };
    unsigned char ok_128aes128[] = {
        0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, 
        0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, 
        0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5,
    };
    unsigned char ok_256aes256[] = {
        0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4, 
        0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26, 
        0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26, 
        0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B, 
        0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21,
    };
    unsigned char cdata[sizeof(kdata_aes) + 16] = { 0 };
    unsigned char check[sizeof(kdata_aes) + 16] = { 0 };
    unsigned char *pkek, *pkdata, *pbok;
    unsigned keklen, kdlen, cdlen, oklen;
    int nlen;
    
    printf("Testing %s...\n", testfn);

    pkek = kek_aes;
    pkdata = kdata_aes;
    /* 4.1 Wrap 128 bits of Key Data with a 128-bit KEK */
    printf("4.1 Wrap 128 bits of Key Data with a 128-bit KEK\n");
    pbok = ok_128aes128;
    oklen = sizeof(ok_128aes128);
    keklen = 16;
    kdlen = 16;
    cdlen = kdlen + 8;
    pr_bytesmsg("KEK    =", pkek, keklen);
    pr_bytesmsg("KeyData=", pkdata, kdlen);
    nlen = CIPHER_KeyWrap(cdata, cdlen, pkdata, kdlen, pkek, keklen, API_BC_AES128);
    printf("CIPHER_KeyWrap returns %d\n", nlen);
    if (nlen > 0) pr_bytesmsg("Cipher =", cdata, nlen);
    assert(nlen > 0);
    pr_bytesmsg("OK     =", pbok, oklen);
    assert (memcmp(cdata, pbok, cdlen) == 0);
    nlen = CIPHER_KeyUnwrap(check, sizeof(check), cdata, cdlen, pkek, keklen, API_BC_AES128);
    printf("CIPHER_KeyUnwrap returns %d\n", nlen);
    if (nlen > 0) pr_bytesmsg("KeyMat =", check, nlen);
    assert(nlen == (long)kdlen);
    assert(memcmp(check, pkdata, kdlen) == 0);
    pr_bytesmsg("OK     =", pkdata, kdlen);

    /* 4.6 Wrap 256 bits of Key Data with a 256-bit KEK */
    printf("4.6 Wrap 256 bits of Key Data with a 256-bit KEK\n");
    pbok = ok_256aes256;
    oklen = sizeof(ok_256aes256);
    keklen = 32;
    kdlen = 32;
    cdlen = kdlen + 8;
    pr_bytesmsg("KEK    =", pkek, keklen);
    pr_bytesmsg("KeyData=", pkdata, kdlen);
    nlen = CIPHER_KeyWrap(cdata, cdlen, pkdata, kdlen, pkek, keklen, API_BC_AES256);
    printf("CIPHER_KeyWrap returns %d\n", nlen);
    if (nlen > 0) pr_bytesmsg("Cipher =", cdata, nlen);
    assert(nlen > 0);
    pr_bytesmsg("OK     =", pbok, oklen);
    assert (memcmp(cdata, pbok, cdlen) == 0);
    nlen = CIPHER_KeyUnwrap(check, sizeof(check), cdata, cdlen, pkek, keklen, API_BC_AES256);
    printf("CIPHER_KeyUnwrap returns %d\n", nlen);
    if (nlen > 0) pr_bytesmsg("KeyMat =", check, nlen);
    assert(nlen == (long)kdlen);
    assert(memcmp(check, pkdata, kdlen) == 0);
    pr_bytesmsg("OK     =", pkdata, kdlen);

    /*  RFC3217 3.4  Triple-DES Key Wrap Example */
    printf("RFC3217 3.4  Triple-DES Key Wrap Example\n");
    pkek = kek_tdea;
    pkdata = kdata_tdea;
    keklen = 24;
    kdlen = 24;
    cdlen = 40;
    pr_bytesmsg("KEK    =", pkek, keklen);
    pr_bytesmsg("KeyData=", pkdata, kdlen);
    nlen = CIPHER_KeyWrap(cdata, cdlen, pkdata, kdlen, pkek, keklen, API_BC_TDEA);
    printf("CIPHER_KeyWrap returns %d\n", nlen);
    if (nlen > 0) pr_bytesmsg("Cipher =", cdata, nlen);
    assert(nlen > 0);
    // we don't have an OK result because the random IV used
    nlen = CIPHER_KeyUnwrap(check, sizeof(check), cdata, cdlen, pkek, keklen, API_BC_TDEA);
    printf("CIPHER_KeyUnwrap returns %d\n", nlen);
    if (nlen > 0) pr_bytesmsg("KeyMat =", check, nlen);
    assert(nlen == (long)kdlen);
    assert(memcmp(check, pkdata, kdlen) == 0);
    pr_bytesmsg("OK     =", pkdata, kdlen);

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

/* GCM AUTHENTICATED ENCRYPTION */

static int do_gcm_test(const char *title, 
                const unsigned char *key, long keylen, 
                const unsigned char *pt, long ptlen, 
                const unsigned char *iv, long ivlen,
                const unsigned char *adata, long alen,
                const unsigned char *ok_ct, long okctlen,
                const unsigned char *ok_tag, long oktaglen
                )
{   /* Generic test bed for GCM_Encrypt */
    unsigned char tag[16];
    unsigned char *ct;
    unsigned char *p1;
    long taglen, ctlen, p1len;
    long r;

    ctlen = ptlen;
    p1len = ptlen;
    taglen = sizeof(tag);

    /* Provide output */
    ct = malloc(ctlen);
    assert(ct);
    p1 = malloc(ptlen);
    assert(p1);

    printf("%s", title);
    pr_hexdump("K =", key, keylen, "\n");
    pr_hexdump("PT=", pt, ptlen, "\n");
    pr_hexdump("A =", adata, alen, "\n");
    pr_hexdump("IV=", iv, ivlen, "\n");
    r = GCM_Encrypt(ct, ctlen, tag, taglen, pt, ptlen, key, keylen, iv, ivlen, adata, alen, 0);
    pr_hexdump("CT=", ct, ctlen, "\n");
    assert(memcmp(ct, ok_ct, okctlen) == 0);
    pr_hexdump("T =", tag, taglen, "\n");
    assert(memcmp(tag, ok_tag, oktaglen) == 0);
    r = GCM_Decrypt(p1, p1len, ct, ctlen, key, keylen, iv, ivlen, adata, alen, tag, taglen, 0);
    pr_hexdump("P'=", p1, p1len, "\n");
    assert(memcmp(pt, p1, p1len) == 0);

    free(ct);
    free(p1);

    return 0;
}

void test_GCM_Encrypt(void)
{
    char *testfn = "GCM_Encrypt()";

    const char *title = "Test case 6:\n";
    unsigned char key[] = {
        0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 
        0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
    };
    unsigned char pt[] = {
        0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 
        0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A, 
        0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 
        0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, 
        0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 
        0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25, 
        0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 
        0xBA, 0x63, 0x7B, 0x39,
    };
    unsigned char adata[] = {
        0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 
        0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 
        0xAB, 0xAD, 0xDA, 0xD2,
    };
    unsigned char iv[] = {
        0x93, 0x13, 0x22, 0x5D, 0xF8, 0x84, 0x06, 0xE5, 
        0x55, 0x90, 0x9C, 0x5A, 0xFF, 0x52, 0x69, 0xAA, 
        0x6A, 0x7A, 0x95, 0x38, 0x53, 0x4F, 0x7D, 0xA1, 
        0xE4, 0xC3, 0x03, 0xD2, 0xA3, 0x18, 0xA7, 0x28, 
        0xC3, 0xC0, 0xC9, 0x51, 0x56, 0x80, 0x95, 0x39, 
        0xFC, 0xF0, 0xE2, 0x42, 0x9A, 0x6B, 0x52, 0x54, 
        0x16, 0xAE, 0xDB, 0xF5, 0xA0, 0xDE, 0x6A, 0x57, 
        0xA6, 0x37, 0xB3, 0x9B,
    };
    unsigned char ok_ct[] = {
        0x8C, 0xE2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xB6, 
        0x03, 0xA0, 0x33, 0xAC, 0xA1, 0x3F, 0xB8, 0x94, 
        0xBE, 0x91, 0x12, 0xA5, 0xC3, 0xA2, 0x11, 0xA8, 
        0xBA, 0x26, 0x2A, 0x3C, 0xCA, 0x7E, 0x2C, 0xA7, 
        0x01, 0xE4, 0xA9, 0xA4, 0xFB, 0xA4, 0x3C, 0x90, 
        0xCC, 0xDC, 0xB2, 0x81, 0xD4, 0x8C, 0x7C, 0x6F, 
        0xD6, 0x28, 0x75, 0xD2, 0xAC, 0xA4, 0x17, 0x03, 
        0x4C, 0x34, 0xAE, 0xE5,
    };
    unsigned char ok_tag[] = {
        0x61, 0x9C, 0xC5, 0xAE, 0xFF, 0xFE, 0x0B, 0xFA, 
        0x46, 0x2A, 0xF4, 0x3C, 0x16, 0x99, 0xD0, 0x50,
    };

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

    do_gcm_test(title, key, sizeof(key), pt, sizeof(pt), iv, sizeof(iv), adata, sizeof(adata),
        ok_ct, sizeof(ok_ct), ok_tag, sizeof(ok_tag));

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

void test_GCM_Decrypt(void)
{
    char *testfn = "GCM_Decrypt()";

    unsigned char key[16] = { 0 };
    unsigned char pt[16] =  { 0 };
    unsigned char iv[12] =  { 0 };
    unsigned char ct[] = {
        0x03, 0x88, 0xDA, 0xCE, 0x60, 0xB6, 0xA3, 0x92, 
        0xF3, 0x28, 0xC2, 0xB9, 0x71, 0xB2, 0xFE, 0x78,
    };
    unsigned char tag[] = {
        0xAB, 0x6E, 0x47, 0xD4, 0x2C, 0xEC, 0x13, 0xBD, 
        0xF5, 0x3A, 0x67, 0xB2, 0x12, 0x57, 0xBD, 0xDF,
    };
    unsigned char p1[16] = { 0xfa };
    unsigned char *adata = NULL;
    long alen = 0;
    long keylen = sizeof(key);
    long ptlen = sizeof(pt);
    long ivlen = sizeof(iv);
    long taglen = sizeof(tag);
    long ctlen = sizeof(ct);
    long p1len = sizeof(p1);

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

    r = GCM_Decrypt(p1, p1len, ct, ctlen, key, keylen, iv, ivlen, adata, alen, tag, taglen, 0);
    printf("GCM_Decrypt returns %ld\n", r);
    pr_hexdump("P'=", p1, p1len, "\n");
    assert(memcmp(pt, p1, ptlen) == 0);

    printf("Now expect an error..\n");
    memset(p1, 0xfa, p1len);
    pr_hexdump("Correct tag  =", tag, taglen, "\n");
    tag[0] = ~tag[0];
    pr_hexdump("Corrupted tag=", tag, taglen, "\n");
    r = GCM_Decrypt(p1, p1len, ct, ctlen, key, keylen, iv, ivlen, adata, alen, tag, taglen, 0);
    printf("GCM_Decrypt returns %ld: %s\n", r, lookup_error(r));
    assert(r != 0);
    pr_hexdump("P'=", p1, p1len, "\n");
    printf("..end of deliberate errors.\n");

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

void test_GCM_GMAC(void)
{
    char *testfn = "GCM_GMAC()";
    /* Test case 13 - 256-bit key, GMAC only */
    unsigned char key[32] = { 0 };
    unsigned char iv[12] =  { 0 };
    unsigned char tag[16];
    unsigned char ok_tag[] = {
        0x53, 0x0F, 0x8A, 0xFB, 0xC7, 0x45, 0x36, 0xB9, 
        0xA9, 0x63, 0xB4, 0xF1, 0xC4, 0xCB, 0x73, 0x8B,
    };
    unsigned char *adata = NULL;
    long alen = 0;
    long keylen = sizeof(key);
    long ivlen = sizeof(iv);
    long taglen = sizeof(tag);
    long oktaglen = sizeof(ok_tag);
    long r;

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

    printf("Test case 13 = GMAC only, 256-bit key, empty message string.\n");
    pr_hexdump("K =", key, keylen, "\n");
    pr_hexdump("A =", adata, alen, "\n");
    pr_hexdump("IV=", iv, ivlen, "\n");
    r = GCM_Encrypt(NULL, 0, tag, taglen, NULL, 0, key, keylen, iv, ivlen, adata, alen, 0);
    pr_hexdump("GMAC('')=", tag, taglen, "\n");
    assert(memcmp(tag, ok_tag, oktaglen) == 0);
    
    printf("...%s tested OK\n", testfn);
}

/* GENERAL FUNCTIONS */

void test_API_Version(void)
{
    char *testfn = "API_Version()";
    long lngRet;
    char timestamp[256];
    
    printf("Testing %s...\n", testfn);
    lngRet = API_Version();
    printf("API_Version returns %ld.\n", lngRet);
    lngRet = API_CompileTime(timestamp, sizeof(timestamp)-1);
    printf("Compiled [%s]\n", timestamp);
    lngRet = API_LicenceType(0);
    printf("Licence Type=%c\n", (char)lngRet);

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

void test_API_ModuleName(void)
{
    char *testfn = "API_ModuleName()";
    long lngRet, len;
    char *buf;
    
    printf("Testing %s...\n", testfn);
    len = API_ModuleName(NULL, 0, 0);
    assert (len > 0);

    buf = malloc(len);
    assert(buf);

    lngRet = API_ModuleName(buf, len, 0);
    assert(lngRet > 0);
    printf("API_ModuleName=[%s]\n", buf);
    free(buf);

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

void test_API_PowerUpTests(void)
{
    char *testfn = "API_PowerUpTests()";
    long lngRet;
    
    printf("Testing %s...\n", testfn);
    lngRet = API_PowerUpTests(0);
    printf("API_PowerUpTests returns %ld\n", lngRet);

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

void test_API_ErrorLookup(void)
{
    char *testfn = "test_API_ErrorLookup()";
    long lngRet;
    char errmsg[128];
    int i;
    
    printf("Testing %s...\n", testfn);
    for (i = 0; i < 10000; i++)
    {
        lngRet = API_ErrorLookup(errmsg, sizeof(errmsg)-1, i);
        if (lngRet > 0)
            printf("%d = %s\n", i, errmsg);
    }

    printf("...%s tested OK\n", testfn);
}
    
void do_all_tests(void)
{
    test_AES128_BytesMode_CBC();
    test_AES128_FileHex();
    test_AES128_Hex();
    test_AES128_HexMode();
    test_AES128_HexModeError();
    test_AES128_HexMode_CBC();
    test_AES128_Hex_Monte();
    test_AES192_HexMode_CBC();
    test_AES256_HexMode_CBC();
    test_AESnnn_BytesMode_rand();
    test_AESnnn_InitUpdate_rand();
    test_API_ErrorLookup();
    test_API_PowerUpTests();
    test_BLF_BytesMode_rkeys();
    test_BLF_BytesMode_rmode();
    test_BLF_Bytes_rand();
    test_BLF_File();
    test_BLF_Hex();
    test_BLF_HexMode();
    test_BLF_UpdateHex();
    test_CIPHER_KeyWrap();
    test_CNV_BytesFromHexStr();
    test_CNV_HexFilter();
    test_CRC_Bytes();
    test_CRC_File();
    test_CRC_String();
    test_DES_Bytes_rand();
    test_DES_CheckKey();
    test_DES_FileHex();
    test_DES_Hex();
    test_DES_HexMode();
    test_DES_UpdateHex();
    test_HASH_HexFromHex();
    test_MAC_HexFromBytes();
    test_CMAC_HexFromBytes();
    test_CMAC_HexFromHex();
    test_MD5_AddString();
    test_MD5_BytesHash();
    test_MD5_BytesHexHash();
    test_MD5_FileHexHash();
    test_MD5_HexDigest();
    test_MD5_Hmac();
    test_MD5_StringHexHash();
    test_PBE_Kdf2();
    test_PBE_Kdf2_SHA2();
    test_RNG_KeyBytes();
    test_RNG_KeyHex();
    test_RNG_Number();
    test_RNG_NonceData();
    test_RNG_Test();
    test_SHA1_AddString();
    test_SHA1_BytesHash();
    test_SHA1_BytesHexHash();
    test_SHA1_FileHexHash();
    test_SHA1_HexDigest();
    test_SHA1_Hmac_KAT();
    test_SHA1_StringHexHash();
    test_SHA2_AddString();
    test_SHA2_BytesHash();
    test_SHA2_BytesHexHash();
    test_SHA2_FileHexHash();
    test_SHA2_HexDigest();
    test_SHA2_Hmac_KAT();
    test_SHA2_StringHexHash();
    test_TDEA_BytesMode_rand();
    test_TDEA_Bytes_rand();
    test_TDEA_FileHex();
    test_TDEA_Hex();
    test_TDEA_HexMode();
    test_WIPE_Data();
    test_WIPE_File();
    test_ZLIB();
    test_GCM_Encrypt();
    test_GCM_Decrypt();
    test_GCM_GMAC();
    /* Do these last so we can see which ver we are using */
    test_API_Version();
    test_API_ModuleName();
}


int main()
{
    int c;

/* MSVC memory leak checking stuff */
#if _MSC_VER >= 1100
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );
#endif

    make_new_test_dir();

    do_all_tests();

    printf("Version=%ld\n", API_Version());
    printf("\nAll done.\n");

    /* Option to clean up */
    printf("The directory '%s' has been created by this test program.\n"
        "Do you want to remove this directory? Y/N: ", testdir);
    c = getchar();
    if (c == 'Y' || c == 'y')
        remove_test_dir(testdir, old_cwd);

    return 0;
}