/*
 * pk11mode.c - Test FIPS or NONFIPS Modes for the NSS PKCS11 api.
 *              The goal of this program is to test every function
 *              entry point of the PKCS11 api at least once.
 *              To test in FIPS mode: pk11mode
 *              To test in NONFIPS mode: pk11mode -n
 *              usage: pk11mode -h
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#if defined(XP_UNIX) && defined(DO_FORK_CHECK)
#include <unistd.h>
#include <sys/wait.h>
#else
#ifndef NO_FORK_CHECK
#define NO_FORK_CHECK
#endif
#endif

#ifdef _WIN32
#include <windows.h>
#define LIB_NAME "softokn3.dll"
#endif
#include "prlink.h"
#include "prprf.h"
#include "plgetopt.h"
#include "prenv.h"

#include "pk11table.h"

#define NUM_ELEM(array) (sizeof(array) / sizeof(array[0]))

#ifndef NULL_PTR
#define NULL_PTR 0
#endif

/* Returns constant error string for "CRV".
 * Returns "unknown error" if errNum is unknown.
 */
const char *
PKM_CK_RVtoStr(CK_RV errNum)
{
    const char *err;

    err = getName(errNum, ConstResult);

    if (err)
        return err;

    return "unknown error";
}

#include "pkcs11p.h"

typedef struct CK_C_INITIALIZE_ARGS_NSS {
    CK_CREATEMUTEX CreateMutex;
    CK_DESTROYMUTEX DestroyMutex;
    CK_LOCKMUTEX LockMutex;
    CK_UNLOCKMUTEX UnlockMutex;
    CK_FLAGS flags;
    /* The official PKCS #11 spec does not have a 'LibraryParameters' field, but
     * a reserved field. NSS needs a way to pass instance-specific information
     * to the library (like where to find its config files, etc). This
     * information is usually provided by the installer and passed uninterpreted
     * by NSS to the library, though NSS does know the specifics of the softoken
     * version of this parameter. Most compliant PKCS#11 modules expect this
     * parameter to be NULL, and will return CKR_ARGUMENTS_BAD from
     * C_Initialize if Library parameters is supplied. */
    CK_CHAR_PTR *LibraryParameters;
    /* This field is only present if the LibraryParameters is not NULL. It must
     * be NULL in all cases */
    CK_VOID_PTR pReserved;
} CK_C_INITIALIZE_ARGS_NSS;

#include "pkcs11u.h"

#define MAX_SIG_SZ 128
#define MAX_CIPHER_SZ 128
#define MAX_DATA_SZ 64
#define MAX_DIGEST_SZ 64
#define HMAC_MAX_LENGTH 64
#define FIPSMODE 0
#define NONFIPSMODE 1
#define HYBRIDMODE 2
#define NOMODE 3
int MODE = FIPSMODE;

CK_BBOOL true = CK_TRUE;
CK_BBOOL false = CK_FALSE;
static const CK_BYTE PLAINTEXT[] = { "Firefox  Rules!" };
static const CK_BYTE PLAINTEXT_PAD[] = { "Firefox and thunderbird rule the world!" };
CK_ULONG NUMTESTS = 0;

static const char *slotFlagName[] = {
    "CKF_TOKEN_PRESENT",
    "CKF_REMOVABLE_DEVICE",
    "CKF_HW_SLOT",
    "unknown token flag 0x00000008",
    "unknown token flag 0x00000010",
    "unknown token flag 0x00000020",
    "unknown token flag 0x00000040",
    "unknown token flag 0x00000080",
    "unknown token flag 0x00000100",
    "unknown token flag 0x00000200",
    "unknown token flag 0x00000400",
    "unknown token flag 0x00000800",
    "unknown token flag 0x00001000",
    "unknown token flag 0x00002000",
    "unknown token flag 0x00004000",
    "unknown token flag 0x00008000"
    "unknown token flag 0x00010000",
    "unknown token flag 0x00020000",
    "unknown token flag 0x00040000",
    "unknown token flag 0x00080000",
    "unknown token flag 0x00100000",
    "unknown token flag 0x00200000",
    "unknown token flag 0x00400000",
    "unknown token flag 0x00800000"
    "unknown token flag 0x01000000",
    "unknown token flag 0x02000000",
    "unknown token flag 0x04000000",
    "unknown token flag 0x08000000",
    "unknown token flag 0x10000000",
    "unknown token flag 0x20000000",
    "unknown token flag 0x40000000",
    "unknown token flag 0x80000000"
};

static const char *tokenFlagName[] = {
    "CKF_PKM_RNG",
    "CKF_WRITE_PROTECTED",
    "CKF_LOGIN_REQUIRED",
    "CKF_USER_PIN_INITIALIZED",
    "unknown token flag 0x00000010",
    "CKF_RESTORE_KEY_NOT_NEEDED",
    "CKF_CLOCK_ON_TOKEN",
    "unknown token flag 0x00000080",
    "CKF_PROTECTED_AUTHENTICATION_PATH",
    "CKF_DUAL_CRYPTO_OPERATIONS",
    "CKF_TOKEN_INITIALIZED",
    "CKF_SECONDARY_AUTHENTICATION",
    "unknown token flag 0x00001000",
    "unknown token flag 0x00002000",
    "unknown token flag 0x00004000",
    "unknown token flag 0x00008000",
    "CKF_USER_PIN_COUNT_LOW",
    "CKF_USER_PIN_FINAL_TRY",
    "CKF_USER_PIN_LOCKED",
    "CKF_USER_PIN_TO_BE_CHANGED",
    "CKF_SO_PIN_COUNT_LOW",
    "CKF_SO_PIN_FINAL_TRY",
    "CKF_SO_PIN_LOCKED",
    "CKF_SO_PIN_TO_BE_CHANGED",
    "unknown token flag 0x01000000",
    "unknown token flag 0x02000000",
    "unknown token flag 0x04000000",
    "unknown token flag 0x08000000",
    "unknown token flag 0x10000000",
    "unknown token flag 0x20000000",
    "unknown token flag 0x40000000",
    "unknown token flag 0x80000000"
};

static const unsigned char TLSClientRandom[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0d, 0x90, 0xbb, 0x5e, 0xc6, 0xe1, 0x3f, 0x71,
    0x0a, 0xa2, 0x70, 0x5a, 0x4f, 0xbc, 0x3f, 0x0d
};
static const unsigned char TLSServerRandom[] = {
    0x00, 0x00, 0x1d, 0x4a, 0x7a, 0x0a, 0xa5, 0x01,
    0x8e, 0x79, 0x72, 0xde, 0x9e, 0x2f, 0x8a, 0x0d,
    0xed, 0xb2, 0x5d, 0xf1, 0x14, 0xc2, 0xc6, 0x66,
    0x95, 0x86, 0xb0, 0x0d, 0x87, 0x2a, 0x2a, 0xc9
};

typedef enum {
    CORRECT,
    BOGUS_CLIENT_RANDOM,
    BOGUS_CLIENT_RANDOM_LEN,
    BOGUS_SERVER_RANDOM,
    BOGUS_SERVER_RANDOM_LEN
} enum_random_t;

void
dumpToHash64(const unsigned char *buf, unsigned int bufLen)
{
    unsigned int i;
    for (i = 0; i < bufLen; i += 8) {
        if (i % 32 == 0)
            printf("\n");
        printf(" 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,",
               buf[i], buf[i + 1], buf[i + 2], buf[i + 3],
               buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]);
    }
    printf("\n");
}

#ifdef _WIN32
HMODULE hModule;
#else
PRLibrary *lib;
#endif

/*
 * All api that belongs to pk11mode.c layer start with the prefix PKM_
 */
void PKM_LogIt(const char *fmt, ...);
void PKM_Error(const char *fmt, ...);
CK_SLOT_ID *PKM_GetSlotList(CK_FUNCTION_LIST_PTR pFunctionList,
                            CK_ULONG slotID);
CK_RV PKM_ShowInfo(CK_FUNCTION_LIST_PTR pFunctionList, CK_ULONG slotID);
CK_RV PKM_InitPWforDB(CK_FUNCTION_LIST_PTR pFunctionList,
                      CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                      CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_Mechanism(CK_FUNCTION_LIST_PTR pFunctionList,
                    CK_SLOT_ID *pSlotList, CK_ULONG slotID);
CK_RV PKM_RNG(CK_FUNCTION_LIST_PTR pFunctionList, CK_SLOT_ID *pSlotList,
              CK_ULONG slotID);
CK_RV PKM_SessionLogin(CK_FUNCTION_LIST_PTR pFunctionList,
                       CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                       CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_SecretKey(CK_FUNCTION_LIST_PTR pFunctionList, CK_SLOT_ID *pSlotList,
                    CK_ULONG slotID, CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_PublicKey(CK_FUNCTION_LIST_PTR pFunctionList, CK_SLOT_ID *pSlotList,
                    CK_ULONG slotID, CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_HybridMode(CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen,
                     CK_C_INITIALIZE_ARGS_NSS *initArgs);
CK_RV PKM_FindAllObjects(CK_FUNCTION_LIST_PTR pFunctionList,
                         CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                         CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_MultiObjectManagement(CK_FUNCTION_LIST_PTR pFunctionList,
                                CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                                CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_OperationalState(CK_FUNCTION_LIST_PTR pFunctionList,
                           CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                           CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_LegacyFunctions(CK_FUNCTION_LIST_PTR pFunctionList,
                          CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                          CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_AttributeCheck(CK_FUNCTION_LIST_PTR pFunctionList,
                         CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE obj,
                         CK_ATTRIBUTE_PTR expected_attrs,
                         CK_ULONG expected_attrs_count);
CK_RV PKM_MechCheck(CK_FUNCTION_LIST_PTR pFunctionList,
                    CK_SESSION_HANDLE hSession, CK_MECHANISM_TYPE mechType,
                    CK_FLAGS flags, CK_BBOOL check_sizes,
                    CK_ULONG minkeysize, CK_ULONG maxkeysize);
CK_RV PKM_TLSKeyAndMacDerive(CK_FUNCTION_LIST_PTR pFunctionList,
                             CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                             CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen,
                             CK_MECHANISM_TYPE mechType, enum_random_t rnd);
CK_RV PKM_TLSMasterKeyDerive(CK_FUNCTION_LIST_PTR pFunctionList,
                             CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                             CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen,
                             CK_MECHANISM_TYPE mechType,
                             enum_random_t rnd);
CK_RV PKM_KeyTests(CK_FUNCTION_LIST_PTR pFunctionList,
                   CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                   CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen);
CK_RV PKM_DualFuncSign(CK_FUNCTION_LIST_PTR pFunctionList,
                       CK_SESSION_HANDLE hRwSession,
                       CK_OBJECT_HANDLE publicKey, CK_OBJECT_HANDLE privateKey,
                       CK_MECHANISM *sigMech, CK_OBJECT_HANDLE secretKey,
                       CK_MECHANISM *cryptMech,
                       const CK_BYTE *pData, CK_ULONG pDataLen);
CK_RV PKM_DualFuncDigest(CK_FUNCTION_LIST_PTR pFunctionList,
                         CK_SESSION_HANDLE hSession,
                         CK_OBJECT_HANDLE hSecKey, CK_MECHANISM *cryptMech,
                         CK_OBJECT_HANDLE hSecKeyDigest,
                         CK_MECHANISM *digestMech,
                         const CK_BYTE *pData, CK_ULONG pDataLen);
CK_RV PKM_PubKeySign(CK_FUNCTION_LIST_PTR pFunctionList,
                     CK_SESSION_HANDLE hRwSession,
                     CK_OBJECT_HANDLE hPubKey, CK_OBJECT_HANDLE hPrivKey,
                     CK_MECHANISM *signMech, const CK_BYTE *pData,
                     CK_ULONG dataLen);
CK_RV PKM_SecKeyCrypt(CK_FUNCTION_LIST_PTR pFunctionList,
                      CK_SESSION_HANDLE hSession,
                      CK_OBJECT_HANDLE hSymKey, CK_MECHANISM *cryptMech,
                      const CK_BYTE *pData, CK_ULONG dataLen);
CK_RV PKM_Hmac(CK_FUNCTION_LIST_PTR pFunctionList, CK_SESSION_HANDLE hSession,
               CK_OBJECT_HANDLE sKey, CK_MECHANISM *hmacMech,
               const CK_BYTE *pData, CK_ULONG pDataLen);
CK_RV PKM_Digest(CK_FUNCTION_LIST_PTR pFunctionList,
                 CK_SESSION_HANDLE hRwSession,
                 CK_MECHANISM *digestMech, CK_OBJECT_HANDLE hSecretKey,
                 const CK_BYTE *pData, CK_ULONG pDataLen);
CK_RV PKM_wrapUnwrap(CK_FUNCTION_LIST_PTR pFunctionList,
                     CK_SESSION_HANDLE hSession,
                     CK_OBJECT_HANDLE hPublicKey,
                     CK_OBJECT_HANDLE hPrivateKey,
                     CK_MECHANISM *wrapMechanism,
                     CK_OBJECT_HANDLE hSecretKey,
                     CK_ATTRIBUTE *sKeyTemplate,
                     CK_ULONG skeyTempSize);
CK_RV PKM_RecoverFunctions(CK_FUNCTION_LIST_PTR pFunctionList,
                           CK_SESSION_HANDLE hSession,
                           CK_OBJECT_HANDLE hPubKey, CK_OBJECT_HANDLE hPrivKey,
                           CK_MECHANISM *signMech, const CK_BYTE *pData,
                           CK_ULONG pDataLen);
CK_RV PKM_ForkCheck(int expected, CK_FUNCTION_LIST_PTR fList,
                    PRBool forkAssert, CK_C_INITIALIZE_ARGS_NSS *initArgs);

void PKM_Help();
void PKM_CheckPath(char *string);
char *PKM_FilePasswd(char *pwFile);
static PRBool verbose = PR_FALSE;

int
main(int argc, char **argv)
{
    CK_C_GetFunctionList pC_GetFunctionList;
    CK_FUNCTION_LIST_PTR pFunctionList;
    CK_RV crv = CKR_OK;
    CK_C_INITIALIZE_ARGS_NSS initArgs;
    CK_C_INITIALIZE_ARGS_NSS initArgsRerun; /* rerun selftests */
    CK_SLOT_ID *pSlotList = NULL;
    CK_TOKEN_INFO tokenInfo;
    CK_ULONG slotID = 0; /* slotID == 0 for FIPSMODE */

    CK_UTF8CHAR *pwd = NULL;
    CK_ULONG pwdLen = 0;
    char *moduleSpec = NULL;
    char *moduleSpecRerun = NULL;
    char *configDir = NULL;
    char *dbPrefix = NULL;
    char *disableUnload = NULL;
    PRBool doForkTests = PR_TRUE;

    PLOptStatus os;
    PLOptState *opt = PL_CreateOptState(argc, argv, "nvhf:Fd:p:");
    while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
        if (PL_OPT_BAD == os)
            continue;
        switch (opt->option) {
            case 'F': /* disable fork tests */
                doForkTests = PR_FALSE;
                break;
            case 'n': /* non fips mode */
                MODE = NONFIPSMODE;
                slotID = 1;
                break;
            case 'f': /* password file */
                pwd = (CK_UTF8CHAR *)PKM_FilePasswd((char *)opt->value);
                if (!pwd)
                    PKM_Help();
                break;
            case 'd': /* opt_CertDir */
                if (!opt->value)
                    PKM_Help();
                configDir = strdup(opt->value);
                PKM_CheckPath(configDir);
                break;
            case 'p': /* opt_DBPrefix */
                if (!opt->value)
                    PKM_Help();
                dbPrefix = strdup(opt->value);
                break;
            case 'v':
                verbose = PR_TRUE;
                break;
            case 'h': /* help message */
            default:
                PKM_Help();
                break;
        }
    }
    PL_DestroyOptState(opt);

    if (!pwd) {
        pwd = (CK_UTF8CHAR *)strdup("1Mozilla");
    }
    pwdLen = strlen((const char *)pwd);
    if (!configDir) {
        configDir = strdup(".");
    }
    if (!dbPrefix) {
        dbPrefix = strdup("");
    }

    if (doForkTests) {
        /* first, try to fork without softoken loaded to make sure
         * everything is OK */
        crv = PKM_ForkCheck(123, NULL, PR_FALSE, NULL);
        if (crv != CKR_OK)
            goto cleanup;
    }

#ifdef _WIN32
    hModule = LoadLibrary(LIB_NAME);
    if (hModule == NULL) {
        PKM_Error("cannot load %s\n", LIB_NAME);
        goto cleanup;
    }
    if (MODE == FIPSMODE) {
        /* FIPS mode == FC_GetFunctionList */
        pC_GetFunctionList = (CK_C_GetFunctionList)
            GetProcAddress(hModule, "FC_GetFunctionList");
    } else {
        /* NON FIPS mode  == C_GetFunctionList */
        pC_GetFunctionList = (CK_C_GetFunctionList)
            GetProcAddress(hModule, "C_GetFunctionList");
    }
    if (pC_GetFunctionList == NULL) {
        PKM_Error("cannot load %s\n", LIB_NAME);
        goto cleanup;
    }
#else
    {
        char *libname = NULL;
        /* Get the platform-dependent library name of the NSS cryptographic module */
        libname = PR_GetLibraryName(NULL, "softokn3");
        assert(libname != NULL);
        lib = PR_LoadLibrary(libname);
        assert(lib != NULL);
        PR_FreeLibraryName(libname);
    }
    if (MODE == FIPSMODE) {
        pC_GetFunctionList = (CK_C_GetFunctionList)PR_FindFunctionSymbol(lib,
                                                                         "FC_GetFunctionList");
        assert(pC_GetFunctionList != NULL);
        slotID = 0;
    } else {
        pC_GetFunctionList = (CK_C_GetFunctionList)PR_FindFunctionSymbol(lib,
                                                                         "C_GetFunctionList");
        assert(pC_GetFunctionList != NULL);
        slotID = 1;
    }
#endif

    if (MODE == FIPSMODE) {
        printf("Loaded FC_GetFunctionList for FIPS MODE; slotID %d \n",
               (int)slotID);
    } else {
        printf("loaded C_GetFunctionList for NON FIPS MODE; slotID %d \n",
               (int)slotID);
    }

    crv = (*pC_GetFunctionList)(&pFunctionList);
    assert(crv == CKR_OK);

    if (doForkTests) {
        /* now, try to fork with softoken loaded, but not initialized */
        crv = PKM_ForkCheck(CKR_CRYPTOKI_NOT_INITIALIZED, pFunctionList,
                            PR_TRUE, NULL);
        if (crv != CKR_OK)
            goto cleanup;
    }

    initArgs.CreateMutex = NULL;
    initArgs.DestroyMutex = NULL;
    initArgs.LockMutex = NULL;
    initArgs.UnlockMutex = NULL;
    initArgs.flags = CKF_OS_LOCKING_OK;
    moduleSpec = PR_smprintf("configdir='%s' certPrefix='%s' "
                             "keyPrefix='%s' secmod='secmod.db' flags= ",
                             configDir, dbPrefix, dbPrefix);
    moduleSpecRerun = PR_smprintf("configdir='%s' certPrefix='%s' "
                                  "keyPrefix='%s' secmod='secmod.db' flags=forcePOST ",
                                  configDir, dbPrefix, dbPrefix);
    initArgs.LibraryParameters = (CK_CHAR_PTR *)moduleSpec;
    initArgs.pReserved = NULL;
    initArgsRerun = initArgs;
    initArgsRerun.LibraryParameters = (CK_CHAR_PTR *)moduleSpecRerun;

    /*DebugBreak();*/
    /* FIPSMODE invokes FC_Initialize as pFunctionList->C_Initialize */
    /* NSS cryptographic module library initialization for the FIPS  */
    /* Approved mode when FC_Initialize is envoked will perfom       */
    /* software integrity test, and power-up self-tests before       */
    /* FC_Initialize returns                                         */
    crv = pFunctionList->C_Initialize(&initArgs);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Initialize succeeded\n");
    } else {
        PKM_Error("C_Initialize failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    if (doForkTests) {
        /* Disable core on fork for this test, since we are testing the
         * pathological case, and if enabled, the child process would dump
         * core in C_GetTokenInfo .
         * We can still differentiate the correct from incorrect behavior
         * by the PKCS#11 return code.
         */
        /* try to fork with softoken both loaded and initialized */
        crv = PKM_ForkCheck(CKR_DEVICE_ERROR, pFunctionList, PR_FALSE, NULL);
        if (crv != CKR_OK)
            goto cleanup;
    }

    if (doForkTests) {
        /* In this next test, we fork and try to re-initialize softoken in
         * the child. This should now work because softoken has the ability
         * to hard reset.
         */
        /* try to fork with softoken both loaded and initialized */
        crv = PKM_ForkCheck(CKR_OK, pFunctionList, PR_TRUE, &initArgs);
        if (crv != CKR_OK)
            goto cleanup;
    }

    crv = PKM_ShowInfo(pFunctionList, slotID);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_ShowInfo succeeded\n");
    } else {
        PKM_Error("PKM_ShowInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    pSlotList = PKM_GetSlotList(pFunctionList, slotID);
    if (pSlotList == NULL) {
        PKM_Error("PKM_GetSlotList failed with \n");
        goto cleanup;
    }
    crv = pFunctionList->C_GetTokenInfo(pSlotList[slotID], &tokenInfo);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GetTokenInfo succeeded\n\n");
    } else {
        PKM_Error("C_GetTokenInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    if (!(tokenInfo.flags & CKF_USER_PIN_INITIALIZED)) {
        PKM_LogIt("Initing PW for DB\n");
        crv = PKM_InitPWforDB(pFunctionList, pSlotList, slotID,
                              pwd, pwdLen);
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_InitPWforDB succeeded\n\n");
        } else {
            PKM_Error("PKM_InitPWforDB failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            goto cleanup;
        }
    } else {
        PKM_LogIt("using existing DB\n");
    }

    /* general mechanism by token */
    crv = PKM_Mechanism(pFunctionList, pSlotList, slotID);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_Mechanism succeeded\n\n");
    } else {
        PKM_Error("PKM_Mechanism failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    /* RNG example without Login */
    crv = PKM_RNG(pFunctionList, pSlotList, slotID);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_RNG succeeded\n\n");
    } else {
        PKM_Error("PKM_RNG failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    crv = PKM_SessionLogin(pFunctionList, pSlotList, slotID,
                           pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_SessionLogin succeeded\n\n");
    } else {
        PKM_Error("PKM_SessionLogin failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    /*
     * PKM_KeyTest creates RSA,DSA public keys
     * and AES, DES3 secret keys.
     * then does digest, hmac, encrypt/decrypt, signing operations.
     */
    crv = PKM_KeyTests(pFunctionList, pSlotList, slotID,
                       pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_KeyTests succeeded\n\n");
    } else {
        PKM_Error("PKM_KeyTest failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    crv = PKM_SecretKey(pFunctionList, pSlotList, slotID, pwd,
                        pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_SecretKey succeeded\n\n");
    } else {
        PKM_Error("PKM_SecretKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    crv = PKM_PublicKey(pFunctionList, pSlotList, slotID,
                        pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_PublicKey succeeded\n\n");
    } else {
        PKM_Error("PKM_PublicKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_OperationalState(pFunctionList, pSlotList, slotID,
                               pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_OperationalState succeeded\n\n");
    } else {
        PKM_Error("PKM_OperationalState failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_MultiObjectManagement(pFunctionList, pSlotList, slotID,
                                    pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_MultiObjectManagement succeeded\n\n");
    } else {
        PKM_Error("PKM_MultiObjectManagement failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_LegacyFunctions(pFunctionList, pSlotList, slotID,
                              pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_LegacyFunctions succeeded\n\n");
    } else {
        PKM_Error("PKM_LegacyFunctions failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_TLSKeyAndMacDerive(pFunctionList, pSlotList, slotID,
                                 pwd, pwdLen,
                                 CKM_TLS_KEY_AND_MAC_DERIVE, CORRECT);

    if (crv == CKR_OK) {
        PKM_LogIt("PKM_TLSKeyAndMacDerive succeeded\n\n");
    } else {
        PKM_Error("PKM_TLSKeyAndMacDerive failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_TLSMasterKeyDerive(pFunctionList, pSlotList, slotID,
                                 pwd, pwdLen,
                                 CKM_TLS_MASTER_KEY_DERIVE,
                                 CORRECT);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_TLSMasterKeyDerive succeeded\n\n");
    } else {
        PKM_Error("PKM_TLSMasterKeyDerive failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_TLSMasterKeyDerive(pFunctionList, pSlotList, slotID,
                                 pwd, pwdLen,
                                 CKM_TLS_MASTER_KEY_DERIVE_DH,
                                 CORRECT);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_TLSMasterKeyDerive succeeded\n\n");
    } else {
        PKM_Error("PKM_TLSMasterKeyDerive failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = PKM_FindAllObjects(pFunctionList, pSlotList, slotID,
                             pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_FindAllObjects succeeded\n\n");
    } else {
        PKM_Error("PKM_FindAllObjects failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }
    crv = pFunctionList->C_Finalize(NULL);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Finalize succeeded\n");
    } else {
        PKM_Error("C_Finalize failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    if (doForkTests) {
        /* try to fork with softoken still loaded, but de-initialized */
        crv = PKM_ForkCheck(CKR_CRYPTOKI_NOT_INITIALIZED, pFunctionList,
                            PR_TRUE, NULL);
        if (crv != CKR_OK)
            goto cleanup;
    }

    free(pSlotList);

    /* demonstrate how an application can be in Hybrid mode */
    /* PKM_HybridMode shows how to switch between NONFIPS */
    /* mode to FIPS mode */

    PKM_LogIt("Testing Hybrid mode \n");
    crv = PKM_HybridMode(pwd, pwdLen, &initArgs);
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_HybridMode succeeded\n");
    } else {
        PKM_Error("PKM_HybridMode failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        goto cleanup;
    }

    if (doForkTests) {
        /* testing one more C_Initialize / C_Finalize to exercise getpid()
         * fork check code */
        crv = pFunctionList->C_Initialize(&initArgsRerun);
        if (crv == CKR_OK) {
            PKM_LogIt("C_Initialize succeeded\n");
        } else {
            PKM_Error("C_Initialize failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            goto cleanup;
        }
        crv = pFunctionList->C_Finalize(NULL);
        if (crv == CKR_OK) {
            PKM_LogIt("C_Finalize succeeded\n");
        } else {
            PKM_Error("C_Finalize failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            goto cleanup;
        }
        /* try to C_Initialize / C_Finalize in child. This should succeed */
        crv = PKM_ForkCheck(CKR_OK, pFunctionList, PR_TRUE, &initArgs);
    }

    PKM_LogIt("unloading NSS PKCS # 11 softoken and exiting\n");

cleanup:

    if (pwd) {
        free(pwd);
    }
    if (configDir) {
        free(configDir);
    }
    if (dbPrefix) {
        free(dbPrefix);
    }
    if (moduleSpec) {
        PR_smprintf_free(moduleSpec);
    }
    if (moduleSpecRerun) {
        PR_smprintf_free(moduleSpecRerun);
    }

#ifdef _WIN32
    FreeLibrary(hModule);
#else
    disableUnload = PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
    if (!disableUnload) {
        PR_UnloadLibrary(lib);
    }
#endif
    if (CKR_OK == crv && doForkTests && !disableUnload) {
        /* try to fork with softoken both de-initialized and unloaded */
        crv = PKM_ForkCheck(123, NULL, PR_TRUE, NULL);
    }

    printf("**** Total number of TESTS ran in %s is %d. ****\n",
           ((MODE == FIPSMODE) ? "FIPS MODE" : "NON FIPS MODE"), (int)NUMTESTS);
    if (CKR_OK == crv) {
        printf("**** ALL TESTS PASSED ****\n");
    }

    return crv;
}

/*
 *  PKM_KeyTests
 *
 *
 */

CK_RV
PKM_KeyTests(CK_FUNCTION_LIST_PTR pFunctionList,
             CK_SLOT_ID *pSlotList, CK_ULONG slotID,
             CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_SESSION_HANDLE hRwSession;

    CK_RV crv = CKR_OK;

    /*** DSA Key ***/
    CK_MECHANISM dsaParamGenMech;
    CK_ULONG primeBits = 1024;
    CK_ATTRIBUTE dsaParamGenTemplate[1];
    CK_OBJECT_HANDLE hDsaParams = CK_INVALID_HANDLE;
    CK_BYTE DSA_P[128];
    CK_BYTE DSA_Q[20];
    CK_BYTE DSA_G[128];
    CK_MECHANISM dsaKeyPairGenMech;
    CK_ATTRIBUTE dsaPubKeyTemplate[5];
    CK_ATTRIBUTE dsaPrivKeyTemplate[5];
    CK_OBJECT_HANDLE hDSApubKey = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE hDSAprivKey = CK_INVALID_HANDLE;

    /**** RSA Key ***/
    CK_KEY_TYPE rsatype = CKK_RSA;
    CK_MECHANISM rsaKeyPairGenMech;
    CK_BYTE subject[] = { "RSA Private Key" };
    CK_ULONG modulusBits = 1024;
    CK_BYTE publicExponent[] = { 0x01, 0x00, 0x01 };
    CK_BYTE id[] = { "RSA123" };
    CK_ATTRIBUTE rsaPubKeyTemplate[9];
    CK_ATTRIBUTE rsaPrivKeyTemplate[11];
    CK_OBJECT_HANDLE hRSApubKey = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE hRSAprivKey = CK_INVALID_HANDLE;

    /*** AES Key ***/
    CK_MECHANISM sAESKeyMech = {
        CKM_AES_KEY_GEN, NULL, 0
    };
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE keyAESType = CKK_AES;
    CK_UTF8CHAR AESlabel[] = "An AES secret key object";
    CK_ULONG AESvalueLen = 32;
    CK_ATTRIBUTE sAESKeyTemplate[9];
    CK_OBJECT_HANDLE hAESSecKey;

    /*** DES3 Key ***/
    CK_KEY_TYPE keyDES3Type = CKK_DES3;
    CK_UTF8CHAR DES3label[] = "An Triple DES secret key object";
    CK_ULONG DES3valueLen = 56;
    CK_MECHANISM sDES3KeyGenMechanism = {
        CKM_DES3_KEY_GEN, NULL, 0
    };
    CK_ATTRIBUTE sDES3KeyTemplate[9];
    CK_OBJECT_HANDLE hDES3SecKey;

    CK_MECHANISM dsaWithSha1Mech = {
        CKM_DSA_SHA1, NULL, 0
    };

    CK_BYTE IV[16];
    CK_MECHANISM mech_DES3_CBC;
    CK_MECHANISM mech_DES3_CBC_PAD;
    CK_MECHANISM mech_AES_CBC_PAD;
    CK_MECHANISM mech_AES_CBC;
    struct mech_str {
        CK_ULONG mechanism;
        const char *mechanismStr;
    };

    typedef struct mech_str mech_str;

    mech_str digestMechs[] = {
        { CKM_SHA_1, "CKM_SHA_1 " },
        { CKM_SHA224, "CKM_SHA224" },
        { CKM_SHA256, "CKM_SHA256" },
        { CKM_SHA384, "CKM_SHA384" },
        { CKM_SHA512, "CKM_SHA512" }
    };
    mech_str hmacMechs[] = {
        { CKM_SHA_1_HMAC, "CKM_SHA_1_HMAC" },
        { CKM_SHA224_HMAC, "CKM_SHA224_HMAC" },
        { CKM_SHA256_HMAC, "CKM_SHA256_HMAC" },
        { CKM_SHA384_HMAC, "CKM_SHA384_HMAC" },
        { CKM_SHA512_HMAC, "CKM_SHA512_HMAC" }
    };
    mech_str sigRSAMechs[] = {
        { CKM_SHA1_RSA_PKCS, "CKM_SHA1_RSA_PKCS" },
        { CKM_SHA224_RSA_PKCS, "CKM_SHA224_RSA_PKCS" },
        { CKM_SHA256_RSA_PKCS, "CKM_SHA256_RSA_PKCS" },
        { CKM_SHA384_RSA_PKCS, "CKM_SHA384_RSA_PKCS" },
        { CKM_SHA512_RSA_PKCS, "CKM_SHA512_RSA_PKCS" }
    };

    CK_ULONG digestMechsSZ = NUM_ELEM(digestMechs);
    CK_ULONG sigRSAMechsSZ = NUM_ELEM(sigRSAMechs);
    CK_ULONG hmacMechsSZ = NUM_ELEM(hmacMechs);
    CK_MECHANISM mech;

    unsigned int i;

    NUMTESTS++; /* increment NUMTESTS */

    /* DSA key init */
    dsaParamGenMech.mechanism = CKM_DSA_PARAMETER_GEN;
    dsaParamGenMech.pParameter = NULL_PTR;
    dsaParamGenMech.ulParameterLen = 0;
    dsaParamGenTemplate[0].type = CKA_PRIME_BITS;
    dsaParamGenTemplate[0].pValue = &primeBits;
    dsaParamGenTemplate[0].ulValueLen = sizeof(primeBits);
    dsaPubKeyTemplate[0].type = CKA_PRIME;
    dsaPubKeyTemplate[0].pValue = DSA_P;
    dsaPubKeyTemplate[0].ulValueLen = sizeof(DSA_P);
    dsaPubKeyTemplate[1].type = CKA_SUBPRIME;
    dsaPubKeyTemplate[1].pValue = DSA_Q;
    dsaPubKeyTemplate[1].ulValueLen = sizeof(DSA_Q);
    dsaPubKeyTemplate[2].type = CKA_BASE;
    dsaPubKeyTemplate[2].pValue = DSA_G;
    dsaPubKeyTemplate[2].ulValueLen = sizeof(DSA_G);
    dsaPubKeyTemplate[3].type = CKA_TOKEN;
    dsaPubKeyTemplate[3].pValue = &true;
    dsaPubKeyTemplate[3].ulValueLen = sizeof(true);
    dsaPubKeyTemplate[4].type = CKA_VERIFY;
    dsaPubKeyTemplate[4].pValue = &true;
    dsaPubKeyTemplate[4].ulValueLen = sizeof(true);
    dsaKeyPairGenMech.mechanism = CKM_DSA_KEY_PAIR_GEN;
    dsaKeyPairGenMech.pParameter = NULL_PTR;
    dsaKeyPairGenMech.ulParameterLen = 0;
    dsaPrivKeyTemplate[0].type = CKA_TOKEN;
    dsaPrivKeyTemplate[0].pValue = &true;
    dsaPrivKeyTemplate[0].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[1].type = CKA_PRIVATE;
    dsaPrivKeyTemplate[1].pValue = &true;
    dsaPrivKeyTemplate[1].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[2].type = CKA_SENSITIVE;
    dsaPrivKeyTemplate[2].pValue = &true;
    dsaPrivKeyTemplate[2].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[3].type = CKA_SIGN,
    dsaPrivKeyTemplate[3].pValue = &true;
    dsaPrivKeyTemplate[3].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[4].type = CKA_EXTRACTABLE;
    dsaPrivKeyTemplate[4].pValue = &true;
    dsaPrivKeyTemplate[4].ulValueLen = sizeof(true);

    /* RSA key init */
    rsaKeyPairGenMech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
    rsaKeyPairGenMech.pParameter = NULL_PTR;
    rsaKeyPairGenMech.ulParameterLen = 0;

    rsaPubKeyTemplate[0].type = CKA_KEY_TYPE;
    rsaPubKeyTemplate[0].pValue = &rsatype;
    rsaPubKeyTemplate[0].ulValueLen = sizeof(rsatype);
    rsaPubKeyTemplate[1].type = CKA_PRIVATE;
    rsaPubKeyTemplate[1].pValue = &true;
    rsaPubKeyTemplate[1].ulValueLen = sizeof(true);
    rsaPubKeyTemplate[2].type = CKA_ENCRYPT;
    rsaPubKeyTemplate[2].pValue = &true;
    rsaPubKeyTemplate[2].ulValueLen = sizeof(true);
    rsaPubKeyTemplate[3].type = CKA_DECRYPT;
    rsaPubKeyTemplate[3].pValue = &true;
    rsaPubKeyTemplate[3].ulValueLen = sizeof(true);
    rsaPubKeyTemplate[4].type = CKA_VERIFY;
    rsaPubKeyTemplate[4].pValue = &true;
    rsaPubKeyTemplate[4].ulValueLen = sizeof(true);
    rsaPubKeyTemplate[5].type = CKA_SIGN;
    rsaPubKeyTemplate[5].pValue = &true;
    rsaPubKeyTemplate[5].ulValueLen = sizeof(true);
    rsaPubKeyTemplate[6].type = CKA_WRAP;
    rsaPubKeyTemplate[6].pValue = &true;
    rsaPubKeyTemplate[6].ulValueLen = sizeof(true);
    rsaPubKeyTemplate[7].type = CKA_MODULUS_BITS;
    rsaPubKeyTemplate[7].pValue = &modulusBits;
    rsaPubKeyTemplate[7].ulValueLen = sizeof(modulusBits);
    rsaPubKeyTemplate[8].type = CKA_PUBLIC_EXPONENT;
    rsaPubKeyTemplate[8].pValue = publicExponent;
    rsaPubKeyTemplate[8].ulValueLen = sizeof(publicExponent);

    rsaPrivKeyTemplate[0].type = CKA_KEY_TYPE;
    rsaPrivKeyTemplate[0].pValue = &rsatype;
    rsaPrivKeyTemplate[0].ulValueLen = sizeof(rsatype);
    rsaPrivKeyTemplate[1].type = CKA_TOKEN;
    rsaPrivKeyTemplate[1].pValue = &true;
    rsaPrivKeyTemplate[1].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[2].type = CKA_PRIVATE;
    rsaPrivKeyTemplate[2].pValue = &true;
    rsaPrivKeyTemplate[2].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[3].type = CKA_SUBJECT;
    rsaPrivKeyTemplate[3].pValue = subject;
    rsaPrivKeyTemplate[3].ulValueLen = sizeof(subject);
    rsaPrivKeyTemplate[4].type = CKA_ID;
    rsaPrivKeyTemplate[4].pValue = id;
    rsaPrivKeyTemplate[4].ulValueLen = sizeof(id);
    rsaPrivKeyTemplate[5].type = CKA_SENSITIVE;
    rsaPrivKeyTemplate[5].pValue = &true;
    rsaPrivKeyTemplate[5].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[6].type = CKA_ENCRYPT;
    rsaPrivKeyTemplate[6].pValue = &true;
    rsaPrivKeyTemplate[6].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[7].type = CKA_DECRYPT;
    rsaPrivKeyTemplate[7].pValue = &true;
    rsaPrivKeyTemplate[7].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[8].type = CKA_VERIFY;
    rsaPrivKeyTemplate[8].pValue = &true;
    rsaPrivKeyTemplate[8].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[9].type = CKA_SIGN;
    rsaPrivKeyTemplate[9].pValue = &true;
    rsaPrivKeyTemplate[9].ulValueLen = sizeof(true);
    rsaPrivKeyTemplate[10].type = CKA_UNWRAP;
    rsaPrivKeyTemplate[10].pValue = &true;
    rsaPrivKeyTemplate[10].ulValueLen = sizeof(true);

    /* AES key template */
    sAESKeyTemplate[0].type = CKA_CLASS;
    sAESKeyTemplate[0].pValue = &class;
    sAESKeyTemplate[0].ulValueLen = sizeof(class);
    sAESKeyTemplate[1].type = CKA_KEY_TYPE;
    sAESKeyTemplate[1].pValue = &keyAESType;
    sAESKeyTemplate[1].ulValueLen = sizeof(keyAESType);
    sAESKeyTemplate[2].type = CKA_LABEL;
    sAESKeyTemplate[2].pValue = AESlabel;
    sAESKeyTemplate[2].ulValueLen = sizeof(AESlabel) - 1;
    sAESKeyTemplate[3].type = CKA_ENCRYPT;
    sAESKeyTemplate[3].pValue = &true;
    sAESKeyTemplate[3].ulValueLen = sizeof(true);
    sAESKeyTemplate[4].type = CKA_DECRYPT;
    sAESKeyTemplate[4].pValue = &true;
    sAESKeyTemplate[4].ulValueLen = sizeof(true);
    sAESKeyTemplate[5].type = CKA_SIGN;
    sAESKeyTemplate[5].pValue = &true;
    sAESKeyTemplate[5].ulValueLen = sizeof(true);
    sAESKeyTemplate[6].type = CKA_VERIFY;
    sAESKeyTemplate[6].pValue = &true;
    sAESKeyTemplate[6].ulValueLen = sizeof(true);
    sAESKeyTemplate[7].type = CKA_UNWRAP;
    sAESKeyTemplate[7].pValue = &true;
    sAESKeyTemplate[7].ulValueLen = sizeof(true);
    sAESKeyTemplate[8].type = CKA_VALUE_LEN;
    sAESKeyTemplate[8].pValue = &AESvalueLen;
    sAESKeyTemplate[8].ulValueLen = sizeof(AESvalueLen);

    /* DES3 key template */
    sDES3KeyTemplate[0].type = CKA_CLASS;
    sDES3KeyTemplate[0].pValue = &class;
    sDES3KeyTemplate[0].ulValueLen = sizeof(class);
    sDES3KeyTemplate[1].type = CKA_KEY_TYPE;
    sDES3KeyTemplate[1].pValue = &keyDES3Type;
    sDES3KeyTemplate[1].ulValueLen = sizeof(keyDES3Type);
    sDES3KeyTemplate[2].type = CKA_LABEL;
    sDES3KeyTemplate[2].pValue = DES3label;
    sDES3KeyTemplate[2].ulValueLen = sizeof(DES3label) - 1;
    sDES3KeyTemplate[3].type = CKA_ENCRYPT;
    sDES3KeyTemplate[3].pValue = &true;
    sDES3KeyTemplate[3].ulValueLen = sizeof(true);
    sDES3KeyTemplate[4].type = CKA_DECRYPT;
    sDES3KeyTemplate[4].pValue = &true;
    sDES3KeyTemplate[4].ulValueLen = sizeof(true);
    sDES3KeyTemplate[5].type = CKA_UNWRAP;
    sDES3KeyTemplate[5].pValue = &true;
    sDES3KeyTemplate[5].ulValueLen = sizeof(true);
    sDES3KeyTemplate[6].type = CKA_SIGN,
    sDES3KeyTemplate[6].pValue = &true;
    sDES3KeyTemplate[6].ulValueLen = sizeof(true);
    sDES3KeyTemplate[7].type = CKA_VERIFY;
    sDES3KeyTemplate[7].pValue = &true;
    sDES3KeyTemplate[7].ulValueLen = sizeof(true);
    sDES3KeyTemplate[8].type = CKA_VALUE_LEN;
    sDES3KeyTemplate[8].pValue = &DES3valueLen;
    sDES3KeyTemplate[8].ulValueLen = sizeof(DES3valueLen);

    /* mech init */
    memset(IV, 0x01, sizeof(IV));
    mech_DES3_CBC.mechanism = CKM_DES3_CBC;
    mech_DES3_CBC.pParameter = IV;
    mech_DES3_CBC.ulParameterLen = sizeof(IV);
    mech_DES3_CBC_PAD.mechanism = CKM_DES3_CBC_PAD;
    mech_DES3_CBC_PAD.pParameter = IV;
    mech_DES3_CBC_PAD.ulParameterLen = sizeof(IV);
    mech_AES_CBC.mechanism = CKM_AES_CBC;
    mech_AES_CBC.pParameter = IV;
    mech_AES_CBC.ulParameterLen = sizeof(IV);
    mech_AES_CBC_PAD.mechanism = CKM_AES_CBC_PAD;
    mech_AES_CBC_PAD.pParameter = IV;
    mech_AES_CBC_PAD.ulParameterLen = sizeof(IV);

    crv = pFunctionList->C_OpenSession(pSlotList[slotID],
                                       CKF_RW_SESSION | CKF_SERIAL_SESSION,
                                       NULL, NULL, &hRwSession);
    if (crv == CKR_OK) {
        PKM_LogIt("Opening a read/write session succeeded\n");
    } else {
        PKM_Error("Opening a read/write session failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (MODE == FIPSMODE) {
        crv = pFunctionList->C_GenerateKey(hRwSession, &sAESKeyMech,
                                           sAESKeyTemplate,
                                           NUM_ELEM(sAESKeyTemplate),
                                           &hAESSecKey);
        if (crv == CKR_OK) {
            PKM_Error("C_GenerateKey succeeded when not logged in.\n");
            return CKR_GENERAL_ERROR;
        } else {
            PKM_LogIt("C_GenerateKey returned as EXPECTED with 0x%08X, %-26s\n"
                      "since not logged in\n",
                      crv, PKM_CK_RVtoStr(crv));
        }
        crv = pFunctionList->C_GenerateKeyPair(hRwSession, &rsaKeyPairGenMech,
                                               rsaPubKeyTemplate,
                                               NUM_ELEM(rsaPubKeyTemplate),
                                               rsaPrivKeyTemplate,
                                               NUM_ELEM(rsaPrivKeyTemplate),
                                               &hRSApubKey, &hRSAprivKey);
        if (crv == CKR_OK) {
            PKM_Error("C_GenerateKeyPair succeeded when not logged in.\n");
            return CKR_GENERAL_ERROR;
        } else {
            PKM_LogIt("C_GenerateKeyPair returned as EXPECTED with 0x%08X, "
                      "%-26s\n since not logged in\n",
                      crv,
                      PKM_CK_RVtoStr(crv));
        }
    }

    crv = pFunctionList->C_Login(hRwSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate an AES key ... \n");
    /* generate an AES Secret Key */
    crv = pFunctionList->C_GenerateKey(hRwSession, &sAESKeyMech,
                                       sAESKeyTemplate,
                                       NUM_ELEM(sAESKeyTemplate),
                                       &hAESSecKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKey AES succeeded\n");
    } else {
        PKM_Error("C_GenerateKey AES failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate an 3DES key ...\n");
    /* generate an 3DES Secret Key */
    crv = pFunctionList->C_GenerateKey(hRwSession, &sDES3KeyGenMechanism,
                                       sDES3KeyTemplate,
                                       NUM_ELEM(sDES3KeyTemplate),
                                       &hDES3SecKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKey DES3 succeeded\n");
    } else {
        PKM_Error("C_GenerateKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate DSA PQG domain parameters ... \n");
    /* Generate DSA domain parameters PQG */
    crv = pFunctionList->C_GenerateKey(hRwSession, &dsaParamGenMech,
                                       dsaParamGenTemplate,
                                       1,
                                       &hDsaParams);
    if (crv == CKR_OK) {
        PKM_LogIt("DSA domain parameter generation succeeded\n");
    } else {
        PKM_Error("DSA domain parameter generation failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_GetAttributeValue(hRwSession, hDsaParams,
                                             dsaPubKeyTemplate, 3);
    if (crv == CKR_OK) {
        PKM_LogIt("Getting DSA domain parameters succeeded\n");
    } else {
        PKM_Error("Getting DSA domain parameters failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DestroyObject(hRwSession, hDsaParams);
    if (crv == CKR_OK) {
        PKM_LogIt("Destroying DSA domain parameters succeeded\n");
    } else {
        PKM_Error("Destroying DSA domain parameters failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate a DSA key pair ... \n");
    /* Generate a persistent DSA key pair */
    crv = pFunctionList->C_GenerateKeyPair(hRwSession, &dsaKeyPairGenMech,
                                           dsaPubKeyTemplate,
                                           NUM_ELEM(dsaPubKeyTemplate),
                                           dsaPrivKeyTemplate,
                                           NUM_ELEM(dsaPrivKeyTemplate),
                                           &hDSApubKey, &hDSAprivKey);
    if (crv == CKR_OK) {
        PKM_LogIt("DSA key pair generation succeeded\n");
    } else {
        PKM_Error("DSA key pair generation failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate a RSA key pair ... \n");
    /*** GEN RSA Key ***/
    crv = pFunctionList->C_GenerateKeyPair(hRwSession, &rsaKeyPairGenMech,
                                           rsaPubKeyTemplate,
                                           NUM_ELEM(rsaPubKeyTemplate),
                                           rsaPrivKeyTemplate,
                                           NUM_ELEM(rsaPrivKeyTemplate),
                                           &hRSApubKey, &hRSAprivKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKeyPair created an RSA key pair. \n");
    } else {
        PKM_Error("C_GenerateKeyPair failed to create an RSA key pair.\n"
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("**** Generation of keys completed ***** \n");

    mech.mechanism = CKM_RSA_PKCS;
    mech.pParameter = NULL;
    mech.ulParameterLen = 0;

    crv = PKM_wrapUnwrap(pFunctionList,
                         hRwSession,
                         hRSApubKey, hRSAprivKey,
                         &mech,
                         hAESSecKey,
                         sAESKeyTemplate,
                         NUM_ELEM(sAESKeyTemplate));

    if (crv == CKR_OK) {
        PKM_LogIt("PKM_wrapUnwrap using RSA keypair to wrap AES key "
                  "succeeded\n\n");
    } else {
        PKM_Error("PKM_wrapUnwrap using RSA keypair to wrap AES key failed "
                  "with 0x%08X, %-26s\n",
                  crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = PKM_wrapUnwrap(pFunctionList,
                         hRwSession,
                         hRSApubKey, hRSAprivKey,
                         &mech,
                         hDES3SecKey,
                         sDES3KeyTemplate,
                         NUM_ELEM(sDES3KeyTemplate));

    if (crv == CKR_OK) {
        PKM_LogIt("PKM_wrapUnwrap using RSA keypair to wrap DES3 key "
                  "succeeded\n\n");
    } else {
        PKM_Error("PKM_wrapUnwrap using RSA keypair to wrap DES3 key "
                  "failed with 0x%08X, %-26s\n",
                  crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = PKM_SecKeyCrypt(pFunctionList, hRwSession,
                          hAESSecKey, &mech_AES_CBC_PAD,
                          PLAINTEXT_PAD, sizeof(PLAINTEXT_PAD));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_SecKeyCrypt succeeded \n\n");
    } else {
        PKM_Error("PKM_SecKeyCrypt failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = PKM_SecKeyCrypt(pFunctionList, hRwSession,
                          hAESSecKey, &mech_AES_CBC,
                          PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_SecKeyCrypt AES succeeded \n\n");
    } else {
        PKM_Error("PKM_SecKeyCrypt failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = PKM_SecKeyCrypt(pFunctionList, hRwSession,
                          hDES3SecKey, &mech_DES3_CBC,
                          PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_SecKeyCrypt DES3 succeeded \n");
    } else {
        PKM_Error("PKM_SecKeyCrypt DES3 failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = PKM_SecKeyCrypt(pFunctionList, hRwSession,
                          hDES3SecKey, &mech_DES3_CBC_PAD,
                          PLAINTEXT_PAD, sizeof(PLAINTEXT_PAD));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_SecKeyCrypt DES3 succeeded \n\n");
    } else {
        PKM_Error("PKM_SecKeyCrypt DES3 failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    mech.mechanism = CKM_RSA_PKCS;
    crv = PKM_RecoverFunctions(pFunctionList, hRwSession,
                               hRSApubKey, hRSAprivKey,
                               &mech,
                               PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_RecoverFunctions for CKM_RSA_PKCS succeeded\n\n");
    } else {
        PKM_Error("PKM_RecoverFunctions failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    mech.pParameter = NULL;
    mech.ulParameterLen = 0;

    for (i = 0; i < sigRSAMechsSZ; i++) {

        mech.mechanism = sigRSAMechs[i].mechanism;

        crv = PKM_PubKeySign(pFunctionList, hRwSession,
                             hRSApubKey, hRSAprivKey,
                             &mech,
                             PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_PubKeySign succeeded for %-10s\n\n",
                      sigRSAMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_PubKeySign failed for %-10s  "
                      "with 0x%08X, %-26s\n",
                      sigRSAMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
        crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                               hRSApubKey, hRSAprivKey,
                               &mech,
                               hAESSecKey, &mech_AES_CBC,
                               PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_DualFuncSign with AES secret key succeeded "
                      "for %-10s\n\n",
                      sigRSAMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_DualFuncSign with AES secret key failed "
                      "for %-10s  "
                      "with 0x%08X, %-26s\n",
                      sigRSAMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
        crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                               hRSApubKey, hRSAprivKey,
                               &mech,
                               hDES3SecKey, &mech_DES3_CBC,
                               PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_DualFuncSign with DES3 secret key succeeded "
                      "for %-10s\n\n",
                      sigRSAMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_DualFuncSign with DES3 secret key failed "
                      "for %-10s  "
                      "with 0x%08X, %-26s\n",
                      sigRSAMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
        crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                               hRSApubKey, hRSAprivKey,
                               &mech,
                               hAESSecKey, &mech_AES_CBC_PAD,
                               PLAINTEXT_PAD, sizeof(PLAINTEXT_PAD));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_DualFuncSign with AES secret key CBC_PAD "
                      "succeeded for %-10s\n\n",
                      sigRSAMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_DualFuncSign with AES secret key CBC_PAD "
                      "failed for %-10s  "
                      "with 0x%08X, %-26s\n",
                      sigRSAMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
        crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                               hRSApubKey, hRSAprivKey,
                               &mech,
                               hDES3SecKey, &mech_DES3_CBC_PAD,
                               PLAINTEXT_PAD, sizeof(PLAINTEXT_PAD));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_DualFuncSign with DES3 secret key CBC_PAD "
                      "succeeded for %-10s\n\n",
                      sigRSAMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_DualFuncSign with DES3 secret key CBC_PAD "
                      "failed for %-10s  "
                      "with 0x%08X, %-26s\n",
                      sigRSAMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }

    } /* end of RSA for loop */

    crv = PKM_PubKeySign(pFunctionList, hRwSession,
                         hDSApubKey, hDSAprivKey,
                         &dsaWithSha1Mech, PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_PubKeySign for DSAwithSHA1 succeeded \n\n");
    } else {
        PKM_Error("PKM_PubKeySign failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                           hDSApubKey, hDSAprivKey,
                           &dsaWithSha1Mech,
                           hAESSecKey, &mech_AES_CBC,
                           PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_DualFuncSign with AES secret key succeeded "
                  "for DSAWithSHA1\n\n");
    } else {
        PKM_Error("PKM_DualFuncSign with AES secret key failed "
                  "for DSAWithSHA1 with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                           hDSApubKey, hDSAprivKey,
                           &dsaWithSha1Mech,
                           hDES3SecKey, &mech_DES3_CBC,
                           PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_DualFuncSign with DES3 secret key succeeded "
                  "for DSAWithSHA1\n\n");
    } else {
        PKM_Error("PKM_DualFuncSign with DES3 secret key failed "
                  "for DSAWithSHA1 with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                           hDSApubKey, hDSAprivKey,
                           &dsaWithSha1Mech,
                           hAESSecKey, &mech_AES_CBC_PAD,
                           PLAINTEXT_PAD, sizeof(PLAINTEXT_PAD));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_DualFuncSign with AES secret key CBC_PAD succeeded "
                  "for DSAWithSHA1\n\n");
    } else {
        PKM_Error("PKM_DualFuncSign with AES secret key CBC_PAD failed "
                  "for DSAWithSHA1 with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = PKM_DualFuncSign(pFunctionList, hRwSession,
                           hDSApubKey, hDSAprivKey,
                           &dsaWithSha1Mech,
                           hDES3SecKey, &mech_DES3_CBC_PAD,
                           PLAINTEXT_PAD, sizeof(PLAINTEXT_PAD));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_DualFuncSign with DES3 secret key CBC_PAD succeeded "
                  "for DSAWithSHA1\n\n");
    } else {
        PKM_Error("PKM_DualFuncSign with DES3 secret key CBC_PAD failed "
                  "for DSAWithSHA1 with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    for (i = 0; i < digestMechsSZ; i++) {
        mech.mechanism = digestMechs[i].mechanism;
        crv = PKM_Digest(pFunctionList, hRwSession,
                         &mech, hAESSecKey,
                         PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_Digest with AES secret key succeeded for %-10s\n\n",
                      digestMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_Digest with AES secret key failed for "
                      "%-10s with 0x%08X,  %-26s\n",
                      digestMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
        crv = PKM_DualFuncDigest(pFunctionList, hRwSession,
                                 hAESSecKey, &mech_AES_CBC,
                                 0, &mech,
                                 PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_DualFuncDigest with AES secret key succeeded\n\n");
        } else {
            PKM_Error("PKM_DualFuncDigest with AES secret key "
                      "failed with 0x%08X, %-26s\n",
                      crv,
                      PKM_CK_RVtoStr(crv));
        }

        crv = PKM_Digest(pFunctionList, hRwSession,
                         &mech, hDES3SecKey,
                         PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_Digest with DES3 secret key succeeded for %-10s\n\n",
                      digestMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_Digest with DES3 secret key failed for "
                      "%-10s with 0x%08X,  %-26s\n",
                      digestMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
        crv = PKM_DualFuncDigest(pFunctionList, hRwSession,
                                 hDES3SecKey, &mech_DES3_CBC,
                                 0, &mech,
                                 PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_DualFuncDigest DES3 secret key succeeded\n\n");
        } else {
            PKM_Error("PKM_DualFuncDigest DES3 secret key "
                      "failed with 0x%08X, %-26s\n",
                      crv,
                      PKM_CK_RVtoStr(crv));
        }

        crv = PKM_Digest(pFunctionList, hRwSession,
                         &mech, 0,
                         PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_Digest with no secret key succeeded for %-10s\n\n",
                      digestMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_Digest with no secret key failed for %-10s  "
                      "with 0x%08X, %-26s\n",
                      digestMechs[i].mechanismStr, crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
    } /* end of digest loop */

    for (i = 0; i < hmacMechsSZ; i++) {
        mech.mechanism = hmacMechs[i].mechanism;
        crv = PKM_Hmac(pFunctionList, hRwSession,
                       hAESSecKey, &mech,
                       PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_Hmac with AES secret key succeeded for %-10s\n\n",
                      hmacMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_Hmac with AES secret key failed for %-10s "
                      "with 0x%08X, %-26s\n",
                      hmacMechs[i].mechanismStr, crv, PKM_CK_RVtoStr(crv));
            return crv;
        }
        if ((MODE == FIPSMODE) && (mech.mechanism == CKM_SHA512_HMAC))
            break;
        crv = PKM_Hmac(pFunctionList, hRwSession,
                       hDES3SecKey, &mech,
                       PLAINTEXT, sizeof(PLAINTEXT));
        if (crv == CKR_OK) {
            PKM_LogIt("PKM_Hmac with DES3 secret key succeeded for %-10s\n\n",
                      hmacMechs[i].mechanismStr);
        } else {
            PKM_Error("PKM_Hmac with DES3 secret key failed for %-10s "
                      "with 0x%08X,  %-26s\n",
                      hmacMechs[i].mechanismStr, crv, PKM_CK_RVtoStr(crv));
            return crv;
        }

    } /* end of hmac loop */

    crv = pFunctionList->C_Logout(hRwSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_CloseSession(hRwSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

void
PKM_LogIt(const char *fmt, ...)
{
    va_list args;

    if (verbose) {
        va_start(args, fmt);
        if (MODE == FIPSMODE) {
            printf("FIPS MODE: ");
        } else if (MODE == NONFIPSMODE) {
            printf("NON FIPS MODE: ");
        } else if (MODE == HYBRIDMODE) {
            printf("Hybrid MODE: ");
        }
        vprintf(fmt, args);
        va_end(args);
    }
}

void
PKM_Error(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    if (MODE == FIPSMODE) {
        fprintf(stderr, "\nFIPS MODE PKM_Error: ");
    } else if (MODE == NONFIPSMODE) {
        fprintf(stderr, "NON FIPS MODE PKM_Error: ");
    } else if (MODE == HYBRIDMODE) {
        fprintf(stderr, "Hybrid MODE PKM_Error: ");
    } else
        fprintf(stderr, "NOMODE PKM_Error: ");
    vfprintf(stderr, fmt, args);
    va_end(args);
}
CK_SLOT_ID *
PKM_GetSlotList(CK_FUNCTION_LIST_PTR pFunctionList,
                CK_ULONG slotID)
{
    CK_RV crv = CKR_OK;
    CK_SLOT_ID *pSlotList = NULL;
    CK_ULONG slotCount;

    NUMTESTS++; /* increment NUMTESTS */

    /* Get slot list */
    crv = pFunctionList->C_GetSlotList(CK_FALSE /* all slots */,
                                       NULL, &slotCount);
    if (crv != CKR_OK) {
        PKM_Error("C_GetSlotList failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return NULL;
    }
    PKM_LogIt("C_GetSlotList reported there are %lu slots\n", slotCount);
    pSlotList = (CK_SLOT_ID *)malloc(slotCount * sizeof(CK_SLOT_ID));
    if (!pSlotList) {
        PKM_Error("failed to allocate slot list\n");
        return NULL;
    }
    crv = pFunctionList->C_GetSlotList(CK_FALSE /* all slots */,
                                       pSlotList, &slotCount);
    if (crv != CKR_OK) {
        PKM_Error("C_GetSlotList failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        if (pSlotList)
            free(pSlotList);
        return NULL;
    }
    return pSlotList;
}

CK_RV
PKM_InitPWforDB(CK_FUNCTION_LIST_PTR pFunctionList,
                CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_RV crv = CKR_OK;
    CK_SESSION_HANDLE hSession;
    static const CK_UTF8CHAR testPin[] = { "0Mozilla" };
    static const CK_UTF8CHAR weakPin[] = { "mozilla" };

    crv = pFunctionList->C_OpenSession(pSlotList[slotID],
                                       CKF_RW_SESSION | CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("CKU_USER 0x%08X \n", CKU_USER);

    crv = pFunctionList->C_Login(hSession, CKU_SO, NULL, 0);
    if (crv != CKR_OK) {
        PKM_Error("C_Login failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    if (MODE == FIPSMODE) {
        crv = pFunctionList->C_InitPIN(hSession, (CK_UTF8CHAR *)weakPin,
                                       strlen((char *)weakPin));
        if (crv == CKR_OK) {
            PKM_Error("C_InitPIN with a weak password succeeded\n");
            return crv;
        } else {
            PKM_LogIt("C_InitPIN with a weak password failed with "
                      "0x%08X, %-26s\n",
                      crv, PKM_CK_RVtoStr(crv));
        }
    }
    crv = pFunctionList->C_InitPIN(hSession, (CK_UTF8CHAR *)testPin,
                                   strlen((char *)testPin));
    if (crv == CKR_OK) {
        PKM_LogIt("C_InitPIN succeeded\n");
    } else {
        PKM_Error("C_InitPIN failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Logout(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_OpenSession(pSlotList[slotID],
                                       CKF_RW_SESSION | CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("CKU_USER 0x%08X \n", CKU_USER);

    crv = pFunctionList->C_Login(hSession, CKU_USER, (CK_UTF8CHAR *)testPin,
                                 strlen((const char *)testPin));
    if (crv != CKR_OK) {
        PKM_Error("C_Login failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    if (MODE == FIPSMODE) {
        crv = pFunctionList->C_SetPIN(
            hSession, (CK_UTF8CHAR *)testPin,
            strlen((const char *)testPin),
            (CK_UTF8CHAR *)weakPin,
            strlen((const char *)weakPin));
        if (crv == CKR_OK) {
            PKM_Error("C_SetPIN with a weak password succeeded\n");
            return crv;
        } else {
            PKM_LogIt("C_SetPIN with a weak password returned with "
                      "0x%08X, %-26s\n",
                      crv, PKM_CK_RVtoStr(crv));
        }
    }
    crv = pFunctionList->C_SetPIN(
        hSession, (CK_UTF8CHAR *)testPin,
        strlen((const char *)testPin),
        pwd, pwdLen);
    if (crv != CKR_OK) {
        PKM_Error("C_CSetPin failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Logout(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    return crv;
}

CK_RV
PKM_ShowInfo(CK_FUNCTION_LIST_PTR pFunctionList, CK_ULONG slotID)
{
    CK_RV crv = CKR_OK;
    CK_INFO info;
    CK_SLOT_ID *pSlotList = NULL;
    unsigned i;

    CK_SLOT_INFO slotInfo;
    CK_TOKEN_INFO tokenInfo;
    CK_FLAGS bitflag;

    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_GetInfo(&info);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GetInfo succeeded\n");
    } else {
        PKM_Error("C_GetInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("General information about the PKCS #11 library:\n");
    PKM_LogIt("    PKCS #11 version: %d.%d\n",
              (int)info.cryptokiVersion.major,
              (int)info.cryptokiVersion.minor);
    PKM_LogIt("    manufacturer ID: %.32s\n", info.manufacturerID);
    PKM_LogIt("    flags: 0x%08lX\n", info.flags);
    PKM_LogIt("    library description: %.32s\n", info.libraryDescription);
    PKM_LogIt("    library version: %d.%d\n",
              (int)info.libraryVersion.major, (int)info.libraryVersion.minor);
    PKM_LogIt("\n");

    /* Get slot list */
    pSlotList = PKM_GetSlotList(pFunctionList, slotID);
    if (pSlotList == NULL) {
        PKM_Error("PKM_GetSlotList failed with \n");
        return crv;
    }
    crv = pFunctionList->C_GetSlotInfo(pSlotList[slotID], &slotInfo);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GetSlotInfo succeeded\n");
    } else {
        PKM_Error("C_GetSlotInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("Information about slot %lu:\n", pSlotList[slotID]);
    PKM_LogIt("    slot description: %.64s\n", slotInfo.slotDescription);
    PKM_LogIt("    slot manufacturer ID: %.32s\n", slotInfo.manufacturerID);
    PKM_LogIt("    flags: 0x%08lX\n", slotInfo.flags);
    bitflag = 1;
    for (i = 0; i < sizeof(slotFlagName) / sizeof(slotFlagName[0]); i++) {
        if (slotInfo.flags & bitflag) {
            PKM_LogIt("           %s\n", slotFlagName[i]);
        }
        bitflag <<= 1;
    }
    PKM_LogIt("    slot's hardware version number: %d.%d\n",
              (int)slotInfo.hardwareVersion.major,
              (int)slotInfo.hardwareVersion.minor);
    PKM_LogIt("    slot's firmware version number: %d.%d\n",
              (int)slotInfo.firmwareVersion.major,
              (int)slotInfo.firmwareVersion.minor);
    PKM_LogIt("\n");

    crv = pFunctionList->C_GetTokenInfo(pSlotList[slotID], &tokenInfo);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GetTokenInfo succeeded\n");
    } else {
        PKM_Error("C_GetTokenInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("Information about the token in slot %lu:\n",
              pSlotList[slotID]);
    PKM_LogIt("    label: %.32s\n", tokenInfo.label);
    PKM_LogIt("    device manufacturer ID: %.32s\n",
              tokenInfo.manufacturerID);
    PKM_LogIt("    device model: %.16s\n", tokenInfo.model);
    PKM_LogIt("    device serial number: %.16s\n", tokenInfo.serialNumber);
    PKM_LogIt("    flags: 0x%08lX\n", tokenInfo.flags);
    bitflag = 1;
    for (i = 0; i < sizeof(tokenFlagName) / sizeof(tokenFlagName[0]); i++) {
        if (tokenInfo.flags & bitflag) {
            PKM_LogIt("           %s\n", tokenFlagName[i]);
        }
        bitflag <<= 1;
    }
    PKM_LogIt("    maximum session count: %lu\n",
              tokenInfo.ulMaxSessionCount);
    PKM_LogIt("    session count: %lu\n", tokenInfo.ulSessionCount);
    PKM_LogIt("    maximum read/write session count: %lu\n",
              tokenInfo.ulMaxRwSessionCount);
    PKM_LogIt("    read/write session count: %lu\n",
              tokenInfo.ulRwSessionCount);
    PKM_LogIt("    maximum PIN length: %lu\n", tokenInfo.ulMaxPinLen);
    PKM_LogIt("    minimum PIN length: %lu\n", tokenInfo.ulMinPinLen);
    PKM_LogIt("    total public memory: %lu\n",
              tokenInfo.ulTotalPublicMemory);
    PKM_LogIt("    free public memory: %lu\n",
              tokenInfo.ulFreePublicMemory);
    PKM_LogIt("    total private memory: %lu\n",
              tokenInfo.ulTotalPrivateMemory);
    PKM_LogIt("    free private memory: %lu\n",
              tokenInfo.ulFreePrivateMemory);
    PKM_LogIt("    hardware version number: %d.%d\n",
              (int)tokenInfo.hardwareVersion.major,
              (int)tokenInfo.hardwareVersion.minor);
    PKM_LogIt("    firmware version number: %d.%d\n",
              (int)tokenInfo.firmwareVersion.major,
              (int)tokenInfo.firmwareVersion.minor);
    if (tokenInfo.flags & CKF_CLOCK_ON_TOKEN) {
        PKM_LogIt("    current time: %.16s\n", tokenInfo.utcTime);
    }
    PKM_LogIt("PKM_ShowInfo done \n\n");
    free(pSlotList);
    return crv;
}

/* PKM_HybridMode                                                         */
/* The NSS cryptographic module has two modes of operation: FIPS Approved */
/* mode and NONFIPS Approved mode. The two modes of operation are         */
/* independent of each other -- they have their own copies of data        */
/* structures and they are even allowed to be active at the same time.    */
/* The module is FIPS 140-2 compliant only when the NONFIPS mode          */
/* is inactive.                                                           */
/* PKM_HybridMode demostrates how an application can switch between the   */
/* two modes: FIPS Approved mode and NONFIPS mode.                        */
CK_RV
PKM_HybridMode(CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen,
               CK_C_INITIALIZE_ARGS_NSS *initArgs)
{

    CK_C_GetFunctionList pC_GetFunctionList; /* NONFIPSMode */
    CK_FUNCTION_LIST_PTR pC_FunctionList;
    CK_SLOT_ID *pC_SlotList = NULL;
    CK_ULONG slotID_C = 1;
    CK_C_GetFunctionList pFC_GetFunctionList; /* FIPSMode */
    CK_FUNCTION_LIST_PTR pFC_FunctionList;
    CK_SLOT_ID *pFC_SlotList = NULL;
    CK_ULONG slotID_FC = 0;
    CK_RV crv = CKR_OK;
    CK_SESSION_HANDLE hSession;
    int origMode = MODE; /* remember the orginal MODE value */

    NUMTESTS++; /* increment NUMTESTS */
    MODE = NONFIPSMODE;
#ifdef _WIN32
    /* NON FIPS mode  == C_GetFunctionList */
    pC_GetFunctionList = (CK_C_GetFunctionList)
        GetProcAddress(hModule, "C_GetFunctionList");
    if (pC_GetFunctionList == NULL) {
        PKM_Error("cannot load %s\n", LIB_NAME);
        return crv;
    }
#else
    pC_GetFunctionList = (CK_C_GetFunctionList)PR_FindFunctionSymbol(lib,
                                                                     "C_GetFunctionList");
    assert(pC_GetFunctionList != NULL);
#endif
    PKM_LogIt("loading C_GetFunctionList for Non FIPS Mode; slotID %d \n",
              slotID_C);
    crv = (*pC_GetFunctionList)(&pC_FunctionList);
    assert(crv == CKR_OK);

    /* invoke C_Initialize as pC_FunctionList->C_Initialize */
    crv = pC_FunctionList->C_Initialize(initArgs);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Initialize succeeded\n");
    } else {
        PKM_Error("C_Initialize failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    pC_SlotList = PKM_GetSlotList(pC_FunctionList, slotID_C);
    if (pC_SlotList == NULL) {
        PKM_Error("PKM_GetSlotList failed with \n");
        return crv;
    }
    crv = pC_FunctionList->C_OpenSession(pC_SlotList[slotID_C],
                                         CKF_SERIAL_SESSION,
                                         NULL, NULL, &hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("NONFIPS C_OpenSession succeeded\n");
    } else {
        PKM_Error("C_OpenSession failed for NONFIPS token "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pC_FunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("able to login in NONFIPS token\n");
    } else {
        PKM_Error("Unable to login in to NONFIPS token "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pC_FunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_ShowInfo(pC_FunctionList, slotID_C);
    MODE = HYBRIDMODE;

    /* Now load the FIPS token */
    /* FIPS mode == FC_GetFunctionList */
    pFC_GetFunctionList = NULL;
#ifdef _WIN32
    pFC_GetFunctionList = (CK_C_GetFunctionList)
        GetProcAddress(hModule, "FC_GetFunctionList");
#else
    pFC_GetFunctionList = (CK_C_GetFunctionList)PR_FindFunctionSymbol(lib,
                                                                      "FC_GetFunctionList");
    assert(pFC_GetFunctionList != NULL);
#endif

    PKM_LogIt("loading FC_GetFunctionList for FIPS Mode; slotID %d \n",
              slotID_FC);
    PKM_LogIt("pFC_FunctionList->C_Foo == pFC_FunctionList->FC_Foo\n");
    if (pFC_GetFunctionList == NULL) {
        PKM_Error("unable to load pFC_GetFunctionList\n");
        return crv;
    }

    crv = (*pFC_GetFunctionList)(&pFC_FunctionList);
    assert(crv == CKR_OK);

    /* invoke FC_Initialize as pFunctionList->C_Initialize */
    crv = pFC_FunctionList->C_Initialize(initArgs);
    if (crv == CKR_OK) {
        PKM_LogIt("FC_Initialize succeeded\n");
    } else {
        PKM_Error("FC_Initialize failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_ShowInfo(pFC_FunctionList, slotID_FC);

    pFC_SlotList = PKM_GetSlotList(pFC_FunctionList, slotID_FC);
    if (pFC_SlotList == NULL) {
        PKM_Error("PKM_GetSlotList failed with \n");
        return crv;
    }

    crv = pC_FunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv != CKR_OK) {
        PKM_LogIt("NONFIPS token cannot log in when FIPS token is loaded\n");
    } else {
        PKM_Error("Able to login in to NONFIPS token\n");
        return crv;
    }
    crv = pC_FunctionList->C_CloseSession(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("NONFIPS pC_CloseSession succeeded\n");
    } else {
        PKM_Error("pC_CloseSession failed for NONFIPS token "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("The module is FIPS 140-2 compliant\n"
              "only when the NONFIPS Approved mode is inactive by \n"
              "calling C_Finalize on the NONFIPS token.\n");

    /* to go in FIPSMODE you must Finalize the NONFIPS mode pointer */
    crv = pC_FunctionList->C_Finalize(NULL);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Finalize of NONFIPS Token succeeded\n");
        MODE = FIPSMODE;
    } else {
        PKM_Error("C_Finalize of NONFIPS Token failed with "
                  "0x%08X, %-26s\n",
                  crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("*** In FIPS mode!  ***\n");

    /* could do some operations in FIPS MODE */

    crv = pFC_FunctionList->C_Finalize(NULL);
    if (crv == CKR_OK) {
        PKM_LogIt("Exiting FIPSMODE by caling FC_Finalize.\n");
        MODE = NOMODE;
    } else {
        PKM_Error("FC_Finalize failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (pC_SlotList)
        free(pC_SlotList);
    if (pFC_SlotList)
        free(pFC_SlotList);

    MODE = origMode; /* set the mode back to the orginal Mode value */
    PKM_LogIt("PKM_HybridMode test Completed\n\n");
    return crv;
}

CK_RV
PKM_Mechanism(CK_FUNCTION_LIST_PTR pFunctionList,
              CK_SLOT_ID *pSlotList, CK_ULONG slotID)
{

    CK_RV crv = CKR_OK;
    CK_MECHANISM_TYPE *pMechanismList;
    CK_ULONG mechanismCount;
    CK_ULONG i;
    const char *mechName = NULL;

    NUMTESTS++; /* increment NUMTESTS */

    /* Get the mechanism list */
    crv = pFunctionList->C_GetMechanismList(pSlotList[slotID],
                                            NULL, &mechanismCount);
    if (crv != CKR_OK) {
        PKM_Error("C_GetMechanismList failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("C_GetMechanismList reported there are %lu mechanisms\n",
              mechanismCount);
    pMechanismList = (CK_MECHANISM_TYPE *)
        malloc(mechanismCount * sizeof(CK_MECHANISM_TYPE));
    if (!pMechanismList) {
        PKM_Error("failed to allocate mechanism list\n");
        return crv;
    }
    crv = pFunctionList->C_GetMechanismList(pSlotList[slotID],
                                            pMechanismList, &mechanismCount);
    if (crv != CKR_OK) {
        PKM_Error("C_GetMechanismList failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("C_GetMechanismList returned the mechanism types:\n");
    if (verbose) {
        for (i = 0; i < mechanismCount; i++) {
            mechName = getName(pMechanismList[(i)], ConstMechanism);

            /* output two mechanism name on each line */
            /* currently the longest known mechansim name length is 37 */
            if (mechName) {
                printf("%-40s", mechName);
            } else {
                printf("Unknown mechanism: 0x%08lX ", pMechanismList[i]);
            }
            if ((i % 2) == 1)
                printf("\n");
        }
        printf("\n\n");
    }

    for (i = 0; i < mechanismCount; i++) {
        CK_MECHANISM_INFO minfo;

        memset(&minfo, 0, sizeof(CK_MECHANISM_INFO));
        crv = pFunctionList->C_GetMechanismInfo(pSlotList[slotID],
                                                pMechanismList[i], &minfo);
        if (CKR_OK != crv) {
            PKM_Error("C_GetMechanismInfo(%lu, %lu) returned 0x%08X, %-26s\n",
                      pSlotList[slotID], pMechanismList[i], crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }

        mechName = getName(pMechanismList[i], ConstMechanism);
        if (!mechName)
            mechName = "Unknown mechanism";
        PKM_LogIt("    [%lu]: CK_MECHANISM_TYPE = %s 0x%08lX\n", (i + 1),
                  mechName,
                  pMechanismList[i]);
        PKM_LogIt("    ulMinKeySize = %lu\n", minfo.ulMinKeySize);
        PKM_LogIt("    ulMaxKeySize = %lu\n", minfo.ulMaxKeySize);
        PKM_LogIt("    flags = 0x%08x\n", minfo.flags);
        PKM_LogIt("        -> HW = %s\n", minfo.flags & CKF_HW ? "TRUE" : "FALSE");
        PKM_LogIt("        -> ENCRYPT = %s\n", minfo.flags & CKF_ENCRYPT ? "TRUE" : "FALSE");
        PKM_LogIt("        -> DECRYPT = %s\n", minfo.flags & CKF_DECRYPT ? "TRUE" : "FALSE");
        PKM_LogIt("        -> DIGEST = %s\n", minfo.flags & CKF_DIGEST ? "TRUE" : "FALSE");
        PKM_LogIt("        -> SIGN = %s\n", minfo.flags & CKF_SIGN ? "TRUE" : "FALSE");
        PKM_LogIt("        -> SIGN_RECOVER = %s\n", minfo.flags & CKF_SIGN_RECOVER ? "TRUE" : "FALSE");
        PKM_LogIt("        -> VERIFY = %s\n", minfo.flags & CKF_VERIFY ? "TRUE" : "FALSE");
        PKM_LogIt("        -> VERIFY_RECOVER = %s\n",
                  minfo.flags & CKF_VERIFY_RECOVER ? "TRUE" : "FALSE");
        PKM_LogIt("        -> GENERATE = %s\n", minfo.flags & CKF_GENERATE ? "TRUE" : "FALSE");
        PKM_LogIt("        -> GENERATE_KEY_PAIR = %s\n",
                  minfo.flags & CKF_GENERATE_KEY_PAIR ? "TRUE" : "FALSE");
        PKM_LogIt("        -> WRAP = %s\n", minfo.flags & CKF_WRAP ? "TRUE" : "FALSE");
        PKM_LogIt("        -> UNWRAP = %s\n", minfo.flags & CKF_UNWRAP ? "TRUE" : "FALSE");
        PKM_LogIt("        -> DERIVE = %s\n", minfo.flags & CKF_DERIVE ? "TRUE" : "FALSE");
        PKM_LogIt("        -> EXTENSION = %s\n", minfo.flags & CKF_EXTENSION ? "TRUE" : "FALSE");

        PKM_LogIt("\n");
    }

    return crv;
}

CK_RV
PKM_RNG(CK_FUNCTION_LIST_PTR pFunctionList, CK_SLOT_ID *pSlotList,
        CK_ULONG slotID)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv = CKR_OK;
    CK_BYTE randomData[16];
    CK_BYTE seed[] = { 0x01, 0x03, 0x35, 0x55, 0xFF };

    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_GenerateRandom(hSession,
                                          randomData, sizeof randomData);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateRandom without login succeeded\n");
    } else {
        PKM_Error("C_GenerateRandom without login failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_SeedRandom(hSession, seed, sizeof(seed));
    if (crv == CKR_OK) {
        PKM_LogIt("C_SeedRandom without login succeeded\n");
    } else {
        PKM_Error("C_SeedRandom without login failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_GenerateRandom(hSession,
                                          randomData, sizeof randomData);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateRandom without login succeeded\n");
    } else {
        PKM_Error("C_GenerateRandom without login failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

CK_RV
PKM_SessionLogin(CK_FUNCTION_LIST_PTR pFunctionList,
                 CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                 CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv = CKR_OK;

    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Login(hSession, CKU_USER, (unsigned char *)"netscape", 8);
    if (crv == CKR_OK) {
        PKM_Error("C_Login with wrong password succeeded\n");
        return CKR_FUNCTION_FAILED;
    } else {
        PKM_LogIt("As expected C_Login with wrong password returned 0x%08X, "
                  "%-26s.\n ",
                  crv, PKM_CK_RVtoStr(crv));
    }
    crv = pFunctionList->C_Login(hSession, CKU_USER, (unsigned char *)"red hat", 7);
    if (crv == CKR_OK) {
        PKM_Error("C_Login with wrong password succeeded\n");
        return CKR_FUNCTION_FAILED;
    } else {
        PKM_LogIt("As expected C_Login with wrong password returned 0x%08X, "
                  "%-26s.\n ",
                  crv, PKM_CK_RVtoStr(crv));
    }
    crv = pFunctionList->C_Login(hSession, CKU_USER,
                                 (unsigned char *)"sun", 3);
    if (crv == CKR_OK) {
        PKM_Error("C_Login with wrong password succeeded\n");
        return CKR_FUNCTION_FAILED;
    } else {
        PKM_LogIt("As expected C_Login with wrong password returned 0x%08X, "
                  "%-26s.\n ",
                  crv, PKM_CK_RVtoStr(crv));
    }
    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

/*
 * PKM_LegacyFunctions
 *
 * Legacyfunctions exist only for backwards compatibility.
 * C_GetFunctionStatus and C_CancelFunction functions were
 * meant for managing parallel execution of cryptographic functions.
 *
 * C_GetFunctionStatus is a legacy function which should simply return
 * the value CKR_FUNCTION_NOT_PARALLEL.
 *
 * C_CancelFunction is a legacy function which should simply return the
 * value CKR_FUNCTION_NOT_PARALLEL.
 *
 */
CK_RV
PKM_LegacyFunctions(CK_FUNCTION_LIST_PTR pFunctionList,
                    CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                    CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv = CKR_OK;
    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_GetFunctionStatus(hSession);
    if (crv == CKR_FUNCTION_NOT_PARALLEL) {
        PKM_LogIt("C_GetFunctionStatus correctly"
                  "returned CKR_FUNCTION_NOT_PARALLEL \n");
    } else {
        PKM_Error("C_GetFunctionStatus failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_CancelFunction(hSession);
    if (crv == CKR_FUNCTION_NOT_PARALLEL) {
        PKM_LogIt("C_CancelFunction correctly "
                  "returned CKR_FUNCTION_NOT_PARALLEL \n");
    } else {
        PKM_Error("C_CancelFunction failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

/*
 *  PKM_DualFuncDigest - demostrates the Dual-function
 *  cryptograpic functions:
 *
 *   C_DigestEncryptUpdate - multi-part Digest and Encrypt
 *   C_DecryptDigestUpdate - multi-part Decrypt and Digest
 *
 *
 */

CK_RV
PKM_DualFuncDigest(CK_FUNCTION_LIST_PTR pFunctionList,
                   CK_SESSION_HANDLE hSession,
                   CK_OBJECT_HANDLE hSecKey, CK_MECHANISM *cryptMech,
                   CK_OBJECT_HANDLE hSecKeyDigest,
                   CK_MECHANISM *digestMech,
                   const CK_BYTE *pData, CK_ULONG pDataLen)
{
    CK_RV crv = CKR_OK;
    CK_BYTE eDigest[MAX_DIGEST_SZ];
    CK_BYTE dDigest[MAX_DIGEST_SZ];
    CK_ULONG ulDigestLen;
    CK_BYTE ciphertext[MAX_CIPHER_SZ];
    CK_ULONG ciphertextLen, lastLen;
    CK_BYTE plaintext[MAX_DATA_SZ];
    CK_ULONG plaintextLen;
    unsigned int i;

    memset(eDigest, 0, sizeof(eDigest));
    memset(dDigest, 0, sizeof(dDigest));
    memset(ciphertext, 0, sizeof(ciphertext));
    memset(plaintext, 0, sizeof(plaintext));

    NUMTESTS++; /* increment NUMTESTS */

    /*
     * First init the Digest and Ecrypt operations
     */
    crv = pFunctionList->C_EncryptInit(hSession, cryptMech, hSecKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DigestInit(hSession, digestMech);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    ciphertextLen = sizeof(ciphertext);
    crv = pFunctionList->C_DigestEncryptUpdate(hSession, (CK_BYTE *)pData,
                                               pDataLen,
                                               ciphertext, &ciphertextLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestEncryptUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    ulDigestLen = sizeof(eDigest);
    crv = pFunctionList->C_DigestFinal(hSession, eDigest, &ulDigestLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* get the last piece of ciphertext (length should be 0 */
    lastLen = sizeof(ciphertext) - ciphertextLen;
    crv = pFunctionList->C_EncryptFinal(hSession,
                                        (CK_BYTE *)&ciphertext[ciphertextLen],
                                        &lastLen);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    ciphertextLen = ciphertextLen + lastLen;
    if (verbose) {
        printf("ciphertext = ");
        for (i = 0; i < ciphertextLen; i++) {
            printf("%02x", (unsigned)ciphertext[i]);
        }
        printf("\n");
        printf("eDigest = ");
        for (i = 0; i < ulDigestLen; i++) {
            printf("%02x", (unsigned)eDigest[i]);
        }
        printf("\n");
    }

    /* Decrypt the text */
    crv = pFunctionList->C_DecryptInit(hSession, cryptMech, hSecKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DigestInit(hSession, digestMech);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    plaintextLen = sizeof(plaintext);
    crv = pFunctionList->C_DecryptDigestUpdate(hSession, ciphertext,
                                               ciphertextLen,
                                               plaintext,
                                               &plaintextLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptDigestUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    lastLen = sizeof(plaintext) - plaintextLen;

    crv = pFunctionList->C_DecryptFinal(hSession,
                                        (CK_BYTE *)&plaintext[plaintextLen],
                                        &lastLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    plaintextLen = plaintextLen + lastLen;

    ulDigestLen = sizeof(dDigest);
    crv = pFunctionList->C_DigestFinal(hSession, dDigest, &ulDigestLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (plaintextLen != pDataLen) {
        PKM_Error("plaintextLen is %lu\n", plaintextLen);
        return crv;
    }

    if (verbose) {
        printf("plaintext = ");
        for (i = 0; i < plaintextLen; i++) {
            printf("%02x", (unsigned)plaintext[i]);
        }
        printf("\n");
        printf("dDigest = ");
        for (i = 0; i < ulDigestLen; i++) {
            printf("%02x", (unsigned)dDigest[i]);
        }
        printf("\n");
    }

    if (memcmp(eDigest, dDigest, ulDigestLen) == 0) {
        PKM_LogIt("Encrypted Digest equals Decrypted Digest\n");
    } else {
        PKM_Error("Digests don't match\n");
    }

    if ((plaintextLen == pDataLen) &&
        (memcmp(plaintext, pData, pDataLen)) == 0) {
        PKM_LogIt("DualFuncDigest decrypt test case passed\n");
    } else {
        PKM_Error("DualFuncDigest derypt test case failed\n");
    }

    return crv;
}

/*
 * PKM_SecKeyCrypt - Symmetric key encrypt/decyprt
 *
 */

CK_RV
PKM_SecKeyCrypt(CK_FUNCTION_LIST_PTR pFunctionList,
                CK_SESSION_HANDLE hSession,
                CK_OBJECT_HANDLE hSymKey, CK_MECHANISM *cryptMech,
                const CK_BYTE *pData, CK_ULONG dataLen)
{
    CK_RV crv = CKR_OK;

    CK_BYTE cipher1[MAX_CIPHER_SZ];
    CK_BYTE cipher2[MAX_CIPHER_SZ];
    CK_BYTE data1[MAX_DATA_SZ];
    CK_BYTE data2[MAX_DATA_SZ];
    CK_ULONG cipher1Len = 0, cipher2Len = 0, lastLen = 0;
    CK_ULONG data1Len = 0, data2Len = 0;

    NUMTESTS++; /* increment NUMTESTS */

    memset(cipher1, 0, sizeof(cipher1));
    memset(cipher2, 0, sizeof(cipher2));
    memset(data1, 0, sizeof(data1));
    memset(data2, 0, sizeof(data2));

    /* C_Encrypt */
    crv = pFunctionList->C_EncryptInit(hSession, cryptMech, hSymKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    cipher1Len = sizeof(cipher1);
    crv = pFunctionList->C_Encrypt(hSession, (CK_BYTE *)pData, dataLen,
                                   cipher1, &cipher1Len);
    if (crv != CKR_OK) {
        PKM_Error("C_Encrypt failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* C_EncryptUpdate */
    crv = pFunctionList->C_EncryptInit(hSession, cryptMech, hSymKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    cipher2Len = sizeof(cipher2);
    crv = pFunctionList->C_EncryptUpdate(hSession, (CK_BYTE *)pData,
                                         dataLen,
                                         cipher2, &cipher2Len);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    lastLen = sizeof(cipher2) - cipher2Len;

    crv = pFunctionList->C_EncryptFinal(hSession,
                                        (CK_BYTE *)&cipher2[cipher2Len],
                                        &lastLen);
    cipher2Len = cipher2Len + lastLen;

    if ((cipher1Len == cipher2Len) &&
        (memcmp(cipher1, cipher2, sizeof(cipher1Len)) == 0)) {
        PKM_LogIt("encrypt test case passed\n");
    } else {
        PKM_Error("encrypt test case failed\n");
        return CKR_GENERAL_ERROR;
    }

    /* C_Decrypt */
    crv = pFunctionList->C_DecryptInit(hSession, cryptMech, hSymKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    data1Len = sizeof(data1);
    crv = pFunctionList->C_Decrypt(hSession, cipher1, cipher1Len,
                                   data1, &data1Len);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    /* now use C_DecryptUpdate the text */
    crv = pFunctionList->C_DecryptInit(hSession, cryptMech, hSymKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    data2Len = sizeof(data2);
    crv = pFunctionList->C_DecryptUpdate(hSession, cipher2,
                                         cipher2Len,
                                         data2, &data2Len);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    lastLen = sizeof(data2) - data2Len;
    crv = pFunctionList->C_DecryptFinal(hSession,
                                        (CK_BYTE *)&data2[data2Len],
                                        &lastLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    data2Len = data2Len + lastLen;

    /* Comparison of Decrypt data */

    if ((data1Len == data2Len) && (dataLen == data1Len) &&
        (memcmp(data1, pData, dataLen) == 0) &&
        (memcmp(data2, pData, dataLen) == 0)) {
        PKM_LogIt("decrypt test case passed\n");
    } else {
        PKM_Error("derypt test case failed\n");
    }

    return crv;
}

CK_RV
PKM_SecretKey(CK_FUNCTION_LIST_PTR pFunctionList,
              CK_SLOT_ID *pSlotList, CK_ULONG slotID,
              CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv = CKR_OK;
    CK_MECHANISM sAESKeyMech = {
        CKM_AES_KEY_GEN, NULL, 0
    };
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE keyAESType = CKK_AES;
    CK_UTF8CHAR AESlabel[] = "An AES secret key object";
    CK_ULONG AESvalueLen = 16;
    CK_ATTRIBUTE sAESKeyTemplate[9];
    CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE;

    CK_BYTE KEY[16];
    CK_BYTE IV[16];
    static const CK_BYTE CIPHERTEXT[] = {
        0x7e, 0x6a, 0x3f, 0x3b, 0x39, 0x3c, 0xf2, 0x4b,
        0xce, 0xcc, 0x23, 0x6d, 0x80, 0xfd, 0xe0, 0xff
    };
    CK_BYTE ciphertext[64];
    CK_BYTE ciphertext2[64];
    CK_ULONG ciphertextLen, ciphertext2Len, lastLen;
    CK_BYTE plaintext[32];
    CK_BYTE plaintext2[32];
    CK_ULONG plaintextLen, plaintext2Len;
    CK_BYTE wrappedKey[16];
    CK_ULONG wrappedKeyLen;
    CK_MECHANISM aesEcbMech = {
        CKM_AES_ECB, NULL, 0
    };
    CK_OBJECT_HANDLE hTestKey;
    CK_MECHANISM mech_AES_CBC;

    NUMTESTS++; /* increment NUMTESTS */

    memset(ciphertext, 0, sizeof(ciphertext));
    memset(ciphertext2, 0, sizeof(ciphertext2));
    memset(IV, 0x00, sizeof(IV));
    memset(KEY, 0x00, sizeof(KEY));

    mech_AES_CBC.mechanism = CKM_AES_CBC;
    mech_AES_CBC.pParameter = IV;
    mech_AES_CBC.ulParameterLen = sizeof(IV);

    /* AES key template */
    sAESKeyTemplate[0].type = CKA_CLASS;
    sAESKeyTemplate[0].pValue = &class;
    sAESKeyTemplate[0].ulValueLen = sizeof(class);
    sAESKeyTemplate[1].type = CKA_KEY_TYPE;
    sAESKeyTemplate[1].pValue = &keyAESType;
    sAESKeyTemplate[1].ulValueLen = sizeof(keyAESType);
    sAESKeyTemplate[2].type = CKA_LABEL;
    sAESKeyTemplate[2].pValue = AESlabel;
    sAESKeyTemplate[2].ulValueLen = sizeof(AESlabel) - 1;
    sAESKeyTemplate[3].type = CKA_ENCRYPT;
    sAESKeyTemplate[3].pValue = &true;
    sAESKeyTemplate[3].ulValueLen = sizeof(true);
    sAESKeyTemplate[4].type = CKA_DECRYPT;
    sAESKeyTemplate[4].pValue = &true;
    sAESKeyTemplate[4].ulValueLen = sizeof(true);
    sAESKeyTemplate[5].type = CKA_SIGN;
    sAESKeyTemplate[5].pValue = &true;
    sAESKeyTemplate[5].ulValueLen = sizeof(true);
    sAESKeyTemplate[6].type = CKA_VERIFY;
    sAESKeyTemplate[6].pValue = &true;
    sAESKeyTemplate[6].ulValueLen = sizeof(true);
    sAESKeyTemplate[7].type = CKA_UNWRAP;
    sAESKeyTemplate[7].pValue = &true;
    sAESKeyTemplate[7].ulValueLen = sizeof(true);
    sAESKeyTemplate[8].type = CKA_VALUE_LEN;
    sAESKeyTemplate[8].pValue = &AESvalueLen;
    sAESKeyTemplate[8].ulValueLen = sizeof(AESvalueLen);

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate an AES key ... \n");
    /* generate an AES Secret Key */
    crv = pFunctionList->C_GenerateKey(hSession, &sAESKeyMech,
                                       sAESKeyTemplate,
                                       NUM_ELEM(sAESKeyTemplate),
                                       &hKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKey AES succeeded\n");
    } else {
        PKM_Error("C_GenerateKey AES failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_EncryptInit(hSession, &aesEcbMech, hKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    wrappedKeyLen = sizeof(wrappedKey);
    crv = pFunctionList->C_Encrypt(hSession, KEY, sizeof(KEY),
                                   wrappedKey, &wrappedKeyLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Encrypt failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    if (wrappedKeyLen != sizeof(wrappedKey)) {
        PKM_Error("wrappedKeyLen is %lu\n", wrappedKeyLen);
        return crv;
    }
    /* Import an encrypted key */
    crv = pFunctionList->C_UnwrapKey(hSession, &aesEcbMech, hKey,
                                     wrappedKey, wrappedKeyLen,
                                     sAESKeyTemplate,
                                     NUM_ELEM(sAESKeyTemplate),
                                     &hTestKey);
    if (crv != CKR_OK) {
        PKM_Error("C_UnwraPKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    /* AES Encrypt the text */
    crv = pFunctionList->C_EncryptInit(hSession, &mech_AES_CBC, hTestKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    ciphertextLen = sizeof(ciphertext);
    crv = pFunctionList->C_Encrypt(hSession, (CK_BYTE *)PLAINTEXT,
                                   sizeof(PLAINTEXT),
                                   ciphertext, &ciphertextLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Encrypt failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    if ((ciphertextLen == sizeof(CIPHERTEXT)) &&
        (memcmp(ciphertext, CIPHERTEXT, ciphertextLen) == 0)) {
        PKM_LogIt("AES CBCVarKey128 encrypt test case 1 passed\n");
    } else {
        PKM_Error("AES CBCVarKey128 encrypt test case 1 failed\n");
        return crv;
    }

    /* now use EncryptUpdate the text */
    crv = pFunctionList->C_EncryptInit(hSession, &mech_AES_CBC, hTestKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    ciphertext2Len = sizeof(ciphertext2);
    crv = pFunctionList->C_EncryptUpdate(hSession, (CK_BYTE *)PLAINTEXT,
                                         sizeof(PLAINTEXT),
                                         ciphertext2, &ciphertext2Len);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    lastLen = sizeof(ciphertext2) - ciphertext2Len;

    crv = pFunctionList->C_EncryptFinal(hSession,
                                        (CK_BYTE *)&ciphertext2[ciphertext2Len],
                                        &lastLen);
    ciphertext2Len = ciphertext2Len + lastLen;

    if ((ciphertextLen == ciphertext2Len) &&
        (memcmp(ciphertext, ciphertext2, sizeof(CIPHERTEXT)) == 0) &&
        (memcmp(ciphertext2, CIPHERTEXT, sizeof(CIPHERTEXT)) == 0)) {
        PKM_LogIt("AES CBCVarKey128 encrypt test case 2 passed\n");
    } else {
        PKM_Error("AES CBCVarKey128 encrypt test case 2 failed\n");
        return CKR_GENERAL_ERROR;
    }

    /* AES CBC Decrypt the text */
    crv = pFunctionList->C_DecryptInit(hSession, &mech_AES_CBC, hTestKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    plaintextLen = sizeof(plaintext);
    crv = pFunctionList->C_Decrypt(hSession, ciphertext, ciphertextLen,
                                   plaintext, &plaintextLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    if ((plaintextLen == sizeof(PLAINTEXT)) &&
        (memcmp(plaintext, PLAINTEXT, plaintextLen) == 0)) {
        PKM_LogIt("AES CBCVarKey128 decrypt test case 1 passed\n");
    } else {
        PKM_Error("AES CBCVarKey128 derypt test case 1 failed\n");
    }
    /* now use DecryptUpdate the text */
    crv = pFunctionList->C_DecryptInit(hSession, &mech_AES_CBC, hTestKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    plaintext2Len = sizeof(plaintext2);
    crv = pFunctionList->C_DecryptUpdate(hSession, ciphertext2,
                                         ciphertext2Len,
                                         plaintext2, &plaintext2Len);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    lastLen = sizeof(plaintext2) - plaintext2Len;
    crv = pFunctionList->C_DecryptFinal(hSession,
                                        (CK_BYTE *)&plaintext2[plaintext2Len],
                                        &lastLen);
    plaintext2Len = plaintext2Len + lastLen;

    if ((plaintextLen == plaintext2Len) &&
        (memcmp(plaintext, plaintext2, plaintext2Len) == 0) &&
        (memcmp(plaintext2, PLAINTEXT, sizeof(PLAINTEXT)) == 0)) {
        PKM_LogIt("AES CBCVarKey128 decrypt test case 2 passed\n");
    } else {
        PKM_Error("AES CBCVarKey128 decrypt test case 2 failed\n");
        return CKR_GENERAL_ERROR;
    }

    crv = pFunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

CK_RV
PKM_PubKeySign(CK_FUNCTION_LIST_PTR pFunctionList,
               CK_SESSION_HANDLE hRwSession,
               CK_OBJECT_HANDLE hPubKey, CK_OBJECT_HANDLE hPrivKey,
               CK_MECHANISM *signMech, const CK_BYTE *pData,
               CK_ULONG pDataLen)
{
    CK_RV crv = CKR_OK;
    CK_BYTE sig[MAX_SIG_SZ];
    CK_ULONG sigLen = 0;

    NUMTESTS++; /* increment NUMTESTS */
    memset(sig, 0, sizeof(sig));

    /* C_Sign  */
    crv = pFunctionList->C_SignInit(hRwSession, signMech, hPrivKey);
    if (crv != CKR_OK) {
        PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    sigLen = sizeof(sig);
    crv = pFunctionList->C_Sign(hRwSession, (CK_BYTE *)pData, pDataLen,
                                sig, &sigLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* C_Verify the signature */
    crv = pFunctionList->C_VerifyInit(hRwSession, signMech, hPubKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Verify(hRwSession, (CK_BYTE *)pData, pDataLen,
                                  sig, sigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Verify succeeded\n");
    } else {
        PKM_Error("C_Verify failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Check that the mechanism is Multi-part */
    if (signMech->mechanism == CKM_DSA ||
        signMech->mechanism == CKM_RSA_PKCS) {
        return crv;
    }

    memset(sig, 0, sizeof(sig));
    /* SignUpdate  */
    crv = pFunctionList->C_SignInit(hRwSession, signMech, hPrivKey);
    if (crv != CKR_OK) {
        PKM_Error("C_SignInit failed with 0x%08lX %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_SignUpdate(hRwSession, (CK_BYTE *)pData, pDataLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed with 0x%08lX %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    sigLen = sizeof(sig);
    crv = pFunctionList->C_SignFinal(hRwSession, sig, &sigLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* C_VerifyUpdate the signature  */
    crv = pFunctionList->C_VerifyInit(hRwSession, signMech,
                                      hPubKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyUpdate(hRwSession, (CK_BYTE *)pData,
                                        pDataLen);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyFinal(hRwSession, sig, sigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyFinal succeeded\n");
    } else {
        PKM_Error("C_VerifyFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    return crv;
}

CK_RV
PKM_PublicKey(CK_FUNCTION_LIST_PTR pFunctionList,
              CK_SLOT_ID *pSlotList,
              CK_ULONG slotID, CK_UTF8CHAR_PTR pwd,
              CK_ULONG pwdLen)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv = CKR_OK;

    /*** DSA Key ***/
    CK_MECHANISM dsaParamGenMech;
    CK_ULONG primeBits = 1024;
    CK_ATTRIBUTE dsaParamGenTemplate[1];
    CK_OBJECT_HANDLE hDsaParams = CK_INVALID_HANDLE;
    CK_BYTE DSA_P[128];
    CK_BYTE DSA_Q[20];
    CK_BYTE DSA_G[128];
    CK_MECHANISM dsaKeyPairGenMech;
    CK_ATTRIBUTE dsaPubKeyTemplate[5];
    CK_ATTRIBUTE dsaPrivKeyTemplate[5];
    CK_OBJECT_HANDLE hDSApubKey = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE hDSAprivKey = CK_INVALID_HANDLE;

    /* From SHA1ShortMsg.req, Len = 136 */
    CK_BYTE MSG[] = {
        0xba, 0x33, 0x95, 0xfb,
        0x5a, 0xfa, 0x8e, 0x6a,
        0x43, 0xdf, 0x41, 0x6b,
        0x32, 0x7b, 0x74, 0xfa,
        0x44
    };
    CK_BYTE MD[] = {
        0xf7, 0x5d, 0x92, 0xa4,
        0xbb, 0x4d, 0xec, 0xc3,
        0x7c, 0x5c, 0x72, 0xfa,
        0x04, 0x75, 0x71, 0x0a,
        0x06, 0x75, 0x8c, 0x1d
    };

    CK_BYTE sha1Digest[20];
    CK_ULONG sha1DigestLen;
    CK_BYTE dsaSig[40];
    CK_ULONG dsaSigLen;
    CK_MECHANISM sha1Mech = {
        CKM_SHA_1, NULL, 0
    };
    CK_MECHANISM dsaMech = {
        CKM_DSA, NULL, 0
    };
    CK_MECHANISM dsaWithSha1Mech = {
        CKM_DSA_SHA1, NULL, 0
    };

    NUMTESTS++; /* increment NUMTESTS */

    /* DSA key init */
    dsaParamGenMech.mechanism = CKM_DSA_PARAMETER_GEN;
    dsaParamGenMech.pParameter = NULL_PTR;
    dsaParamGenMech.ulParameterLen = 0;
    dsaParamGenTemplate[0].type = CKA_PRIME_BITS;
    dsaParamGenTemplate[0].pValue = &primeBits;
    dsaParamGenTemplate[0].ulValueLen = sizeof(primeBits);
    dsaPubKeyTemplate[0].type = CKA_PRIME;
    dsaPubKeyTemplate[0].pValue = DSA_P;
    dsaPubKeyTemplate[0].ulValueLen = sizeof(DSA_P);
    dsaPubKeyTemplate[1].type = CKA_SUBPRIME;
    dsaPubKeyTemplate[1].pValue = DSA_Q;
    dsaPubKeyTemplate[1].ulValueLen = sizeof(DSA_Q);
    dsaPubKeyTemplate[2].type = CKA_BASE;
    dsaPubKeyTemplate[2].pValue = DSA_G;
    dsaPubKeyTemplate[2].ulValueLen = sizeof(DSA_G);
    dsaPubKeyTemplate[3].type = CKA_TOKEN;
    dsaPubKeyTemplate[3].pValue = &true;
    dsaPubKeyTemplate[3].ulValueLen = sizeof(true);
    dsaPubKeyTemplate[4].type = CKA_VERIFY;
    dsaPubKeyTemplate[4].pValue = &true;
    dsaPubKeyTemplate[4].ulValueLen = sizeof(true);
    dsaKeyPairGenMech.mechanism = CKM_DSA_KEY_PAIR_GEN;
    dsaKeyPairGenMech.pParameter = NULL_PTR;
    dsaKeyPairGenMech.ulParameterLen = 0;
    dsaPrivKeyTemplate[0].type = CKA_TOKEN;
    dsaPrivKeyTemplate[0].pValue = &true;
    dsaPrivKeyTemplate[0].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[1].type = CKA_PRIVATE;
    dsaPrivKeyTemplate[1].pValue = &true;
    dsaPrivKeyTemplate[1].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[2].type = CKA_SENSITIVE;
    dsaPrivKeyTemplate[2].pValue = &true;
    dsaPrivKeyTemplate[2].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[3].type = CKA_SIGN,
    dsaPrivKeyTemplate[3].pValue = &true;
    dsaPrivKeyTemplate[3].ulValueLen = sizeof(true);
    dsaPrivKeyTemplate[4].type = CKA_EXTRACTABLE;
    dsaPrivKeyTemplate[4].pValue = &true;
    dsaPrivKeyTemplate[4].ulValueLen = sizeof(true);

    crv = pFunctionList->C_OpenSession(pSlotList[slotID],
                                       CKF_RW_SESSION | CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate DSA PQG domain parameters ... \n");
    /* Generate DSA domain parameters PQG */
    crv = pFunctionList->C_GenerateKey(hSession, &dsaParamGenMech,
                                       dsaParamGenTemplate,
                                       1,
                                       &hDsaParams);
    if (crv == CKR_OK) {
        PKM_LogIt("DSA domain parameter generation succeeded\n");
    } else {
        PKM_Error("DSA domain parameter generation failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_GetAttributeValue(hSession, hDsaParams,
                                             dsaPubKeyTemplate, 3);
    if (crv == CKR_OK) {
        PKM_LogIt("Getting DSA domain parameters succeeded\n");
    } else {
        PKM_Error("Getting DSA domain parameters failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DestroyObject(hSession, hDsaParams);
    if (crv == CKR_OK) {
        PKM_LogIt("Destroying DSA domain parameters succeeded\n");
    } else {
        PKM_Error("Destroying DSA domain parameters failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate a DSA key pair ... \n");
    /* Generate a persistent DSA key pair */
    crv = pFunctionList->C_GenerateKeyPair(hSession, &dsaKeyPairGenMech,
                                           dsaPubKeyTemplate,
                                           NUM_ELEM(dsaPubKeyTemplate),
                                           dsaPrivKeyTemplate,
                                           NUM_ELEM(dsaPrivKeyTemplate),
                                           &hDSApubKey, &hDSAprivKey);
    if (crv == CKR_OK) {
        PKM_LogIt("DSA key pair generation succeeded\n");
    } else {
        PKM_Error("DSA key pair generation failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Compute SHA-1 digest */
    crv = pFunctionList->C_DigestInit(hSession, &sha1Mech);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    sha1DigestLen = sizeof(sha1Digest);
    crv = pFunctionList->C_Digest(hSession, MSG, sizeof(MSG),
                                  sha1Digest, &sha1DigestLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Digest failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    if (sha1DigestLen != sizeof(sha1Digest)) {
        PKM_Error("sha1DigestLen is %lu\n", sha1DigestLen);
        return crv;
    }

    if (memcmp(sha1Digest, MD, sizeof(MD)) == 0) {
        PKM_LogIt("SHA-1 SHA1ShortMsg test case Len = 136 passed\n");
    } else {
        PKM_Error("SHA-1 SHA1ShortMsg test case Len = 136 failed\n");
    }

    crv = PKM_PubKeySign(pFunctionList, hSession,
                         hDSApubKey, hDSAprivKey,
                         &dsaMech, sha1Digest, sizeof(sha1Digest));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_PubKeySign CKM_DSA succeeded \n");
    } else {
        PKM_Error("PKM_PubKeySign failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = PKM_PubKeySign(pFunctionList, hSession,
                         hDSApubKey, hDSAprivKey,
                         &dsaWithSha1Mech, PLAINTEXT, sizeof(PLAINTEXT));
    if (crv == CKR_OK) {
        PKM_LogIt("PKM_PubKeySign CKM_DSA_SHA1 succeeded \n");
    } else {
        PKM_Error("PKM_PubKeySign failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Sign with DSA */
    crv = pFunctionList->C_SignInit(hSession, &dsaMech, hDSAprivKey);
    if (crv != CKR_OK) {
        PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    dsaSigLen = sizeof(dsaSig);
    crv = pFunctionList->C_Sign(hSession, sha1Digest, sha1DigestLen,
                                dsaSig, &dsaSigLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Verify the DSA signature */
    crv = pFunctionList->C_VerifyInit(hSession, &dsaMech, hDSApubKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Verify(hSession, sha1Digest, sha1DigestLen,
                                  dsaSig, dsaSigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Verify succeeded\n");
    } else {
        PKM_Error("C_Verify failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Verify the signature in a different way */
    crv = pFunctionList->C_VerifyInit(hSession, &dsaWithSha1Mech,
                                      hDSApubKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyUpdate(hSession, MSG, 1);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyUpdate(hSession, MSG + 1, sizeof(MSG) - 1);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyFinal(hSession, dsaSig, dsaSigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyFinal succeeded\n");
    } else {
        PKM_Error("C_VerifyFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Verify the signature in a different way */
    crv = pFunctionList->C_VerifyInit(hSession, &dsaWithSha1Mech,
                                      hDSApubKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyUpdate(hSession, MSG, 1);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyUpdate failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyUpdate(hSession, MSG + 1, sizeof(MSG) - 1);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyUpdate failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyFinal(hSession, dsaSig, dsaSigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyFinal of multi update succeeded.\n");
    } else {
        PKM_Error("C_VerifyFinal of multi update failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    /* Now modify the data */
    MSG[0] += 1;
    /* Compute SHA-1 digest */
    crv = pFunctionList->C_DigestInit(hSession, &sha1Mech);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    sha1DigestLen = sizeof(sha1Digest);
    crv = pFunctionList->C_Digest(hSession, MSG, sizeof(MSG),
                                  sha1Digest, &sha1DigestLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Digest failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyInit(hSession, &dsaMech, hDSApubKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Verify(hSession, sha1Digest, sha1DigestLen,
                                  dsaSig, dsaSigLen);
    if (crv != CKR_SIGNATURE_INVALID) {
        PKM_Error("C_Verify of modified data succeeded\n");
        return crv;
    } else {
        PKM_LogIt("C_Verify of modified data returned as EXPECTED "
                  " with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
    }

    crv = pFunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

CK_RV
PKM_Hmac(CK_FUNCTION_LIST_PTR pFunctionList, CK_SESSION_HANDLE hSession,
         CK_OBJECT_HANDLE sKey, CK_MECHANISM *hmacMech,
         const CK_BYTE *pData, CK_ULONG pDataLen)
{

    CK_RV crv = CKR_OK;

    CK_BYTE hmac1[HMAC_MAX_LENGTH];
    CK_ULONG hmac1Len = 0;
    CK_BYTE hmac2[HMAC_MAX_LENGTH];
    CK_ULONG hmac2Len = 0;

    memset(hmac1, 0, sizeof(hmac1));
    memset(hmac2, 0, sizeof(hmac2));

    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_SignInit(hSession, hmacMech, sKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_SignInit succeeded\n");
    } else {
        PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    hmac1Len = sizeof(hmac1);
    crv = pFunctionList->C_Sign(hSession, (CK_BYTE *)pData,
                                pDataLen,
                                (CK_BYTE *)hmac1, &hmac1Len);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Sign succeeded\n");
    } else {
        PKM_Error("C_Sign failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_SignInit(hSession, hmacMech, sKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_SignInit succeeded\n");
    } else {
        PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_SignUpdate(hSession, (CK_BYTE *)pData,
                                      pDataLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_SignUpdate succeeded\n");
    } else {
        PKM_Error("C_SignUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    hmac2Len = sizeof(hmac2);
    crv = pFunctionList->C_SignFinal(hSession, (CK_BYTE *)hmac2, &hmac2Len);
    if (crv == CKR_OK) {
        PKM_LogIt("C_SignFinal succeeded\n");
    } else {
        PKM_Error("C_SignFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    if ((hmac1Len == hmac2Len) && (memcmp(hmac1, hmac2, hmac1Len) == 0)) {
        PKM_LogIt("hmacs are equal!\n");
    } else {
        PKM_Error("hmacs are not equal!\n");
    }
    crv = pFunctionList->C_VerifyInit(hSession, hmacMech, sKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Verify(hSession, (CK_BYTE *)pData,
                                  pDataLen,
                                  (CK_BYTE *)hmac2, hmac2Len);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Verify of hmac succeeded\n");
    } else {
        PKM_Error("C_Verify failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyInit(hSession, hmacMech, sKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyUpdate(hSession, (CK_BYTE *)pData,
                                        pDataLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyUpdate of hmac succeeded\n");
    } else {
        PKM_Error("C_VerifyUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyFinal(hSession, (CK_BYTE *)hmac1,
                                       hmac1Len);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyFinal of hmac succeeded\n");
    } else {
        PKM_Error("C_VerifyFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    return crv;
}

CK_RV
PKM_FindAllObjects(CK_FUNCTION_LIST_PTR pFunctionList,
                   CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                   CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_RV crv = CKR_OK;

    CK_SESSION_HANDLE h = (CK_SESSION_HANDLE)0;
    CK_SESSION_INFO sinfo;
    CK_ATTRIBUTE_PTR pTemplate;
    CK_ULONG tnObjects = 0;
    int curMode;
    unsigned int i;
    unsigned int number_of_all_known_attribute_types = totalKnownType(ConstAttribute);

    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &h);
    if (CKR_OK != crv) {
        PKM_Error("C_OpenSession(%lu, CKF_SERIAL_SESSION, , )"
                  "returned 0x%08X, %-26s\n",
                  pSlotList[slotID], crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Opened a session: handle = 0x%08x\n", h);

    (void)memset(&sinfo, 0, sizeof(CK_SESSION_INFO));
    crv = pFunctionList->C_GetSessionInfo(h, &sinfo);
    if (CKR_OK != crv) {
        PKM_LogIt("C_GetSessionInfo(%lu, ) returned 0x%08X, %-26s\n", h, crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    SESSION INFO:\n");
    PKM_LogIt("        slotID = %lu\n", sinfo.slotID);
    PKM_LogIt("        state = %lu\n", sinfo.state);
    PKM_LogIt("        flags = 0x%08x\n", sinfo.flags);
#ifdef CKF_EXCLUSIVE_SESSION
    PKM_LogIt("            -> EXCLUSIVE SESSION = %s\n", sinfo.flags & CKF_EXCLUSIVE_SESSION ? "TRUE" : "FALSE");
#endif /* CKF_EXCLUSIVE_SESSION */
    PKM_LogIt("            -> RW SESSION = %s\n", sinfo.flags & CKF_RW_SESSION ? "TRUE" : "FALSE");
    PKM_LogIt("            -> SERIAL SESSION = %s\n", sinfo.flags & CKF_SERIAL_SESSION ? "TRUE" : "FALSE");
#ifdef CKF_INSERTION_CALLBACK
    PKM_LogIt("            -> INSERTION CALLBACK = %s\n", sinfo.flags & CKF_INSERTION_CALLBACK ? "TRUE" : "FALSE");
#endif /* CKF_INSERTION_CALLBACK */
    PKM_LogIt("        ulDeviceError = %lu\n", sinfo.ulDeviceError);
    PKM_LogIt("\n");

    crv = pFunctionList->C_FindObjectsInit(h, NULL, 0);
    if (CKR_OK != crv) {
        PKM_LogIt("C_FindObjectsInit(%lu, NULL, 0) returned "
                  "0x%08X, %-26s\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    pTemplate = (CK_ATTRIBUTE_PTR)calloc(number_of_all_known_attribute_types,
                                         sizeof(CK_ATTRIBUTE));
    if ((CK_ATTRIBUTE_PTR)NULL == pTemplate) {
        PKM_Error("[pTemplate memory allocation of %lu bytes failed]\n",
                  number_of_all_known_attribute_types *
                      sizeof(CK_ATTRIBUTE));
        return crv;
    }

    PKM_LogIt("    All objects:\n");
    /* Printing table set to NOMODE */
    curMode = MODE;
    MODE = NOMODE;

    while (1) {
        CK_OBJECT_HANDLE o = (CK_OBJECT_HANDLE)0;
        CK_ULONG nObjects = 0;
        CK_ULONG k;
        CK_ULONG nAttributes = 0;
        CK_ATTRIBUTE_PTR pT2;
        CK_ULONG l;
        const char *attName = NULL;

        crv = pFunctionList->C_FindObjects(h, &o, 1, &nObjects);
        if (CKR_OK != crv) {
            PKM_Error("C_FindObjects(%lu, , 1, ) returned 0x%08X, %-26s\n",
                      h, crv, PKM_CK_RVtoStr(crv));
            return crv;
        }

        if (0 == nObjects) {
            PKM_LogIt("\n");
            break;
        }

        tnObjects++;

        PKM_LogIt("        OBJECT HANDLE %lu:\n", o);

        k = 0;
        for (i = 0; i < constCount; i++) {
            if (consts[i].type == ConstAttribute) {
                pTemplate[k].type = consts[i].value;
                pTemplate[k].pValue = (CK_VOID_PTR)NULL;
                pTemplate[k].ulValueLen = 0;
                k++;
            }
            assert(k <= number_of_all_known_attribute_types);
        }

        crv = pFunctionList->C_GetAttributeValue(h, o, pTemplate,
                                                 number_of_all_known_attribute_types);
        switch (crv) {
            case CKR_OK:
            case CKR_ATTRIBUTE_SENSITIVE:
            case CKR_ATTRIBUTE_TYPE_INVALID:
            case CKR_BUFFER_TOO_SMALL:
                break;
            default:
                PKM_Error("C_GetAtributeValue(%lu, %lu, {all attribute types},"
                          "%lu) returned 0x%08X, %-26s\n",
                          h, o, number_of_all_known_attribute_types, crv,
                          PKM_CK_RVtoStr(crv));
                return crv;
        }

        for (k = 0; k < (CK_ULONG)number_of_all_known_attribute_types; k++) {
            if (-1 != (CK_LONG)pTemplate[k].ulValueLen) {
                nAttributes++;
            }
        }

        PKM_LogIt("            %lu attributes:\n", nAttributes);
        for (k = 0; k < (CK_ULONG)number_of_all_known_attribute_types;
             k++) {
            if (-1 != (CK_LONG)pTemplate[k].ulValueLen) {
                attName = getNameFromAttribute(pTemplate[k].type);
                if (!attName) {
                    PKM_Error("Unable to find attribute name update pk11table.c\n");
                }
                PKM_LogIt("                %s 0x%08x (len = %lu)\n",
                          attName,
                          pTemplate[k].type,
                          pTemplate[k].ulValueLen);
            }
        }
        PKM_LogIt("\n");

        pT2 = (CK_ATTRIBUTE_PTR)calloc(nAttributes, sizeof(CK_ATTRIBUTE));
        if ((CK_ATTRIBUTE_PTR)NULL == pT2) {
            PKM_Error("[pT2 memory allocation of %lu bytes failed]\n",
                      nAttributes * sizeof(CK_ATTRIBUTE));
            return crv;
        }

        /* allocate memory for the attribute values */
        for (l = 0, k = 0; k < (CK_ULONG)number_of_all_known_attribute_types;
             k++) {
            if (-1 != (CK_LONG)pTemplate[k].ulValueLen) {
                pT2[l].type = pTemplate[k].type;
                pT2[l].ulValueLen = pTemplate[k].ulValueLen;
                if (pT2[l].ulValueLen > 0) {
                    pT2[l].pValue = (CK_VOID_PTR)malloc(pT2[l].ulValueLen);
                    if ((CK_VOID_PTR)NULL == pT2[l].pValue) {
                        PKM_Error("pValue memory allocation of %lu bytes failed]\n",
                                  pT2[l].ulValueLen);
                        return crv;
                    }
                } else
                    pT2[l].pValue = (CK_VOID_PTR)NULL;
                l++;
            }
        }

        assert(l == nAttributes);

        crv = pFunctionList->C_GetAttributeValue(h, o, pT2, nAttributes);
        switch (crv) {
            case CKR_OK:
            case CKR_ATTRIBUTE_SENSITIVE:
            case CKR_ATTRIBUTE_TYPE_INVALID:
            case CKR_BUFFER_TOO_SMALL:
                break;
            default:
                PKM_Error("C_GetAtributeValue(%lu, %lu, {existent attribute"
                          " types}, %lu) returned 0x%08X, %-26s\n",
                          h, o, nAttributes, crv, PKM_CK_RVtoStr(crv));
                return crv;
        }

        for (l = 0; l < nAttributes; l++) {
            attName = getNameFromAttribute(pT2[l].type);
            if (!attName)
                attName = "unknown attribute";
            PKM_LogIt("            type = %s len = %ld",
                      attName, (CK_LONG)pT2[l].ulValueLen);

            if (-1 == (CK_LONG)pT2[l].ulValueLen) {
                ;
            } else {
                CK_ULONG m;

                if (pT2[l].ulValueLen <= 8) {
                    PKM_LogIt(", value = ");
                } else {
                    PKM_LogIt(", value = \n                ");
                }

                for (m = 0; (m < pT2[l].ulValueLen) && (m < 20); m++) {
                    PKM_LogIt("%02x", (CK_ULONG)(0xff &
                                                 ((CK_CHAR_PTR)pT2[l].pValue)[m]));
                }

                PKM_LogIt(" ");

                for (m = 0; (m < pT2[l].ulValueLen) && (m < 20); m++) {
                    CK_CHAR c = ((CK_CHAR_PTR)pT2[l].pValue)[m];
                    if ((c < 0x20) || (c >= 0x7f)) {
                        c = '.';
                    }
                    PKM_LogIt("%c", c);
                }
            }

            PKM_LogIt("\n");
        }

        PKM_LogIt("\n");

        for (l = 0; l < nAttributes; l++) {
            if (pT2[l].pValue) {
                free(pT2[l].pValue);
            }
        }
        free(pT2);
    } /* while(1) */

    MODE = curMode; /* reset the logging MODE */

    crv = pFunctionList->C_FindObjectsFinal(h);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjectsFinal(%lu) returned 0x%08X, %-26s\n", h, crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    (%lu objects total)\n", tnObjects);

    crv = pFunctionList->C_CloseSession(h);
    if (CKR_OK != crv) {
        PKM_Error("C_CloseSession(%lu) returned 0x%08X, %-26s\n", h, crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}
/* session to create, find, and delete a couple session objects */
CK_RV
PKM_MultiObjectManagement(CK_FUNCTION_LIST_PTR pFunctionList,
                          CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                          CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{

    CK_RV crv = CKR_OK;

    CK_SESSION_HANDLE h = (CK_SESSION_HANDLE)0;
    CK_SESSION_HANDLE h2 = (CK_SESSION_HANDLE)0;
    CK_ATTRIBUTE one[7], two[7], three[7], delta[1], mask[1];
    CK_OBJECT_CLASS cko_data = CKO_DATA;
    char *key = "TEST PROGRAM";
    CK_ULONG key_len = 0;
    CK_OBJECT_HANDLE hOneIn = (CK_OBJECT_HANDLE)0;
    CK_OBJECT_HANDLE hTwoIn = (CK_OBJECT_HANDLE)0;
    CK_OBJECT_HANDLE hThreeIn = (CK_OBJECT_HANDLE)0;
    CK_OBJECT_HANDLE hDeltaIn = (CK_OBJECT_HANDLE)0;
    CK_OBJECT_HANDLE found[10];
    CK_ULONG nFound;
    CK_ULONG hDeltaLen, hThreeLen = 0;

    CK_TOKEN_INFO tinfo;

    NUMTESTS++; /* increment NUMTESTS */
    key_len = sizeof(key);
    crv = pFunctionList->C_OpenSession(pSlotList[slotID],
                                       CKF_SERIAL_SESSION, NULL, NULL, &h);
    if (CKR_OK != crv) {
        PKM_Error("C_OpenSession(%lu, CKF_SERIAL_SESSION, , )"
                  "returned 0x%08X, %-26s\n",
                  pSlotList[slotID], crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Login(h, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    (void)memset(&tinfo, 0, sizeof(CK_TOKEN_INFO));
    crv = pFunctionList->C_GetTokenInfo(pSlotList[slotID], &tinfo);
    if (CKR_OK != crv) {
        PKM_Error("C_GetTokenInfo(%lu, ) returned 0x%08X, %-26s\n",
                  pSlotList[slotID], crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Opened a session: handle = 0x%08x\n", h);

    one[0].type = CKA_CLASS;
    one[0].pValue = &cko_data;
    one[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
    one[1].type = CKA_TOKEN;
    one[1].pValue = &false;
    one[1].ulValueLen = sizeof(CK_BBOOL);
    one[2].type = CKA_PRIVATE;
    one[2].pValue = &false;
    one[2].ulValueLen = sizeof(CK_BBOOL);
    one[3].type = CKA_MODIFIABLE;
    one[3].pValue = &true;
    one[3].ulValueLen = sizeof(CK_BBOOL);
    one[4].type = CKA_LABEL;
    one[4].pValue = "Test data object one";
    one[4].ulValueLen = strlen(one[4].pValue);
    one[5].type = CKA_APPLICATION;
    one[5].pValue = key;
    one[5].ulValueLen = key_len;
    one[6].type = CKA_VALUE;
    one[6].pValue = "Object one";
    one[6].ulValueLen = strlen(one[6].pValue);

    two[0].type = CKA_CLASS;
    two[0].pValue = &cko_data;
    two[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
    two[1].type = CKA_TOKEN;
    two[1].pValue = &false;
    two[1].ulValueLen = sizeof(CK_BBOOL);
    two[2].type = CKA_PRIVATE;
    two[2].pValue = &false;
    two[2].ulValueLen = sizeof(CK_BBOOL);
    two[3].type = CKA_MODIFIABLE;
    two[3].pValue = &true;
    two[3].ulValueLen = sizeof(CK_BBOOL);
    two[4].type = CKA_LABEL;
    two[4].pValue = "Test data object two";
    two[4].ulValueLen = strlen(two[4].pValue);
    two[5].type = CKA_APPLICATION;
    two[5].pValue = key;
    two[5].ulValueLen = key_len;
    two[6].type = CKA_VALUE;
    two[6].pValue = "Object two";
    two[6].ulValueLen = strlen(two[6].pValue);

    three[0].type = CKA_CLASS;
    three[0].pValue = &cko_data;
    three[0].ulValueLen = sizeof(CK_OBJECT_CLASS);
    three[1].type = CKA_TOKEN;
    three[1].pValue = &false;
    three[1].ulValueLen = sizeof(CK_BBOOL);
    three[2].type = CKA_PRIVATE;
    three[2].pValue = &false;
    three[2].ulValueLen = sizeof(CK_BBOOL);
    three[3].type = CKA_MODIFIABLE;
    three[3].pValue = &true;
    three[3].ulValueLen = sizeof(CK_BBOOL);
    three[4].type = CKA_LABEL;
    three[4].pValue = "Test data object three";
    three[4].ulValueLen = strlen(three[4].pValue);
    three[5].type = CKA_APPLICATION;
    three[5].pValue = key;
    three[5].ulValueLen = key_len;
    three[6].type = CKA_VALUE;
    three[6].pValue = "Object three";
    three[6].ulValueLen = strlen(three[6].pValue);

    crv = pFunctionList->C_CreateObject(h, one, 7, &hOneIn);
    if (CKR_OK != crv) {
        PKM_Error("C_CreateObject(%lu, one, 7, ) returned 0x%08X, %-26s\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Created object one: handle = %lu\n", hOneIn);

    crv = pFunctionList->C_CreateObject(h, two, 7, &hTwoIn);
    if (CKR_OK != crv) {
        PKM_Error("C_CreateObject(%lu, two, 7, ) returned 0x%08X, %-26s\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Created object two: handle = %lu\n", hTwoIn);

    crv = pFunctionList->C_CreateObject(h, three, 7, &hThreeIn);
    if (CKR_OK != crv) {
        PKM_Error("C_CreateObject(%lu, three, 7, ) returned 0x%08x\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_GetObjectSize(h, hThreeIn, &hThreeLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GetObjectSize succeeded\n");
    } else {
        PKM_Error("C_GetObjectSize failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Created object three: handle = %lu\n", hThreeIn);

    delta[0].type = CKA_VALUE;
    delta[0].pValue = "Copied object";
    delta[0].ulValueLen = strlen(delta[0].pValue);

    crv = pFunctionList->C_CopyObject(h, hThreeIn, delta, 1, &hDeltaIn);
    if (CKR_OK != crv) {
        PKM_Error("C_CopyObject(%lu, %lu, delta, 1, ) returned "
                  "0x%08X, %-26s\n",
                  h, hThreeIn, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_GetObjectSize(h, hDeltaIn, &hDeltaLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GetObjectSize succeeded\n");
    } else {
        PKM_Error("C_GetObjectSize failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (hThreeLen == hDeltaLen) {
        PKM_LogIt("Copied object size same as orginal\n");
    } else {
        PKM_Error("Copied object different from original\n");
        return CKR_DEVICE_ERROR;
    }

    PKM_LogIt("    Copied object three: new handle = %lu\n", hDeltaIn);

    mask[0].type = CKA_APPLICATION;
    mask[0].pValue = key;
    mask[0].ulValueLen = key_len;

    crv = pFunctionList->C_FindObjectsInit(h, mask, 1);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjectsInit(%lu, mask, 1) returned 0x%08X, %-26s\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    (void)memset(&found, 0, sizeof(found));
    nFound = 0;
    crv = pFunctionList->C_FindObjects(h, found, 10, &nFound);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjects(%lu,, 10, ) returned 0x%08X, %-26s\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (4 != nFound) {
        PKM_Error("Found %lu objects, not 4.\n", nFound);
        return crv;
    }

    PKM_LogIt("    Found 4 objects: %lu, %lu, %lu, %lu\n",
              found[0], found[1], found[2], found[3]);

    crv = pFunctionList->C_FindObjectsFinal(h);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjectsFinal(%lu) returned 0x%08X, %-26s\n",
                  h, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_DestroyObject(h, hThreeIn);
    if (CKR_OK != crv) {
        PKM_Error("C_DestroyObject(%lu, %lu) returned 0x%08X, %-26s\n", h,
                  hThreeIn, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Destroyed object three (handle = %lu)\n", hThreeIn);

    delta[0].type = CKA_APPLICATION;
    delta[0].pValue = "Changed application";
    delta[0].ulValueLen = strlen(delta[0].pValue);

    crv = pFunctionList->C_SetAttributeValue(h, hTwoIn, delta, 1);
    if (CKR_OK != crv) {
        PKM_Error("C_SetAttributeValue(%lu, %lu, delta, 1) returned "
                  "0x%08X, %-26s\n",
                  h, hTwoIn, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("    Changed object two (handle = %lu).\n", hTwoIn);

    /* Can another session find these session objects? */

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &h2);
    if (CKR_OK != crv) {
        PKM_Error("C_OpenSession(%lu, CKF_SERIAL_SESSION, , )"
                  " returned 0x%08X, %-26s\n",
                  pSlotList[slotID], crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    PKM_LogIt("    Opened a second session: handle = 0x%08x\n", h2);

    /* mask is still the same */

    crv = pFunctionList->C_FindObjectsInit(h2, mask, 1);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjectsInit(%lu, mask, 1) returned 0x%08X, %-26s\n",
                  h2, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    (void)memset(&found, 0, sizeof(found));
    nFound = 0;
    crv = pFunctionList->C_FindObjects(h2, found, 10, &nFound);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjects(%lu,, 10, ) returned 0x%08X, %-26s\n",
                  h2, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (2 != nFound) {
        PKM_Error("Found %lu objects, not 2.\n", nFound);
        return crv;
    }

    PKM_LogIt("    Found 2 objects: %lu, %lu\n",
              found[0], found[1]);

    crv = pFunctionList->C_FindObjectsFinal(h2);
    if (CKR_OK != crv) {
        PKM_Error("C_FindObjectsFinal(%lu) returned 0x%08X, %-26s\n", h2, crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Logout(h);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseAllSessions(pSlotList[slotID]);
    if (CKR_OK != crv) {
        PKM_Error("C_CloseAllSessions(%lu) returned 0x%08X, %-26s\n",
                  pSlotList[slotID], crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("\n");
    return crv;
}

CK_RV
PKM_OperationalState(CK_FUNCTION_LIST_PTR pFunctionList,
                     CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                     CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv = CKR_OK;
    CK_MECHANISM sAESKeyMech = {
        CKM_AES_KEY_GEN, NULL, 0
    };
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE keyAESType = CKK_AES;
    CK_UTF8CHAR AESlabel[] = "An AES secret key object";
    CK_ULONG AESvalueLen = 16;
    CK_ATTRIBUTE sAESKeyTemplate[9];
    CK_OBJECT_HANDLE sKey = CK_INVALID_HANDLE;
    CK_BYTE_PTR pstate = NULL;
    CK_ULONG statelen, digestlen, plainlen, plainlen_1, plainlen_2, slen;

    static const CK_UTF8CHAR *plaintext = (CK_UTF8CHAR *)"Firefox rules.";
    static const CK_UTF8CHAR *plaintext_1 = (CK_UTF8CHAR *)"Thunderbird rules.";
    static const CK_UTF8CHAR *plaintext_2 = (CK_UTF8CHAR *)"Firefox and Thunderbird.";

    char digest[MAX_DIGEST_SZ], digest_1[MAX_DIGEST_SZ];
    char sign[MAX_SIG_SZ];
    CK_MECHANISM signmech;
    CK_MECHANISM digestmech;

    NUMTESTS++; /* increment NUMTESTS */

    /* AES key template */
    sAESKeyTemplate[0].type = CKA_CLASS;
    sAESKeyTemplate[0].pValue = &class;
    sAESKeyTemplate[0].ulValueLen = sizeof(class);
    sAESKeyTemplate[1].type = CKA_KEY_TYPE;
    sAESKeyTemplate[1].pValue = &keyAESType;
    sAESKeyTemplate[1].ulValueLen = sizeof(keyAESType);
    sAESKeyTemplate[2].type = CKA_LABEL;
    sAESKeyTemplate[2].pValue = AESlabel;
    sAESKeyTemplate[2].ulValueLen = sizeof(AESlabel) - 1;
    sAESKeyTemplate[3].type = CKA_ENCRYPT;
    sAESKeyTemplate[3].pValue = &true;
    sAESKeyTemplate[3].ulValueLen = sizeof(true);
    sAESKeyTemplate[4].type = CKA_DECRYPT;
    sAESKeyTemplate[4].pValue = &true;
    sAESKeyTemplate[4].ulValueLen = sizeof(true);
    sAESKeyTemplate[5].type = CKA_SIGN;
    sAESKeyTemplate[5].pValue = &true;
    sAESKeyTemplate[5].ulValueLen = sizeof(true);
    sAESKeyTemplate[6].type = CKA_VERIFY;
    sAESKeyTemplate[6].pValue = &true;
    sAESKeyTemplate[6].ulValueLen = sizeof(true);
    sAESKeyTemplate[7].type = CKA_UNWRAP;
    sAESKeyTemplate[7].pValue = &true;
    sAESKeyTemplate[7].ulValueLen = sizeof(true);
    sAESKeyTemplate[8].type = CKA_VALUE_LEN;
    sAESKeyTemplate[8].pValue = &AESvalueLen;
    sAESKeyTemplate[8].ulValueLen = sizeof(AESvalueLen);

    signmech.mechanism = CKM_SHA_1_HMAC;
    signmech.pParameter = NULL;
    signmech.ulParameterLen = 0;
    digestmech.mechanism = CKM_SHA256;
    digestmech.pParameter = NULL;
    digestmech.ulParameterLen = 0;

    plainlen = strlen((char *)plaintext);
    plainlen_1 = strlen((char *)plaintext_1);
    plainlen_2 = strlen((char *)plaintext_2);
    digestlen = MAX_DIGEST_SZ;

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    PKM_LogIt("Generate an AES key ...\n");
    /* generate an AES Secret Key */
    crv = pFunctionList->C_GenerateKey(hSession, &sAESKeyMech,
                                       sAESKeyTemplate,
                                       NUM_ELEM(sAESKeyTemplate),
                                       &sKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKey AES succeeded\n");
    } else {
        PKM_Error("C_GenerateKey AES failed with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_SignInit(hSession, &signmech, sKey);
    if (crv != CKR_OK) {
        PKM_Error("C_SignInit failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    slen = sizeof(sign);
    crv = pFunctionList->C_Sign(hSession, (CK_BYTE_PTR)plaintext, plainlen,
                                (CK_BYTE_PTR)sign, &slen);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_DestroyObject(hSession, sKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DestroyObject failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    digestlen = MAX_DIGEST_SZ;
    crv = pFunctionList->C_DigestInit(hSession, &digestmech);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestInit failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DigestUpdate(hSession, (CK_BYTE_PTR)plaintext,
                                        plainlen);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestUpdate failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_GetOperationState(hSession, NULL, &statelen);
    if (crv != CKR_OK) {
        PKM_Error("C_GetOperationState failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    pstate = (CK_BYTE_PTR)malloc(statelen * sizeof(CK_BYTE_PTR));
    crv = pFunctionList->C_GetOperationState(hSession, pstate, &statelen);
    if (crv != CKR_OK) {
        PKM_Error("C_GetOperationState failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_DigestUpdate(hSession, (CK_BYTE_PTR)plaintext_1,
                                        plainlen_1);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestUpdate failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DigestUpdate(hSession, (CK_BYTE_PTR)plaintext_2,
                                        plainlen_2);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestUpdate failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /*
     *      This will override/negate the above 2 digest_update
     *      operations
     */
    crv = pFunctionList->C_SetOperationState(hSession, pstate, statelen,
                                             0, 0);
    if (crv != CKR_OK) {
        PKM_Error("C_SetOperationState failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_DigestFinal(hSession, (CK_BYTE_PTR)digest,
                                       &digestlen);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestFinal failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    digestlen = MAX_DIGEST_SZ;
    crv = pFunctionList->C_DigestInit(hSession, &digestmech);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestInit failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Digest(hSession, (CK_BYTE_PTR)plaintext, plainlen,
                                  (CK_BYTE_PTR)digest_1, &digestlen);
    if (crv != CKR_OK) {
        PKM_Error("C_Digest failed returned 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    if (memcmp(digest, digest_1, digestlen) == 0) {
        PKM_LogIt("Digest and digest_1 are equal!\n");
    } else {
        PKM_Error("Digest and digest_1 are not equal!\n");
    }
    crv = pFunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseSession(hSession);
    if (CKR_OK != crv) {
        PKM_Error("C_CloseSession(%lu) returned 0x%08X, %-26s\n",
                  hSession, crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

/*
 * Recover Functions
 */
CK_RV
PKM_RecoverFunctions(CK_FUNCTION_LIST_PTR pFunctionList,
                     CK_SESSION_HANDLE hSession,
                     CK_OBJECT_HANDLE hPubKey, CK_OBJECT_HANDLE hPrivKey,
                     CK_MECHANISM *signMech, const CK_BYTE *pData,
                     CK_ULONG pDataLen)
{
    CK_RV crv = CKR_OK;
    CK_BYTE sig[MAX_SIG_SZ];
    CK_ULONG sigLen = MAX_SIG_SZ;
    CK_BYTE recover[MAX_SIG_SZ];
    CK_ULONG recoverLen = MAX_SIG_SZ;

    NUMTESTS++; /* increment NUMTESTS */

    /* initializes a signature operation,
     *  where the data can be recovered from the signature
     */
    crv = pFunctionList->C_SignRecoverInit(hSession, signMech,
                                           hPrivKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_SignRecoverInit succeeded. \n");
    } else {
        PKM_Error("C_SignRecoverInit failed.\n"
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* signs single-part data,
     * where the data can be recovered from the signature
     */
    crv = pFunctionList->C_SignRecover(hSession, (CK_BYTE *)pData,
                                       pDataLen,
                                       (CK_BYTE *)sig, &sigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_SignRecover succeeded. \n");
    } else {
        PKM_Error("C_SignRecoverInit failed to create an RSA key pair.\n"
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /*
     * initializes a verification operation
     *where the data is recovered from the signature
     */
    crv = pFunctionList->C_VerifyRecoverInit(hSession, signMech,
                                             hPubKey);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyRecoverInit succeeded. \n");
    } else {
        PKM_Error("C_VerifyRecoverInit failed.\n"
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /*
     * verifies a signature on single-part data,
     * where the data is recovered from the signature
     */
    crv = pFunctionList->C_VerifyRecover(hSession, (CK_BYTE *)sig,
                                         sigLen,
                                         (CK_BYTE *)recover, &recoverLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyRecover succeeded. \n");
    } else {
        PKM_Error("C_VerifyRecover failed.\n"
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    if ((recoverLen == pDataLen) &&
        (memcmp(recover, pData, pDataLen) == 0)) {
        PKM_LogIt("VerifyRecover test case passed\n");
    } else {
        PKM_Error("VerifyRecover test case failed\n");
    }

    return crv;
}
/*
 * wrapUnwrap
 * wrap the secretkey with the public key.
 * unwrap the secretkey with the private key.
 */
CK_RV
PKM_wrapUnwrap(CK_FUNCTION_LIST_PTR pFunctionList,
               CK_SESSION_HANDLE hSession,
               CK_OBJECT_HANDLE hPublicKey,
               CK_OBJECT_HANDLE hPrivateKey,
               CK_MECHANISM *wrapMechanism,
               CK_OBJECT_HANDLE hSecretKey,
               CK_ATTRIBUTE *sKeyTemplate,
               CK_ULONG skeyTempSize)
{
    CK_RV crv = CKR_OK;
    CK_OBJECT_HANDLE hSecretKeyUnwrapped = CK_INVALID_HANDLE;
    CK_BYTE wrappedKey[128];
    CK_ULONG ulWrappedKeyLen = 0;

    NUMTESTS++; /* increment NUMTESTS */

    ulWrappedKeyLen = sizeof(wrappedKey);
    crv = pFunctionList->C_WrapKey(
        hSession, wrapMechanism,
        hPublicKey, hSecretKey,
        wrappedKey, &ulWrappedKeyLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_WrapKey succeeded\n");
    } else {
        PKM_Error("C_WrapKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_UnwrapKey(
        hSession, wrapMechanism, hPrivateKey,
        wrappedKey, ulWrappedKeyLen, sKeyTemplate,
        skeyTempSize,
        &hSecretKeyUnwrapped);
    if ((crv == CKR_OK) && (hSecretKeyUnwrapped != CK_INVALID_HANDLE)) {
        PKM_LogIt("C_UnwrapKey succeeded\n");
    } else {
        PKM_Error("C_UnwrapKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return crv;
}

/*
 * Tests if the object's attributes match the expected_attrs
 */
CK_RV
PKM_AttributeCheck(CK_FUNCTION_LIST_PTR pFunctionList,
                   CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE obj,
                   CK_ATTRIBUTE_PTR expected_attrs,
                   CK_ULONG expected_attrs_count)
{
    CK_RV crv;
    CK_ATTRIBUTE_PTR tmp_attrs;
    unsigned int i;

    NUMTESTS++; /* increment NUMTESTS */

    /* First duplicate the themplate */
    tmp_attrs = malloc(expected_attrs_count * sizeof(CK_ATTRIBUTE));

    if (tmp_attrs == NULL) {
        PKM_Error("Internal test memory failure\n");
        return (CKR_HOST_MEMORY);
    }

    for (i = 0; i < expected_attrs_count; i++) {
        tmp_attrs[i].type = expected_attrs[i].type;
        tmp_attrs[i].ulValueLen = expected_attrs[i].ulValueLen;

        /* Don't give away the expected one. just zeros */
        tmp_attrs[i].pValue = calloc(expected_attrs[i].ulValueLen, 1);

        if (tmp_attrs[i].pValue == NULL) {
            unsigned int j;
            for (j = 0; j < i; j++)
                free(tmp_attrs[j].pValue);

            free(tmp_attrs);
            printf("Internal test memory failure\n");
            return (CKR_HOST_MEMORY);
        }
    }

    /* then get the attributes from the object */
    crv = pFunctionList->C_GetAttributeValue(hSession, obj, tmp_attrs,
                                             expected_attrs_count);
    if (crv != CKR_OK) {
        PKM_Error("C_GetAttributeValue failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        crv = CKR_FUNCTION_FAILED;
        goto out;
    }

    /* Finally compare with the expected ones */
    for (i = 0; i < expected_attrs_count; i++) {

        if (memcmp(tmp_attrs[i].pValue, expected_attrs[i].pValue,
                   expected_attrs[i].ulValueLen) != 0) {
            PKM_LogIt("comparing attribute type 0x%x with expected  0x%x\n",
                      tmp_attrs[i].type, expected_attrs[i].type);
            PKM_LogIt("comparing attribute type value 0x%x with expected 0x%x\n",
                      tmp_attrs[i].pValue, expected_attrs[i].pValue);
            /* don't report error at this time */
        }
    }

out:
    for (i = 0; i < expected_attrs_count; i++)
        free(tmp_attrs[i].pValue);
    free(tmp_attrs);
    return (crv);
}

/*
 * Check the validity of a mech
 */
CK_RV
PKM_MechCheck(CK_FUNCTION_LIST_PTR pFunctionList, CK_SESSION_HANDLE hSession,
              CK_MECHANISM_TYPE mechType, CK_FLAGS flags,
              CK_BBOOL check_sizes, CK_ULONG minkeysize, CK_ULONG maxkeysize)
{
    CK_SESSION_INFO sess_info;
    CK_MECHANISM_INFO mech_info;
    CK_RV crv;

    NUMTESTS++; /* increment NUMTESTS */

    if ((crv = pFunctionList->C_GetSessionInfo(hSession, &sess_info)) !=
        CKR_OK) {
        PKM_Error("C_GetSessionInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return (CKR_FUNCTION_FAILED);
    }

    crv = pFunctionList->C_GetMechanismInfo(0, mechType,
                                            &mech_info);

    crv = pFunctionList->C_GetMechanismInfo(sess_info.slotID, mechType,
                                            &mech_info);

    if (crv != CKR_OK) {
        PKM_Error("C_GetMechanismInfo failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return (CKR_FUNCTION_FAILED);
    }

    if ((mech_info.flags & flags) == 0) {
        PKM_Error("0x%x flag missing from mech\n", flags);
        return (CKR_MECHANISM_INVALID);
    }
    if (!check_sizes)
        return (CKR_OK);

    if (mech_info.ulMinKeySize != minkeysize) {
        PKM_Error("Bad MinKeySize %d expected %d\n", mech_info.ulMinKeySize,
                  minkeysize);
        return (CKR_MECHANISM_INVALID);
    }
    if (mech_info.ulMaxKeySize != maxkeysize) {
        PKM_Error("Bad MaxKeySize %d expected %d\n", mech_info.ulMaxKeySize,
                  maxkeysize);
        return (CKR_MECHANISM_INVALID);
    }
    return (CKR_OK);
}

/*
 * Can be called with a non-null premaster_key_len for the
 * *_DH mechanisms. In that case, no checking for the matching of
 * the expected results is done.
 * The rnd argument tells which correct/bogus randomInfo to use.
 */
CK_RV
PKM_TLSMasterKeyDerive(CK_FUNCTION_LIST_PTR pFunctionList,
                       CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                       CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen,
                       CK_MECHANISM_TYPE mechType,
                       enum_random_t rnd)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv;
    CK_MECHANISM mk_mech;
    CK_VERSION version;
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE type = CKK_GENERIC_SECRET;
    CK_BBOOL derive_bool = true;
    CK_ATTRIBUTE attrs[4];
    CK_ULONG attrs_count = 4;
    CK_OBJECT_HANDLE pmk_obj = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE mk_obj = CK_INVALID_HANDLE;
    CK_SSL3_MASTER_KEY_DERIVE_PARAMS mkd_params;
    CK_MECHANISM skmd_mech;

    CK_BBOOL isDH = false;

    NUMTESTS++; /* increment NUMTESTS */

    attrs[0].type = CKA_CLASS;
    attrs[0].pValue = &class;
    attrs[0].ulValueLen = sizeof(class);
    attrs[1].type = CKA_KEY_TYPE;
    attrs[1].pValue = &type;
    attrs[1].ulValueLen = sizeof(type);
    attrs[2].type = CKA_DERIVE;
    attrs[2].pValue = &derive_bool;
    attrs[2].ulValueLen = sizeof(derive_bool);
    attrs[3].type = CKA_VALUE;
    attrs[3].pValue = NULL;
    attrs[3].ulValueLen = 0;

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Before all, check if the mechanism is supported correctly */
    if (MODE == FIPSMODE) {
        crv = PKM_MechCheck(pFunctionList, hSession, mechType, CKF_DERIVE, false,
                            0, 0);
        if (crv != CKR_OK) {
            PKM_Error("PKM_MechCheck failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            return (crv);
        }
    }

    mk_mech.mechanism = mechType;
    mk_mech.pParameter = &mkd_params;
    mk_mech.ulParameterLen = sizeof(mkd_params);

    switch (mechType) {
        case CKM_TLS_MASTER_KEY_DERIVE_DH:
            isDH = true;
        /* FALLTHRU */
        case CKM_TLS_MASTER_KEY_DERIVE:
            attrs[3].pValue = NULL;
            attrs[3].ulValueLen = 0;

            mkd_params.RandomInfo.pClientRandom = (unsigned char *)TLSClientRandom;
            mkd_params.RandomInfo.ulClientRandomLen =
                sizeof(TLSClientRandom);
            mkd_params.RandomInfo.pServerRandom = (unsigned char *)TLSServerRandom;
            mkd_params.RandomInfo.ulServerRandomLen =
                sizeof(TLSServerRandom);
            break;
    }
    mkd_params.pVersion = (!isDH) ? &version : NULL;

    /* First create the pre-master secret key */

    skmd_mech.mechanism = CKM_SSL3_PRE_MASTER_KEY_GEN;
    skmd_mech.pParameter = &mkd_params;
    skmd_mech.ulParameterLen = sizeof(mkd_params);

    crv = pFunctionList->C_GenerateKey(hSession, &skmd_mech,
                                       attrs,
                                       attrs_count,
                                       &pmk_obj);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKey succeeded\n");
    } else {
        PKM_Error("C_GenerateKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    /* Test the bad cases */
    switch (rnd) {
        case CORRECT:
            goto correct;

        case BOGUS_CLIENT_RANDOM:
            mkd_params.RandomInfo.pClientRandom = NULL;
            break;

        case BOGUS_CLIENT_RANDOM_LEN:
            mkd_params.RandomInfo.ulClientRandomLen = 0;
            break;

        case BOGUS_SERVER_RANDOM:
            mkd_params.RandomInfo.pServerRandom = NULL;
            break;

        case BOGUS_SERVER_RANDOM_LEN:
            mkd_params.RandomInfo.ulServerRandomLen = 0;
            break;
    }
    crv = pFunctionList->C_DeriveKey(hSession, &mk_mech, pmk_obj, NULL, 0,
                                     &mk_obj);
    if (crv != CKR_MECHANISM_PARAM_INVALID) {
        PKM_LogIt("C_DeriveKey returned as EXPECTED with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
    } else {
        PKM_Error("C_DeriveKey did not fail  with  bad data \n");
    }
    goto out;

correct:
    /* Now derive the master secret key */
    crv = pFunctionList->C_DeriveKey(hSession, &mk_mech, pmk_obj, NULL, 0,
                                     &mk_obj);
    if (crv == CKR_OK) {
        PKM_LogIt("C_DeriveKey succeeded\n");
    } else {
        PKM_Error("C_DeriveKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

out:
    if (pmk_obj != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, pmk_obj);
    if (mk_obj != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, mk_obj);
    crv = pFunctionList->C_Logout(hSession);

    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    return (crv);
}

CK_RV
PKM_TLSKeyAndMacDerive(CK_FUNCTION_LIST_PTR pFunctionList,
                       CK_SLOT_ID *pSlotList, CK_ULONG slotID,
                       CK_UTF8CHAR_PTR pwd, CK_ULONG pwdLen,
                       CK_MECHANISM_TYPE mechType, enum_random_t rnd)
{
    CK_SESSION_HANDLE hSession;
    CK_RV crv;
    CK_MECHANISM kmd_mech;
    CK_MECHANISM skmd_mech;
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE type = CKK_GENERIC_SECRET;
    CK_BBOOL derive_bool = true;
    CK_BBOOL sign_bool = true, verify_bool = true;
    CK_BBOOL encrypt_bool = true, decrypt_bool = true;
    CK_ULONG value_len;

    /*
     * We arrange this template so that:
     * . Attributes 0-6 are good for a MAC key comparison template.
     * . Attributes 2-5 are good for the master key creation template.
     * . Attributes 3-8 are good for a cipher key comparison template.
     */
    CK_ATTRIBUTE attrs[9];

    CK_OBJECT_HANDLE mk_obj = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE derive_obj = CK_INVALID_HANDLE;
    CK_SSL3_KEY_MAT_PARAMS km_params;
    CK_SSL3_KEY_MAT_OUT kmo;
    CK_BYTE IVClient[8];
    CK_BYTE IVServer[8];

    NUMTESTS++; /* increment NUMTESTS */

    attrs[0].type = CKA_SIGN;
    attrs[0].pValue = &sign_bool;
    attrs[0].ulValueLen = sizeof(sign_bool);
    attrs[1].type = CKA_VERIFY;
    attrs[1].pValue = &verify_bool;
    attrs[1].ulValueLen = sizeof(verify_bool);
    attrs[2].type = CKA_KEY_TYPE;
    attrs[2].pValue = &type;
    attrs[2].ulValueLen = sizeof(type);
    attrs[3].type = CKA_CLASS;
    attrs[3].pValue = &class;
    attrs[3].ulValueLen = sizeof(class);
    attrs[4].type = CKA_DERIVE;
    attrs[4].pValue = &derive_bool;
    attrs[4].ulValueLen = sizeof(derive_bool);
    attrs[5].type = CKA_VALUE;
    attrs[5].pValue = NULL;
    attrs[5].ulValueLen = 0;
    attrs[6].type = CKA_VALUE_LEN;
    attrs[6].pValue = &value_len;
    attrs[6].ulValueLen = sizeof(value_len);
    attrs[7].type = CKA_ENCRYPT;
    attrs[7].pValue = &encrypt_bool;
    attrs[7].ulValueLen = sizeof(encrypt_bool);
    attrs[8].type = CKA_DECRYPT;
    attrs[8].pValue = &decrypt_bool;
    attrs[8].ulValueLen = sizeof(decrypt_bool);

    crv = pFunctionList->C_OpenSession(pSlotList[slotID], CKF_SERIAL_SESSION,
                                       NULL, NULL, &hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_OpenSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_Login(hSession, CKU_USER, pwd, pwdLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Login with correct password succeeded\n");
    } else {
        PKM_Error("C_Login with correct password failed "
                  "with 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Before all, check if the mechanism is supported correctly */
    if (MODE == FIPSMODE) {
        crv = PKM_MechCheck(pFunctionList, hSession, mechType, CKF_DERIVE,
                            CK_TRUE, 48, 48);

        if (crv != CKR_OK) {
            PKM_Error("PKM_MechCheck failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            return (crv);
        }
    }
    kmd_mech.mechanism = mechType;
    kmd_mech.pParameter = &km_params;
    kmd_mech.ulParameterLen = sizeof(km_params);

    km_params.ulMacSizeInBits = 128; /* an MD5 based MAC */
    km_params.ulKeySizeInBits = 192; /* 3DES key size */
    km_params.ulIVSizeInBits = 64;   /* 3DES block size */
    km_params.pReturnedKeyMaterial = &kmo;
    km_params.bIsExport = false;
    kmo.hClientMacSecret = CK_INVALID_HANDLE;
    kmo.hServerMacSecret = CK_INVALID_HANDLE;
    kmo.hClientKey = CK_INVALID_HANDLE;
    kmo.hServerKey = CK_INVALID_HANDLE;
    kmo.pIVClient = IVClient;
    kmo.pIVServer = IVServer;

    skmd_mech.mechanism = CKM_SSL3_PRE_MASTER_KEY_GEN;
    skmd_mech.pParameter = &km_params;
    skmd_mech.ulParameterLen = sizeof(km_params);

    crv = pFunctionList->C_GenerateKey(hSession, &skmd_mech,
                                       &attrs[2],
                                       4,
                                       &mk_obj);
    if (crv == CKR_OK) {
        PKM_LogIt("C_GenerateKey succeeded\n");
    } else {
        PKM_Error("C_GenerateKey failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    attrs[5].pValue = NULL;
    attrs[5].ulValueLen = 0;

    km_params.RandomInfo.pClientRandom = (unsigned char *)TLSClientRandom;
    km_params.RandomInfo.ulClientRandomLen =
        sizeof(TLSClientRandom);
    km_params.RandomInfo.pServerRandom = (unsigned char *)TLSServerRandom;
    km_params.RandomInfo.ulServerRandomLen =
        sizeof(TLSServerRandom);

    /* Test the bad cases */
    switch (rnd) {
        case CORRECT:
            goto correct;

        case BOGUS_CLIENT_RANDOM:
            km_params.RandomInfo.pClientRandom = NULL;
            break;

        case BOGUS_CLIENT_RANDOM_LEN:
            km_params.RandomInfo.ulClientRandomLen = 0;
            break;

        case BOGUS_SERVER_RANDOM:
            km_params.RandomInfo.pServerRandom = NULL;
            break;

        case BOGUS_SERVER_RANDOM_LEN:
            km_params.RandomInfo.ulServerRandomLen = 0;
            break;
    }
    crv = pFunctionList->C_DeriveKey(hSession, &kmd_mech, mk_obj, NULL, 0,
                                     &derive_obj);
    if (crv != CKR_MECHANISM_PARAM_INVALID) {
        PKM_Error("key materials derivation returned unexpected "
                  "error 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        (void)pFunctionList->C_DestroyObject(hSession, mk_obj);
        return (CKR_FUNCTION_FAILED);
    }
    return (CKR_OK);

correct:
    /*
     * Then use the master key and the client 'n server random data to
     * derive the key materials
     */
    crv = pFunctionList->C_DeriveKey(hSession, &kmd_mech, mk_obj, NULL, 0,
                                     &derive_obj);
    if (crv != CKR_OK) {
        PKM_Error("Cannot derive the key materials, crv 0x%08X, %-26s\n",
                  crv, PKM_CK_RVtoStr(crv));
        (void)pFunctionList->C_DestroyObject(hSession, mk_obj);
        return (crv);
    }

    if (mk_obj != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, mk_obj);
    if (kmo.hClientMacSecret != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, kmo.hClientMacSecret);
    if (kmo.hServerMacSecret != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, kmo.hServerMacSecret);
    if (kmo.hClientKey != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, kmo.hClientKey);
    if (kmo.hServerKey != CK_INVALID_HANDLE)
        (void)pFunctionList->C_DestroyObject(hSession, kmo.hServerKey);

    crv = pFunctionList->C_Logout(hSession);
    if (crv == CKR_OK) {
        PKM_LogIt("C_Logout succeeded\n");
    } else {
        PKM_Error("C_Logout failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_CloseSession(hSession);
    if (crv != CKR_OK) {
        PKM_Error("C_CloseSession failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    return (crv);
}

CK_RV
PKM_DualFuncSign(CK_FUNCTION_LIST_PTR pFunctionList,
                 CK_SESSION_HANDLE hRwSession,
                 CK_OBJECT_HANDLE publicKey, CK_OBJECT_HANDLE privateKey,
                 CK_MECHANISM *sigMech,
                 CK_OBJECT_HANDLE secretKey, CK_MECHANISM *cryptMech,
                 const CK_BYTE *pData, CK_ULONG pDataLen)
{

    CK_RV crv = CKR_OK;
    CK_BYTE encryptedData[MAX_CIPHER_SZ];
    CK_ULONG ulEncryptedDataLen = 0;
    CK_ULONG ulLastUpdateSize = 0;
    CK_BYTE sig[MAX_SIG_SZ];
    CK_ULONG ulSigLen = 0;
    CK_BYTE data[MAX_DATA_SZ];
    CK_ULONG ulDataLen = 0;

    memset(encryptedData, 0, sizeof(encryptedData));
    memset(sig, 0, sizeof(sig));
    memset(data, 0, sizeof(data));

    NUMTESTS++; /* increment NUMTESTS */

    /* Check that the mechanism is Multi-part */
    if (sigMech->mechanism == CKM_DSA || sigMech->mechanism == CKM_RSA_PKCS) {
        PKM_Error("PKM_DualFuncSign must be called with a Multi-part "
                  "operation mechanism\n");
        return CKR_DEVICE_ERROR;
    }

    /* Sign and Encrypt */
    if (privateKey == 0 && publicKey == 0) {
        crv = pFunctionList->C_SignInit(hRwSession, sigMech, secretKey);
        if (crv != CKR_OK) {
            PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
    } else {
        crv = pFunctionList->C_SignInit(hRwSession, sigMech, privateKey);
        if (crv != CKR_OK) {
            PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
    }
    crv = pFunctionList->C_EncryptInit(hRwSession, cryptMech, secretKey);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    ulEncryptedDataLen = sizeof(encryptedData);
    crv = pFunctionList->C_SignEncryptUpdate(hRwSession, (CK_BYTE *)pData,
                                             pDataLen,
                                             encryptedData,
                                             &ulEncryptedDataLen);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    ulLastUpdateSize = sizeof(encryptedData) - ulEncryptedDataLen;
    crv = pFunctionList->C_EncryptFinal(hRwSession,
                                        (CK_BYTE *)&encryptedData[ulEncryptedDataLen], &ulLastUpdateSize);
    if (crv != CKR_OK) {
        PKM_Error("C_EncryptFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    ulEncryptedDataLen = ulEncryptedDataLen + ulLastUpdateSize;
    ulSigLen = sizeof(sig);
    crv = pFunctionList->C_SignFinal(hRwSession, sig, &ulSigLen);
    if (crv != CKR_OK) {
        PKM_Error("C_SignFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Decrypt and Verify */

    crv = pFunctionList->C_DecryptInit(hRwSession, cryptMech, secretKey);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    crv = pFunctionList->C_VerifyInit(hRwSession, sigMech,
                                      publicKey);
    if (crv != CKR_OK) {
        PKM_Error("C_VerifyInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    ulDataLen = sizeof(data);
    crv = pFunctionList->C_DecryptVerifyUpdate(hRwSession,
                                               encryptedData,
                                               ulEncryptedDataLen,
                                               data, &ulDataLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptVerifyUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    ulLastUpdateSize = sizeof(data) - ulDataLen;
    /* Get last little piece of plaintext.  Should have length 0 */
    crv = pFunctionList->C_DecryptFinal(hRwSession, &data[ulDataLen],
                                        &ulLastUpdateSize);
    if (crv != CKR_OK) {
        PKM_Error("C_DecryptFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (ulLastUpdateSize != 0) {
        crv = pFunctionList->C_VerifyUpdate(hRwSession, &data[ulDataLen],
                                            ulLastUpdateSize);
        if (crv != CKR_OK) {
            PKM_Error("C_DecryptFinal failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
    }
    ulDataLen = ulDataLen + ulLastUpdateSize;

    /* input for the verify operation is the decrypted data */
    crv = pFunctionList->C_VerifyFinal(hRwSession, sig, ulSigLen);
    if (crv == CKR_OK) {
        PKM_LogIt("C_VerifyFinal succeeded\n");
    } else {
        PKM_Error("C_VerifyFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* Comparison of Decrypted data with inputed data */
    if ((ulDataLen == pDataLen) &&
        (memcmp(data, pData, pDataLen) == 0)) {
        PKM_LogIt("PKM_DualFuncSign decrypt test case passed\n");
    } else {
        PKM_Error("PKM_DualFuncSign derypt test case failed\n");
    }

    return crv;
}

CK_RV
PKM_Digest(CK_FUNCTION_LIST_PTR pFunctionList,
           CK_SESSION_HANDLE hSession,
           CK_MECHANISM *digestMech, CK_OBJECT_HANDLE hSecretKey,
           const CK_BYTE *pData, CK_ULONG pDataLen)
{
    CK_RV crv = CKR_OK;
    CK_BYTE digest1[MAX_DIGEST_SZ];
    CK_ULONG digest1Len = 0;
    CK_BYTE digest2[MAX_DIGEST_SZ];
    CK_ULONG digest2Len = 0;

    /* Tested with CKM_SHA_1, CKM_SHA224, CKM_SHA256, CKM_SHA384, CKM_SHA512 */

    memset(digest1, 0, sizeof(digest1));
    memset(digest2, 0, sizeof(digest2));

    NUMTESTS++; /* increment NUMTESTS */

    crv = pFunctionList->C_DigestInit(hSession, digestMech);
    if (crv != CKR_OK) {
        PKM_Error("C_SignInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }
    digest1Len = sizeof(digest1);
    crv = pFunctionList->C_Digest(hSession, (CK_BYTE *)pData, pDataLen,
                                  digest1, &digest1Len);
    if (crv != CKR_OK) {
        PKM_Error("C_Sign failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_DigestInit(hSession, digestMech);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestInit failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    crv = pFunctionList->C_DigestUpdate(hSession, (CK_BYTE *)pData, pDataLen);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestUpdate failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    /* C_DigestKey continues a multiple-part message-digesting operation by*/
    /* digesting the value of a secret key. (only used with C_DigestUpdate)*/
    if (hSecretKey != 0) {
        crv = pFunctionList->C_DigestKey(hSession, hSecretKey);
        if (crv != CKR_OK) {
            PKM_Error("C_DigestKey failed with 0x%08X, %-26s\n", crv,
                      PKM_CK_RVtoStr(crv));
            return crv;
        }
    }

    digest2Len = sizeof(digest2);
    crv = pFunctionList->C_DigestFinal(hSession, digest2, &digest2Len);
    if (crv != CKR_OK) {
        PKM_Error("C_DigestFinal failed with 0x%08X, %-26s\n", crv,
                  PKM_CK_RVtoStr(crv));
        return crv;
    }

    if (hSecretKey == 0) {
        /* did not digest a secret key so digests should equal */
        if ((digest1Len == digest2Len) &&
            (memcmp(digest1, digest2, digest1Len) == 0)) {
            PKM_LogIt("Single and Multiple-part message digest "
                      "operations successful\n");
        } else {
            PKM_Error("Single and Multiple-part message digest "
                      "operations failed\n");
        }
    } else {
        if (digest1Len == digest2Len) {
            PKM_LogIt("PKM_Digest Single and Multiple-part message digest "
                      "operations successful\n");
        } else {
            PKM_Error("PKM_Digest Single and Multiple-part message digest "
                      "operations failed\n");
        }
    }

    return crv;
}

char *
PKM_FilePasswd(char *pwFile)
{
    unsigned char phrase[500];
    PRFileDesc *fd;
    PRInt32 nb;
    int i;

    if (!pwFile)
        return 0;

    fd = PR_Open(pwFile, PR_RDONLY, 0);
    if (!fd) {
        fprintf(stderr, "No password file \"%s\" exists.\n", pwFile);
        return NULL;
    }

    nb = PR_Read(fd, phrase, sizeof(phrase));

    PR_Close(fd);
    /* handle the Windows EOL case */
    i = 0;
    while (phrase[i] != '\r' && phrase[i] != '\n' && i < nb)
        i++;
    phrase[i] = '\0';
    if (nb == 0) {
        fprintf(stderr, "password file contains no data\n");
        return NULL;
    }
    return (char *)strdup((char *)phrase);
}

void
PKM_Help()
{
    PRFileDesc *debug_out = PR_GetSpecialFD(PR_StandardError);
    PR_fprintf(debug_out, "pk11mode test program usage:\n");
    PR_fprintf(debug_out, "\t-f <file>   Password File : echo pw > file \n");
    PR_fprintf(debug_out, "\t-F          Disable Unix fork tests\n");
    PR_fprintf(debug_out, "\t-n          Non Fips Mode \n");
    PR_fprintf(debug_out, "\t-d <path>   Database path location\n");
    PR_fprintf(debug_out, "\t-p <prefix> DataBase prefix\n");
    PR_fprintf(debug_out, "\t-v          verbose\n");
    PR_fprintf(debug_out, "\t-h          this help message\n");
    exit(1);
}

void
PKM_CheckPath(char *string)
{
    char *src;
    char *dest;

    /*
     * windows support convert any back slashes to
     * forward slashes.
     */
    for (src = string, dest = string; *src; src++, dest++) {
        if (*src == '\\') {
            *dest = '/';
        }
    }
    dest--;
    /* if the last char is a / set it to 0 */
    if (*dest == '/')
        *dest = 0;
}

CK_RV
PKM_ForkCheck(int expected, CK_FUNCTION_LIST_PTR fList,
              PRBool forkAssert, CK_C_INITIALIZE_ARGS_NSS *initArgs)
{
    CK_RV crv = CKR_OK;
#ifndef NO_FORK_CHECK
    int rc = -1;
    pid_t child, ret;
    NUMTESTS++; /* increment NUMTESTS */
    if (forkAssert) {
        putenv("NSS_STRICT_NOFORK=1");
    } else {
        putenv("NSS_STRICT_NOFORK=0");
    }
    child = fork();
    switch (child) {
        case -1:
            PKM_Error("Fork failed.\n");
            crv = CKR_DEVICE_ERROR;
            break;
        case 0:
            if (fList) {
                if (!initArgs) {
                    /* If softoken is loaded, make a PKCS#11 call to C_GetTokenInfo
                     * in the child. This call should always fail.
                     * If softoken is uninitialized,
                     * it fails with CKR_CRYPTOKI_NOT_INITIALIZED.
                     * If it was initialized in the parent, the fork check should
                     * kick in, and make it return CKR_DEVICE_ERROR.
                     */
                    CK_RV child_crv = fList->C_GetTokenInfo(0, NULL);
                    exit(child_crv & 255);
                } else {
                    /* If softoken is loaded, make a PKCS#11 call to C_Initialize
                     * in the child. This call should always fail.
                     * If softoken is uninitialized, this should succeed.
                     * If it was initialized in the parent, the fork check should
                     * kick in, and make it return CKR_DEVICE_ERROR.
                     */
                    CK_RV child_crv = fList->C_Initialize(initArgs);
                    if (CKR_OK == child_crv) {
                        child_crv = fList->C_Finalize(NULL);
                    }
                    exit(child_crv & 255);
                }
            }
            exit(expected & 255);
        default:
            PKM_LogIt("Fork succeeded.\n");
            ret = wait(&rc);
            if (ret != child || (!WIFEXITED(rc)) ||
                ((expected & 255) != (WEXITSTATUS(rc) & 255))) {
                int retStatus = -1;
                if (WIFEXITED(rc)) {
                    retStatus = WEXITSTATUS(rc);
                }
                PKM_Error("Child misbehaved.\n");
                printf("Child return status : %d.\n", retStatus & 255);
                crv = CKR_DEVICE_ERROR;
            }
            break;
    }
#endif
    return crv;
}
