/*H********************************************************************************/
/*!
    \File crypt.c

    \Description
        Test the crypto modules.

    \Copyright
        Copyright (c) 2013 Electronic Arts Inc.

    \Version 01/11/2013 (jbrookes) First Version
*/
/********************************************************************************H*/

/*** Include files ****************************************************************/

#include <stdlib.h>
#include <string.h>

#include "DirtySDK/platform.h"
#include "DirtySDK/dirtysock.h"
#include "DirtySDK/crypt/cryptaes.h"
#include "DirtySDK/crypt/cryptarc4.h"
#include "DirtySDK/crypt/cryptchacha.h"
#include "DirtySDK/crypt/cryptcurve.h"
#include "DirtySDK/crypt/cryptgcm.h"
#include "DirtySDK/crypt/cryptmont.h"
#include "DirtySDK/crypt/cryptnist.h"
#include "DirtySDK/crypt/cryptrand.h"
#include "DirtySDK/crypt/cryptrsa.h"
#include "DirtySDK/crypt/cryptsha2.h"
#include "DirtySDK/crypt/crypthash.h"
#include "DirtySDK/crypt/crypthmac.h"
#include "DirtySDK/proto/protossl.h"
#include "DirtySDK/util/base64.h"
#include "DirtySDK/util/murmurhash3.h"

#include "testermodules.h"

#include "libsample/zlib.h"
#include "libsample/zmem.h"

/*** Defines **********************************************************************/

/*** Type Definitions *************************************************************/

//! type to encapsulate gcm test data
typedef struct CryptTestGcmT
{
    const uint8_t aKey[32];
    int32_t iKeySize;
    const uint8_t aInitVec[12];
    const uint8_t aInput[256];
    int32_t iInputSize;
    const uint8_t aData[256];
    int32_t iDataSize;
    const uint8_t aOutput[256];
    int32_t iOutputSize;
    const uint8_t aTagRslt[16];
} CryptTestGcmT;

//! type to encapsulate sha2 test data
typedef struct CryptTestSha2
{
    const char *pString;
    const uint8_t strHashRslt[4][CRYPTHASH_MAXDIGEST];
} CryptTestSha2;

//! type to encapsulate hmac test params
typedef struct CryptTestHmacParam
{
    uint8_t uKeyLen;
    uint8_t uInpLen;
    uint8_t uOutLen;
    const uint8_t strRslt[CRYPTHASH_MAXDIGEST];
} CryptTestHmacParam;

//! type to encapsulate hmac test data
typedef struct CryptTestHmac
{
    const char *pKey;
    const char *pString;
    const CryptTestHmacParam Param[CRYPTHASH_NUMHASHES];
} CryptTestHmac;

typedef struct CryptTestDsaCaseT
{
    const uint8_t aMessage[16];
    CryptHashTypeE eHashType;
    const char strCaseName[16];
    const uint8_t aSignature[97];
} CryptTestDsaCaseT;

//! type to encapsulate dsa test data
typedef struct CryptTestDsaT
{
    CryptCurveE eCurveType;
    int32_t iCurveSize;
    const char strTestName[16];
    const uint8_t aPrivateKey[48];
    const uint8_t aPublicKey[97];
    const CryptTestDsaCaseT aCases[10];
} CryptTestDsaT;

/*** Variables ********************************************************************/

//! gcm test data from the FIPS standard
static const CryptTestGcmT _Crypt_GcmTest[] =
{
    // Test cases 0-3 use 128 bit keys

    // Test Case 0: validate tag result with zero key and no data
    {
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // key
        16,                                                                                     // key size
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 },                      // iv
        { 0 }, 0,                                                                               // input, input size
        { 0 }, 0,                                                                               // data, data size
        { 0 }, 0,                                                                               // output, output size
        { 0x58,0xe2,0xfc,0xce, 0xfa,0x7e,0x30,0x61, 0x36,0x7f,0x1d,0x57, 0xa4,0xe7,0x45,0x5a }  // tag result
    },
    // Test Case 1: validate encryption and tag result with zero key and zero data
    {
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // key
        16,                                                                                     // key size
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 },                      // iv
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // input
        16,                                                                                     // input size
        { 0 }, 0,                                                                               // data, data size
        { 0x03,0x88,0xda,0xce, 0x60,0xb6,0xa3,0x92, 0xf3,0x28,0xc2,0xb9, 0x71,0xb2,0xfe,0x78 }, // output
        16,                                                                                     // output size
        { 0xab,0x6e,0x47,0xd4, 0x2c,0xec,0x13,0xbd, 0xf5,0x3a,0x67,0xb2, 0x12,0x57,0xbd,0xdf }  // tag result
    },
    // Test Case 2: validate encryption and tag result with non-zero key and data
    {
        { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 }, // key
        16,                                                                                     // key size
        { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88  },                     // iv
        {
            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, 0x1a,0xaf,0xd2,0x55
        }, 64,                                                                                  // input, input size
        { 0 }, 0,                                                                               // data, data size
        {
            0x42,0x83,0x1e,0xc2, 0x21,0x77,0x74,0x24, 0x4b,0x72,0x21,0xb7, 0x84,0xd0,0xd4,0x9c,
            0xe3,0xaa,0x21,0x2f, 0x2c,0x02,0xa4,0xe0, 0x35,0xc1,0x7e,0x23, 0x29,0xac,0xa1,0x2e,
            0x21,0xd5,0x14,0xb2, 0x54,0x66,0x93,0x1c, 0x7d,0x8f,0x6a,0x5a, 0xac,0x84,0xaa,0x05,
            0x1b,0xa3,0x0b,0x39, 0x6a,0x0a,0xac,0x97, 0x3d,0x58,0xe0,0x91, 0x47,0x3f,0x59,0x85
        }, 64,                                                                                  // output, output size
        { 0x4d,0x5c,0x2a,0xf3, 0x27,0xcd,0x64,0xa6, 0x2c,0xf3,0x5a,0xbd, 0x2b,0xa6,0xfa,0xb4 }  // tag result
    },
    // Test Case 3: validate encryption and tag result with non-zero key, un-aligned data, and additional data
    {
        { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 }, // key
        16,                                                                                     // key size
        { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88  },                     // iv
        {
            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
        }, 60,                                                                                  // input, input size
        {
            0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef, 0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef,
            0xab,0xad,0xda,0xd2
        }, 20,                                                                                  // data, data size
        {                                                                                       // output
            0x42,0x83,0x1e,0xc2, 0x21,0x77,0x74,0x24, 0x4b,0x72,0x21,0xb7, 0x84,0xd0,0xd4,0x9c,
            0xe3,0xaa,0x21,0x2f, 0x2c,0x02,0xa4,0xe0, 0x35,0xc1,0x7e,0x23, 0x29,0xac,0xa1,0x2e,
            0x21,0xd5,0x14,0xb2, 0x54,0x66,0x93,0x1c, 0x7d,0x8f,0x6a,0x5a, 0xac,0x84,0xaa,0x05,
            0x1b,0xa3,0x0b,0x39, 0x6a,0x0a,0xac,0x97, 0x3d,0x58,0xe0,0x91
        },
        60,                                                                                     // output size
        { 0x5b,0xc9,0x4f,0xbc, 0x32,0x21,0xa5,0xdb, 0x94,0xfa,0xe9,0x5a, 0xe7,0x12,0x1a,0x47 }  // tag result
    },

    // Test cases 4-7 use 256 bit keys

    // Test Case 4: validate tag result with zero key and no data
    {
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   // key
          0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 },
        32,                                                                                     // key size
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 },                      // iv
        { 0 }, 0,                                                                               // input, input size
        { 0 }, 0,                                                                               // data, data size
        { 0 }, 0,                                                                               // output, output size
        { 0x53,0x0f,0x8a,0xfb, 0xc7,0x45,0x36,0xb9, 0xa9,0x63,0xb4,0xf1, 0xc4,0xcb,0x73,0x8b }  // tag result
    },
    // Test Case 5: validate encryption and tag result with zero key and zero data
    {
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,   // key
          0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 },
        32,                                                                                     // key size
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 },                      // iv
        { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // input
        16,                                                                                     // input size
        { 0 }, 0,                                                                               // data, data size
        { 0xce,0xa7,0x40,0x3d, 0x4d,0x60,0x6b,0x6e, 0x07,0x4e,0xc5,0xd3, 0xba,0xf3,0x9d,0x18 }, // output
        16,                                                                                     // output size
        { 0xd0,0xd1,0xc8,0xa7, 0x99,0x99,0x6b,0xf0, 0x26,0x5b,0x98,0xb5, 0xd4,0x8a,0xb9,0x19 }  // tag result
    },
    // Test Case 6: validate encryption and tag result with non-zero key and data
    {
        { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08,   // key
          0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 },
        32,                                                                                     // key size
        { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88  },                     // iv
        {
            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, 0x1a,0xaf,0xd2,0x55
        }, 64,                                                                                  // input, input size
        { 0 }, 0,                                                                               // data, data size
        {
            0x52,0x2d,0xc1,0xf0, 0x99,0x56,0x7d,0x07, 0xf4,0x7f,0x37,0xa3, 0x2a,0x84,0x42,0x7d,
            0x64,0x3a,0x8c,0xdc, 0xbf,0xe5,0xc0,0xc9, 0x75,0x98,0xa2,0xbd, 0x25,0x55,0xd1,0xaa,
            0x8c,0xb0,0x8e,0x48, 0x59,0x0d,0xbb,0x3d, 0xa7,0xb0,0x8b,0x10, 0x56,0x82,0x88,0x38,
            0xc5,0xf6,0x1e,0x63, 0x93,0xba,0x7a,0x0a, 0xbc,0xc9,0xf6,0x62, 0x89,0x80,0x15,0xad,
        }, 64,                                                                                  // output, output size
        { 0xb0,0x94,0xda,0xc5, 0xd9,0x34,0x71,0xbd, 0xec,0x1a,0x50,0x22, 0x70,0xe3,0xcc,0x6c }  // tag result
    },
    // Test Case 3: validate encryption and tag result with non-zero key, un-aligned data, and additional data
    {
        { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08,   // key
          0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 },
        32,                                                                                     // key size
        { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88  },                     // iv
        {
            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
        }, 60,                                                                                  // input, input size
        {
            0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef, 0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef,
            0xab,0xad,0xda,0xd2
        }, 20,                                                                                  // data, data size
        {                                                                                       // output
            0x52,0x2d,0xc1,0xf0, 0x99,0x56,0x7d,0x07, 0xf4,0x7f,0x37,0xa3, 0x2a,0x84,0x42,0x7d,
            0x64,0x3a,0x8c,0xdc, 0xbf,0xe5,0xc0,0xc9, 0x75,0x98,0xa2,0xbd, 0x25,0x55,0xd1,0xaa,
            0x8c,0xb0,0x8e,0x48, 0x59,0x0d,0xbb,0x3d, 0xa7,0xb0,0x8b,0x10, 0x56,0x82,0x88,0x38,
            0xc5,0xf6,0x1e,0x63, 0x93,0xba,0x7a,0x0a, 0xbc,0xc9,0xf6,0x62,
        },
        60,                                                                                     // output size
        { 0x76,0xfc,0x6e,0xce, 0x0f,0x4e,0x17,0x68, 0xcd,0xdf,0x88,0x53, 0xbb,0x2d,0x55,0x1b }  // tag result
    },
};

//! sha2 test data
static const CryptTestSha2 _Crypt_Sha2Test[] =
{
    {
        "",
        {
            {   // sha224
                0xd1,0x4a,0x02,0x8c, 0x2a,0x3a,0x2b,0xc9, 0x47,0x61,0x02,0xbb, 0x28,0x82,0x34,0xc4,
                0x15,0xa2,0xb0,0x1f, 0x82,0x8e,0xa6,0x2a, 0xc5,0xb3,0xe4,0x2f
            },
            {   // sha256
                0xe3,0xb0,0xc4,0x42, 0x98,0xfc,0x1c,0x14, 0x9a,0xfb,0xf4,0xc8, 0x99,0x6f,0xb9,0x24,
                0x27,0xae,0x41,0xe4, 0x64,0x9b,0x93,0x4c, 0xa4,0x95,0x99,0x1b, 0x78,0x52,0xb8,0x55
            },
            {   // sha384
                0x38,0xb0,0x60,0xa7, 0x51,0xac,0x96,0x38, 0x4c,0xd9,0x32,0x7e, 0xb1,0xb1,0xe3,0x6a,
                0x21,0xfd,0xb7,0x11, 0x14,0xbe,0x07,0x43, 0x4c,0x0c,0xc7,0xbf, 0x63,0xf6,0xe1,0xda,
                0x27,0x4e,0xde,0xbf, 0xe7,0x6f,0x65,0xfb, 0xd5,0x1a,0xd2,0xf1, 0x48,0x98,0xb9,0x5b
            },
            {   // sha512
                0xcf,0x83,0xe1,0x35, 0x7e,0xef,0xb8,0xbd, 0xf1,0x54,0x28,0x50, 0xd6,0x6d,0x80,0x07,
                0xd6,0x20,0xe4,0x05, 0x0b,0x57,0x15,0xdc, 0x83,0xf4,0xa9,0x21, 0xd3,0x6c,0xe9,0xce,
                0x47,0xd0,0xd1,0x3c, 0x5d,0x85,0xf2,0xb0, 0xff,0x83,0x18,0xd2, 0x87,0x7e,0xec,0x2f,
                0x63,0xb9,0x31,0xbd, 0x47,0x41,0x7a,0x81, 0xa5,0x38,0x32,0x7a, 0xf9,0x27,0xda,0x3e
            }
        }
    },
    {
        "The quick brown fox jumps over the lazy dog",
        {
            {   // sha224
                0x73,0x0e,0x10,0x9b, 0xd7,0xa8,0xa3,0x2b, 0x1c,0xb9,0xd9,0xa0, 0x9a,0xa2,0x32,0x5d,
                0x24,0x30,0x58,0x7d, 0xdb,0xc0,0xc3,0x8b, 0xad,0x91,0x15,0x25
            },
            {   // sha256
                0xd7,0xa8,0xfb,0xb3, 0x07,0xd7,0x80,0x94, 0x69,0xca,0x9a,0xbc, 0xb0,0x08,0x2e,0x4f,
                0x8d,0x56,0x51,0xe4, 0x6d,0x3c,0xdb,0x76, 0x2d,0x02,0xd0,0xbf, 0x37,0xc9,0xe5,0x92
            },
            {   // sha384
                0xca,0x73,0x7f,0x10, 0x14,0xa4,0x8f,0x4c, 0x0b,0x6d,0xd4,0x3c, 0xb1,0x77,0xb0,0xaf,
                0xd9,0xe5,0x16,0x93, 0x67,0x54,0x4c,0x49, 0x40,0x11,0xe3,0x31, 0x7d,0xbf,0x9a,0x50,
                0x9c,0xb1,0xe5,0xdc, 0x1e,0x85,0xa9,0x41, 0xbb,0xee,0x3d,0x7f, 0x2a,0xfb,0xc9,0xb1
            },
            {   // sha512
                0x07,0xe5,0x47,0xd9, 0x58,0x6f,0x6a,0x73, 0xf7,0x3f,0xba,0xc0, 0x43,0x5e,0xd7,0x69,
                0x51,0x21,0x8f,0xb7, 0xd0,0xc8,0xd7,0x88, 0xa3,0x09,0xd7,0x85, 0x43,0x6b,0xbb,0x64,
                0x2e,0x93,0xa2,0x52, 0xa9,0x54,0xf2,0x39, 0x12,0x54,0x7d,0x1e, 0x8a,0x3b,0x5e,0xd6,
                0xe1,0xbf,0xd7,0x09, 0x78,0x21,0x23,0x3f, 0xa0,0x53,0x8f,0x3d, 0xb8,0x54,0xfe,0xe6
            }
        }
    },
    {
        "The quick brown fox jumps over the lazy dog.",
        {
            {   // sha224
                0x61,0x9c,0xba,0x8e, 0x8e,0x05,0x82,0x6e, 0x9b,0x8c,0x51,0x9c, 0x0a,0x5c,0x68,0xf4,
                0xfb,0x65,0x3e,0x8a, 0x3d,0x8a,0xa0,0x4b, 0xb2,0xc8,0xcd,0x4c
            },
            {   // sha256
                0xef,0x53,0x7f,0x25, 0xc8,0x95,0xbf,0xa7, 0x82,0x52,0x65,0x29, 0xa9,0xb6,0x3d,0x97,
                0xaa,0x63,0x15,0x64, 0xd5,0xd7,0x89,0xc2, 0xb7,0x65,0x44,0x8c, 0x86,0x35,0xfb,0x6c
            },
            {   // sha384
                0xed,0x89,0x24,0x81, 0xd8,0x27,0x2c,0xa6, 0xdf,0x37,0x0b,0xf7, 0x06,0xe4,0xd7,0xbc,
                0x1b,0x57,0x39,0xfa, 0x21,0x77,0xaa,0xe6, 0xc5,0x0e,0x94,0x66, 0x78,0x71,0x8f,0xc6,
                0x7a,0x7a,0xf2,0x81, 0x9a,0x02,0x1c,0x2f, 0xc3,0x4e,0x91,0xbd, 0xb6,0x34,0x09,0xd7
            },
            {   // sha512
                0x91,0xea,0x12,0x45, 0xf2,0x0d,0x46,0xae, 0x9a,0x03,0x7a,0x98, 0x9f,0x54,0xf1,0xf7,
                0x90,0xf0,0xa4,0x76, 0x07,0xee,0xb8,0xa1, 0x4d,0x12,0x89,0x0c, 0xea,0x77,0xa1,0xbb,
                0xc6,0xc7,0xed,0x9c, 0xf2,0x05,0xe6,0x7b, 0x7f,0x2b,0x8f,0xd4, 0xc7,0xdf,0xd3,0xa7,
                0xa8,0x61,0x7e,0x45, 0xf3,0xc4,0x63,0xd4, 0x81,0xc7,0xe5,0x86, 0xc3,0x9a,0xc1,0xed
            }
        }
    },
    // test to validate sha384/sha512 128-bit length pad is correct
    {
        "The quick brown fox jumps over the lazy dog...\r\n"
        "The quick brown fox jumps over the lazy dog...\r\n"
        "The quick brown fox j\r\n",
        {
            {   // sha224
                0x6b,0x7c,0x3d,0xfb, 0xe4,0x3d,0x5c,0x0a, 0x9d,0xe5,0xa0,0x9e, 0x2c,0x90,0xc1,0x62,
                0xa5,0x1c,0xb3,0x32, 0x99,0x9e,0x88,0xce, 0xdb,0x5f,0xbc,0x57
            },
            {   // sha256
                0x0d,0x4c,0xaf,0x0a, 0x31,0x1c,0xb6,0x26, 0xdf,0x91,0x02,0xc2, 0x01,0xc1,0x8a,0x12,
                0x8a,0x0b,0x4b,0x4d, 0x1d,0xfa,0xa3,0xa9, 0xd9,0x43,0xc6,0x47, 0xd3,0x92,0xb6,0x54
            },
            {   // sha384
                0xed,0xae,0xd3,0xa8, 0xc6,0x3b,0x44,0x63, 0xb9,0xd6,0x34,0x8b, 0x86,0xda,0x9f,0xaf,
                0x69,0xe9,0x91,0x48, 0xe6,0xef,0xfc,0x71, 0x8a,0xf9,0x64,0x1e, 0x46,0x69,0x05,0x97,
                0x32,0xb8,0x0f,0xc8, 0x0b,0x84,0x39,0x7f, 0x58,0x3e,0xc3,0x50, 0x2b,0xa4,0x91,0xd8
            },
            {   // sha512
                0xb6,0x39,0x3a,0x1a, 0x27,0x4d,0x62,0xe3, 0x6c,0x9b,0xbf,0x83, 0xc4,0x39,0x68,0xcd,
                0x7b,0xaa,0x36,0x04, 0xb7,0x05,0xea,0x28, 0xd8,0x43,0x23,0x20, 0xe1,0xf5,0x29,0x6b,
                0xc1,0x93,0x6c,0xcb, 0x08,0x4e,0xad,0xfe, 0xd8,0xc9,0x38,0xd2, 0x91,0x9c,0x47,0xbd,
                0x04,0xdc,0xa1,0x3c, 0x66,0x7b,0xaf,0x47, 0xbd,0xc7,0xa4,0xfd, 0xa8,0xa2,0xc0,0xde
            }
        }
    },
    {
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567"
        "0123456701234567012345670123456701234567012345670123456701234567",
        {
            {   // sha224
                0x56,0x7F,0x69,0xF1, 0x68,0xCD,0x78,0x44, 0xE6,0x52,0x59,0xCE, 0x65,0x8F,0xE7,0xAA,
                0xDF,0xA2,0x52,0x16, 0xE6,0x8E,0xCA,0x0E, 0xB7,0xAB,0x82,0x62
            },
            {   // sha256
                0x59,0x48,0x47,0x32, 0x84,0x51,0xBD,0xFA, 0x85,0x05,0x62,0x25, 0x46,0x2C,0xC1,0xD8,
                0x67,0xD8,0x77,0xFB, 0x38,0x8D,0xF0,0xCE, 0x35,0xF2,0x5A,0xB5, 0x56,0x2B,0xFB,0xB5
            },
            {   // sha384
                0x2F,0xC6,0x4A,0x4F, 0x50,0x0D,0xDB,0x68, 0x28,0xF6,0xA3,0x43, 0x0B,0x8D,0xD7,0x2A,
                0x36,0x8E,0xB7,0xF3, 0xA8,0x32,0x2A,0x70, 0xBC,0x84,0x27,0x5B, 0x9C,0x0B,0x3A,0xB0,
                0x0D,0x27,0xA5,0xCC, 0x3C,0x2D,0x22,0x4A, 0xA6,0xB6,0x1A,0x0D, 0x79,0xFB,0x45,0x96
            },
            {   // sha512
                0x89,0xD0,0x5B,0xA6, 0x32,0xC6,0x99,0xC3, 0x12,0x31,0xDE,0xD4, 0xFF,0xC1,0x27,0xD5,
                0xA8,0x94,0xDA,0xD4, 0x12,0xC0,0xE0,0x24, 0xDB,0x87,0x2D,0x1A, 0xBD,0x2B,0xA8,0x14,
                0x1A,0x0F,0x85,0x07, 0x2A,0x9B,0xE1,0xE2, 0xAA,0x04,0xCF,0x33, 0xC7,0x65,0xCB,0x51,
                0x08,0x13,0xA3,0x9C, 0xD5,0xA8,0x4C,0x4A, 0xCA,0xA6,0x4D,0x3F, 0x3F,0xB7,0xBA,0xE9
            }
        }
    }
};

//! HMAC test data from https://tools.ietf.org/html/rfc2202 (md5, sha1) and https://tools.ietf.org/html/rfc4231#section-4.2 (sha2), respectively
static const CryptTestHmac _Crypt_HmacTest[] =
{
    // test case 1
    {
        "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
        "Hi There",
        {
            {   // murmurhash3
                16, 8, 16, { 0x0C,0x55,0x91,0xD1, 0x01,0x73,0x5B,0x63, 0x43,0x0F,0x65,0x26, 0x9E,0x80,0x7A,0x63 }
            },
            {   // md5
                16, 8, 16, { 0x92,0x94,0x72,0x7a, 0x36,0x38,0xbb,0x1c, 0x13,0xf4,0x8e,0xf8, 0x15,0x8b,0xfc,0x9d }
            },
            {   // sha1
                20, 8, 20, { 0xB6,0x17,0x31,0x86, 0x55,0x05,0x72,0x64, 0xE2,0x8B,0xC0,0xB6, 0xFB,0x37,0x8C,0x8E,
                             0xF1,0x46,0xBE,0x00 }
            },
            {   // sha224
                20, 8, 28, { 0x89,0x6F,0xB1,0x12, 0x8A,0xBB,0xDF,0x19, 0x68,0x32,0x10,0x7C, 0xD4,0x9D,0xF3,0x3F,
                             0x47,0xB4,0xB1,0x16, 0x99,0x12,0xBA,0x4F, 0x53,0x68,0x4B,0x22 }
            },
            {   // sha256
                20, 8, 32, { 0xB0,0x34,0x4C,0x61, 0xD8,0xDB,0x38,0x53, 0x5C,0xA8,0xAF,0xCE, 0xAF,0x0B,0xF1,0x2B,
                             0x88,0x1D,0xC2,0x00, 0xC9,0x83,0x3D,0xA7, 0x26,0xE9,0x37,0x6C, 0x2E,0x32,0xCF,0xF7 }
            },
            {   // sha384
                20, 8, 48, { 0xAF,0xD0,0x39,0x44, 0xD8,0x48,0x95,0x62, 0x6B,0x08,0x25,0xF4, 0xAB,0x46,0x90,0x7F,
                             0x15,0xF9,0xDA,0xDB, 0xE4,0x10,0x1E,0xC6, 0x82,0xAA,0x03,0x4C, 0x7C,0xEB,0xC5,0x9C,
                             0xFA,0xEA,0x9E,0xA9, 0x07,0x6E,0xDE,0x7F, 0x4A,0xF1,0x52,0xE8, 0xB2,0xFA,0x9C,0xB6 }
            },
            {   // sha512
                20, 8, 64, { 0x87,0xAA,0x7C,0xDE, 0xA5,0xEF,0x61,0x9D, 0x4F,0xF0,0xB4,0x24, 0x1A,0x1D,0x6C,0xB0,
                             0x23,0x79,0xF4,0xE2, 0xCE,0x4E,0xC2,0x78, 0x7A,0xD0,0xB3,0x05, 0x45,0xE1,0x7C,0xDE,
                             0xDA,0xA8,0x33,0xB7, 0xD6,0xB8,0xA7,0x02, 0x03,0x8B,0x27,0x4E, 0xAE,0xA3,0xF4,0xE4,
                             0xBE,0x9D,0x91,0x4E, 0xEB,0x61,0xF1,0x70, 0x2E,0x69,0x6C,0x20, 0x3A,0x12,0x68,0x54 }
            },
        },
    },
    // test case 2
    {
        "Jefe",
        "what do ya want for nothing?",
        {
            {   // murmurhash3
                4, 28, 16, { 0xD9,0x8B,0x6A,0x6D, 0x61,0x65,0xCB,0xEF, 0x42,0xA4,0x66,0x71, 0x5B,0xD1,0xFE,0xEF }
            },
            {   // md5
                4, 28, 16, { 0x75,0x0C,0x78,0x3E, 0x6A,0xB0,0xB5,0x03, 0xEA,0xA8,0x6E,0x31, 0x0A,0x5D,0xB7,0x38 }
            },
            {   // sha1
                4, 28, 20, { 0xEF,0xFC,0xDF,0x6A, 0xE5,0xEB,0x2F,0xA2, 0xD2,0x74,0x16,0xD5, 0xF1,0x84,0xDF,0x9C,
                             0x25,0x9A,0x7C,0x79 }
            },
            {   // sha224
                4, 28, 28, { 0xA3,0x0E,0x01,0x09, 0x8B,0xC6,0xDB,0xBF, 0x45,0x69,0x0F,0x3A, 0x7E,0x9E,0x6D,0x0F,
                             0x8B,0xBE,0xA2,0xA3, 0x9E,0x61,0x48,0x00, 0x8F,0xD0,0x5E,0x44 }
            },
            {   // sha256
                4, 28, 32, { 0x5B,0xDC,0xC1,0x46, 0xBF,0x60,0x75,0x4E, 0x6A,0x04,0x24,0x26, 0x08,0x95,0x75,0xC7,
                             0x5A,0x00,0x3F,0x08, 0x9D,0x27,0x39,0x83, 0x9D,0xEC,0x58,0xB9, 0x64,0xEC,0x38,0x43 }
            },
            {   // sha384
                4, 28, 48, { 0xAF,0x45,0xD2,0xE3, 0x76,0x48,0x40,0x31, 0x61,0x7F,0x78,0xD2, 0xB5,0x8A,0x6B,0x1B,
                             0x9C,0x7E,0xF4,0x64, 0xF5,0xA0,0x1B,0x47, 0xE4,0x2E,0xC3,0x73, 0x63,0x22,0x44,0x5E,
                             0x8E,0x22,0x40,0xCA, 0x5E,0x69,0xE2,0xC7, 0x8B,0x32,0x39,0xEC, 0xFA,0xB2,0x16,0x49 }
            },
            {   // sha512
                4, 28, 64, { 0x16,0x4B,0x7A,0x7B, 0xFC,0xF8,0x19,0xE2, 0xE3,0x95,0xFB,0xE7, 0x3B,0x56,0xE0,0xA3,
                             0x87,0xBD,0x64,0x22, 0x2E,0x83,0x1F,0xD6, 0x10,0x27,0x0C,0xD7, 0xEA,0x25,0x05,0x54,
                             0x97,0x58,0xBF,0x75, 0xC0,0x5A,0x99,0x4A, 0x6D,0x03,0x4F,0x65, 0xF8,0xF0,0xE6,0xFD,
                             0xCA,0xEA,0xB1,0xA3, 0x4D,0x4A,0x6B,0x4B, 0x63,0x6E,0x07,0x0A, 0x38,0xBC,0xE7,0x37 }
            }
        }
    },
    // test case 3
    {
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa",
        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
        "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
        "\xdd\xdd",
        {
            { 0, 0, 0, { 0 } }, // murmurhash3
            {      // md5
                16, 50, 16, { 0x56,0xbe,0x34,0x52, 0x1d,0x14,0x4c,0x88, 0xdb,0xb8,0xc7,0x33, 0xf0,0xe8,0xb3,0xf6 }
            },
            {      // sha1
                20, 50, 20, { 0x12,0x5d,0x73,0x42, 0xb9,0xac,0x11,0xcd, 0x91,0xa3,0x9a,0xf4, 0x8a,0xa1,0x7b,0x4f,
                              0x63,0xf1,0x75,0xd3 }
            },
            {      // sha224
                20, 50, 20, { 0x7f,0xb3,0xcb,0x35, 0x88,0xc6,0xc1,0xf6, 0xff,0xa9,0x69,0x4d, 0x7d,0x6a,0xd2,0x64,
                              0x93,0x65,0xb0,0xc1, 0xf6,0x5d,0x69,0xd1, 0xec,0x83,0x33,0xea }
            },
            {  // sha256
                20, 50, 20, { 0x77,0x3e,0xa9,0x1e, 0x36,0x80,0x0e,0x46, 0x85,0x4d,0xb8,0xeb, 0xd0,0x91,0x81,0xa7,
                              0x29,0x59,0x09,0x8b, 0x3e,0xf8,0xc1,0x22, 0xd9,0x63,0x55,0x14, 0xce,0xd5,0x65,0xfe }
            },
            { // sha256
                20, 50, 20, { 0x88,0x06,0x26,0x08, 0xd3,0xe6,0xad,0x8a, 0x0a,0xa2,0xac,0xe0, 0x14,0xc8,0xa8,0x6f,
                              0x0a,0xa6,0x35,0xd9, 0x47,0xac,0x9f,0xeb, 0xe8,0x3e,0xf4,0xe5, 0x59,0x66,0x14,0x4b,
                              0x2a,0x5a,0xb3,0x9d, 0xc1,0x38,0x14,0xb9, 0x4e,0x3a,0xb6,0xe1, 0x01,0xa3,0x4f,0x27 }
            },
            { // sha512
                20, 50, 20, { 0xfa,0x73,0xb0,0x08, 0x9d,0x56,0xa2,0x84, 0xef,0xb0,0xf0,0x75, 0x6c,0x89,0x0b,0xe9,
                              0xb1,0xb5,0xdb,0xdd, 0x8e,0xe8,0x1a,0x36, 0x55,0xf8,0x3e,0x33, 0xb2,0x27,0x9d,0x39,
                              0xbf,0x3e,0x84,0x82, 0x79,0xa7,0x22,0xc8, 0x06,0xb4,0x85,0xa4, 0x7e,0x67,0xc8,0x07,
                              0xb9,0x46,0xa3,0x37, 0xbe,0xe8,0x94,0x26, 0x74,0x27,0x88,0x59, 0xe1,0x32,0x92,0xfb }
            }
        }
    },
    // test case 4
    {
        "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
        "\x11\x12\x13\x14\x15\x16\x17\x18\x19",
        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
        "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
        "\xcd\xcd",
        {
            { 0, 0, 0, { 0 } }, // murmurhash3
            {      // md5
                25, 50, 16, { 0x69,0x7e,0xaf,0x0a, 0xca,0x3a,0x3a,0xea, 0x3a,0x75,0x16,0x47, 0x46,0xff,0xaa,0x79 }
            },
            {      // sha1
                25, 50, 20, { 0x4c,0x90,0x07,0xf4, 0x02,0x62,0x50,0xc6, 0xbc,0x84,0x14,0xf9, 0xbf,0x50,0xc8,0x6c,
                              0x2d,0x72,0x35,0xda }
            },
            {      // sha224
                25, 50, 28, { 0x6c,0x11,0x50,0x68, 0x74,0x01,0x3c,0xac, 0x6a,0x2a,0xbc,0x1b, 0xb3,0x82,0x62,0x7c,
                              0xec,0x6a,0x90,0xd8, 0x6e,0xfc,0x01,0x2d, 0xe7,0xaf,0xec,0x5a }
            },
            {      // sha256
                25, 50, 32, { 0x82,0x55,0x8a,0x38, 0x9a,0x44,0x3c,0x0e, 0xa4,0xcc,0x81,0x98, 0x99,0xf2,0x08,0x3a,
                              0x85,0xf0,0xfa,0xa3, 0xe5,0x78,0xf8,0x07, 0x7a,0x2e,0x3f,0xf4, 0x67,0x29,0x66,0x5b }
            },
            {      // sha384
                25, 50, 48, { 0x3e,0x8a,0x69,0xb7, 0x78,0x3c,0x25,0x85, 0x19,0x33,0xab,0x62, 0x90,0xaf,0x6c,0xa7,
                              0x7a,0x99,0x81,0x48, 0x08,0x50,0x00,0x9c, 0xc5,0x57,0x7c,0x6e, 0x1f,0x57,0x3b,0x4e,
                              0x68,0x01,0xdd,0x23, 0xc4,0xa7,0xd6,0x79, 0xcc,0xf8,0xa3,0x86, 0xc6,0x74,0xcf,0xfb }
            },
            {       // sha512
                25, 50, 64, { 0xb0,0xba,0x46,0x56, 0x37,0x45,0x8c,0x69, 0x90,0xe5,0xa8,0xc5, 0xf6,0x1d,0x4a,0xf7,
                              0xe5,0x76,0xd9,0x7f, 0xf9,0x4b,0x87,0x2d, 0xe7,0x6f,0x80,0x50, 0x36,0x1e,0xe3,0xdb,
                              0xa9,0x1c,0xa5,0xc1, 0x1a,0xa2,0x5e,0xb4, 0xd6,0x79,0x27,0x5c, 0xc5,0x78,0x80,0x63,
                              0xa5,0xf1,0x97,0x41, 0x12,0x0c,0x4f,0x2d, 0xe2,0xad,0xeb,0xeb, 0x10,0xa2,0x98,0xdd }
            }
        }
    },
    // test case 5
    {
        "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
        "Test With Truncation",
        {
            { 0, 0, 0, { 0 } }, // murmurhash3
            {      // md5
                16, 20, 12, { 0x56,0x46,0x1e,0xf2, 0x34,0x2e,0xdc,0x00, 0xf9,0xba,0xb9,0x95, 0x69,0x0e,0xfd,0x4c }
            },
            {      // sha1
                20, 20, 16, { 0x4c,0x1a,0x03,0x42, 0x4b,0x55,0xe0,0x7f, 0xe7,0xf2,0x7b,0xe1, 0xd5,0x8b,0xb9,0x32 }
            },
            {      // sha224
                20, 20, 16, { 0x0e,0x2a,0xea,0x68, 0xa9,0x0c,0x8d,0x37, 0xc9,0x88,0xbc,0xdb, 0x9f,0xca,0x6f,0xa8 }
            },
            {      // sha256
                20, 20, 16, { 0xa3,0xb6,0x16,0x74, 0x73,0x10,0x0e,0xe0, 0x6e,0x0c,0x79,0x6c, 0x29,0x55,0x55,0x2b }
            },
            {      // sha384
                20, 20, 16, { 0x3a,0xbf,0x34,0xc3, 0x50,0x3b,0x2a,0x23, 0xa4,0x6e,0xfc,0x61, 0x9b,0xae,0xf8,0x97 }
            },
            {       // sha512
                20, 20, 16, { 0x41,0x5f,0xad,0x62, 0x71,0x58,0x0a,0x53, 0x1d,0x41,0x79,0xbc, 0x89,0x1d,0x87,0xa6 }
            }
        }
    },
    // test case 6
    {
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa",
        "Test Using Larger Than Block-Size Key - Hash Key First",
        {
            { 0, 0, 0, { 0 } }, // murmurhash3
            {    // md5
                 80, 54, 16, { 0x6b,0x1a,0xb7,0xfe, 0x4b,0xd7,0xbf,0x8f, 0x0b,0x62,0xe6,0xce, 0x61,0xb9,0xd0,0xcd }
            },
            {    // sha1
                 80, 54, 20, { 0xaa,0x4a,0xe5,0xe1, 0x52,0x72,0xd0,0x0e, 0x95,0x70,0x56,0x37, 0xce,0x8a,0x3b,0x55,
                               0xed,0x40,0x21,0x12 }
            },
            {   // sha224
                131, 54, 28, { 0x95,0xe9,0xa0,0xdb, 0x96,0x20,0x95,0xad, 0xae,0xbe,0x9b,0x2d, 0x6f,0x0d,0xbc,0xe2,
                               0xd4,0x99,0xf1,0x12, 0xf2,0xd2,0xb7,0x27, 0x3f,0xa6,0x87,0x0e }
            },
            {   // sha256
                131, 54, 32, { 0x60,0xe4,0x31,0x59, 0x1e,0xe0,0xb6,0x7f, 0x0d,0x8a,0x26,0xaa, 0xcb,0xf5,0xb7,0x7f,
                               0x8e,0x0b,0xc6,0x21, 0x37,0x28,0xc5,0x14, 0x05,0x46,0x04,0x0f, 0x0e,0xe3,0x7f,0x54 }
            },
            {   // sha384
                131, 54, 48, { 0x4e,0xce,0x08,0x44, 0x85,0x81,0x3e,0x90, 0x88,0xd2,0xc6,0x3a, 0x04,0x1b,0xc5,0xb4,
                               0x4f,0x9e,0xf1,0x01, 0x2a,0x2b,0x58,0x8f, 0x3c,0xd1,0x1f,0x05, 0x03,0x3a,0xc4,0xc6,
                               0x0c,0x2e,0xf6,0xab, 0x40,0x30,0xfe,0x82, 0x96,0x24,0x8d,0xf1, 0x63,0xf4,0x49,0x52 }
            },
            {   // sha512
                131, 54, 64, { 0x80,0xb2,0x42,0x63, 0xc7,0xc1,0xa3,0xeb, 0xb7,0x14,0x93,0xc1, 0xdd,0x7b,0xe8,0xb4,
                               0x9b,0x46,0xd1,0xf4, 0x1b,0x4a,0xee,0xc1, 0x12,0x1b,0x01,0x37, 0x83,0xf8,0xf3,0x52,
                               0x6b,0x56,0xd0,0x37, 0xe0,0x5f,0x25,0x98, 0xbd,0x0f,0xd2,0x21, 0x5d,0x6a,0x1e,0x52,
                               0x95,0xe6,0x4f,0x73, 0xf6,0x3f,0x0a,0xec, 0x8b,0x91,0x5a,0x98, 0x5d,0x78,0x65,0x98 }
            }
        },
    },
    // test case 7 for md5/sha1
    {
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
        "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
        {
            { 0, 0, 0, { 0 } }, // murmurhash3
            {      // md5
                80, 73, 16, { 0x6f,0x63,0x0f,0xad, 0x67,0xcd,0xa0,0xee, 0x1f,0xb1,0xf5,0x62, 0xdb,0x3a,0xa5,0x3e }
            },
            {      // sha1
                80, 73, 20, { 0xe8,0xe9,0x9d,0x0f, 0x45,0x23,0x7d,0x78, 0x6d,0x6b,0xba,0xa7, 0x96,0x5c,0x78,0x08,
                              0xbb,0xff,0x1a,0x91 }
            },
            { 0, 0, 0, { 0 } }, // sha224
            { 0, 0, 0, { 0 } }, // sha256
            { 0, 0, 0, { 0 } }, // sha384
            { 0, 0, 0, { 0 } }, // sha512
        }
    },
    // test case 7 for sha2
    {
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
        "\xaa\xaa\xaa",
        "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
        {
            { 0, 0, 0, { 0 } }, // murmurhash3
            { 0, 0, 0, { 0 } }, // md5
            { 0, 0, 0, { 0 } }, // sha1
            {   // sha224
                131, 152, 28, { 0x3a,0x85,0x41,0x66, 0xac,0x5d,0x9f,0x02, 0x3f,0x54,0xd5,0x17, 0xd0,0xb3,0x9d,0xbd,
                                0x94,0x67,0x70,0xdb, 0x9c,0x2b,0x95,0xc9, 0xf6,0xf5,0x65,0xd1 }
            },
            {   // sha256
                131, 152, 28, { 0x9b,0x09,0xff,0xa7, 0x1b,0x94,0x2f,0xcb, 0x27,0x63,0x5f,0xbc, 0xd5,0xb0,0xe9,0x44,
                                0xbf,0xdc,0x63,0x64, 0x4f,0x07,0x13,0x93, 0x8a,0x7f,0x51,0x53, 0x5c,0x3a,0x35,0xe2 }
            },
            {   // sha384
                131, 152, 28, { 0x66,0x17,0x17,0x8e, 0x94,0x1f,0x02,0x0d, 0x35,0x1e,0x2f,0x25, 0x4e,0x8f,0xd3,0x2c,
                                0x60,0x24,0x20,0xfe, 0xb0,0xb8,0xfb,0x9a, 0xdc,0xce,0xbb,0x82, 0x46,0x1e,0x99,0xc5,
                                0xa6,0x78,0xcc,0x31, 0xe7,0x99,0x17,0x6d, 0x38,0x60,0xe6,0x11, 0x0c,0x46,0x52,0x3e }
            },
            {   // sha512
                131, 152, 28, { 0xe3,0x7b,0x6a,0x77, 0x5d,0xc8,0x7d,0xba, 0xa4,0xdf,0xa9,0xf9, 0x6e,0x5e,0x3f,0xfd,
                                0xde,0xbd,0x71,0xf8, 0x86,0x72,0x89,0x86, 0x5d,0xf5,0xa3,0x2d, 0x20,0xcd,0xc9,0x44,
                                0xb6,0x02,0x2c,0xac, 0x3c,0x49,0x82,0xb1, 0x0d,0x5e,0xeb,0x55, 0xc3,0xe4,0xde,0x15,
                                0x13,0x46,0x76,0xfb, 0x6d,0xe0,0x44,0x60, 0x65,0xc9,0x74,0x40, 0xfa,0x8c,0x6a,0x58 }
            }
        }
    }
};

//! test vectors from https://tools.ietf.org/html/rfc6979#appendix-A.2
static const CryptTestDsaT _Crypt_DsaTests[] =
{
    {
        CRYPTCURVE_SECP256R1, 32, "secp256r1",
        { 0xC9, 0xAF, 0xA9, 0xD8, 0x45, 0xBA, 0x75, 0x16, 0x6B, 0x5C, 0x21, 0x57, 0x67, 0xB1, 0xD6, 0x93,
          0x4E, 0x50, 0xC3, 0xDB, 0x36, 0xE8, 0x9B, 0x12, 0x7B, 0x8A, 0x62, 0x2B, 0x12, 0x0F, 0x67, 0x21 },
        { 0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D, 0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D, 0x68,
          0xC0, 0x49, 0xB8, 0x92, 0x3B, 0x61, 0xFA, 0x6C, 0xE6, 0x69, 0x62, 0x2E, 0x60, 0xF2, 0x9F, 0xB6,
          0x79, 0x03, 0xFE, 0x10, 0x08, 0xB8, 0xBC, 0x99, 0xA4, 0x1A, 0xE9, 0xE9, 0x56, 0x28, 0xBC, 0x64,
          0xF2, 0xF1, 0xB2, 0x0C, 0x2D, 0x7E, 0x9F, 0x51, 0x77, 0xA3, 0xC2, 0x94, 0xD4, 0x46, 0x22, 0x99 },
        {
            { "sample", CRYPTHASH_SHA1, "sha1",
                { 0x61, 0x34, 0x0C, 0x88, 0xC3, 0xAA, 0xEB, 0xEB, 0x4F, 0x6D, 0x66, 0x7F, 0x67, 0x2C, 0xA9, 0x75,
                  0x9A, 0x6C, 0xCA, 0xA9, 0xFA, 0x88, 0x11, 0x31, 0x30, 0x39, 0xEE, 0x4A, 0x35, 0x47, 0x1D, 0x32,
                  0x6D, 0x7F, 0x14, 0x7D, 0xAC, 0x08, 0x94, 0x41, 0xBB, 0x2E, 0x2F, 0xE8, 0xF7, 0xA3, 0xFA, 0x26,
                  0x4B, 0x9C, 0x47, 0x50, 0x98, 0xFD, 0xCF, 0x6E, 0x00, 0xD7, 0xC9, 0x96, 0xE1, 0xB8, 0xB7, 0xEB }
            },
            { "sample", CRYPTHASH_SHA224, "sha224",
                { 0x53, 0xB2, 0xFF, 0xF5, 0xD1, 0x75, 0x2B, 0x2C, 0x68, 0x9D, 0xF2, 0x57, 0xC0, 0x4C, 0x40, 0xA5,
                  0x87, 0xFA, 0xBA, 0xBB, 0x3F, 0x6F, 0xC2, 0x70, 0x2F, 0x13, 0x43, 0xAF, 0x7C, 0xA9, 0xAA, 0x3F,
                  0xB9, 0xAF, 0xB6, 0x4F, 0xDC, 0x03, 0xDC, 0x1A, 0x13, 0x1C, 0x7D, 0x23, 0x86, 0xD1, 0x1E, 0x34,
                  0x9F, 0x07, 0x0A, 0xA4, 0x32, 0xA4, 0xAC, 0xC9, 0x18, 0xBE, 0xA9, 0x88, 0xBF, 0x75, 0xC7, 0x4C }
            },
            { "sample", CRYPTHASH_SHA256, "sha256",
                { 0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 0x11, 0x40, 0xDD, 0x9C, 0xD4, 0x5E, 0x81, 0xD6,
                  0x9D, 0x2C, 0x87, 0x7B, 0x56, 0xAA, 0xF9, 0x91, 0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16,
                  0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 0xD4, 0x36, 0xC7, 0xA1, 0xB6, 0xE2, 0x9F, 0x65,
                  0xF3, 0xE9, 0x00, 0xDB, 0xB9, 0xAF, 0xF4, 0x06, 0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8 }
            },
            { "sample", CRYPTHASH_SHA384, "sha384",
                { 0x0E, 0xAF, 0xEA, 0x03, 0x9B, 0x20, 0xE9, 0xB4, 0x23, 0x09, 0xFB, 0x1D, 0x89, 0xE2, 0x13, 0x05,
                  0x7C, 0xBF, 0x97, 0x3D, 0xC0, 0xCF, 0xC8, 0xF1, 0x29, 0xED, 0xDD, 0xC8, 0x00, 0xEF, 0x77, 0x19,
                  0x48, 0x61, 0xF0, 0x49, 0x1E, 0x69, 0x98, 0xB9, 0x45, 0x51, 0x93, 0xE3, 0x4E, 0x7B, 0x0D, 0x28,
                  0x4D, 0xDD, 0x71, 0x49, 0xA7, 0x4B, 0x95, 0xB9, 0x26, 0x1F, 0x13, 0xAB, 0xDE, 0x94, 0x09, 0x54 }
            },
            { "sample", CRYPTHASH_SHA512, "sha512",
                { 0x84, 0x96, 0xA6, 0x0B, 0x5E, 0x9B, 0x47, 0xC8, 0x25, 0x48, 0x88, 0x27, 0xE0, 0x49, 0x5B, 0x0E,
                  0x3F, 0xA1, 0x09, 0xEC, 0x45, 0x68, 0xFD, 0x3F, 0x8D, 0x10, 0x97, 0x67, 0x8E, 0xB9, 0x7F, 0x00,
                  0x23, 0x62, 0xAB, 0x1A, 0xDB, 0xE2, 0xB8, 0xAD, 0xF9, 0xCB, 0x9E, 0xDA, 0xB7, 0x40, 0xEA, 0x60,
                  0x49, 0xC0, 0x28, 0x11, 0x4F, 0x24, 0x60, 0xF9, 0x65, 0x54, 0xF6, 0x1F, 0xAE, 0x33, 0x02, 0xFE }
            },
            { "test", CRYPTHASH_SHA1, "sha1",
                { 0x0C, 0xBC, 0xC8, 0x6F, 0xD6, 0xAB, 0xD1, 0xD9, 0x9E, 0x70, 0x3E, 0x1E, 0xC5, 0x00, 0x69, 0xEE,
                  0x5C, 0x0B, 0x4B, 0xA4, 0xB9, 0xAC, 0x60, 0xE4, 0x09, 0xE8, 0xEC, 0x59, 0x10, 0xD8, 0x1A, 0x89,
                  0x01, 0xB9, 0xD7, 0xB7, 0x3D, 0xFA, 0xA6, 0x0D, 0x56, 0x51, 0xEC, 0x45, 0x91, 0xA0, 0x13, 0x6F,
                  0x87, 0x65, 0x3E, 0x0F, 0xD7, 0x80, 0xC3, 0xB1, 0xBC, 0x87, 0x2F, 0xFD, 0xEA, 0xE4, 0x79, 0xB1 }
            },
            { "test", CRYPTHASH_SHA224, "sha224",
                { 0xC3, 0x7E, 0xDB, 0x6F, 0x0A, 0xE7, 0x9D, 0x47, 0xC3, 0xC2, 0x7E, 0x96, 0x2F, 0xA2, 0x69, 0xBB,
                  0x4F, 0x44, 0x17, 0x70, 0x35, 0x7E, 0x11, 0x4E, 0xE5, 0x11, 0xF6, 0x62, 0xEC, 0x34, 0xA6, 0x92,
                  0xC8, 0x20, 0x05, 0x3A, 0x05, 0x79, 0x1E, 0x52, 0x1F, 0xCA, 0xAD, 0x60, 0x42, 0xD4, 0x0A, 0xEA,
                  0x1D, 0x6B, 0x1A, 0x54, 0x01, 0x38, 0x55, 0x8F, 0x47, 0xD0, 0x71, 0x98, 0x00, 0xE1, 0x8F, 0x2D }
            },
            { "test", CRYPTHASH_SHA256, "sha256",
                { 0xF1, 0xAB, 0xB0, 0x23, 0x51, 0x83, 0x51, 0xCD, 0x71, 0xD8, 0x81, 0x56, 0x7B, 0x1E, 0xA6, 0x63,
                  0xED, 0x3E, 0xFC, 0xF6, 0xC5, 0x13, 0x2B, 0x35, 0x4F, 0x28, 0xD3, 0xB0, 0xB7, 0xD3, 0x83, 0x67,
                  0x01, 0x9F, 0x41, 0x13, 0x74, 0x2A, 0x2B, 0x14, 0xBD, 0x25, 0x92, 0x6B, 0x49, 0xC6, 0x49, 0x15,
                  0x5F, 0x26, 0x7E, 0x60, 0xD3, 0x81, 0x4B, 0x4C, 0x0C, 0xC8, 0x42, 0x50, 0xE4, 0x6F, 0x00, 0x83 }
            },
            { "test", CRYPTHASH_SHA384, "sha384",
                { 0x83, 0x91, 0x0E, 0x8B, 0x48, 0xBB, 0x0C, 0x74, 0x24, 0x4E, 0xBD, 0xF7, 0xF0, 0x7A, 0x1C, 0x54,
                  0x13, 0xD6, 0x14, 0x72, 0xBD, 0x94, 0x1E, 0xF3, 0x92, 0x0E, 0x62, 0x3F, 0xBC, 0xCE, 0xBE, 0xB6,
                  0x8D, 0xDB, 0xEC, 0x54, 0xCF, 0x8C, 0xD5, 0x87, 0x48, 0x83, 0x84, 0x1D, 0x71, 0x21, 0x42, 0xA5,
                  0x6A, 0x8D, 0x0F, 0x21, 0x8F, 0x50, 0x03, 0xCB, 0x02, 0x96, 0xB6, 0xB5, 0x09, 0x61, 0x9F, 0x2C }
            },
            { "test", CRYPTHASH_SHA512, "sha512",
                { 0x46, 0x1D, 0x93, 0xF3, 0x1B, 0x65, 0x40, 0x89, 0x47, 0x88, 0xFD, 0x20, 0x6C, 0x07, 0xCF, 0xA0,
                  0xCC, 0x35, 0xF4, 0x6F, 0xA3, 0xC9, 0x18, 0x16, 0xFF, 0xF1, 0x04, 0x0A, 0xD1, 0x58, 0x1A, 0x04,
                  0x39, 0xAF, 0x9F, 0x15, 0xDE, 0x0D, 0xB8, 0xD9, 0x7E, 0x72, 0x71, 0x9C, 0x74, 0x82, 0x0D, 0x30,
                  0x4C, 0xE5, 0x22, 0x6E, 0x32, 0xDE, 0xDA, 0xE6, 0x75, 0x19, 0xE8, 0x40, 0xD1, 0x19, 0x4E, 0x55 }
            },
        }
    },
    {
        CRYPTCURVE_SECP384R1, 48, "secp384r1",
        { 0x6B, 0x9D, 0x3D, 0xAD, 0x2E, 0x1B, 0x8C, 0x1C, 0x05, 0xB1, 0x98, 0x75, 0xB6, 0x65, 0x9F, 0x4D,
          0xE2, 0x3C, 0x3B, 0x66, 0x7B, 0xF2, 0x97, 0xBA, 0x9A, 0xA4, 0x77, 0x40, 0x78, 0x71, 0x37, 0xD8,
          0x96, 0xD5, 0x72, 0x4E, 0x4C, 0x70, 0xA8, 0x25, 0xF8, 0x72, 0xC9, 0xEA, 0x60, 0xD2, 0xED, 0xF5 },
        { 0x04, 0xEC, 0x3A, 0x4E, 0x41, 0x5B, 0x4E, 0x19, 0xA4, 0x56, 0x86, 0x18, 0x02, 0x9F, 0x42, 0x7F, 0xA5,
          0xDA, 0x9A, 0x8B, 0xC4, 0xAE, 0x92, 0xE0, 0x2E, 0x06, 0xAA, 0xE5, 0x28, 0x6B, 0x30, 0x0C, 0x64,
          0xDE, 0xF8, 0xF0, 0xEA, 0x90, 0x55, 0x86, 0x60, 0x64, 0xA2, 0x54, 0x51, 0x54, 0x80, 0xBC, 0x13,
          0x80, 0x15, 0xD9, 0xB7, 0x2D, 0x7D, 0x57, 0x24, 0x4E, 0xA8, 0xEF, 0x9A, 0xC0, 0xC6, 0x21, 0x89,
          0x67, 0x08, 0xA5, 0x93, 0x67, 0xF9, 0xDF, 0xB9, 0xF5, 0x4C, 0xA8, 0x4B, 0x3F, 0x1C, 0x9D, 0xB1,
          0x28, 0x8B, 0x23, 0x1C, 0x3A, 0xE0, 0xD4, 0xFE, 0x73, 0x44, 0xFD, 0x25, 0x33, 0x26, 0x47, 0x20 },
        {
            { "sample", CRYPTHASH_SHA1, "sha1",
                { 0xEC, 0x74, 0x8D, 0x83, 0x92, 0x43, 0xD6, 0xFB, 0xEF, 0x4F, 0xC5, 0xC4, 0x85, 0x9A, 0x7D, 0xFF,
                  0xD7, 0xF3, 0xAB, 0xDD, 0xF7, 0x20, 0x14, 0x54, 0x0C, 0x16, 0xD7, 0x33, 0x09, 0x83, 0x4F, 0xA3,
                  0x7B, 0x9B, 0xA0, 0x02, 0x89, 0x9F, 0x6F, 0xDA, 0x3A, 0x4A, 0x93, 0x86, 0x79, 0x0D, 0x4E, 0xB2,
                  0xA3, 0xBC, 0xFA, 0x94, 0x7B, 0xEE, 0xF4, 0x73, 0x2B, 0xF2, 0x47, 0xAC, 0x17, 0xF7, 0x16, 0x76,
                  0xCB, 0x31, 0xA8, 0x47, 0xB9, 0xFF, 0x0C, 0xBC, 0x9C, 0x9E, 0xD4, 0xC1, 0xA5, 0xB3, 0xFA, 0xCF,
                  0x26, 0xF4, 0x9C, 0xA0, 0x31, 0xD4, 0x85, 0x75, 0x70, 0xCC, 0xB5, 0xCA, 0x44, 0x24, 0xA4, 0x43 }
            },
            { "sample", CRYPTHASH_SHA224, "sha224",
                { 0x42, 0x35, 0x6E, 0x76, 0xB5, 0x5A, 0x6D, 0x9B, 0x46, 0x31, 0xC8, 0x65, 0x44, 0x5D, 0xBE, 0x54,
                  0xE0, 0x56, 0xD3, 0xB3, 0x43, 0x17, 0x66, 0xD0, 0x50, 0x92, 0x44, 0x79, 0x3C, 0x3F, 0x93, 0x66,
                  0x45, 0x0F, 0x76, 0xEE, 0x3D, 0xE4, 0x3F, 0x5A, 0x12, 0x53, 0x33, 0xA6, 0xBE, 0x06, 0x01, 0x22,
                  0x9D, 0xA0, 0xC8, 0x17, 0x87, 0x06, 0x40, 0x21, 0xE7, 0x8D, 0xF6, 0x58, 0xF2, 0xFB, 0xB0, 0xB0,
                  0x42, 0xBF, 0x30, 0x46, 0x65, 0xDB, 0x72, 0x1F, 0x07, 0x7A, 0x42, 0x98, 0xB0, 0x95, 0xE4, 0x83,
                  0x4C, 0x08, 0x2C, 0x03, 0xD8, 0x30, 0x28, 0xEF, 0xBF, 0x93, 0xA3, 0xC2, 0x39, 0x40, 0xCA, 0x8D }
            },
            { "sample", CRYPTHASH_SHA256, "sha256",
                { 0x21, 0xB1, 0x3D, 0x1E, 0x01, 0x3C, 0x7F, 0xA1, 0x39, 0x2D, 0x03, 0xC5, 0xF9, 0x9A, 0xF8, 0xB3,
                  0x0C, 0x57, 0x0C, 0x6F, 0x98, 0xD4, 0xEA, 0x8E, 0x35, 0x4B, 0x63, 0xA2, 0x1D, 0x3D, 0xAA, 0x33,
                  0xBD, 0xE1, 0xE8, 0x88, 0xE6, 0x33, 0x55, 0xD9, 0x2F, 0xA2, 0xB3, 0xC3, 0x6D, 0x8F, 0xB2, 0xCD,
                  0xF3, 0xAA, 0x44, 0x3F, 0xB1, 0x07, 0x74, 0x5B, 0xF4, 0xBD, 0x77, 0xCB, 0x38, 0x91, 0x67, 0x46,
                  0x32, 0x06, 0x8A, 0x10, 0xCA, 0x67, 0xE3, 0xD4, 0x5D, 0xB2, 0x26, 0x6F, 0xA7, 0xD1, 0xFE, 0xEB,
                  0xEF, 0xDC, 0x63, 0xEC, 0xCD, 0x1A, 0xC4, 0x2E, 0xC0, 0xCB, 0x86, 0x68, 0xA4, 0xFA, 0x0A, 0xB0 }
            },
            { "sample", CRYPTHASH_SHA384, "sha384",
                { 0x94, 0xED, 0xBB, 0x92, 0xA5, 0xEC, 0xB8, 0xAA, 0xD4, 0x73, 0x6E, 0x56, 0xC6, 0x91, 0x91, 0x6B,
                  0x3F, 0x88, 0x14, 0x06, 0x66, 0xCE, 0x9F, 0xA7, 0x3D, 0x64, 0xC4, 0xEA, 0x95, 0xAD, 0x13, 0x3C,
                  0x81, 0xA6, 0x48, 0x15, 0x2E, 0x44, 0xAC, 0xF9, 0x6E, 0x36, 0xDD, 0x1E, 0x80, 0xFA, 0xBE, 0x46,
                  0x99, 0xEF, 0x4A, 0xEB, 0x15, 0xF1, 0x78, 0xCE, 0xA1, 0xFE, 0x40, 0xDB, 0x26, 0x03, 0x13, 0x8F,
                  0x13, 0x0E, 0x74, 0x0A, 0x19, 0x62, 0x45, 0x26, 0x20, 0x3B, 0x63, 0x51, 0xD0, 0xA3, 0xA9, 0x4F,
                  0xA3, 0x29, 0xC1, 0x45, 0x78, 0x6E, 0x67, 0x9E, 0x7B, 0x82, 0xC7, 0x1A, 0x38, 0x62, 0x8A, 0xC8 }
            },
            { "sample", CRYPTHASH_SHA512, "sha512",
                { 0xED, 0x09, 0x59, 0xD5, 0x88, 0x0A, 0xB2, 0xD8, 0x69, 0xAE, 0x7F, 0x6C, 0x29, 0x15, 0xC6, 0xD6,
                  0x0F, 0x96, 0x50, 0x7F, 0x9C, 0xB3, 0xE0, 0x47, 0xC0, 0x04, 0x68, 0x61, 0xDA, 0x4A, 0x79, 0x9C,
                  0xFE, 0x30, 0xF3, 0x5C, 0xC9, 0x00, 0x05, 0x6D, 0x7C, 0x99, 0xCD, 0x78, 0x82, 0x43, 0x37, 0x09,
                  0x51, 0x2C, 0x8C, 0xCE, 0xEE, 0x38, 0x90, 0xA8, 0x40, 0x58, 0xCE, 0x1E, 0x22, 0xDB, 0xC2, 0x19,
                  0x8F, 0x42, 0x32, 0x3C, 0xE8, 0xAC, 0xA9, 0x13, 0x53, 0x29, 0xF0, 0x3C, 0x06, 0x8E, 0x51, 0x12,
                  0xDC, 0x7C, 0xC3, 0xEF, 0x34, 0x46, 0xDE, 0xFC, 0xEB, 0x01, 0xA4, 0x5C, 0x26, 0x67, 0xFD, 0xD5 }
            },
            { "test", CRYPTHASH_SHA1, "sha1",
                { 0x4B, 0xC3, 0x5D, 0x3A, 0x50, 0xEF, 0x4E, 0x30, 0x57, 0x6F, 0x58, 0xCD, 0x96, 0xCE, 0x6B, 0xF6,
                  0x38, 0x02, 0x5E, 0xE6, 0x24, 0x00, 0x4A, 0x1F, 0x77, 0x89, 0xA8, 0xB8, 0xE4, 0x3D, 0x06, 0x78,
                  0xAC, 0xD9, 0xD2, 0x98, 0x76, 0xDA, 0xF4, 0x66, 0x38, 0x64, 0x5F, 0x7F, 0x40, 0x4B, 0x11, 0xC7,
                  0xD5, 0xA6, 0x32, 0x6C, 0x49, 0x4E, 0xD3, 0xFF, 0x61, 0x47, 0x03, 0x87, 0x89, 0x61, 0xC0, 0xFD,
                  0xE7, 0xB2, 0xC2, 0x78, 0xF9, 0xA6, 0x5F, 0xD8, 0xC4, 0xB7, 0x18, 0x62, 0x01, 0xA2, 0x99, 0x16,
                  0x95, 0xBA, 0x1C, 0x84, 0x54, 0x13, 0x27, 0xE9, 0x66, 0xFA, 0x7B, 0x50, 0xF7, 0x38, 0x22, 0x82 }
            },
            { "test", CRYPTHASH_SHA224, "sha224",
                { 0xE8, 0xC9, 0xD0, 0xB6, 0xEA, 0x72, 0xA0, 0xE7, 0x83, 0x7F, 0xEA, 0x1D, 0x14, 0xA1, 0xA9, 0x55,
                  0x7F, 0x29, 0xFA, 0xA4, 0x5D, 0x3E, 0x7E, 0xE8, 0x88, 0xFC, 0x5B, 0xF9, 0x54, 0xB5, 0xE6, 0x24,
                  0x64, 0xA9, 0xA8, 0x17, 0xC4, 0x7F, 0xF7, 0x8B, 0x8C, 0x11, 0x06, 0x6B, 0x24, 0x08, 0x0E, 0x72,
                  0x07, 0x04, 0x1D, 0x4A, 0x7A, 0x03, 0x79, 0xAC, 0x72, 0x32, 0xFF, 0x72, 0xE6, 0xF7, 0x7B, 0x6D,
                  0xDB, 0x8F, 0x09, 0xB1, 0x6C, 0xCE, 0x0E, 0xC3, 0x28, 0x6B, 0x2B, 0xD4, 0x3F, 0xA8, 0xC6, 0x14,
                  0x1C, 0x53, 0xEA, 0x5A, 0xBE, 0xF0, 0xD8, 0x23, 0x10, 0x77, 0xA0, 0x45, 0x40, 0xA9, 0x6B, 0x66 }
            },
            { "test", CRYPTHASH_SHA256, "sha256",
                { 0x6D, 0x6D, 0xEF, 0xAC, 0x9A, 0xB6, 0x4D, 0xAB, 0xAF, 0xE3, 0x6C, 0x6B, 0xF5, 0x10, 0x35, 0x2A,
                  0x4C, 0xC2, 0x70, 0x01, 0x26, 0x36, 0x38, 0xE5, 0xB1, 0x6D, 0x9B, 0xB5, 0x1D, 0x45, 0x15, 0x59,
                  0xF9, 0x18, 0xEE, 0xDA, 0xF2, 0x29, 0x3B, 0xE5, 0xB4, 0x75, 0xCC, 0x8F, 0x01, 0x88, 0x63, 0x6B,
                  0x2D, 0x46, 0xF3, 0xBE, 0xCB, 0xCC, 0x52, 0x3D, 0x5F, 0x1A, 0x12, 0x56, 0xBF, 0x0C, 0x9B, 0x02,
                  0x4D, 0x87, 0x9B, 0xA9, 0xE8, 0x38, 0x14, 0x4C, 0x8B, 0xA6, 0xBA, 0xEB, 0x4B, 0x53, 0xB4, 0x7D,
                  0x51, 0xAB, 0x37, 0x3F, 0x98, 0x45, 0xC0, 0x51, 0x4E, 0xEF, 0xB1, 0x40, 0x24, 0x78, 0x72, 0x65 }
            },
            { "test", CRYPTHASH_SHA384, "sha384",
                { 0x82, 0x03, 0xB6, 0x3D, 0x3C, 0x85, 0x3E, 0x8D, 0x77, 0x22, 0x7F, 0xB3, 0x77, 0xBC, 0xF7, 0xB7,
                  0xB7, 0x72, 0xE9, 0x78, 0x92, 0xA8, 0x0F, 0x36, 0xAB, 0x77, 0x5D, 0x50, 0x9D, 0x7A, 0x5F, 0xEB,
                  0x05, 0x42, 0xA7, 0xF0, 0x81, 0x29, 0x98, 0xDA, 0x8F, 0x1D, 0xD3, 0xCA, 0x3C, 0xF0, 0x23, 0xDB,
                  0xDD, 0xD0, 0x76, 0x04, 0x48, 0xD4, 0x2D, 0x8A, 0x43, 0xAF, 0x45, 0xAF, 0x83, 0x6F, 0xCE, 0x4D,
                  0xE8, 0xBE, 0x06, 0xB4, 0x85, 0xE9, 0xB6, 0x1B, 0x82, 0x7C, 0x2F, 0x13, 0x17, 0x39, 0x23, 0xE0,
                  0x6A, 0x73, 0x9F, 0x04, 0x06, 0x49, 0xA6, 0x67, 0xBF, 0x3B, 0x82, 0x82, 0x46, 0xBA, 0xA5, 0xA5 }
            },
            { "test", CRYPTHASH_SHA512, "sha512",
                { 0xA0, 0xD5, 0xD0, 0x90, 0xC9, 0x98, 0x0F, 0xAF, 0x3C, 0x2C, 0xE5, 0x7B, 0x7A, 0xE9, 0x51, 0xD3,
                  0x19, 0x77, 0xDD, 0x11, 0xC7, 0x75, 0xD3, 0x14, 0xAF, 0x55, 0xF7, 0x6C, 0x67, 0x64, 0x47, 0xD0,
                  0x6F, 0xB6, 0x49, 0x5C, 0xD2, 0x1B, 0x4B, 0x6E, 0x34, 0x0F, 0xC2, 0x36, 0x58, 0x4F, 0xB2, 0x77,
                  0x97, 0x69, 0x84, 0xE5, 0x9B, 0x4C, 0x77, 0xB0, 0xE8, 0xE4, 0x46, 0x0D, 0xCA, 0x3D, 0x9F, 0x20,
                  0xE0, 0x7B, 0x9B, 0xB1, 0xF6, 0x3B, 0xEE, 0xFA, 0xF5, 0x76, 0xF6, 0xB2, 0xE8, 0xB2, 0x24, 0x63,
                  0x4A, 0x20, 0x92, 0xCD, 0x37, 0x92, 0xE0, 0x15, 0x9A, 0xD9, 0xCE, 0xE3, 0x76, 0x59, 0xC7, 0x36 }
            }
        }
    }
};

// buffer for large random number used in timing tests
static uint8_t _Crypt_aLargeRandom[2*1024*1024];
const int32_t  _Crypt_iTimingIter = 16;


/*** Private Functions ************************************************************/


/*F********************************************************************************/
/*!
    \Function _CmdCryptTestArc4

    \Description
        Test the CryptArc4 module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 01/11/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestArc4(ZContext *argz, int32_t argc, char *argv[])
{
    static const char *_strEncrypt[] =
    {
        "Test first string encryption.",
        "Another string to test encrypt.",
        "Strings are fun to encrypt!",
        "This string is exactly 127 characters long, sans null.  It is meant to test the behavior of Encrypt/Decrypt with a full buffer.",
        "This string is more than 127 characters long.  It will safely truncate the result string, which will result in the test failing.",
    };
    char strEncryptBuf[128], strDecryptBuf[128];
    uint8_t aEncryptKey[32];
    int32_t iFailed, iString;

    ZPrintf("%s: testing CryptArc4\n", argv[0]);

    // generate a random encryption key
    CryptRandGet(aEncryptKey, sizeof(aEncryptKey));

    // test string encryption/decryption
    for (iString = 0, iFailed = 0; iString < (signed)(sizeof(_strEncrypt)/sizeof(_strEncrypt[0])); iString += 1)
    {
        CryptArc4StringEncrypt(strEncryptBuf, sizeof(strEncryptBuf), _strEncrypt[iString], aEncryptKey, sizeof(aEncryptKey), 1);
        CryptArc4StringDecrypt(strDecryptBuf, sizeof(strDecryptBuf), strEncryptBuf, aEncryptKey, sizeof(aEncryptKey), 1);
        ZPrintf("%s: '%s'->'%s'\n", argv[0], _strEncrypt[iString], strEncryptBuf);
        if (strcmp(strDecryptBuf, _strEncrypt[iString]))
        {
            ZPrintf("%s: encrypt/decrypt failed; '%s' != '%s'\n", argv[0], _strEncrypt[iString], strDecryptBuf);
            iFailed += 1;
        }
    }

    ZPrintf("%s: ---------------------\n", argv[0]);

    // one test intentionally fails, so subtract that out
    iFailed -= 1;
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestGcm

    \Description
        Test the CryptGcm module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 07/08/2014 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestGcm(ZContext *argz, int32_t argc, char *argv[])
{
    uint8_t aBuffer[1024], aTagRslt[16];
    int32_t iFail, iFailed, iTest;
    const CryptTestGcmT *pTest;
    CryptGcmT Gcm;

    ZPrintf("%s: testing CryptGcm\n", argv[0]);

    for (iTest = 0, iFailed = 0; iTest < (signed)(sizeof(_Crypt_GcmTest)/sizeof(_Crypt_GcmTest[0])); iTest += 1)
    {
        pTest = &_Crypt_GcmTest[iTest];
        iFail = 0;

        // do encryption
        CryptGcmInit(&Gcm, pTest->aKey, pTest->iKeySize);
        ds_memcpy(aBuffer, pTest->aInput, pTest->iInputSize);
        CryptGcmEncrypt(&Gcm, aBuffer, pTest->iInputSize, pTest->aInitVec, sizeof(pTest->aInitVec), pTest->aData, pTest->iDataSize, aTagRslt, sizeof(aTagRslt));

        if (memcmp(pTest->aTagRslt, aTagRslt, sizeof(pTest->aTagRslt)))
        {
            ZPrintf("%s: gcm encrypt test %d failed with invalid tag result\n", argv[0], iTest);
            NetPrintMem(pTest->aTagRslt, sizeof(pTest->aTagRslt), "expected");
            NetPrintMem(aTagRslt, sizeof(pTest->aTagRslt), "actual");
            iFail |= 1;
        }
        if ((pTest->iInputSize != 0) && memcmp(pTest->aOutput, aBuffer, pTest->iOutputSize))
        {
            ZPrintf("%s: gcm encrypt test %d failed with invalid output result\n", argv[0], iTest);
            NetPrintMem(pTest->aOutput, pTest->iOutputSize, "expected");
            NetPrintMem(aBuffer, pTest->iOutputSize, "actual");
            iFail |= 1;
        }

        // decrypt
        CryptGcmInit(&Gcm, pTest->aKey, pTest->iKeySize);
        CryptGcmDecrypt(&Gcm, aBuffer, pTest->iInputSize, pTest->aInitVec, sizeof(pTest->aInitVec), pTest->aData, pTest->iDataSize, aTagRslt, sizeof(aTagRslt));

        if (memcmp(pTest->aTagRslt, aTagRslt, sizeof(pTest->aTagRslt)))
        {
            ZPrintf("%s: gcm decrypt test %d failed with invalid tag result\n", argv[0], iTest);
            NetPrintMem(pTest->aTagRslt, sizeof(pTest->aTagRslt), "expected");
            NetPrintMem(aTagRslt, sizeof(pTest->aTagRslt), "actual");
            iFail |= 1;
        }
        if ((pTest->iInputSize != 0) && memcmp(pTest->aInput, aBuffer, pTest->iInputSize))
        {
            ZPrintf("%s: gcm decrypt test %d failed with invalid output result\n", argv[0], iTest);
            NetPrintMem(pTest->aInput, pTest->iInputSize, "expected");
            NetPrintMem(aBuffer, pTest->iOutputSize, "actual");
            iFail |= 1;
        }

        ZPrintf("%s: test #%d %s\n", argv[0], iTest, iFail ? "failed" : "passed");

        // add to failed count
        iFailed += iFail;
    }

    ZPrintf("%s: ---------------------\n", argv[0]);
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestChaCha

    \Description
        Test the CryptChaCha module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 02/14/2018 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestChaCha(ZContext *argz, int32_t argc, char *argv[])
{
    CryptChaChaT ChaChaState;
    int32_t iResult;

    // test vectors from https://tools.ietf.org/html/rfc7539#section-2.8.2
    static const uint8_t _aKey[] =
    {
        0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
        0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
    };
    static const uint8_t _aNonce[] =
    {
        0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47
    };
    static const uint8_t _aAddData[] =
    {
        0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7
    };
    uint8_t aTagBuf[16];
    static uint8_t aData[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";

    ZPrintf("%s: testing CryptChaCha\n", argv[0]);

    // encrypt the data
    CryptChaChaInit(&ChaChaState, _aKey, sizeof(_aKey));
    CryptChaChaEncrypt(&ChaChaState, aData, (int32_t)sizeof(aData)-1, _aNonce, (int32_t)sizeof(_aNonce), _aAddData, (int32_t)sizeof(_aAddData), aTagBuf, (int32_t)sizeof(aTagBuf));

    // decrypt and authenticate the data
    CryptChaChaInit(&ChaChaState, _aKey, sizeof(_aKey));
    iResult = CryptChaChaDecrypt(&ChaChaState, aData, (int32_t)sizeof(aData)-1, _aNonce, (int32_t)sizeof(_aNonce), _aAddData, (int32_t)sizeof(_aAddData), aTagBuf, (int32_t)sizeof(aTagBuf));

    ZPrintf("%s: ---------------------\n", argv[0]);

    return(iResult != -1 ? 0 : 1);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestMurmurHash3

    \Description
        Test MurmurHash3

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 11/04/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestMurmurHash3(ZContext *argz, int32_t argc, char *argv[])
{
    char strHashBuf0[16], strHashBuf1[16];
    int32_t iFailed = 0, iCount, iString, iDataSize, iNumIter;
    uint32_t uStartTick;
    const uint8_t aKeyBuf[16] = // init vector from MurmurHash3Init, little-endian 64bit words
    {
        0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67,
        0x76,0x54,0x32,0x10,0xfe,0xdc,0xba,0x98,
    };
    MurmurHash3T MurmurCtx;

    ZPrintf("%s: testing MurmurHash3 with a %d byte random buffer", argv[0], sizeof(_Crypt_aLargeRandom));

    // test versions for compatibility; first the crypt-style Init/Update/Final
    MurmurHash3Init(&MurmurCtx);
    MurmurHash3Update(&MurmurCtx, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom));
    MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0));
    // now the all-in-one-go version
    MurmurHash3(strHashBuf1, sizeof(strHashBuf1), _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom), aKeyBuf, sizeof(aKeyBuf));
    // test result
    if (memcmp(strHashBuf0, strHashBuf1, sizeof(strHashBuf0)))
    {
        ZPrintf("; hash failed!\n");
        NetPrintMem(strHashBuf0, sizeof(strHashBuf0), "hash0 result");
        NetPrintMem(strHashBuf1, sizeof(strHashBuf1), "hash1 result");
        iFailed += 1;
    }
    else
    {
        ZPrintf("; success\n");
    }

    ZPrintf("%s: testing MurmurHash3 with a %d byte random buffer and random sizes", argv[0], sizeof(_Crypt_aLargeRandom));

    // now test crypt version with multiple updates that are not 16-byte aligned
    MurmurHash3Init(&MurmurCtx);
    for (iCount = 0, iNumIter = 0; iCount <  (int32_t)sizeof(_Crypt_aLargeRandom); iCount += iDataSize, iNumIter += 1)
    {
        CryptRandGet((uint8_t *)&iDataSize, sizeof(iDataSize));
        iDataSize &= (sizeof(_Crypt_aLargeRandom)-1)/16;
        if (iCount+iDataSize > (int32_t)sizeof(_Crypt_aLargeRandom))
        {
            iDataSize = sizeof(_Crypt_aLargeRandom)-iCount;
        }
        MurmurHash3Update(&MurmurCtx, _Crypt_aLargeRandom+iCount, iDataSize);
    }
    MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0));
    // compare to previous result
    if (memcmp(strHashBuf0, strHashBuf1, sizeof(strHashBuf0)))
    {
        ZPrintf("; hash failed!\n");
        NetPrintMem(strHashBuf0, sizeof(strHashBuf0), "hash0 result");
        NetPrintMem(strHashBuf1, sizeof(strHashBuf1), "hash1 result");
        iFailed += 1;
    }
    else
    {
        ZPrintf("; success (%d iterations)\n", iNumIter);
    }

    // test all strings
    for (iString = 0; iString < (signed)(sizeof(_Crypt_Sha2Test)/sizeof(_Crypt_Sha2Test[0])); iString += 1)
    {
        ZPrintf("%s: hashing \"%s\"", argv[0], _Crypt_Sha2Test[iString].pString);

        // test versions for compatibility; first the crypt-style Init/Update/Final
        MurmurHash3Init(&MurmurCtx);
        MurmurHash3Update(&MurmurCtx, (uint8_t *)_Crypt_Sha2Test[iString].pString, (uint32_t)strlen(_Crypt_Sha2Test[iString].pString));
        MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0));
        // now the all-in-one-go version
        MurmurHash3(strHashBuf1, sizeof(strHashBuf1), (uint8_t *)_Crypt_Sha2Test[iString].pString, (uint32_t)strlen(_Crypt_Sha2Test[iString].pString), aKeyBuf, sizeof(aKeyBuf));

        if (memcmp(strHashBuf0, strHashBuf1, sizeof(strHashBuf0)))
        {
            ZPrintf("; hash failed!\n");
            NetPrintMem(strHashBuf0, sizeof(strHashBuf0), "hash0 result");
            NetPrintMem(strHashBuf1, sizeof(strHashBuf1), "hash1 result");
            iFailed += 1;
        }
        else
        {
            ZPrintf("; success\n");
        }
    }

    // timing tests

    // time atomic version
    ZPrintf("%s: hashing %d bytes of random data %d times for timing test (atomic version); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter);
    for (iCount = 0, uStartTick = NetTick(); iCount < _Crypt_iTimingIter; iCount += 1)
    {
        MurmurHash3(strHashBuf0, sizeof(strHashBuf0), _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)-16, aKeyBuf, sizeof(aKeyBuf));
    }
    ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick));

    // time crypt-style version
    ZPrintf("%s: hashing %d bytes of random data %d times for timing test (crypt version); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter);
    for (iCount = 0, uStartTick = NetTick(); iCount < _Crypt_iTimingIter; iCount += 1)
    {
        MurmurHash3Init(&MurmurCtx);
        MurmurHash3Update(&MurmurCtx, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom));
        MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0));
    }
    ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick));

    ZPrintf("%s: ---------------------\n", argv[0]);
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestMD5

    \Description
        Test the CryptMD5 module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 11/04/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestMD5(ZContext *argz, int32_t argc, char *argv[])
{
    char strHashBuf[MD5_BINARY_OUT];
    int32_t iFailed = 0, iCount;
    uint32_t uStartTick;
    CryptMD5T MD5;

    ZPrintf("%s: testing CryptMD5\n", argv[0]);

    // do a timing test

    // test all modes
    ZPrintf("%s: hashing %d bytes of random data %d times for timing test; ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter);
    uStartTick = NetTick();

    for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1)
    {
        CryptMD5Init(&MD5);
        CryptMD5Update(&MD5, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom));
        CryptMD5Final(&MD5, (uint8_t *)strHashBuf, sizeof(strHashBuf));
    }

    ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick));

    ZPrintf("%s: ---------------------\n", argv[0]);
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestSha1

    \Description
        Test the CryptSha1 module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 11/04/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestSha1(ZContext *argz, int32_t argc, char *argv[])
{
    char strHashBuf[20];
    int32_t iFailed = 0, iCount;
    uint32_t uStartTick;
    CryptSha1T Sha1;

    ZPrintf("%s: testing CryptSha1\n", argv[0]);

    // do a timing test

    // test all modes
    ZPrintf("%s: hashing %d bytes of random data %d times for timing test; ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter);
    uStartTick = NetTick();

    for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1)
    {
        CryptSha1Init(&Sha1);
        CryptSha1Update(&Sha1, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom));
        CryptSha1Final(&Sha1, (uint8_t *)strHashBuf, sizeof(strHashBuf));
    }

    ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick));

    ZPrintf("%s: ---------------------\n", argv[0]);
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestSha2

    \Description
        Test the CryptSha2 module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 11/04/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestSha2(ZContext *argz, int32_t argc, char *argv[])
{
    char strHashBuf[128];
    static const uint8_t _HashSizes[] = { CRYPTSHA224_HASHSIZE, CRYPTSHA256_HASHSIZE, CRYPTSHA384_HASHSIZE, CRYPTSHA512_HASHSIZE };
    int32_t iFailed, iString, iMode, iCount;
    uint32_t uStartTick;
    CryptSha2T Sha2;

    ZPrintf("%s: testing CryptSha2\n", argv[0]);

    // test all strings
    for (iFailed = 0, iString = 0; iString < (signed)(sizeof(_Crypt_Sha2Test)/sizeof(_Crypt_Sha2Test[0])); iString += 1)
    {
        // test all modes
        for (iMode = 0; iMode < 4; iMode += 1)
        {
            ZPrintf("%s: hashing \"%s\" (mode %d)", argv[0], _Crypt_Sha2Test[iString].pString, iMode);

            CryptSha2Init(&Sha2, _HashSizes[iMode]);
            CryptSha2Update(&Sha2, (uint8_t *)_Crypt_Sha2Test[iString].pString, (uint32_t)strlen(_Crypt_Sha2Test[iString].pString));
            CryptSha2Final(&Sha2, (uint8_t *)strHashBuf, _HashSizes[iMode]);

            if (memcmp(strHashBuf, _Crypt_Sha2Test[iString].strHashRslt[iMode], (uint32_t)_HashSizes[iMode]))
            {
                ZPrintf("; hash failed!\n");
                NetPrintMem(strHashBuf, (uint32_t)_HashSizes[iMode], "hash result");
                NetPrintMem(_Crypt_Sha2Test[iString].strHashRslt[iMode], (uint32_t)_HashSizes[iMode], "expected result");
                iFailed += 1;
            }
            else
            {
                ZPrintf("; success\n");
            }
        }
    }

    // do a timing test

    // test all modes
    for (iMode = 0; iMode < 4; iMode += 1)
    {
        ZPrintf("%s: hashing %d bytes of random data %d times for timing test (mode %d); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter, iMode);
        uStartTick = NetTick();

        for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1)
        {
            CryptSha2Init(&Sha2, _HashSizes[iMode]);
            CryptSha2Update(&Sha2, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom));
            CryptSha2Final(&Sha2, (uint8_t *)strHashBuf, _HashSizes[iMode]);
        }

        ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick));
    }

    ZPrintf("%s: ---------------------\n", argv[0]);
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestHmac

    \Description
        Test the CryptHmac module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 11/05/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestHmac(ZContext *argz, int32_t argc, char *argv[])
{
    uint8_t strHmacBuf[128];
    const uint8_t strZeroBuf[64] = { 0 };
    int32_t iFailed, iTest, iMode, iCount;
    uint32_t uStartTick;
    int32_t iHashSize;
    const CryptTestHmac *pTest;

    ZPrintf("%s: testing CryptHmac\n", argv[0]);

    // test all strings
    for (iFailed = 0, iTest = 0; iTest < (signed)(sizeof(_Crypt_HmacTest)/sizeof(_Crypt_HmacTest[0])); iTest += 1)
    {
        ZPrintf("%s: test %d\n", argv[0], iTest+1);

        for (iMode = CRYPTHASH_MURMUR3; iMode <= CRYPTHASH_SHA512; iMode += 1)
        {
            iHashSize = CryptHashGetSize((CryptHashTypeE)iMode);
            pTest = &_Crypt_HmacTest[iTest];

            // skip tests with blank results
            if (!memcmp(pTest->Param[iMode-1].strRslt, strZeroBuf, iHashSize))
            {
                continue;
            }

            // run the test
            ZPrintf("%s: hashing \"%s\" (mode %d)", argv[0], pTest->pString, iMode);
            CryptHmacCalc(strHmacBuf, (int32_t)sizeof(strHmacBuf), (uint8_t *)pTest->pString, pTest->Param[iMode-1].uInpLen, (uint8_t *)pTest->pKey, pTest->Param[iMode-1].uKeyLen, iMode);

            // check test result
            if (memcmp(strHmacBuf, pTest->Param[iMode-1].strRslt, pTest->Param[iMode-1].uOutLen))
            {
                ZPrintf("; hmac failed!\n");
                NetPrintMem(strHmacBuf, pTest->Param[iMode-1].uOutLen, "hmac result");
                NetPrintMem(pTest->Param[iMode-1].strRslt, pTest->Param[iMode-1].uOutLen, "expected result");
                iFailed += 1;
            }
            else
            {
                ZPrintf("; success\n");
            }
        }
    }

    // do a timing test

    // test all modes
    for (iMode = CRYPTHASH_MURMUR3, iTest = 0; iMode <= CRYPTHASH_SHA512; iMode += 1)
    {
        ZPrintf("%s: hashing %d bytes of random data %d times for timing test (mode %d); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter, iMode);
        uStartTick = NetTick();

        for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1)
        {
            CryptHmacCalc(strHmacBuf, (int32_t)sizeof(strHmacBuf), _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom), (uint8_t *)_Crypt_HmacTest[iTest].pKey, (int32_t)strlen(_Crypt_HmacTest[iTest].pKey), (CryptHashTypeE)iMode);
        }

        ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick));
    }

    ZPrintf("%s: ---------------------\n", argv[0]);
    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestRSA

    \Description
        Test the CryptRSA module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else number of failed tests

    \Version 11/22/2013 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestRSA(ZContext *argz, int32_t argc, char *argv[])
{
    static const uint8_t _aModulus[] =
    {
        0xe2, 0x02, 0x41, 0x01, 0xa1, 0x02, 0xb8, 0xe2, 0x3e, 0x2a, 0x0a, 0x6d, 0x8b, 0xf9,
        0xf4, 0x73, 0xd3, 0xdb, 0x15, 0xac, 0x0a, 0xf6, 0xf2, 0x4c, 0x6d, 0x46, 0xf7, 0xd4, 0x1b,
        0xb9, 0xce, 0x62, 0xbc, 0x58, 0x20, 0x57, 0x4c, 0xf9, 0xa6, 0x6b, 0xf4, 0xe1, 0x29, 0x93,
        0x15, 0x0e, 0xce, 0x35, 0x7e, 0x02, 0x9f, 0x18, 0xf4, 0xa2, 0x4f, 0x49, 0x69, 0x4a, 0xd0,
        0x98, 0xdd, 0x28, 0x6e, 0x5f, 0xf5, 0x3e, 0xe2, 0xa3, 0x56, 0xe3, 0x8c, 0xcd, 0xf9, 0x07,
        0x8d, 0x44, 0x42, 0x69, 0xf7, 0x41, 0x44, 0x54, 0x13, 0xa7, 0x35, 0x2e, 0x87, 0xc3, 0x0e,
        0x10, 0x00, 0xa7, 0x14, 0xc1, 0x3b, 0x36, 0xfb, 0x37, 0xee, 0x25, 0xc2, 0xa5, 0x04, 0xcb,
        0x7e, 0x26, 0x27, 0xe8, 0x61, 0xb9, 0x33, 0x96, 0x49, 0x5b, 0x1e, 0xa2, 0x09, 0xff, 0xf8,
        0xe2, 0x13, 0x7c, 0x99, 0x34, 0x25, 0xdc, 0xdf, 0xdf, 0xc8, 0x3a, 0x8d, 0x56, 0xbb, 0xc4,
        0x18, 0xe3, 0x8a, 0x7a, 0x1b, 0x38, 0x80, 0x7e, 0x9c, 0x9d, 0x43, 0x04, 0x11, 0x47, 0x81,
        0xf0, 0x32, 0x29, 0xe6, 0x9d, 0x72, 0xc7, 0x8d, 0xff, 0x0e, 0x03, 0x6b, 0xb6, 0xa3, 0xb0,
        0x6d, 0xb6, 0x5e, 0xc7, 0x78, 0x14, 0x08, 0xea, 0xdd, 0xcb, 0x15, 0xb6, 0x1c, 0xdd, 0xe7,
        0x5a, 0x25, 0x49, 0xe7, 0xbd, 0xe7, 0x1b, 0xcc, 0x81, 0xf2, 0x32, 0x93, 0xc8, 0x27, 0xc9,
        0x5d, 0xd8, 0x89, 0x11, 0xc4, 0xf3, 0x6b, 0x37, 0x9b, 0x6d, 0x36, 0x0a, 0x8d, 0xef, 0xca,
        0x79, 0xfa, 0x6e, 0x06, 0xcf, 0x96, 0x12, 0xe3, 0x94, 0xd7, 0x69, 0x34, 0x9d, 0xf5, 0xf6,
        0xe0, 0x54, 0x77, 0xba, 0xc9, 0xe6, 0x74, 0x2a, 0xbe, 0x20, 0xc3, 0xe2, 0x05, 0xa9, 0x61,
        0x11, 0xb8, 0x16, 0x35, 0xb7, 0xad, 0xda, 0xe9, 0xed, 0x60, 0xb4, 0x13, 0xc9, 0xeb, 0x45,
        0xe3, 0xf7
    };
    static const uint8_t _aPublicExponent[] = { 0x01, 0x00, 0x01 };
    static const uint8_t _aPrivateExponent[] =
    {
        0x7d, 0x66, 0x78, 0xc1, 0x39, 0xa0, 0x34, 0x0b, 0x3c, 0x28, 0xc1, 0x6e, 0x74, 0xeb, 0x34,
        0x6a, 0x8c, 0x13, 0x14, 0x5c, 0x48, 0x1d, 0x2a, 0xe5, 0xa3, 0x00, 0x6c, 0x83, 0xe0, 0xfa,
        0x60, 0x7c, 0x42, 0x3a, 0xb7, 0x7f, 0x18, 0xf3, 0xb0, 0x16, 0x58, 0x62, 0x12, 0x5a, 0x4c,
        0xa5, 0xd1, 0x5e, 0xb6, 0xd3, 0x27, 0x89, 0x86, 0x3b, 0x04, 0xb9, 0x1b, 0xd5, 0xea, 0x15,
        0xd7, 0x28, 0x16, 0xcd, 0xe1, 0x5a, 0x8a, 0x0f, 0xcd, 0x27, 0x26, 0xba, 0x26, 0x41, 0xbd,
        0x6d, 0x31, 0x58, 0x70, 0x5b, 0x63, 0x59, 0x2f, 0x2a, 0x68, 0x84, 0xaf, 0xc9, 0x57, 0x65,
        0x23, 0xa7, 0x91, 0x09, 0x82, 0x1c, 0x88, 0x99, 0x48, 0xe6, 0xe4, 0xb0, 0x01, 0x10, 0x13,
        0xa7, 0x82, 0x1f, 0x1b, 0x11, 0xd2, 0x80, 0xc1, 0xa4, 0xf0, 0x43, 0x42, 0x3f, 0x27, 0xcd,
        0xf0, 0xb8, 0x02, 0x9d, 0x7f, 0xa0, 0xa5, 0x0a, 0x0f, 0xb0, 0x68, 0xdc, 0x23, 0x46, 0x77,
        0x6b, 0x87, 0xed, 0x29, 0x5f, 0x08, 0xfc, 0xd7, 0x58, 0x0e, 0x7c, 0x85, 0x24, 0x72, 0x2b,
        0xf1, 0x1d, 0x0a, 0x86, 0xe6, 0xe6, 0x05, 0x6f, 0xef, 0x3f, 0x74, 0x6a, 0xf3, 0x53, 0x57,
        0xf1, 0x80, 0x3e, 0x0d, 0x26, 0x5f, 0xce, 0xfd, 0x20, 0x28, 0x8c, 0x61, 0xfe, 0x18, 0x23,
        0xf2, 0x1f, 0x08, 0x20, 0xd6, 0x96, 0x95, 0x32, 0x6c, 0x61, 0xd2, 0xdb, 0xf8, 0x37, 0x37,
        0x02, 0xf7, 0x54, 0x77, 0xe3, 0x8b, 0x2e, 0x59, 0xd8, 0x9c, 0x61, 0x45, 0x54, 0x95, 0x27,
        0x29, 0x16, 0x19, 0xa1, 0xae, 0x76, 0xf4, 0xbf, 0x07, 0x39, 0xc4, 0x5f, 0xac, 0xb8, 0x94,
        0x38, 0x1d, 0xfa, 0x40, 0x0e, 0x4f, 0xb8, 0xa7, 0xa1, 0xb6, 0xe5, 0xe1, 0x91, 0x86, 0x1d,
        0x7f, 0xc2, 0x3e, 0x0d, 0x55, 0x0d, 0xbc, 0x28, 0x40, 0x3d, 0xce, 0xb5, 0xc9, 0xee, 0x17,
        0xc9
    };
    static const uint8_t _aPrime1[] =
    {
        0xf3, 0xd4, 0x21, 0xef, 0xb5, 0xab, 0x27, 0x40, 0xb2, 0xb2, 0x55, 0x6c, 0x9b, 0x9f,
        0x68, 0xe0, 0x41, 0x8f, 0x34, 0xa2, 0x2b, 0x53, 0x2d, 0xd4, 0x04, 0x35, 0xd4, 0x37, 0x13,
        0x88, 0x89, 0xef, 0xdd, 0x6f, 0x00, 0x49, 0xbc, 0x8a, 0x32, 0x0a, 0x86, 0x87, 0x79, 0x07,
        0x6c, 0x66, 0xfd, 0x72, 0x7b, 0x84, 0xf5, 0xdd, 0x27, 0x33, 0x87, 0xdb, 0x01, 0x89, 0xd3,
        0x4a, 0x72, 0x48, 0xba, 0xf2, 0x1a, 0x25, 0x8e, 0x6c, 0x46, 0xe4, 0x04, 0x72, 0x5c, 0x69,
        0xa5, 0x0f, 0xc9, 0x99, 0xed, 0x54, 0x64, 0xa5, 0x5f, 0xbe, 0xf4, 0x5f, 0xea, 0xc9, 0x4c,
        0x20, 0xc9, 0xda, 0xdd, 0xc2, 0x33, 0x95, 0xce, 0x3c, 0x09, 0xf9, 0x9b, 0xad, 0xca, 0x00,
        0x43, 0xeb, 0xe4, 0x19, 0xa9, 0x26, 0xb5, 0x4b, 0xa8, 0xa7, 0x35, 0xd2, 0xc1, 0x9d, 0x56,
        0x3c, 0x83, 0x32, 0x94, 0xa8, 0xee, 0xee, 0x87, 0x53
    };
    static const uint8_t _aPrime2[] =
    {
        0xed, 0x4a, 0x67, 0x2c, 0xac, 0xa0, 0x96, 0x8d, 0x0e, 0xed, 0x85, 0x0a, 0x3e, 0xf1,
        0xaf, 0xe7, 0xda, 0x63, 0x04, 0x8e, 0x7a, 0x12, 0x5c, 0x15, 0x6d, 0x74, 0x63, 0x1e, 0x3c,
        0x65, 0xc5, 0x0a, 0x37, 0x44, 0xa2, 0x4b, 0x34, 0x9e, 0xaa, 0xbe, 0xce, 0x71, 0xab, 0x49,
        0x3a, 0xc0, 0x66, 0x6f, 0x13, 0xfe, 0x13, 0x54, 0x70, 0x88, 0x24, 0x25, 0x0e, 0x06, 0xde,
        0x85, 0x46, 0x5d, 0xa2, 0x5c, 0x72, 0xde, 0x57, 0x19, 0x4f, 0x33, 0x2b, 0xdb, 0xdf, 0xfb,
        0x3e, 0x86, 0xc6, 0x93, 0x52, 0xb9, 0x21, 0xc1, 0x19, 0xf5, 0xde, 0x96, 0x55, 0x30, 0x76,
        0xc4, 0x22, 0x72, 0x0c, 0x40, 0x4a, 0xa5, 0x61, 0xa4, 0xd7, 0xb4, 0xb4, 0x89, 0x30, 0xf3,
        0x06, 0xe1, 0x32, 0xd4, 0xaf, 0x72, 0x80, 0x86, 0xaa, 0x34, 0x9a, 0x64, 0x89, 0xd3, 0x80,
        0x7c, 0x4b, 0x39, 0xcb, 0x14, 0x9e, 0x66, 0x10, 0x4d
    };
    static const uint8_t _aExponent1[] =
    {
        0xa7, 0xdd, 0x30, 0xab, 0xf6, 0x37, 0x69, 0xe3, 0xb9, 0xe2, 0xda, 0xba, 0xd5, 0xfd,
        0x0e, 0x57, 0xed, 0xea, 0xa8, 0x82, 0xc9, 0x2f, 0x0f, 0xca, 0xfa, 0x47, 0x10, 0xde, 0x06,
        0x1d, 0xa7, 0x51, 0x32, 0xf2, 0x9b, 0x91, 0x28, 0x33, 0x40, 0x36, 0x4c, 0xdd, 0xf1, 0xad,
        0xf1, 0xac, 0x89, 0xea, 0x8a, 0x2d, 0x44, 0x93, 0x47, 0xcc, 0xcb, 0x48, 0x34, 0xab, 0xed,
        0x82, 0x40, 0x61, 0xe0, 0x0a, 0x93, 0x83, 0xad, 0xa4, 0xcf, 0xbd, 0x65, 0x6e, 0x52, 0x3f,
        0x0d, 0x3b, 0x6c, 0x41, 0x03, 0xca, 0x69, 0x2c, 0x0d, 0x59, 0xca, 0xa6, 0x4a, 0x5e, 0xe1,
        0x81, 0x65, 0x56, 0xbf, 0xfb, 0x56, 0x46, 0x59, 0x60, 0xae, 0x41, 0x61, 0x33, 0x69, 0x71,
        0x7b, 0x51, 0x68, 0x8d, 0x5e, 0x0d, 0xdf, 0x1c, 0xc2, 0x74, 0xb3, 0xb2, 0x70, 0x47, 0x60,
        0xba, 0x72, 0x5c, 0x9d, 0x4a, 0x1c, 0x8c, 0xad, 0x2f
    };
    static const uint8_t _aExponent2[] =
    {
        0x84, 0x99, 0x05, 0x1a, 0x93, 0xc4, 0x91, 0x1c, 0x75, 0xf1, 0x08, 0x5c, 0xf7, 0x5b,
        0x7b, 0x1e, 0xa6, 0x8c, 0x9a, 0x69, 0x3b, 0x91, 0xb2, 0xdf, 0x4e, 0x70, 0xb1, 0x4a, 0x9e,
        0x19, 0x88, 0x87, 0xf2, 0xe6, 0x69, 0x82, 0x78, 0xff, 0x09, 0x0e, 0xe2, 0xb1, 0xe6, 0x33,
        0x5f, 0x9f, 0x50, 0x1e, 0x56, 0x1f, 0xae, 0x91, 0x8a, 0xe8, 0xa8, 0xba, 0x04, 0x22, 0x96,
        0x8a, 0x07, 0x0e, 0x1f, 0xc2, 0x65, 0x76, 0x15, 0x59, 0xd1, 0x46, 0x19, 0x06, 0x1f, 0x1d,
        0x78, 0x8d, 0x3b, 0xbd, 0xeb, 0x86, 0x04, 0x74, 0xb1, 0x9b, 0x11, 0x2d, 0x14, 0xa1, 0xa6,
        0x5c, 0x67, 0x9b, 0x2f, 0x79, 0x65, 0xbd, 0x10, 0xd9, 0x5a, 0xa8, 0x62, 0x12, 0x1f, 0xc6,
        0x4e, 0x5b, 0xdd, 0x59, 0xb8, 0x48, 0xd5, 0xc5, 0x6a, 0xab, 0x46, 0x73, 0x54, 0x09, 0x5a,
        0x4d, 0x1a, 0x84, 0x4b, 0x15, 0x54, 0x86, 0x58, 0x29
    };
    static const uint8_t _aCoefficient[] =
    {
        0x90, 0xba, 0x74, 0xe6, 0x17, 0xc2, 0xf9, 0x0f, 0xad, 0x57, 0x9d, 0x86, 0x78, 0x8d,
        0xa5, 0x6f, 0x86, 0xd1, 0x9a, 0xfe, 0xb6, 0x95, 0x47, 0x14, 0x0f, 0x92, 0xc9, 0xb6, 0x25,
        0x43, 0x63, 0x70, 0xdc, 0xbf, 0x83, 0x8b, 0x27, 0x7a, 0x2d, 0x3c, 0xe7, 0x50, 0xfc, 0x29,
        0x4b, 0xda, 0xbd, 0xa2, 0x38, 0x4c, 0x37, 0xfc, 0xf5, 0x9b, 0x3e, 0xbb, 0x28, 0x0f, 0xda,
        0x52, 0x06, 0x69, 0xd6, 0xf7, 0x3f, 0x2b, 0xd0, 0xae, 0xc0, 0x85, 0x85, 0xb7, 0xd9, 0xbc,
        0x3b, 0x20, 0x2d, 0xe1, 0x10, 0xc9, 0x84, 0x76, 0xbe, 0xa2, 0x6c, 0xaf, 0x36, 0x84, 0x13,
        0xfa, 0x4c, 0x80, 0x6d, 0x09, 0x89, 0xa6, 0x6d, 0xa4, 0x6b, 0x96, 0x58, 0x5f, 0x05, 0x37,
        0x70, 0xee, 0x78, 0x08, 0xc1, 0xa3, 0x26, 0xef, 0xda, 0xa4, 0x71, 0x11, 0x09, 0xcb, 0xa9,
        0x49, 0x05, 0x55, 0x2d, 0x10, 0x98, 0xca, 0xa9, 0x42
    };
    static const CryptBinaryObjT _Prime1 = { (uint8_t *)_aPrime1, sizeof(_aPrime1) };
    static const CryptBinaryObjT _Prime2 = { (uint8_t *)_aPrime2, sizeof(_aPrime2) };
    static const CryptBinaryObjT _Exponent1 = { (uint8_t *)_aExponent1, sizeof(_aExponent1) };
    static const CryptBinaryObjT _Exponent2 = { (uint8_t *)_aExponent2, sizeof(_aExponent2) };
    static const CryptBinaryObjT _Coefficient = { (uint8_t *)_aCoefficient, sizeof(_aCoefficient) };
    static const uint8_t _strRsaEncryptMessage[] = "test rsa";

    CryptRSAT RSA, RSA2, RSA3;
    CryptSha2T Sha2;
    ProtoSSLPkcs1T Pkcs1;
    uint8_t aHashObj[CRYPTSHA256_HASHSIZE], aSigObj[sizeof(_aModulus)];

    // encrypt
    if (CryptRSAInit(&RSA, _aModulus, sizeof(_aModulus), _aPublicExponent, sizeof(_aPublicExponent)) != 0)
    {
        return(1);
    }
    CryptRSAInitMaster(&RSA, _strRsaEncryptMessage, sizeof(_strRsaEncryptMessage));
    CryptRSAEncrypt(&RSA, 0);
    ZPrintf("crypt: rsa encrypt took %ums\n", RSA.uCryptMsecs);

    // decrypt using private exponent
    if (CryptRSAInit(&RSA2, _aModulus, sizeof(_aModulus), _aPrivateExponent, sizeof(_aPrivateExponent)) != 0)
    {
        return(1);
    }
    CryptRSAInitSignature(&RSA2, RSA.EncryptBlock, sizeof(_aModulus));
    CryptRSAEncrypt(&RSA2, 0);
    ZPrintf("crypt: rsa decrypt took %ums\n", RSA2.uCryptMsecs);

    // verify result
    if (memcmp(_strRsaEncryptMessage, RSA2.EncryptBlock+sizeof(_aModulus)-sizeof(_strRsaEncryptMessage), sizeof(_strRsaEncryptMessage)) != 0)
    {
        return(1);
    }

    // decrypt using crt
    if (CryptRSAInit2(&RSA3, sizeof(_aModulus), &_Prime1, &_Prime2, &_Exponent1, &_Exponent2, &_Coefficient) != 0)
    {
        return(1);
    }
    CryptRSAInitSignature(&RSA3, RSA.EncryptBlock, sizeof(_aModulus));
    CryptRSAEncrypt(&RSA3, 0);
    ZPrintf("crypt: rsa decrypt using crt took %ums\n", RSA3.uCryptMsecs);

    // verify result
    if (memcmp(_strRsaEncryptMessage, RSA3.EncryptBlock+sizeof(_aModulus)-sizeof(_strRsaEncryptMessage), sizeof(_strRsaEncryptMessage)) != 0)
    {
        return(1);
    }

    // hash the message
    CryptSha2Init(&Sha2, sizeof(aHashObj));
    CryptSha2Update(&Sha2, _strRsaEncryptMessage, sizeof(_strRsaEncryptMessage));
    CryptSha2Final(&Sha2, aHashObj, sizeof(aHashObj));

    // generate the signature
    if (ProtoSSLPkcs1GenerateInit(&Pkcs1, aHashObj, sizeof(aHashObj), CRYPTHASH_SHA256, sizeof(_aModulus), &_Prime1, &_Prime2, &_Exponent1, &_Exponent2, &_Coefficient) != 0)
    {
        return(1);
    }
    ProtoSSLPkcs1GenerateUpdate(&Pkcs1, 0, aSigObj, sizeof(aSigObj));
    // verify the signature
    if (ProtoSSLPkcs1Verify(aSigObj, sizeof(aSigObj), aHashObj, sizeof(aHashObj), CRYPTHASH_SHA256, _aModulus, sizeof(_aModulus), _aPublicExponent, sizeof(_aPublicExponent)) != 0)
    {
        return(1);
    }

    return(0);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestNist

    \Description
        Test the CryptNist module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else test failed

    \Version 11/29/2018 (eesponda)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestNist(ZContext *argz, int32_t argc, char *argv[])
{
    uint8_t bResult = TRUE;
    CryptNistDhT Alice, Bob;
    CryptEccPointT AlicePublic, BobPublic, AliceShared, BobShared;

    // initialize the secp256r1 states
    CryptNistInitDh(&Alice, CRYPTCURVE_SECP256R1);
    CryptNistInitDh(&Bob, CRYPTCURVE_SECP256R1);

    // generate public key
    while (CryptNistPublic(&Alice, &AlicePublic, NULL) > 0)
        ;
    ZPrintf("crypt: alice public secp256r1 time=%u\n", Alice.Ecc.uCryptUSecs);

    while (CryptNistPublic(&Bob, &BobPublic, NULL) > 0)
        ;
    ZPrintf("crypt: bob public secp256r1 time=%u\n", Bob.Ecc.uCryptUSecs);

    // generate shared secrets
    while (CryptNistSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0)
        ;
    ZPrintf("crypt: alice shared secp256r1 time=%u\n", Alice.Ecc.uCryptUSecs);

    while (CryptNistSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0)
        ;
    ZPrintf("crypt: bob shared secp256r1 time=%u\n", Bob.Ecc.uCryptUSecs);

    // validate secp256r1 results
    bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0;
    bResult &= memcmp(AliceShared.Y.aData, BobShared.Y.aData, sizeof(AliceShared.Y.aData)) == 0;

    // initialize the secp384r1 states
    CryptNistInitDh(&Alice, CRYPTCURVE_SECP384R1);
    CryptNistInitDh(&Bob, CRYPTCURVE_SECP384R1);

    // generate public key
    while (CryptNistPublic(&Alice, &AlicePublic, NULL) > 0)
        ;
    ZPrintf("crypt: alice public secp384r1 time=%u\n", Alice.Ecc.uCryptUSecs);

    while (CryptNistPublic(&Bob, &BobPublic, NULL) > 0)
        ;
    ZPrintf("crypt: bob public secp384r1 time=%u\n", Bob.Ecc.uCryptUSecs);

    // generate shared secrets
    while (CryptNistSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0)
        ;
    ZPrintf("crypt: alice shared secp384r1 time=%u\n", Alice.Ecc.uCryptUSecs);

    while (CryptNistSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0)
        ;
    ZPrintf("crypt: bob shared secp384r1 time=%u\n", Bob.Ecc.uCryptUSecs);

    // validate secp384r1 results
    bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0;
    bResult &= memcmp(AliceShared.Y.aData, BobShared.Y.aData, sizeof(AliceShared.Y.aData)) == 0;

    return(bResult ? 0 : 1);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestDsa

    \Description
        Test the ECDSA signing / verify operations

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else test failed

    \Version 01/17/2019 (eesponda)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestDsa(ZContext *argz, int32_t argc, char *argv[])
{
    int32_t iCurve, iFailed = 0;

    for (iCurve = 0; iCurve < (signed)(sizeof(_Crypt_DsaTests)/sizeof(_Crypt_DsaTests[0])); iCurve += 1)
    {
        const CryptTestDsaT *pTestData = &_Crypt_DsaTests[iCurve];
        int32_t iTest;
        CryptEccPointT PublicKey;
        CryptBnT PrivateKey;

        CryptNistPointInitFrom(&PublicKey, pTestData->aPublicKey, (pTestData->iCurveSize*2)+1);
        CryptBnInitFrom(&PrivateKey, -1, pTestData->aPrivateKey, pTestData->iCurveSize);

        for (iTest = 0; iTest < (signed)(sizeof(pTestData->aCases)/sizeof(pTestData->aCases[0])); iTest += 1)
        {
            const CryptTestDsaCaseT *pCase = &pTestData->aCases[iTest];
            int32_t iHashSize = CryptHashGetSize(pCase->eHashType), iSignResult, iVerifyResult;

            CryptNistDsaT Sign, Verify;
            CryptEccPointT Signature;
            const CryptHashT *pHash;
            uint8_t aHash[CRYPTHASH_MAXDIGEST], aSignature[97];

            // initialize dsa state
            CryptNistInitDsa(&Sign, pTestData->eCurveType);
            CryptNistInitDsa(&Verify, pTestData->eCurveType);

            // hash the message
            if ((pHash = CryptHashGet(pCase->eHashType)) != NULL)
            {
                uint8_t aHashState[CRYPTHASH_MAXSTATE];
                pHash->Init(aHashState, iHashSize);
                pHash->Update(aHashState, pCase->aMessage, (signed)strlen((const char *)pCase->aMessage));
                pHash->Final(aHashState, aHash, iHashSize);
            }

            // generate and check signature for expected result
            while (CryptNistSign(&Sign, &PrivateKey, aHash, iHashSize, &Signature, NULL) > 0)
                ;
            CryptBnFinal(&Signature.X, aSignature, Sign.Ecc.iSize);
            CryptBnFinal(&Signature.Y, aSignature+Sign.Ecc.iSize, Sign.Ecc.iSize);
            iSignResult = memcmp(aSignature, pCase->aSignature, Sign.Ecc.iSize*2);
            ZPrintf("crypt: sign curve=%d, hash=%d, message=%s, time=%u\n",
                pTestData->eCurveType, pCase->eHashType, pCase->aMessage, Sign.Ecc.uCryptUSecs);

            // verify signature
            while ((iVerifyResult = CryptNistVerify(&Verify, &PublicKey, aHash, iHashSize, &Signature, NULL)) > 0)
                ;
            ZPrintf("crypt: verify curve=%d, hash=%d, message=%s, time=%u\n",
                pTestData->eCurveType, pCase->eHashType, pCase->aMessage, Verify.Ecc.uCryptUSecs);

            if ((iSignResult != 0) || (iVerifyResult != 0))
            {
                iFailed += 1;
            }
        }
    }

    return(iFailed);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestMont

    \Description
        Test the CryptMont module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else test failed

    \Version 04/11/2018 (eesponda)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestMont(ZContext *argz, int32_t argc, char *argv[])
{
    #if DIRTYCODE_DEBUG
    const uint8_t aAlicePrivate25519[] =  {
        0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45,
        0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a
    };
    const uint8_t aBobPrivate25519[] =  {
        0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6,
        0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb
    };
    const uint8_t aAlicePublic25519[] = {
        0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a,
        0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a
    };
    const uint8_t aBobPublic25519[] = {
        0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37,
        0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f
    };
    const uint8_t aSharedSecrete25519[] = {
        0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25,
        0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42
    };
    const uint8_t aAlicePrivate448[] = {
        0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58, 0x00, 0xd4,
        0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, 0xd4, 0x98, 0xc2, 0x8d, 0xd9, 0xc9, 0xba, 0xf5,
        0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, 0x00, 0x63, 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d,
        0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b
    };
    const uint8_t aBobPrivate448[] = {
        0x1c, 0x30, 0x6a, 0x7a, 0xc2, 0xa0, 0xe2, 0xe0, 0x99, 0x0b, 0x29, 0x44, 0x70, 0xcb, 0xa3, 0x39,
        0xe6, 0x45, 0x37, 0x72, 0xb0, 0x75, 0x81, 0x1d, 0x8f, 0xad, 0x0d, 0x1d, 0x69, 0x27, 0xc1, 0x20,
        0xbb, 0x5e, 0xe8, 0x97, 0x2b, 0x0d, 0x3e, 0x21, 0x37, 0x4c, 0x9c, 0x92, 0x1b, 0x09, 0xd1, 0xb0,
        0x36, 0x6f, 0x10, 0xb6, 0x51, 0x73, 0x99, 0x2d
    };
    const uint8_t aAlicePublic448[] = {
        0x9b, 0x08, 0xf7, 0xcc, 0x31, 0xb7, 0xe3, 0xe6, 0x7d, 0x22, 0xd5, 0xae, 0xa1, 0x21, 0x07, 0x4a,
        0x27, 0x3b, 0xd2, 0xb8, 0x3d, 0xe0, 0x9c, 0x63, 0xfa, 0xa7, 0x3d, 0x2c, 0x22, 0xc5, 0xd9, 0xbb,
        0xc8, 0x36, 0x64, 0x72, 0x41, 0xd9, 0x53, 0xd4, 0x0c, 0x5b, 0x12, 0xda, 0x88, 0x12, 0x0d, 0x53,
        0x17, 0x7f, 0x80, 0xe5, 0x32, 0xc4, 0x1f, 0xa0
    };
    const uint8_t aBobPublic448[] = {
        0x3e, 0xb7, 0xa8, 0x29, 0xb0, 0xcd, 0x20, 0xf5, 0xbc, 0xfc, 0x0b, 0x59, 0x9b, 0x6f, 0xec, 0xcf,
        0x6d, 0xa4, 0x62, 0x71, 0x07, 0xbd, 0xb0, 0xd4, 0xf3, 0x45, 0xb4, 0x30, 0x27, 0xd8, 0xb9, 0x72,
        0xfc, 0x3e, 0x34, 0xfb, 0x42, 0x32, 0xa1, 0x3c, 0xa7, 0x06, 0xdc, 0xb5, 0x7a, 0xec, 0x3d, 0xae,
        0x07, 0xbd, 0xc1, 0xc6, 0x7b, 0xf3, 0x36, 0x09
    };
    const uint8_t aSharedSecret448[] = {
        0x07, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0x0f, 0x74, 0xd1,
        0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, 0x28, 0x1d, 0x28, 0x2b, 0xb6, 0x0c, 0x0b, 0x56,
        0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, 0x52, 0x1c, 0x24, 0x40, 0x30, 0x85, 0xd5, 0x9a,
        0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d
    };
    #endif

    CryptMontT Alice, Bob;
    CryptEccPointT AlicePublic, BobPublic, AliceShared, BobShared;
    uint8_t bResult = TRUE;

    // initialize the context (x25519)
    CryptMontInit(&Alice, CRYPTCURVE_X25519);
    CryptMontInit(&Bob, CRYPTCURVE_X25519);

    #if DIRTYCODE_DEBUG
    ZPrintf("crypt: validating x25519 using test vectors\n");
    CryptMontSetPrivateKey(&Alice, aAlicePrivate25519);
    CryptMontSetPrivateKey(&Bob, aBobPrivate25519);
    #endif

    // generate alice public
    while (CryptMontPublic(&Alice, &AlicePublic, NULL) > 0)
        ;
    #if DIRTYCODE_DEBUG
    bResult &= memcmp(aAlicePublic25519, AlicePublic.X.aData, 32) == 0;
    #endif
    ZPrintf("crypt: alice public x25519 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");

    // generate bob public
    while (CryptMontPublic(&Bob, &BobPublic, NULL) > 0)
        ;
    #if DIRTYCODE_DEBUG
    bResult &= memcmp(aBobPublic25519, BobPublic.X.aData, 32) == 0;
    #endif
    ZPrintf("crypt: bob public x25519 time=%u reuslt=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");

    // generate alice shared
    while (CryptMontSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0)
        ;

    // generate bob shared
    while (CryptMontSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0)
        ;
    #if DIRTYCODE_DEBUG
    bResult &= memcmp(aSharedSecrete25519, AliceShared.X.aData, 32) == 0;
    bResult &= memcmp(aSharedSecrete25519, BobShared.X.aData, 32) == 0;
    #else
    bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0;
    #endif

    ZPrintf("crypt: alice shared x25519 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");
    ZPrintf("crypt: bob shared x25519 time=%u result=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");

    // initialize the context (x448)
    CryptMontInit(&Alice, CRYPTCURVE_X448);
    CryptMontInit(&Bob, CRYPTCURVE_X448);

    #if DIRTYCODE_DEBUG
    ZPrintf("crypt: validating x448 using test vectors\n");
    CryptMontSetPrivateKey(&Alice, aAlicePrivate448);
    CryptMontSetPrivateKey(&Bob, aBobPrivate448);
    #endif

    // generate alice public
    while (CryptMontPublic(&Alice, &AlicePublic, NULL) > 0)
        ;
    #if DIRTYCODE_DEBUG
    bResult &= memcmp(aAlicePublic448, AlicePublic.X.aData, 56) == 0;
    #endif
    ZPrintf("crypt: alice public x448 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");

    // generate bob public
    while (CryptMontPublic(&Bob, &BobPublic, NULL) > 0)
        ;
    #if DIRTYCODE_DEBUG
    bResult &= memcmp(aBobPublic448, BobPublic.X.aData, 56) == 0;
    #endif
    ZPrintf("crypt: bob public x448 time=%u result=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");

    // generate alice shared
    while (CryptMontSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0)
        ;
    // generate bob shared
    while (CryptMontSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0)
        ;
    #if DIRTYCODE_DEBUG
    bResult &= memcmp(aSharedSecret448, AliceShared.X.aData, 56) == 0;
    bResult &= memcmp(aSharedSecret448, BobShared.X.aData, 56) == 0;
    #else
    bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0;
    #endif

    ZPrintf("crypt: alice shared x448 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");
    ZPrintf("crypt: bob shared x448 time=%u result=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE");

    return(bResult ? 0 : 1);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestRand

    \Description
        Test the CryptRand module

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else test failed

    \Version 01/24/2019 (eesponda)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestRand(ZContext *argz, int32_t argc, char *argv[])
{
    static uint8_t aTest[2097152];
    int32_t iSize = 16, iIter = 1;

    for (iIter = 1; iSize <= (signed)sizeof(aTest); iIter += 1, iSize <<= 1)
    {
        uint64_t uTick = NetTickUsec();
        CryptRandGet(aTest, iSize);
        ZPrintf("crypt: rand %d (size=%d) took %u\n",
            iIter, iSize, NetTickDiff(NetTickUsec(), uTick));
    }

    return(0);
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptAesDecryptPrivateKey

    \Description
        Decrypt a base64 encoded cipher text

    \Input iKeyLen          - size of the key (16, 32)
    \Input *pPass           - password uses to encrypt
    \Input *pIv             - generated IV
    \Input *pEncryptedPem   - cipher text we are decrypting (base64 encoded)
    \Input *pDecryptedPem   - plain text we are validating against (base64 encoded)

    \Output
        int32_t             - zero=success, else test failed

    \Version 03/29/2019 (eesponda)
*/
/********************************************************************************F*/
static int32_t _CmdCryptAesDecryptPrivateKey(int32_t iKeyLen, const char *pPass, const uint8_t *pIv, const char *pEncryptedPem, const char *pDecryptedPem)
{
    CryptAesT Aes;
    CryptMD5T MD5;
    uint8_t aKey[32];
    int32_t iKeySize = 0, iPadSize, iPad, iPadStart, iBytesWritten;
    char aPrivateKeyDER[2048], aPrivateKeyPEM[2048];

    // validate key length is valid
    if ((iKeyLen != 16) && (iKeyLen != 32))
    {
        return(1);
    }

    /* The algorithm hashes the password to generate the key of the appropriate size. This is based on the algorithm used in OpenSSL which is not standard.
       https://www.openssl.org/docs/man1.0.2/man3/EVP_BytesToKey.html
       OpenSSL calls their function with MD5 and count as 1 which is exactly how our function behaves. */
    for (iBytesWritten = 0; iBytesWritten < iKeyLen; iBytesWritten += MD5_BINARY_OUT)
    {
        CryptMD5Init(&MD5);

        // if we have written bytes add in the previous output
        if (iBytesWritten > 0)
        {
            CryptMD5Update(&MD5, aKey, MD5_BINARY_OUT);
        }
        CryptMD5Update(&MD5, pPass, (signed)strlen(pPass));
        CryptMD5Update(&MD5, pIv, 8); // the algorithm uses 8 bytes of the IV as salt
        CryptMD5Final(&MD5, aKey+iBytesWritten, iKeyLen-iBytesWritten);
    }

    // decrypt the private key
    CryptAesInit(&Aes, aKey, iKeyLen, CRYPTAES_KEYTYPE_DECRYPT, pIv);
    iKeySize = Base64Decode2((signed)strlen(pEncryptedPem), pEncryptedPem, aPrivateKeyDER);
    CryptAesDecrypt(&Aes, (uint8_t *)aPrivateKeyDER, iKeySize);

    // verify the padding, remove padding if successful
    iPadSize = aPrivateKeyDER[iKeySize-1];
    for (iPad = 0, iPadStart = iKeySize-iPadSize; iPad < iPadSize; iPad += 1)
    {
        if (aPrivateKeyDER[iPadStart+iPad] == iPadSize)
        {
            continue;
        }
        ZPrintf("crypt: aes padding is invalid");
        return(1);
    }
    iKeySize -= iPadSize;

    // base64 encode to validate
    Base64Encode2(aPrivateKeyDER, iKeySize, aPrivateKeyPEM, sizeof(aPrivateKeyPEM));
    return(memcmp(aPrivateKeyPEM, pDecryptedPem, strlen(pDecryptedPem)));
}

/*F********************************************************************************/
/*!
    \Function _CmdCryptTestAes

    \Description
        Test the CryptAes module by decrypting private keys generated by OpenSSL

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output
        int32_t     - zero=success, else test failed

    \Version 03/29/2019 (eesponda)
*/
/********************************************************************************F*/
static int32_t _CmdCryptTestAes(ZContext *argz, int32_t argc, char *argv[])
{
    // DEK-Info: AES-128-CBC,40EF2D13FDB65054775F4FD1AF4E3D24
    static const uint8_t _strIv128[] = { 0x40, 0xEF, 0x2D, 0x13, 0xFD, 0xB6, 0x50, 0x54, 0x77, 0x5F, 0x4F, 0xD1, 0xAF, 0x4E, 0x3D, 0x24 };
    static const char _strEncrypted128[] = "8xdgF3zSj27fwsQc0QiNrWNY8dN5fqw/olTn9URQSt2szU4P/uDm12/lVJeXj/errhq9tSZmJEdFTPYPKfrBRWSkJdzRy3LFxCR/RhgNI96UOdxqhhTrAAU7F/Zy5rp7tCG/KhiB5E2IAaxrsF4Ver+XfI77UV+6iTIqgEPJqNyLt8WFtm22qXgkWg412+IxEcNk4yuohyDyNR2XYry9KXjZlc3WjFBYPy++//hOR4NEBqVB4bxHukaS8b10CxXjkFwKI+t4CTjE0pJ6o3kt2lLHzCxj9z9PhJZOX3o2ail7aiGSNeA1dAgx3TPP8iVn7K6yFWrZBa9ysTjWxHpJyGLdE+wPgwQcCOwxRT/HZqoi4doa1g5d7C+g9lycVlBqy9G5xUdTA/ANHOXhFuisjaeFAIifX1veITdq4cTEjwzj6vGPqG8TVxoEZjnkS7tr2g01HdWsvMDTGIU5ZGMVkZESbQr1NfIh1+MgFfCFB22LH0zajgmutU8N5V/FOvvP3vWAW/PAtYoubyethUYtJPEAFnTtBTvhyT3m94njr1BdJdsurCymXUkk2zYj2uPEMpRatNV6YgiODP3yWzGjl3NRDvhbVcjgNfpEozfLh8qOta/AO30azD4r48MD/JiodzA905KdcCfHQGOC8mJ+g2UK8BIv2+OGIPFoy1nUFFHl/ZPAfkm8XlxPN+avfR92bSf/ZOLhte/yliGTRlFVPn7dTzNpbsxGGQ1vK+HL7otUgQSAknIrN74G5zlnKcdz4VUG03Lj2gG53en+ijmSTqVUM+J7j65C2ssnBaeRu24azpc0ON+8bieS4Ii0PvEwk+E+4krHoBH7xRm4dSDl/gkFwPT2DbTF7SaztNCtR/HlpLxCBMBJvD9dHu4tKCa+eTyQbbnJ2IWPy4x7JN3B2EGneJhApNJKRENBpDhABpv+wt6g07bQPwlCKysn1YKKjHaIV9uAjzdRNTJufzL+phmwZ6Au7DD6wRyur5VbcSfjcCW8K1PdnxdUBiiQ07/OfIwht5GL+rPz4M8eu0Wd97lI42Alk3ui5gu00BJ+DoSkkyXx8WTiOp1U7emgTURlWjLdf8sqH9AoZQFI9J8kCeP9jHzHOB5d77PGbt7LxTvxAZO0l0SRTrj6PYj+nmMyyBv8nw4+U1Ww0WsShYnkJV4BO1cT6x2/nkZlASoG/iFtlisJQOq7Maz+R9HZIvCHeliqdvnf/fjyicDbVAmOc7BJvCINcmICmTqvwqrN1r5fUKAr9jwJORbaLA0i6reZ4xHVySfK9bdkgeg5Nxm8X/eUt57s6FGJnJKpCYxN8m6mzsuY+1uhIbSZTrrchoPFWk5MrBku1XfvKRDxFaQMpW+MxyMRqvoGJvtx00iK1EC1GYtZtqUx5HZ1eTyCvX5CoY+z7Kc3TBivbOs1YxQCo4ID3YPV7uErW7+OFGshOuamZeyzTHTC0FlxF+DWGE35URjt7NuggEFFSS33my7j6GB2eC6VnIKGfzobIpN9DbheJgQegilV21LsZJkatfkrHQ0XPyAYd7GbzG9bjg2/Q3mNMqQ5rNJGrsH0G2+ARJBHDOpMXx3bgRKitgVRwxau";
    // DEK-Info: AES-256-CBC,A530A7D78DA83CB609551E79A920E01D
    static const uint8_t _strIv256[]  = { 0xA5, 0x30, 0xA7, 0xD7, 0x8D, 0xA8, 0x3C, 0xB6, 0x09, 0x55, 0x1E, 0x79, 0xA9, 0x20, 0xE0, 0x1D };
    static const char _strEncrypted256[] = "QKzr7/7Ig3G4h+Gw6opJRRykoMnP7im3kzVV3kVLPj8ZEOa59CjfzERo2rC2Qyh1klkNHogky1rYKF8IHPXdYquVgCqlN2mspr0dnDO44HsIAuVhtM2UsFH0V80ePbl9W8PaMjR+YYqXanLkVsln+xS87ODUptcbLJnSwF0+nWwyR1b71tuMjV28L4i8sVwaGQMJbWQTmyQjV+dNgLO9+JCw7Nrz+rypnPQ4OWfE5b5hfqc9LKxDJsV5IuJ76+7O4DyURyP4+3kjTUbTz/FERao6/CSSlaQShXNsDs8oV1nz5QfkgZdMFshNr994BudgAwb1Xhnfp6A5+4ZhxpSB/Z/e0uItaGJzcmBcLQZv/FXuOM337egUqWo67nTFEHtUnSlsLFZXx4+lpOMN/y0fFJXJOTly3eaOnyn83CsExZansLQeNXwapi4s4L+KZGnEgRp8ouAwkRs7YFa5B+qd4bxTJe7hNabqzMH4RcOFbSW2esyuWu8tcN831nRHI+i76KaGjz2zerWmVjbAOAKJDynRGnZ0wGtWix4mOiciDhSGe95OTP/ayFbIpJAgGdyp3/AA/JSPk7NqBYEqkyELxzRvsUYJdx86tCTALK7z5Cz7CKVjoaksRLrGurphQbPBMY+SRSlI7zEIg3DmtGJh6+pbJ0osV8bK2ffprfm75pod2EDYpFNCLxGWwkQyBQ0oKHs2vIl84Yv087xaE7bcqkIAcr5rilFDUfQhIL8Rdq4A1C0LBNWOkdlCoY0c2+RRU84F6XFm4FfptJkdAvEflMYH8/wfmrxNGj/NzgFsBKSJQvun9C+VMobRgxd0bxZoRH3Gnzk074D1c4kieWRQTfD2zGBmZB/e6SS2TMrRBCsIjmqXC0SyaxW+49aEa4PuDME+udNobAjVKxPh3Mmaxd94D0MhL2TRA1ATDyKJ/vdH5dvem9gcveNk5ZdQRswiJsmiND1RM7Gibbx9vM9wUU1RxI1AuVnM/DqxJ9dfktDb1qWcNKaBf/PrFjRrrK4gsmGJdcuCn935o6H+1/84YwZBnTkmLpjITxLIP5ee6rmZPqLKlD+1c0RDhM/CEDUqAymjw7YPC3RAgcXqK3h3AXj8T5e/na6izS79SuJoQc/zH/9cFRIC0PDTGVefuja+QagIA6zCSU4dnLKB/5JeykaFDnA6lPbZWqWkrjXCfbqXfXNGZg+8EyP6+EnJt33alcouFjnW4N4ZdzbjV08p6g8+MR72lJqMtVjdenl3ZTp8+0NbVU1qNTzPEOazsg2txKICTIAhrJ7hzk1peHrhTg9DBkgngSedFsuOZxCI3PtVEWPJbGH289vjqy9GBGhHkHXW748CnpxGKgHtG/uhJAA6oS7sjVUhkwaw4uOfREmPczokJeolf2QZIPHTkMWaN0SFmSGa6i1bMeFXSGxVLKDhWS0xZFWBwBr1EjgPA13hgXpxZPJ0xIoXTPzXRhpU/k4ALAhw13wYGIARdLJQWV8ttniSV/4NENo0wNX34iWUKaNY4xe6YGP8G4MSKxqj4t6gIgKMbss9EPUR13S6GjphukLJF6bp/w2MeBU9XW24aGHf9LLNk27ORSmyahGe";
    // decrypted pem
    static const char _strDecrypted[] = "MIIEpAIBAAKCAQEAqegt555/R+0iotSoCDVF2Hnk8Z++Q05rY9mDep2On6Z7IEALqt1YTm46JOlyBQ3DocwUTO6evfxipEe1drJ5j0nxcHAuBQFv7QqCIWFQCNBrmAgAjtMXWVBWRVPx4Bxz6J57JFQWnsBVN3o6qXEmsvu1AQ5BLSnoJkgfVNUooAw4fumrcxrJniL9HWefch+5Twl5QBAq4pcMImy2j5CoGAinf3CMeewfj0gx71zSQrAh79stTvNXhKcll7+RXzQzSJ8x4T2SCnRqZoAkCm57dHHYPlUqMVpo5rp4+cvM8ThnGix3Bm3vHmo3ECTXmxWZnqVnyrW+xLrhwAng23Km5wIDAQABAoIBABoeqmfwEssg5nE31fklD26/FnYaw6ofNR0thCvmatWaR1Vm4yRKUNgF1AR2quOiARAvinNlP5wfX563s2ri9xMTH/3UEQ8N8IH1Wpn9Fsu0FsZRfJx4UR+W3RxdUlLMrPj23CFyl+Oh17fmn3wK/BMJ5QKbiWODDMBN2Bs3hb5MwoZPrNu2bDgUIGHEKKNO8JTeaQaL06NsiYyZ06cONTxVRSzbA6/ItcyaNURn3s8LWwq/Nw9CRE/sH8DngTD3nPcTCl9IW5Ly1Khktsps8FlIsPIcoGXxaYepi+EEEhn72AnC37tc5Hhy0raeR/V70SyQzBnJ2CpDj+WTeye3sWkCgYEA0ljKLDYNvSrTezSPPbbCk9+lvZoDIgtJBn7FERCH6wjAPi91+DsTk8UMtSUoOoRuA3CA8kG+t0N78+kpQv8GOtRynJdLB3XZ3bIao98Uy6GwihuKhlIw8aaammtKv4M/a+c9IAB6S92A32HnN8p3MdkKjH+Oe53U/dPnN4aVDj0CgYEAzsh79SdenVkAxwAhx4KcI5XIj2/1PyJ2HmjzFKPbHwZTbRhg0Bb+aqxtSxLL84YzbidG36ROoXgrR03/1D75c3GBintMpZU6iZLYuk105/JwjFfbeT7iVTMwbCyt2KceqbIPfuj4Fq4QHPk/3v0dyqFYrGx3biRWrb75Jja73/MCgYEAjJBfEipRBg+fYV7fNftyjcTPiiNoYpCuDRQb9upYGb0wUp3+tJIt4/qzIj4hYvFqXwwIb1t16hvDslwmganQbPHZYUFNF0AvBhJl8Qo7aFS1l6UN7fLw2Btghz6Zpd88O4w8ca1ADICKBTz0eXjoLDyA6yC2g28WjUTU52sdInUCgYBcx21+wKxeExobL774Qm0GNmVnnkc+jZrL6DDw2NgvImp6L474ruh2OmlLXuKtoFAhI3RUUeeJ4V4hvyiDNcI8/veth8cLIFrEcWPWq3xBufCvt5fc3c81hSM71gwmgk1qvF7hhWwS2QSXy2nSBmXAjgY4Tu6DN8DAckhd0f8X6wKBgQC2rHX1Z81G4d/ztPRx2FvhOqLMZSeMSzxZMxEWJH52MszQUlhHC7q32MiMkRmeqk9rwUQV2vCNljBBwdJuIV0OeSjwVfKznEOCL6vwyOEnVqxGo3IJhVPFFxxtEjhIOKznIDPzqdEfPqmzKn1AqRfXyizk9iucm4WbO7mvY59m8Q==";

    int32_t iResult = 0;
    // test aes128
    iResult |= _CmdCryptAesDecryptPrivateKey(16, "device.key", _strIv128, _strEncrypted128, _strDecrypted);
    // test aes256
    iResult |= _CmdCryptAesDecryptPrivateKey(32, "device.key", _strIv256, _strEncrypted256, _strDecrypted);
    return(iResult);
}

/*** Public functions *************************************************************/


/*F********************************************************************************/
/*!
    \Function CmdCrypt

    \Description
        Test the crypto modules

    \Input *argz   - environment
    \Input argc    - standard number of arguments
    \Input *argv[] - standard arg list

    \Output standard return value

    \Version 01/11/2013 (jbrookes)
*/
/********************************************************************************F*/
int32_t CmdCrypt(ZContext *argz, int32_t argc, char *argv[])
{
    // populate random buffer
    CryptRandGet(_Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom));

    if (argc < 2)
    {
        int32_t iFailed = 0;
        iFailed += _CmdCryptTestArc4(argz, argc, argv);
        iFailed += _CmdCryptTestChaCha(argz, argc, argv);
        iFailed += _CmdCryptTestGcm(argz, argc, argv);
        iFailed += _CmdCryptTestHmac(argz, argc, argv);
        iFailed += _CmdCryptTestMurmurHash3(argz, argc, argv);
        iFailed += _CmdCryptTestMD5(argz, argc, argv);
        iFailed += _CmdCryptTestSha1(argz, argc, argv);
        iFailed += _CmdCryptTestSha2(argz, argc, argv);
        iFailed += _CmdCryptTestRSA(argz, argc, argv);
        iFailed += _CmdCryptTestMont(argz, argc, argv);
        iFailed += _CmdCryptTestNist(argz, argc, argv);
        iFailed += _CmdCryptTestDsa(argz, argc, argv);
        iFailed += _CmdCryptTestRand(argz, argc, argv);
        iFailed += _CmdCryptTestAes(argz, argc, argv);

        ZPrintf("%s: %d tests failed\n", argv[0], iFailed);
    }
    else if (strcmp(argv[1], "rc4") == 0)
    {
        ZPrintf("%s: rc4 test %s\n", argv[0], _CmdCryptTestArc4(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "chacha20") == 0)
    {
        ZPrintf("%s: chacha20 test %s\n", argv[0], _CmdCryptTestChaCha(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "gcm") == 0)
    {
        ZPrintf("%s: gcm test %s\n", argv[0], _CmdCryptTestGcm(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "hmac") == 0)
    {
        ZPrintf("%s: hmac test %s\n", argv[0], _CmdCryptTestHmac(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "murmur3") == 0)
    {
        ZPrintf("%s: murmur3 test %s\n", argv[0], _CmdCryptTestMurmurHash3(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "md5") == 0)
    {
        ZPrintf("%s: md5 test %s\n", argv[0], _CmdCryptTestMD5(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "sha1") == 0)
    {
        ZPrintf("%s: sha1 test %s\n", argv[0], _CmdCryptTestSha1(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "sha2") == 0)
    {
        ZPrintf("%s: sha2 test %s\n", argv[0], _CmdCryptTestSha2(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "rsa") == 0)
    {
        ZPrintf("%s: rsa test %s\n", argv[0], _CmdCryptTestRSA(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "mont") == 0)
    {
        ZPrintf("%s: mont test %s\n", argv[0], _CmdCryptTestMont(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "nist") == 0)
    {
        ZPrintf("%s: nist test %s\n", argv[0], _CmdCryptTestNist(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "dsa") == 0)
    {
        ZPrintf("%s: dsa test %s\n", argv[0], _CmdCryptTestDsa(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "rand") == 0)
    {
        ZPrintf("%s: rand test %s\n", argv[0], _CmdCryptTestRand(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else if (strcmp(argv[1], "aes") == 0)
    {
        ZPrintf("%s: aes test %s\n", argv[0], _CmdCryptTestAes(argz, argc, argv) == 0 ? "succeeded" : "failed");
    }
    else
    {
        ZPrintf("usage: %s [rc4|chacha20|gcm|hmac|murmur3|md5|sha1|sha2|rsa|mont|nist]\n", argv[0]);
        ZPrintf("omitting command executes all tests in sequence\n");
    }

    return(0);
}


