/////////////////////////////////////////////////////////////////////////////
//  FILE          : nt_key.c                                               //
//  DESCRIPTION   : Crypto CP interfaces:                                  //
//                  CPGenKey                                               //
//                  CPDeriveKey                                            //
//                  CPExportKey                                            //
//                  CPImportKey                                            //
//                  CPDestroyKey                                           //
//                  CPGetUserKey                                           //
//                  CPSetKeyParam                                          //
//                  CPGetKeyParam                                          //
//  AUTHOR        :                                                        //
//  HISTORY       :                                                        //
//  Jan 25 1995 larrys  Changed from Nametag                               //
//  Feb 16 1995 larrys  Fix problem for 944 build                          //
//  Feb 21 1995 larrys  Added SPECIAL_KEY                                  //
//  Feb 23 1995 larrys  Changed NTag_SetLastError to SetLastError          //
//  Mar 08 1995 larrys  Fixed a few problems                               //
//  Mar 23 1995 larrys  Added variable key length                          //
//  Apr  7 1995 larrys  Removed CryptConfigure                             //
//  Apr 17 1995 larrys  Added 1024 key gen                                 //
//  Apr 19 1995 larrys  Changed CRYPT_EXCH_PUB to AT_KEYEXCHANGE           //
//  May 10 1995 larrys  added private api calls                            //
//  May 17 1995 larrys  added key data for DES test                        //
//  Jul 20 1995 larrys  Changed export of PUBLICKEYBLOB                    //
//  Jul 21 1995 larrys  Fixed Export of AUTHENTICATEDBLOB                  //
//  Aug 03 1995 larrys  Allow CryptG(S)etKeyParam for Public keys &        //
//                      Removed CPTranslate                                //
//  Aug 10 1995 larrys  Fixed a few problems in CryptGetKeyParam           //
//  Aug 11 1995 larrys  Return no key for CryptGetUserKey                  //
//  Aug 14 1995 larrys  Removed key exchange stuff                         //
//  Aug 17 1995 larrys  Removed a error                                    //
//  Aug 18 1995 larrys  Changed NTE_BAD_LEN to ERROR_MORE_DATA             //
//  Aug 30 1995 larrys  Removed RETURNASHVALUE from CryptGetHashValue      //
//  Aug 31 1995 larrys  Fixed CryptExportKey if pbData == NULL             //
//  Sep 05 1995 larrys  Fixed bug # 30                                     //
//  Sep 05 1995 larrys  Fixed bug # 31                                     //
//  Sep 11 1995 larrys  Fixed bug # 34                                     //
//  Sep 12 1995 larrys  Removed 2 DWORDS from exported keys                //
//  Sep 14 1995 Jeffspel/ramas  Merged STT onto CSP                        //
//  Sep 18 1995 larrys  Changed def KP_PERMISSIONS to 0xffffffff           //
//  Oct 02 1995 larrys  Fixed bug 43 return error for importkey on hPubkey //
//  Oct 03 1995 larrys  Fixed bug 37 call InflateKey from SetKeyParam      //
//  Oct 03 1995 larrys  Fixed bug 36, removed OFB from SetKeyParam         //
//  Oct 03 1995 larrys  Fixed bug 38, check key type in SetKeyParam        //
//  Oct 13 1995 larrys  Added CPG/setProv/HashParam                        //
//  Oct 13 1995 larrys  Added code for CryptSetHashValue                   //
//  Oct 16 1995 larrys  Changes for CryptGetHashParam                      //
//  Oct 23 1995 larrys  Added code for GetProvParam PP_CONTAINER           //
//  Oct 27 1995 rajeshk RandSeed Stuff added hUID to PKCS2Encrypt+ others  //
//  Nov  3 1995 larrys  Merge changes for NT checkin                       //
//  Nov  9 1995 larrys  Bug fix 10686                                      //
//  Nov 30 1995 larrys  Bug fix                                            //
//  Dec 11 1995 larrys  Added WIN96 password cache                         //
//  Feb 29 1996 rajeshk Added Check for SetHashParam for HASHVALUE         //
//  May 15 1996 larrys  Added private key export                           //
//  May 28 1996 larrys  Fix bug 88                                         //
//  Jun  6 1996 a-johnb Added support for SSL 3.0 signatures               //
//  Aug 28 1996 mattt   Changed enum to calculate size from #defined sizes //
//  Sep 13 1996 mattt   Compat w/RSABase 88-bit 0 salt, FIsLegalKey()      //
//  Sep 16 1996 mattt   Added KP_KEYLEN ability                            //
//  Sep 16 1996 jeffspel Added triple DES functionality                    //
//  Oct 14 1996 jeffspel Changed GenRandoms to NewGenRandoms               //
//  Apr 29 1997 jeffspel Key storage ability GetProvParam, PStore support  //
//  Apr 29 1997 jeffspel Added EnumAlgsEx tp GetProvParam                  //
//  May 23 1997 jeffspel Added provider type checking                      //
//  Jul 15 1997 jeffspel Added ability to decrypt with large RC2 keys      //
//  Jul 28 1997 jeffspel Added ability to delete a persisted key           //
//  Sep 09 1997 jeffspel Added PP_KEYSET_TYPE to CPGetProvParam            //
//  Sep 12 1997 jeffspel Added Opaque blob support                         //
//  May  4 2000 dbarlow Error code return cleanup                          //
//                                                                         //
//  Copyright (C) 1993 - 2000 Microsoft Corporation                        //
//  All Rights Reserved                                                    //
/////////////////////////////////////////////////////////////////////////////

#include "precomp.h"
#include "nt_rsa.h"
#include "nt_blobs.h"
#include "swnt_pk.h"
#include "mac.h"
#include "ntagimp1.h"
#include "tripldes.h"
#include "ntagum.h"
#include "randlib.h"
#ifdef CSP_USE_SSL3
#include "ssl3.h"
#endif
#include "protstor.h"
#include "sgccheck.h"
#include "aes.h"

extern CSP_STRINGS g_Strings;

#ifndef CSP_USE_AES
#define UnsupportedSymKey(pKey) ((CALG_RC4 != pKey->Algid) && \
                                 (CALG_RC2 != pKey->Algid) && \
                                 (CALG_DES != pKey->Algid) && \
                                 (CALG_3DES != pKey->Algid) && \
                                 (CALG_3DES_112 != pKey->Algid))
#else
#define UnsupportedSymKey(pKey) ((CALG_RC4 != pKey->Algid) && \
                                 (CALG_RC2 != pKey->Algid) && \
                                 (CALG_DES != pKey->Algid) && \
                                 (CALG_3DES != pKey->Algid) && \
                                 (CALG_3DES_112 != pKey->Algid) && \
                                 (CALG_AES_128 != pKey->Algid) && \
                                 (CALG_AES_192 != pKey->Algid) && \
                                 (CALG_AES_256 != pKey->Algid))
#endif

#define NTAG_REG_KEY_LOC      "Software\\Microsoft\\Cryptography\\UserKeys"
#define NTAG_MACH_REG_KEY_LOC "Software\\Microsoft\\Cryptography\\MachineKeys"

extern DWORD
InflateKey(
    IN PNTAGKeyList pTmpKey);

/*static*/ DWORD
CopyKey(
    IN PNTAGKeyList pOldKey,
    OUT PNTAGKeyList *ppNewKey);

extern DWORD
SymEncrypt(
    IN PNTAGKeyList pKey,
    IN BOOL fFinal,
    IN OUT BYTE *pbData,
    IN OUT DWORD *pcbData,
    IN DWORD cbBuf);

extern DWORD
SymDecrypt(
    IN PNTAGKeyList pKey,
    IN PNTAGHashList pHash,
    IN BOOL fFinal,
    IN OUT BYTE *pbData,
    IN OUT DWORD *pcbData);

extern DWORD
BlockEncrypt(
    void EncFun(BYTE *In, BYTE *Out, void *key, int op),
    PNTAGKeyList pKey,
    int BlockLen,
    BOOL Final,
    BYTE  *pbData,
    DWORD *pdwDataLen,
    DWORD dwBufLen);

static BYTE rgbSymmetricKeyWrapIV[8]
    = {0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05};


//
// Set the permissions on the key
//

/*static*/ void
SetInitialKeyPermissions(
    PNTAGKeyList pKey)
{
    if (CRYPT_EXPORTABLE == pKey->Rights)
    {
        pKey->Permissions |= CRYPT_EXPORT;
    }

    // UNDONE - set the appopropriate permission with the appropriate
    //          algorithm
    pKey->Permissions |= CRYPT_ENCRYPT | CRYPT_DECRYPT| CRYPT_READ |
                         CRYPT_WRITE | CRYPT_MAC;
}


/* MakeNewKey
 *
 *  Helper routine for ImportKey, GenKey
 *
 *  Allocate a new key record, fill in the data and copy in the key
 *  bytes.
 */

DWORD
MakeNewKey(
    ALG_ID       aiKeyAlg,
    DWORD        dwRights,
    DWORD        dwKeyLen,
    HCRYPTPROV   hUID,
    BYTE         *pbKeyData,
    BOOL         fUsePassedKeyBuffer,
    BOOL         fPreserveExactKey,
    PNTAGKeyList *ppKeyList)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGKeyList    pKey = NULL;

    *ppKeyList = NULL;

    // allocate space for the key record
    pKey = (PNTAGKeyList)_nt_malloc(sizeof(NTAGKeyList));
    if (NULL == pKey)
    {
        dwReturn = ERROR_NOT_ENOUGH_MEMORY;
        goto ErrorExit;
    }

    if (!fUsePassedKeyBuffer)
    {
        pKey->pKeyValue = (BYTE *)_nt_malloc((size_t)dwKeyLen);
        if (NULL == pKey->pKeyValue)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }
    }

    pKey->Algid = aiKeyAlg;
    pKey->Rights = dwRights;
    pKey->cbDataLen = 0;
    pKey->pData = NULL;
    pKey->hUID = hUID;
    memset(pKey->IV, 0, MAX_BLOCKLEN);
    memset(pKey->FeedBack, 0, MAX_BLOCKLEN);
    pKey->InProgress = FALSE;
    pKey->cbSaltLen = 0;
    pKey->Padding = PKCS5_PADDING;
    pKey->Mode = CRYPT_MODE_CBC;
    pKey->ModeBits = 0;

    SetInitialKeyPermissions(pKey);

    pKey->cbKeyLen = dwKeyLen;
    if (pbKeyData != NULL)
    {
        if (fUsePassedKeyBuffer)
            pKey->pKeyValue = pbKeyData;
        else
            memcpy(pKey->pKeyValue, pbKeyData, (size_t)dwKeyLen);
    }

    // Handle special cases
    switch (aiKeyAlg)
    {
    case CALG_RC2:
        // for RC2 set a default effective key length
        pKey->EffectiveKeyLen = RC2_DEFAULT_EFFECTIVE_KEYLEN;
        pKey->dwBlockLen = RC2_BLOCKLEN;
        break;
    case CALG_DES:
        if (DES_KEYSIZE != pKey->cbKeyLen)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }
        if (!fPreserveExactKey)
            desparityonkey(pKey->pKeyValue, pKey->cbKeyLen);
        pKey->dwBlockLen = DES_BLOCKLEN;
        break;
    case CALG_3DES_112:
        if (DES_KEYSIZE * 2 != pKey->cbKeyLen)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }
        if (!fPreserveExactKey)
            desparityonkey(pKey->pKeyValue, pKey->cbKeyLen);
        pKey->dwBlockLen = DES_BLOCKLEN;
        break;
    case CALG_3DES:
        if (DES_KEYSIZE * 3 != pKey->cbKeyLen)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }
        if (!fPreserveExactKey)
            desparityonkey(pKey->pKeyValue, pKey->cbKeyLen);
        pKey->dwBlockLen = DES_BLOCKLEN;
        break;
#ifdef CSP_USE_AES
    case CALG_AES_128:
        pKey->dwBlockLen = CRYPT_AES128_BLKLEN;
        break;
    case CALG_AES_192:
        pKey->dwBlockLen = CRYPT_AES192_BLKLEN;
        break;
    case CALG_AES_256:
        pKey->dwBlockLen = CRYPT_AES256_BLKLEN;
        break;
#endif
    default:
        pKey->dwBlockLen = 0;
    }

    *ppKeyList = pKey;
    return ERROR_SUCCESS;

ErrorExit:
    if (NULL != pKey)
        _nt_free (pKey, sizeof(NTAGKeyList));
    return dwReturn;
}


/* FreeNewKey
 *
 *      Use for cleanup on abort of key build operations.
 *
 */

void
FreeNewKey(
    PNTAGKeyList pOldKey)
{
    if (pOldKey->pKeyValue)
        _nt_free(pOldKey->pKeyValue, pOldKey->cbKeyLen);
    if (pOldKey->pData)
        _nt_free(pOldKey->pData, pOldKey->cbDataLen);
    _nt_free(pOldKey, sizeof(NTAGKeyList));
}


/* FIsLegalKeySize
 *
 *      Check that the length of the key is legal (essentially
 *      complies with export).
 *
 */

BOOL
FIsLegalKeySize(
    IN DWORD  dwCspTypeId,
    IN ALG_ID Algid,
    IN DWORD cbKey,
    IN BOOL fRC2BigKeyOK,
    OUT BOOL *pfPubKey)
{
    BOOL fRet = FALSE;

    *pfPubKey = FALSE;

    switch (Algid)
    {
#ifdef CSP_USE_RC2
    case CALG_RC2:
        if (!fRC2BigKeyOK)
        {
            if (!IsLegalLength(g_AlgTables[dwCspTypeId], Algid,
                               cbKey * 8, NULL))
                goto ErrorExit;
        }
        break;
#endif
#ifdef CSP_USE_DES
    case CALG_DES:
        if ((DES_KEYSIZE != cbKey) && ((DES_KEYSIZE - 1) != cbKey))
            goto ErrorExit;
        break;
#endif
#ifdef CSP_USE_3DES
    case CALG_3DES_112:
        if ((DES2_KEYSIZE != cbKey) && ((DES2_KEYSIZE - 2) != cbKey))
            goto ErrorExit;
        break;
    case CALG_3DES:
        if ((DES3_KEYSIZE != cbKey) && ((DES3_KEYSIZE - 3) != cbKey))
            goto ErrorExit;
        break;
#endif
    case CALG_RSA_SIGN:
    case CALG_RSA_KEYX:
        *pfPubKey = TRUE;
        // Fall through intentionally.
    default:
        if (!IsLegalLength(g_AlgTables[dwCspTypeId], Algid, cbKey * 8, NULL))
            goto ErrorExit;
    }

    fRet = TRUE;

ErrorExit:
    // not of regulation size
    return fRet;
}


#ifdef USE_SGC
/* FIsLegalSGCKeySize
 *
 *      Check that the length of the key is SGC legal (essentially
 *      complies with export).
 *
 */

BOOL
FIsLegalSGCKeySize(
    IN ALG_ID Algid,
    IN DWORD cbKey,
    IN BOOL fRC2BigKeyOK,
    IN BOOL fGenKey,
    OUT BOOL *pfPubKey)
{
    BOOL fSts = FALSE;

    if (!fGenKey)
    {
        if (!FIsLegalKeySize(POLICY_MS_SCHANNEL, Algid, cbKey, fRC2BigKeyOK,
                             pfPubKey))
            goto ErrorExit;
    }
    else
    {
        switch (Algid)
        {
#ifdef CSP_USE_RC2
        case CALG_RC2:
            if (!fRC2BigKeyOK)
                goto ErrorExit;
            break;
#endif
#ifdef CSP_USE_SSL3
        case CALG_SSL3_MASTER:
        case CALG_TLS1_MASTER:
        case CALG_PCT1_MASTER:
        case CALG_SSL2_MASTER:
            if (!FIsLegalKeySize(POLICY_MS_SCHANNEL, Algid, cbKey,
                                 fRC2BigKeyOK, pfPubKey))
                goto ErrorExit;
            break;
        case CALG_SCHANNEL_MAC_KEY:
            break;
#endif
        case CALG_RSA_KEYX:
            if (!FIsLegalKeySize(POLICY_MS_SCHANNEL, Algid, cbKey,
                                 fRC2BigKeyOK, pfPubKey))
                goto ErrorExit;
            break;
        default:
            goto ErrorExit;
        }
    }

    fSts = TRUE;

ErrorExit:
    return fSts;
}
#endif

/* FIsLegalKey
 *
 *      Check that the length of the key is legal (essentially
 *      complies with export).
 *
 */

BOOL
FIsLegalKey(
    IN PNTAGUserList pTmpUser,
    IN PNTAGKeyList pKey,
    IN BOOL fRC2BigKeyOK)
{
    BOOL    fPubKey;
    BOOL    fRet = FALSE;

    if (pKey == NULL)
        goto ErrorExit;

#ifdef USE_SGC
    // check if the provider is an SChannel provider and if so if the
    // SGC flag is set then use the FIsLegalSGCKeySize function
    if ((PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
        && (0 != pTmpUser->dwSGCFlags))
    {
        if (!FIsLegalSGCKeySize(pKey->Algid, pKey->cbKeyLen,
                                fRC2BigKeyOK, FALSE, &fPubKey))
        {
            goto ErrorExit;
        }
    }
    else
#endif
    {
        // 4th parameter, dwFlags, is used for SGC Exch keys so just
        // pass zero in this case
        if (!FIsLegalKeySize(pTmpUser->dwCspTypeId,
                             pKey->Algid, pKey->cbKeyLen,
                             fRC2BigKeyOK, &fPubKey))
        {
            goto ErrorExit;
        }
    }

    fRet = TRUE;

ErrorExit:
    return fRet;
}

/* FIsLegalImportSymKey
 *
 *      Verify that imported symmetric keys meet the size restrictions
 *      of the CSP in use.  In addition, require all DES-variant
 *      keys to be of the full required size, including parity bits.
 */
BOOL
FIsLegalImportSymKey(
    IN PNTAGUserList pTmpUser,
    IN PNTAGKeyList pKey,
    IN BOOL fRC2BigKeyOK)
{
    BOOL fRet = FALSE;

    switch (pKey->Algid)
    {
#ifdef CSP_USE_RC2
    case CALG_RC2:
        if (!fRC2BigKeyOK)
        {
            if (!IsLegalLength(g_AlgTables[pTmpUser->dwCspTypeId], pKey->Algid,
                               pKey->cbKeyLen * 8, NULL))
                goto ErrorExit;
        }
        break;
#endif
#ifdef CSP_USE_DES
    case CALG_DES:
        if (DES_KEYSIZE != pKey->cbKeyLen)
            goto ErrorExit;
        break;
#endif
#ifdef CSP_USE_3DES
    case CALG_3DES_112:
        if (DES2_KEYSIZE != pKey->cbKeyLen)
            goto ErrorExit;
        break;
    case CALG_3DES:
        if (DES3_KEYSIZE != pKey->cbKeyLen) 
            goto ErrorExit;
        break;
#endif
    case CALG_RSA_SIGN:
    case CALG_RSA_KEYX:
        goto ErrorExit;

    default:
        if (!IsLegalLength(g_AlgTables[pTmpUser->dwCspTypeId], pKey->Algid, 
                           pKey->cbKeyLen * 8, NULL))
            goto ErrorExit;
    }

    fRet = TRUE;

ErrorExit:
    return fRet;
}         

/*static*/ DWORD
MakeKeyRSABaseCompatible(
    HCRYPTPROV hUID,
    HCRYPTKEY  hKey)
{
    CRYPT_DATA_BLOB sSaltData;
    BYTE            rgbZeroSalt[11];
    BOOL            fSts;

    ZeroMemory(rgbZeroSalt, 11);

    sSaltData.pbData = rgbZeroSalt;
    sSaltData.cbData = sizeof(rgbZeroSalt);
    fSts = CPSetKeyParam(hUID, hKey, KP_SALT_EX, (PBYTE)&sSaltData, 0);
    if (!fSts)
        return GetLastError();
    return ERROR_SUCCESS;
}


typedef struct
{
    DWORD       magic;                  /* Should always be RSA2 */
    DWORD       bitlen;                 // bit size of key
    DWORD       pubexp;                 // public exponent
} EXPORT_PRV_KEY, FAR *PEXPORT_PRV_KEY;


/* GetLengthOfPrivateKeyForExport
 *
 *      Get the length of the private key
 *      blob from the public key.
 *
 */

/*static*/ void
GetLengthOfPrivateKeyForExport(
    IN BSAFE_PUB_KEY *pPubKey,
    OUT PDWORD pcbBlob)
{
    DWORD cbHalfModLen;

    cbHalfModLen = (pPubKey->bitlen + 15) / 16;
    *pcbBlob = sizeof(EXPORT_PRV_KEY) + 9 * cbHalfModLen;
}


/* PreparePrivateKeyForExport
 *
 *      Massage the key from the registry
 *      into an exportable format.
 *
 */

/*static*/ BOOL
PreparePrivateKeyForExport(
    IN BSAFE_PRV_KEY *pPrvKey,
    OUT PBYTE pbBlob,
    IN OUT PDWORD pcbBlob)
{
    PEXPORT_PRV_KEY pExportKey;
    DWORD           cbHalfModLen;
    DWORD           cbBlobLen;
    DWORD           cbTmpLen;
    DWORD           cbHalfTmpLen;
    PBYTE           pbIn;
    PBYTE           pbOut;

    cbHalfModLen = (pPrvKey->bitlen + 15) / 16;
    cbBlobLen = sizeof(EXPORT_PRV_KEY) + 9 * cbHalfModLen;

    // figure out the number of overflow bytes which are in the private
    // key structure
    cbTmpLen = (sizeof(DWORD) * 2)
               - (((pPrvKey->bitlen + 7) / 8) % (sizeof(DWORD) * 2));
    if ((sizeof(DWORD) * 2) != cbTmpLen)
        cbTmpLen += sizeof(DWORD) * 2;
    cbHalfTmpLen = cbTmpLen / 2;

    if (NULL == pbBlob)
    {
        *pcbBlob = cbBlobLen;
        return TRUE;
    }

    if (*pcbBlob < cbBlobLen)
    {
        *pcbBlob = cbBlobLen;
        return FALSE;
    }

    // take most of the header info
    pExportKey = (PEXPORT_PRV_KEY)pbBlob;
    pExportKey->magic = pPrvKey->magic;
    pExportKey->bitlen = pPrvKey->bitlen;
    pExportKey->pubexp = pPrvKey->pubexp;

    pbIn = (PBYTE)pPrvKey + sizeof(BSAFE_PRV_KEY);
    pbOut = pbBlob + sizeof(EXPORT_PRV_KEY);

    // copy all the private key info
    CopyMemory(pbOut, pbIn, pExportKey->bitlen / 8);
    pbIn += pExportKey->bitlen / 8 + cbTmpLen;
    pbOut += pExportKey->bitlen / 8;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbIn += cbHalfModLen + cbHalfTmpLen;
    pbOut += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbIn += cbHalfModLen + cbHalfTmpLen;
    pbOut += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbIn += cbHalfModLen + cbHalfTmpLen;
    pbOut += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbIn += cbHalfModLen + cbHalfTmpLen;
    pbOut += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbIn += cbHalfModLen + cbHalfTmpLen;
    pbOut += cbHalfModLen;
    CopyMemory(pbOut, pbIn, pExportKey->bitlen / 8);

    *pcbBlob = cbBlobLen;
    return TRUE;
}


/* PreparePrivateKeyForImport
 *
 *      Massage the incoming into a form acceptable for
 *      the registry.
 *
 */

/*static*/ BOOL
PreparePrivateKeyForImport(
    IN PBYTE pbBlob,
    OUT BSAFE_PRV_KEY *pPrvKey,
    IN OUT PDWORD pPrvKeyLen,
    OUT BSAFE_PUB_KEY *pPubKey,
    IN OUT PDWORD pPubKeyLen)
{
    PEXPORT_PRV_KEY pExportKey = (PEXPORT_PRV_KEY)pbBlob;
    DWORD           cbHalfModLen;
    DWORD           cbPub;
    DWORD           cbPrv;
    PBYTE           pbIn;
    PBYTE           pbOut;
    DWORD           cbTmpLen;
    DWORD           cbHalfTmpLen;

    if (RSA2 != pExportKey->magic)
        return FALSE;

    // figure out the number of overflow bytes which are in the private
    // key structure
    cbTmpLen = (sizeof(DWORD) * 2)
               - (((pExportKey->bitlen + 7) / 8) % (sizeof(DWORD) * 2));
    if ((sizeof(DWORD) * 2) != cbTmpLen)
        cbTmpLen += sizeof(DWORD) * 2;
    cbHalfTmpLen = cbTmpLen / 2;
    cbHalfModLen = (pExportKey->bitlen + 15) / 16;

    cbPub = sizeof(BSAFE_PUB_KEY) + (pExportKey->bitlen / 8) + cbTmpLen;
    cbPrv = sizeof(BSAFE_PRV_KEY) + (cbHalfModLen + cbHalfTmpLen) * 10;
    if ((NULL == pPrvKey) || (NULL == pPubKey))
    {
        *pPubKeyLen = cbPub;
        *pPrvKeyLen = cbPrv;
        return TRUE;
    }

    if ((*pPubKeyLen < cbPub) || (*pPrvKeyLen < cbPrv))
    {
        *pPubKeyLen = cbPub;
        *pPrvKeyLen = cbPrv;
        return FALSE;
    }

    // form the public key
    ZeroMemory(pPubKey, *pPubKeyLen);
    pPubKey->magic = RSA1;
    pPubKey->bitlen = pExportKey->bitlen;
    pPubKey->keylen = (pExportKey->bitlen / 8) + cbTmpLen;
    pPubKey->datalen = (pExportKey->bitlen+7)/8 - 1;
    pPubKey->pubexp = pExportKey->pubexp;

    pbIn = pbBlob + sizeof(EXPORT_PRV_KEY);
    pbOut = (PBYTE)pPubKey + sizeof(BSAFE_PUB_KEY);

    CopyMemory(pbOut, pbIn, pExportKey->bitlen / 8);

    // form the private key
    ZeroMemory(pPrvKey, *pPrvKeyLen);
    pPrvKey->magic = pExportKey->magic;
    pPrvKey->keylen = pPubKey->keylen;
    pPrvKey->bitlen = pExportKey->bitlen;
    pPrvKey->datalen = pPubKey->datalen;
    pPrvKey->pubexp = pExportKey->pubexp;

    pbOut = (PBYTE)pPrvKey + sizeof(BSAFE_PRV_KEY);

    CopyMemory(pbOut, pbIn, pExportKey->bitlen / 8);
    pbOut += pExportKey->bitlen / 8 + cbTmpLen;
    pbIn += pExportKey->bitlen / 8;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbOut += cbHalfModLen + cbHalfTmpLen;
    pbIn += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbOut += cbHalfModLen + cbHalfTmpLen;
    pbIn += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbOut += cbHalfModLen + cbHalfTmpLen;
    pbIn += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbOut += cbHalfModLen + cbHalfTmpLen;
    pbIn += cbHalfModLen;
    CopyMemory(pbOut, pbIn, cbHalfModLen);
    pbOut += cbHalfModLen + cbHalfTmpLen;
    pbIn += cbHalfModLen;
    CopyMemory(pbOut, pbIn, pExportKey->bitlen / 8);

    *pPubKeyLen = cbPub;
    *pPrvKeyLen = cbPrv;

    return TRUE;
}


/*static*/ BOOL
ValidKeyAlgid(
    PNTAGUserList pTmpUser,
    ALG_ID Algid)
{
    if (ALG_CLASS_HASH == GET_ALG_CLASS(Algid))
        return FALSE;
        
    return IsLegalAlgorithm(g_AlgTables[pTmpUser->dwCspTypeId],
                            Algid, NULL);
}


#ifdef USE_SGC
/*
 -  GetSGCDefaultKeyLength
 -
 *  Purpose:
 *                Returns the default key size in pcbKey.
 *
 *  Parameters:
 *               IN      Algid   -  For the key to be created
 *               OUT     pcbKey  -  Size of the key in bytes to generate
 *               OUT     pfPubKey-  TRUE if the Algid is a pub key
 *
 *  Returns:  TRUE on success, FALSE on failure.
 */

/*static*/ BOOL
GetSGCDefaultKeyLength(
    IN ALG_ID Algid,
    OUT DWORD *pcbKey,
    OUT BOOL *pfPubKey)
{
    BOOL    fRet = FALSE;

    *pfPubKey = FALSE;

    // determine which crypt algorithm is to be used
    switch (Algid)
    {
#ifdef CSP_USE_SSL3
    case CALG_SSL3_MASTER:
    case CALG_TLS1_MASTER:
        *pcbKey = SSL3_MASTER_KEYSIZE;
        break;

    case CALG_PCT1_MASTER:
        *pcbKey = PCT1_MASTER_KEYSIZE;
        break;

    case CALG_SSL2_MASTER:
        *pcbKey = SSL2_MASTER_KEYSIZE;
        break;
#endif

    case CALG_RSA_KEYX:
        *pcbKey = SGC_RSA_DEF_EXCH_MODLEN;
        *pfPubKey = TRUE;
        break;

    default:
        goto ErrorExit;
    }

    fRet = TRUE;

ErrorExit:
    return fRet;
}
#endif


/*
 -  GetDefaultKeyLength
 -
 *  Purpose:
 *                Returns the default key size in pcbKey.
 *
 *  Parameters:
 *               IN      pTmpUser-  The context info
 *               IN      Algid   -  For the key to be created
 *               OUT     pcbKey  -  Size of the key in bytes to generate
 *               OUT     pfPubKey-  TRUE if the Algid is a pub key
 *
 *  Returns:  TRUE on success, FALSE on failure.
 */

/*static*/ BOOL
GetDefaultKeyLength(
    IN PNTAGUserList pTmpUser,
    IN ALG_ID Algid,
    OUT DWORD *pcbKey,
    OUT BOOL *pfPubKey)
{
    BOOL    fRet = FALSE;
    DWORD   cbits;

    *pfPubKey = FALSE;

    // determine which crypt algorithm is to be used
    switch (Algid)
    {
#ifdef CSP_USE_SSL3
    case CALG_SSL3_MASTER:
    case CALG_TLS1_MASTER:
        *pcbKey = SSL3_MASTER_KEYSIZE;
        break;

    case CALG_PCT1_MASTER:
        *pcbKey = PCT1_MASTER_KEYSIZE;
        break;

    case CALG_SSL2_MASTER:
        *pcbKey = SSL2_MASTER_KEYSIZE;
        break;
#endif

    case CALG_RSA_KEYX:
    case CALG_RSA_SIGN:
        *pfPubKey = TRUE;
        // Fall through intentionally.

    default:
        if (!GetDefaultLength(g_AlgTables[pTmpUser->dwCspTypeId],
                              Algid, NULL, &cbits))
            goto ErrorExit;
        *pcbKey = cbits / 8;
    }

    fRet = TRUE;

ErrorExit:
    return fRet;
}


/*
 -  CheckKeyLength
 -
 *  Purpose:
 *                Checks the settable key length and if it is OK then
 *                returns that as the size of key to use (pcbKey).  If
 *                no key length is in dwFlags then the default key size
 *                is returned (pcbKey).  If a settable key size is
 *                specified but is not legal then a failure occurs.
 *
 *  Parameters:
 *               IN      Algid   -  For the key to be created
 *               IN      dwFlags -  Flag value with possible key size
 *               OUT     pcbKey  -  Size of the key in bytes to generate
 *               OUT     pfPubKey-  TRUE if the Algid is a pub key
 *
 *  Returns:  A DWORD status code.
 */

/*static*/ DWORD
CheckKeyLength(
    IN PNTAGUserList pTmpUser,
    IN ALG_ID Algid,
    IN DWORD dwFlags,
    OUT DWORD *pcbKey,
    OUT BOOL *pfPubKey)
{
    DWORD   dwReturn = ERROR_INTERNAL_ERROR;
    DWORD   cBits;
    DWORD   cbKey = 0;

    cBits = dwFlags >> 16;
    if (cBits)
    {
        // settable key sizes must be divisible by 8 (by bytes)
        if (0 != (cBits % 8))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        // check if requested size is legal
        cbKey = cBits / 8;
#ifdef USE_SGC
        if ((PROV_RSA_SCHANNEL == pTmpUser->dwProvType) &&
            (0 != pTmpUser->dwSGCFlags))
        {
            if (!FIsLegalSGCKeySize(Algid, cbKey,
                                    FALSE, TRUE, pfPubKey))
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }
        else
#endif
        {
            if (!FIsLegalKeySize(pTmpUser->dwCspTypeId,
                                 Algid, cbKey,
                                 FALSE, pfPubKey))
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }
        *pcbKey = cbKey;
    }
    else
    {
#ifdef USE_SGC
        if ((PROV_RSA_SCHANNEL == pTmpUser->dwProvType) &&
            (0 != pTmpUser->dwSGCFlags))
        {
            if (!GetSGCDefaultKeyLength(Algid, pcbKey, pfPubKey))
            {
                dwReturn = (DWORD)NTE_BAD_ALGID;
                goto ErrorExit;
            }
        }
        else
#endif
        {
            if (!GetDefaultKeyLength(pTmpUser, Algid, pcbKey, pfPubKey))
            {
                dwReturn = (DWORD)NTE_BAD_ALGID;
                goto ErrorExit;
            }
        }
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}

#ifdef USE_SGC
/*
 -  CheckSGCSimpleForExport
 -
 *  Purpose:
 *                Check if the SGC key values in the context against the
 *                passed in values to see if an simple blob export with
 *                this key is allowed.
 *
 *
 *  Parameters:
 *               IN      hUID    -  Handle to a CSP
 *               IN      Algid   -  Algorithm identifier
 *               IN      dwFlags -  Flags values
 *               OUT     phKey   -  Handle to a generated key
 *
 *  Returns:
 */

/*static*/ BOOL
CheckSGCSimpleForExport(
    IN PNTAGUserList pTmpUser,
    IN BSAFE_PUB_KEY *pBsafePubKey)
{
    BOOL    fRet = FALSE;
    BYTE    *pb;

    pb = ((BYTE*)pBsafePubKey) + sizeof(BSAFE_PUB_KEY);
    if (((pBsafePubKey->bitlen / 8) != pTmpUser->cbSGCKeyMod)
        || (pBsafePubKey->pubexp != pTmpUser->dwSGCKeyExpo)
        || (0 != memcmp(pb, pTmpUser->pbSGCKeyMod, pTmpUser->cbSGCKeyMod)))
    {
        goto ErrorExit;
    }

    fRet = TRUE;

ErrorExit:
    return fRet;
}
#endif


/*
 -  CPGenKey
 -
 *  Purpose:
 *                Generate cryptographic keys
 *
 *
 *  Parameters:
 *               IN      hUID    -  Handle to a CSP
 *               IN      Algid   -  Algorithm identifier
 *               IN      dwFlags -  Flags values
 *               OUT     phKey   -  Handle to a generated key
 *
 *  Returns:
 */

BOOL WINAPI
CPGenKey(
    IN HCRYPTPROV hUID,
    IN ALG_ID Algid,
    IN DWORD dwFlags,
    OUT HCRYPTKEY * phKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList   pTmpUser;
    PNTAGKeyList    pTmpKey = NULL;
    DWORD           dwRights = 0;
    BYTE            rgbRandom[MAX_KEY_SIZE];
    int             localAlgid;
    DWORD           cbKey;
    BOOL            fPubKey = FALSE;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    if ((dwFlags & ~(CRYPT_EXPORTABLE | CRYPT_USER_PROTECTED |
                     CRYPT_CREATE_SALT | CRYPT_NO_SALT |
                     KEY_LENGTH_MASK | CRYPT_SGCKEY | CRYPT_ARCHIVABLE)) != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    switch (Algid)
    {
    case AT_KEYEXCHANGE:
        localAlgid = CALG_RSA_KEYX;
        break;

    case AT_SIGNATURE:
        localAlgid = CALG_RSA_SIGN;
        break;

    default:
        if (0 != (dwFlags & CRYPT_ARCHIVABLE))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }
        localAlgid = Algid;
        break;
    }

    pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    if ((CRYPT_USER_PROTECTED & dwFlags) && (CRYPT_VERIFYCONTEXT & pTmpUser->Rights))
    {
        dwReturn = (DWORD)NTE_SILENT_CONTEXT;
        goto ErrorExit;
    }

    if (!ValidKeyAlgid(pTmpUser, localAlgid))
    {
        dwReturn = (DWORD)NTE_BAD_ALGID;
        goto ErrorExit;
    }

    // check if the size of the key is set in the dwFlags parameter
    dwSts = CheckKeyLength(pTmpUser, localAlgid, dwFlags, &cbKey, &fPubKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    if (fPubKey)
    {
        dwSts = ReGenKey(hUID,
                         dwFlags,
                         (localAlgid == CALG_RSA_KEYX)
                              ? NTPK_USE_EXCH
                              : NTPK_USE_SIG,
                         phKey,
                         cbKey * 8);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }
    else
    {
        // Force the full key lengths on DES algorithms.
        if (CALG_DES == localAlgid)
            cbKey = DES_KEYSIZE;
        else if (CALG_3DES_112 == localAlgid)
            cbKey = DES2_KEYSIZE;
        else if (CALG_3DES == localAlgid)
            cbKey = DES3_KEYSIZE;

        // generate the random key
        dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
                                 &pTmpUser->ContInfo.pbRandom,
                                 &pTmpUser->ContInfo.ContLens.cbRandom,
                                 rgbRandom, cbKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;   // NTE_FAIL
            goto ErrorExit;
        }

        if ((CALG_DES == localAlgid) || (CALG_3DES_112 == localAlgid) ||
            (CALG_3DES == localAlgid))
        {
            if (dwFlags & CRYPT_CREATE_SALT)
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }
#ifdef CSP_USE_SSL3
        else if (CALG_SSL3_MASTER == localAlgid)
        {
            // set the first byte to 0x03 and the second to 0x00
            rgbRandom[0] = 0x03;
            rgbRandom[1] = 0x00;
        }
        else if (CALG_TLS1_MASTER == localAlgid)
        {
            // set the first byte to 0x03 and the second to 0x01
            rgbRandom[0] = 0x03;
            rgbRandom[1] = 0x01;
        }
#endif
        // check if the key is CRYPT_EXPORTABLE
        if (dwFlags & CRYPT_EXPORTABLE)
            dwRights = CRYPT_EXPORTABLE;

        dwSts = MakeNewKey(localAlgid, dwRights, cbKey, hUID, rgbRandom,
                           FALSE, FALSE, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        if (dwFlags & CRYPT_CREATE_SALT)
        {
            if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
                || (POLICY_MS_STRONG == pTmpUser->dwCspTypeId))
            {
                pTmpKey->cbSaltLen = DEFAULT_WEAK_SALT_LENGTH;
            }
            else
            {
                pTmpKey->cbSaltLen = DEFAULT_STRONG_SALT_LENGTH;
            }

            dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
                                     &pTmpUser->ContInfo.pbRandom,
                                     &pTmpUser->ContInfo.ContLens.cbRandom,
                                     pTmpKey->rgbSalt, pTmpKey->cbSaltLen);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;   // NTE_FAIL
                goto ErrorExit;
            }
        }

        dwSts = NTLMakeItem(phKey, KEY_HANDLE, (void *)pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        // if 40bit key + no mention of salt, set zeroized salt for RSABase compatibility
        if ((5 == cbKey) && (!(dwFlags & CRYPT_NO_SALT)) &&
            (!(dwFlags & CRYPT_CREATE_SALT)) &&
            (CALG_SSL3_MASTER != Algid) && (CALG_TLS1_MASTER != Algid) &&
            (CALG_PCT1_MASTER != Algid) && (CALG_SSL2_MASTER != Algid))
        {
            dwSts = MakeKeyRSABaseCompatible(hUID, *phKey);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
    {
        if (pTmpKey)
            FreeNewKey(pTmpKey);
        SetLastError(dwReturn);
    }
    return fRet;
}


/*
 -  CPDeriveKey
 -
 *  Purpose:
 *                Derive cryptographic keys from base data
 *
 *
 *  Parameters:
 *               IN      hUID       -  Handle to a CSP
 *               IN      Algid      -  Algorithm identifier
 *               IN      hBaseData  -  Handle to hash of base data
 *               IN      dwFlags    -  Flags values
 *               OUT     phKey      -  Handle to a generated key
 *
 *  Returns:
 */

BOOL WINAPI
CPDeriveKey(
    IN HCRYPTPROV hUID,
    IN ALG_ID Algid,
    IN HCRYPTHASH hBaseData,
    IN DWORD dwFlags,
    OUT HCRYPTKEY * phKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList   pTmpUser = NULL;
    PNTAGKeyList    pTmpKey = NULL;
    DWORD           dwRights = 0;
    BYTE            rgbRandom[MAX_KEY_SIZE];
    BYTE            rgbBaseVal[MAX_KEY_SIZE];
    HCRYPTHASH      h1 = 0;
    HCRYPTHASH      h2 = 0;
    BYTE            rgbBuff1[64];
    BYTE            rgbBuff2[64];
    BYTE            rgbHash1[NT_HASH_BYTES];
    BYTE            rgbHash2[NT_HASH_BYTES];
    DWORD           cb1;
    DWORD           cb2;
    DWORD           i;
    PNTAGHashList   pTmpHash;
    DWORD           temp;
    BOOL            fPubKey = FALSE;
    DWORD           cbKey;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    if ((dwFlags & ~(CRYPT_EXPORTABLE | 
                     CRYPT_CREATE_SALT | CRYPT_NO_SALT | CRYPT_SERVER |
                     KEY_LENGTH_MASK)) != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    if (!ValidKeyAlgid(pTmpUser, Algid))
    {
        dwReturn = (DWORD)NTE_BAD_ALGID;
        goto ErrorExit;
    }

    dwSts = NTLValidate(hBaseData, hUID, HASH_HANDLE, &pTmpHash);
    if (ERROR_SUCCESS != dwSts)
    {
        // NTLValidate doesn't know what error to set
        // so it set NTE_FAIL -- fix it up.
        dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_HASH : dwSts;
        goto ErrorExit;
    }

#ifdef CSP_USE_SSL3
    // if the hash is for secure channel usage then go to that derive function
    if (CALG_SCHANNEL_MASTER_HASH == pTmpHash->Algid)
    {
        dwReturn = SecureChannelDeriveKey(pTmpUser, pTmpHash, Algid,
                                          dwFlags, phKey);
        goto ErrorExit;
    }
#endif // CSP_USE_SSL3

    // check if the size of the key is set in the dwFlags parameter
    dwSts = CheckKeyLength(pTmpUser, Algid, dwFlags, &cbKey, &fPubKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // Force the full key lengths on DES algorithms.
    if (CALG_DES == Algid)
        cbKey = DES_KEYSIZE;
    else if (CALG_3DES_112 == Algid)
        cbKey = DES2_KEYSIZE;
    else if (CALG_3DES == Algid)
        cbKey = DES3_KEYSIZE;

    if (fPubKey)
    {
        dwReturn = (DWORD)NTE_BAD_ALGID;
        goto ErrorExit;
    }

    if (pTmpHash->HashFlags & HF_VALUE_SET)
    {
        dwReturn = (DWORD)NTE_BAD_HASH;
        goto ErrorExit;
    }

    memset(rgbBaseVal, 0, MAX_KEY_SIZE);
    temp = MAX_KEY_SIZE;
    if (!CPGetHashParam(hUID, hBaseData, HP_HASHVAL, rgbBaseVal, &temp, 0))
    {
        dwReturn = GetLastError();
        goto ErrorExit;
    }

#ifdef CSP_USE_3DES
    if (CALG_3DES == Algid)
    {
        // the hash value is not long enough so we must expand it
        if (!CPCreateHash(hUID, pTmpHash->Algid, 0, 0, &h1))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }
        if (!CPCreateHash(hUID, pTmpHash->Algid, 0, 0, &h2))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }

        // set up the two buffers to be hashed
        memset(rgbBuff1, 0x36, sizeof(rgbBuff1));
        memset(rgbBuff2, 0x5C, sizeof(rgbBuff2));
        for (i=0;i<temp;i++)
        {
            rgbBuff1[i] ^= rgbBaseVal[i];
            rgbBuff2[i] ^= rgbBaseVal[i];
        }

        // hash the two buffers
        if (!CPHashData(hUID, h1, rgbBuff1, sizeof(rgbBuff1), 0))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }
        if (!CPHashData(hUID, h2, rgbBuff2, sizeof(rgbBuff2), 0))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }

        // finish the hashes and copy them into BaseVal
        memset(rgbHash1, 0, sizeof(rgbHash1));
        cb1 = sizeof(rgbHash1);
        if (!CPGetHashParam(hUID, h1, HP_HASHVAL, rgbHash1, &cb1, 0))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }
        memcpy(rgbBaseVal, rgbHash1, cb1);

        memset(rgbHash2, 0, sizeof(rgbHash2));
        cb2 = sizeof(rgbHash2);
        if (!CPGetHashParam(hUID, h2, HP_HASHVAL, rgbHash2, &cb2, 0))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }
        memcpy(rgbBaseVal + cb1, rgbHash2, cb2);
    }
#endif

    memcpy(rgbRandom, rgbBaseVal, cbKey);

    // check if the key is CRYPT_EXPORTABLE
    if (dwFlags & CRYPT_EXPORTABLE)
        dwRights = CRYPT_EXPORTABLE;

    dwSts = MakeNewKey(Algid, dwRights, cbKey, hUID, rgbRandom,
                       FALSE, FALSE, &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    if (dwFlags & CRYPT_CREATE_SALT)
    {
        if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
            || (POLICY_MS_STRONG == pTmpUser->dwCspTypeId))
        {
            pTmpKey->cbSaltLen = DEFAULT_WEAK_SALT_LENGTH;
        }
        else
        {
            pTmpKey->cbSaltLen = DEFAULT_STRONG_SALT_LENGTH;
        }

        memcpy(pTmpKey->rgbSalt, rgbBaseVal+cbKey, pTmpKey->cbSaltLen);
    }

    dwSts = NTLMakeItem(phKey, KEY_HANDLE, (void *)pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // if 40bit key + no mention of salt, set zeroized salt for RSABase
    // compatibility
    if ((5 == cbKey) && (!(dwFlags & CRYPT_NO_SALT)) &&
        (!(dwFlags & CRYPT_CREATE_SALT)))
    {
        dwSts = MakeKeyRSABaseCompatible(hUID, *phKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (pTmpUser && h1)
        CPDestroyHash(hUID, h1);
    if (pTmpUser && h2)
        CPDestroyHash(hUID, h2);
    if (!fRet)
    {
        if (NULL != pTmpKey)
            FreeNewKey(pTmpKey);
        SetLastError(dwReturn);
    }
    return fRet;
}


/*static*/ DWORD
ExportOpaqueBlob(
    PNTAGKeyList pKey,
    BYTE *pbData,
    DWORD *pcbData)
{
    DWORD dwReturn = ERROR_INTERNAL_ERROR;
    DWORD cb = 0;
    PNTAGPackedKeyList pPackedKey;

    // make sure the key is a symmetric key
    if ((CALG_RSA_SIGN == pKey->Algid) || (CALG_RSA_KEYX == pKey->Algid))
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

    // calculate the length of the blob
    cb = sizeof(BLOBHEADER) +
         sizeof(NTAGPackedKeyList) +
         pKey->cbKeyLen +
         pKey->cbDataLen;

    if (pbData == NULL || *pcbData < cb)
    {
        *pcbData = cb;
        if (pbData == NULL)
            dwReturn = ERROR_SUCCESS;
        else
            dwReturn = ERROR_MORE_DATA;
        goto ErrorExit;
    }

    // set up the blob
    pPackedKey = (PNTAGPackedKeyList)(pbData + sizeof(BLOBHEADER));

    memset(pPackedKey, 0, sizeof(NTAGPackedKeyList));

    pPackedKey->Algid           = pKey->Algid;
    pPackedKey->Rights          = pKey->Rights;
    memcpy(pPackedKey->IV, pKey->IV, sizeof(pKey->IV));
    memcpy(pPackedKey->FeedBack, pKey->FeedBack, sizeof(pKey->FeedBack));
    pPackedKey->InProgress      = pKey->InProgress;
    pPackedKey->cbSaltLen       = pKey->cbSaltLen;
    memcpy(pPackedKey->rgbSalt, pKey->rgbSalt, sizeof(pKey->rgbSalt));
    pPackedKey->Padding         = pKey->Padding;
    pPackedKey->Mode            = pKey->Mode;
    pPackedKey->ModeBits        = pKey->ModeBits;
    pPackedKey->Permissions     = pKey->Permissions;
    pPackedKey->EffectiveKeyLen = pKey->EffectiveKeyLen;
    pPackedKey->dwBlockLen      = pKey->dwBlockLen;

    if (pKey->pKeyValue)
    {
        memcpy((PBYTE)pPackedKey + sizeof(NTAGPackedKeyList),
               pKey->pKeyValue,
               pKey->cbKeyLen);

        pPackedKey->cbKeyLen = pKey->cbKeyLen;
    }

    if(pKey->pData)
    {
        memcpy((PBYTE)pPackedKey + sizeof(NTAGPackedKeyList) + pPackedKey->cbKeyLen,
               pKey->pData,
               pKey->cbDataLen);

        pPackedKey->cbDataLen = pKey->cbDataLen;
    }

    *pcbData = cb;
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}


/*
 -  GetRC4KeyForSymWrap
 -
 *  Purpose:
 *            RC4 or more precisely stream ciphers are not supported by the CMS spec
 *            on symmetric key wrapping so we had to do something proprietary since
 *            we want to support RC4 for applications other than SMIME
 *
 *
 *  Parameters:
 *               IN  pTmpUser   - Pointer to the context
 *               IN  pbSalt     - Pointer to the 8 byte salt buffer
 *               IN  pKey       - Pointer to the orignial key
 *               OUT ppNewKey   - Pointer to a pointer to the new key
 */

/*static*/ DWORD
GetRC4KeyForSymWrap(
    IN BYTE *pbSalt,
    IN PNTAGKeyList pKey,
    OUT PNTAGKeyList *ppNewKey)
{
    DWORD dwReturn = ERROR_INTERNAL_ERROR;
    DWORD dwSts;

    // duplicate the key
    dwSts = CopyKey(pKey, ppNewKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // set the value as salt + current salt
    (*ppNewKey)->cbSaltLen += 8;
    memcpy((*ppNewKey)->rgbSalt + ((*ppNewKey)->cbSaltLen - 8), pbSalt, 8);
    dwSts = InflateKey(*ppNewKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}


/*
 -  GetSymmetricKeyChecksum
 -
 *  Purpose:
 *                Calculates the checksum for a symmetric key which is to be
 *                wrapped with another symmetric key.  This should meet the
 *                CMS specification
 *
 *
 *  Parameters:
 *               IN  pKey       - Pointer to the key
 *               OUT pbChecksum - Pointer to the 8 byte checksum
 */

/*static*/ void
GetSymmetricKeyChecksum(
    IN BYTE *pbKey,
    IN DWORD cbKey,
    OUT BYTE *pbChecksum)
{
    A_SHA_CTX   SHACtx;
    BYTE        rgb[A_SHA_DIGEST_LEN];

    A_SHAInit(&SHACtx);
    A_SHAUpdate(&SHACtx, pbKey, cbKey);
    A_SHAFinal(&SHACtx, rgb);

    memcpy(pbChecksum, rgb, 8);
}


/*
 -  WrapSymKey
 -
 *  Purpose:
 *          Wrap a symmetric key with another symmetric key.  This should
 *          meet the CMS specification for symmetric key wrapping.
 *
 *  Parameters:
 *               IN  pTmpUser   - Pointer to the user context
 *               IN  pKey       - Pointer to the key to be wrapped
 *               IN  pWrapKey   - Pointer to the key to be used for wrapping
 *               IN OUT pbBlob  - Pointer to the resulting blob (may be NULL
 *                                to get the length)
 *               IN OUT pcbBlob - Pointer to the length of the blob buffer
 */

/*static*/ DWORD
WrapSymKey(
    IN PNTAGUserList pTmpUser,
    IN PNTAGKeyList pKey,
    IN PNTAGKeyList pWrapKey,
    IN OUT BYTE *pbBlob,
    IN OUT DWORD *pcbBlob)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    DWORD           cb = 0;
    DWORD           cbIndex = 0;
    DWORD           cbPad = 0;
    BLOBHEADER      *pBlobHdr;
    ALG_ID          *pAlgid;
    BYTE            rgbTmp1[49]; // 1 length + 8 padding + 8 checksum + 8 IV + 24 max key
    BYTE            rgbTmp2[49]; // 1 length + 8 padding + 8 checksum + 8 IV + 24 max key
    BYTE            rgbIV[8];
    PNTAGKeyList    pLocalWrapKey = NULL;
    BOOL            fAlloc = FALSE;
    DWORD           i;
    DWORD           dwSts;

    memset(rgbTmp1, 0, sizeof(rgbTmp1));
    memset(rgbTmp2, 0, sizeof(rgbTmp2));
    memset(rgbIV, 0, sizeof(rgbIV));

    // both keys must be supported symmetric keys
    if (UnsupportedSymKey(pKey) || UnsupportedSymKey(pWrapKey))
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

#ifdef CSP_USE_AES
    // For now, punt on supporting AES algs in this scenario
    if (CALG_AES_128 == pWrapKey->Algid ||
        CALG_AES_192 == pWrapKey->Algid ||
        CALG_AES_256 == pWrapKey->Algid ||
        CALG_AES_128 == pKey->Algid ||
        CALG_AES_192 == pKey->Algid ||
        CALG_AES_256 == pKey->Algid)
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }
#endif

    if ((!FIsLegalKey(pTmpUser, pKey, FALSE)) ||
        (!FIsLegalKey(pTmpUser, pWrapKey, FALSE)))
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

    // Check if we should do an auto-inflate
    if (pWrapKey->pData == NULL)
    {
        dwSts = InflateKey(pWrapKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }

    // calculate how long the encrypted data is going to be.
    if ((CALG_RC4 == pKey->Algid) || (CALG_RC2 == pKey->Algid)) // variable key lengths
    {
        // 1 byte for length, up to 8 bytes for pad and 8 bytes
        // for the checksum and 8 bytes for the IV
        cbPad = 8 - ((pKey->cbKeyLen + 1) % 8);
        cb += pKey->cbKeyLen + 9 + cbPad + 8;

        // place the length in the buffer
        rgbTmp1[0] = (BYTE)pKey->cbKeyLen;
        cbIndex += 1;
    }
    else
    {
        // up to 8 bytes for salt and 8 bytes for the checksum and 8 bytes
        // for the IV
        cb += pKey->cbKeyLen + 16;
    }

    // check if just looking for a length
    if (NULL == pbBlob)
    {
        *pcbBlob = cb + sizeof(BLOBHEADER) + sizeof(ALG_ID);
        dwReturn = ERROR_SUCCESS;
        goto ErrorExit;
    }
    else if (*pcbBlob < (cb + sizeof(BLOBHEADER) + sizeof(ALG_ID)))
    {
        *pcbBlob = cb + sizeof(BLOBHEADER) + sizeof(ALG_ID);
        dwReturn = ERROR_MORE_DATA;
        goto ErrorExit;
    }

    // copy the key data
    memcpy(rgbTmp1 + cbIndex, pKey->pKeyValue, pKey->cbKeyLen);
    cbIndex += pKey->cbKeyLen;

    // generate random pad
    if (cbPad)
    {
        dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
                                 &pTmpUser->ContInfo.pbRandom,
                                 &pTmpUser->ContInfo.ContLens.cbRandom,
                                 rgbTmp1 + cbIndex, cbPad);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        cbIndex += cbPad;
    }

    // get the checksum
    GetSymmetricKeyChecksum(rgbTmp1, cbIndex, rgbTmp1 + cbIndex);
    cbIndex += 8;

    dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
                             &pTmpUser->ContInfo.pbRandom,
                             &pTmpUser->ContInfo.ContLens.cbRandom,
                             rgbIV, 8);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // set the IV if the algorithm is not RC4
    if (CALG_RC4 != pWrapKey->Algid)
    {
        memcpy(pWrapKey->IV, rgbIV, 8);
        pWrapKey->InProgress = FALSE;
        pLocalWrapKey = pWrapKey;
    }
    else
    {
        // RC4 ()or more precisely stream ciphers) are not supported by the
        // CMS spec for symmetric key wrapping.  Therefore we had to do
        // something proprietary to support RC4 for applications other
        // than SMIME.
        dwSts = GetRC4KeyForSymWrap(rgbIV,
                                    pWrapKey,
                                    &pLocalWrapKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        fAlloc = TRUE;
    }

    // encrypt the key blob data
    dwSts = SymEncrypt(pLocalWrapKey, FALSE, rgbTmp1, &cbIndex, cbIndex);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // concatenate the initial ciphertext with the IV
    memcpy(rgbTmp2, rgbIV, 8);
    memcpy(rgbTmp2 + 8, rgbTmp1, cbIndex);
    cbIndex += 8;

    // byte reverse the ciphertext + IV buffer
    for (i = 0; i < cbIndex; i++)
        rgbTmp1[i] = rgbTmp2[cbIndex - (i + 1)];

    // encrypt the key blob data again with the hardcoded IV
    if (CALG_RC4 != pWrapKey->Algid)
    {
        memcpy(pWrapKey->IV, rgbSymmetricKeyWrapIV, 8);
        pWrapKey->InProgress = FALSE;
    }
    else
    {
        if (fAlloc && pLocalWrapKey)
        {
            FreeNewKey(pLocalWrapKey);
            pLocalWrapKey = NULL;
            fAlloc = FALSE;
        }

        // RC4 (or more precisely stream ciphers) are not supported by the
        // CMS spec for symmetric key wrapping.  Therefore we had to do
        // something proprietary to support RC4 for applications other
        // than SMIME
        dwSts = GetRC4KeyForSymWrap(rgbSymmetricKeyWrapIV,
                                    pWrapKey,
                                    &pLocalWrapKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        fAlloc = TRUE;
    }

    dwSts = SymEncrypt(pLocalWrapKey, FALSE, rgbTmp1, &cbIndex, cbIndex);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // set the header info
    pBlobHdr = (BLOBHEADER*)pbBlob;
    pBlobHdr->aiKeyAlg = pKey->Algid;

    pAlgid = (ALG_ID*)(pbBlob + sizeof(BLOBHEADER));
    *pAlgid = pWrapKey->Algid;

    memcpy(pbBlob + sizeof(BLOBHEADER) + sizeof(ALG_ID),
           rgbTmp1, cbIndex);

    *pcbBlob = cbIndex + sizeof(BLOBHEADER) + sizeof(ALG_ID);
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    if (fAlloc && pLocalWrapKey)
        FreeNewKey(pLocalWrapKey);
    memset(rgbTmp1, 0, sizeof(rgbTmp1));
    memset(rgbTmp2, 0, sizeof(rgbTmp2));
    memset(rgbIV, 0, sizeof(rgbIV));
    return dwReturn;
}


/*
 -  UnWrapSymKey
 -
 *  Purpose:
 *          Unwrap a symmetric key with another symmetric key.  This should
 *          meet the CMS specification for symmetric key wrapping.
 *
 *  Parameters:
 *               IN  pTmpUser   - Pointer to the user context
 *               IN  pWrapKey   - Pointer to the key to be used for unwrapping
 *               IN  pbBlob     - Pointer to the blob to be unwrapped
 *               IN  cbBlob     - The length of the blob buffer
 *               OUT phKey      - Handle to the unwrapped key
 */

/*static*/ DWORD
UnWrapSymKey(
    IN HCRYPTPROV hUID,
    IN PNTAGUserList pTmpUser,
    IN PNTAGKeyList pWrapKey,
    IN BYTE *pbBlob,
    IN DWORD cbBlob,
    IN DWORD dwFlags,
    OUT HCRYPTKEY *phKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    DWORD           cb = 0;
    DWORD           cbIndex = 0;
    DWORD           cbKey = 0;
    BYTE            rgbChecksum[8];
    BLOBHEADER      *pBlobHdr = (BLOBHEADER*)pbBlob;
    ALG_ID          *pAlgid;
    BYTE            rgbTmp1[49];  // 1 length + 8 padding + 8 checksum + 8 IV + 24 max key
    BYTE            rgbTmp2[49];  // 1 length + 8 padding + 8 checksum + 8 IV + 24 max key
    DWORD           dwRights = 0;
    PNTAGKeyList    pTmpKey = NULL;
    PNTAGKeyList    pLocalWrapKey = NULL;
    BOOL            fAlloc = FALSE;
    DWORD           i;
    BOOL            fPubKey;
    DWORD           dwSts;

    memset(rgbTmp1, 0, sizeof(rgbTmp1));
    memset(rgbTmp2, 0, sizeof(rgbTmp2));

    cb = cbBlob - (sizeof(BLOBHEADER) + sizeof(ALG_ID));
    if ((sizeof(rgbTmp1) < cb) || (0 != (cb % 8)))
    {
        dwReturn = (DWORD)NTE_BAD_DATA;
        goto ErrorExit;
    }

    // both keys must be supported symmetric keys
    if (UnsupportedSymKey(pWrapKey) || UnsupportedSymKey(pWrapKey))
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

#ifdef CSP_USE_AES
    // For now, punt on supporting AES algs in this scenario
    if (CALG_AES_128 == pWrapKey->Algid ||
        CALG_AES_192 == pWrapKey->Algid ||
        CALG_AES_256 == pWrapKey->Algid)
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }
#endif

    if (!FIsLegalKey(pTmpUser, pWrapKey, FALSE))
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

    // check the wrapping key ALG_ID
    pAlgid = (ALG_ID*)(pbBlob + sizeof(BLOBHEADER));
    if (pWrapKey->Algid != *pAlgid)
    {
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

    // Check if we should do an auto-inflate
    if (pWrapKey->pData == NULL)
    {
        dwSts = InflateKey(pWrapKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }

    // set the hardcoded IV
    if (CALG_RC4 != pWrapKey->Algid)
    {
        memcpy(pWrapKey->IV, rgbSymmetricKeyWrapIV, 8);
        pWrapKey->InProgress = FALSE;
        pLocalWrapKey = pWrapKey;
    }
    else
    {
        // RC4 (or more precisely, stream ciphers) are not supported by the
        // CMS spec for symmetric key wrapping.  Therefore we had to do
        // something proprietary to support RC4 for applications other
        // than SMIME.
        dwSts = GetRC4KeyForSymWrap(rgbSymmetricKeyWrapIV,
                                    pWrapKey,
                                    &pLocalWrapKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        fAlloc = TRUE;
    }

    memcpy(rgbTmp1, pbBlob + sizeof(BLOBHEADER) + sizeof(ALG_ID), cb);

    // decrypt the key blob data
    dwSts = SymDecrypt(pLocalWrapKey, 0, FALSE, rgbTmp1, &cb);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // byte reverse the plaintext + IV buffer
    for (i = 0; i < cb; i++)
        rgbTmp2[i] = rgbTmp1[cb - (i + 1)];

    // set the IV if the algorithm is not RC4
    cb -= 8;
    if (CALG_RC4 != pWrapKey->Algid)
    {
        memcpy(pWrapKey->IV, rgbTmp2, 8);
        pWrapKey->InProgress = FALSE;
    }
    else
    {
        if (fAlloc && pLocalWrapKey)
        {
            FreeNewKey(pLocalWrapKey);
            pLocalWrapKey = NULL;
            fAlloc = FALSE;
        }

        // RC4 (or more precisely, stream ciphers) are not supported by the
        // CMS spec for symmetric key wrapping.  Therefore we had to do
        // something proprietary to support RC4 for applications other
        // than SMIME.
        dwSts = GetRC4KeyForSymWrap(rgbTmp2, pWrapKey, &pLocalWrapKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        fAlloc = TRUE;
    }

    // decrypt the key blob data again
    dwSts = SymDecrypt(pLocalWrapKey, 0, FALSE, rgbTmp2 + 8, &cb);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // check the length of the key
    switch (pBlobHdr->aiKeyAlg)
    {
    case CALG_RC2:  // variable key lengths
    case CALG_RC4:
        cbKey = (DWORD)rgbTmp2[8];
        cbIndex += 1;
        break;

    case CALG_DES:  // Ignore the default length, and use the full DES length.
    case CALG_DESX:
    case CALG_CYLINK_MEK:
        cbKey = DES_KEYSIZE;
        break;
    case CALG_3DES_112:
        cbKey = DES_KEYSIZE * 2;
        break;
    case CALG_3DES:
        cbKey = DES_KEYSIZE * 3;
        break;

    default:
        if (!GetDefaultKeyLength(pTmpUser, pBlobHdr->aiKeyAlg,
                                 &cbKey, &fPubKey))
        {
            dwReturn = (DWORD)NTE_BAD_ALGID;
            goto ErrorExit;
        }
    }

    // get the checksum and make sure it matches
    cb -= 8;
    GetSymmetricKeyChecksum(rgbTmp2 + 8, cb, rgbChecksum);
    if (0 != memcmp(rgbChecksum, rgbTmp2 + 8 + cb, sizeof(rgbChecksum)))
    {
        dwReturn = (DWORD)NTE_BAD_DATA;
        goto ErrorExit;
    }

    // check if the key is to be exportable
    if (dwFlags & CRYPT_EXPORTABLE)
        dwRights = CRYPT_EXPORTABLE;

    dwSts = MakeNewKey(pBlobHdr->aiKeyAlg, dwRights, cbKey, hUID,
                       rgbTmp2 + cbIndex + 8, FALSE, TRUE, &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // check keylength...
    if (!FIsLegalKey(pTmpUser, pTmpKey, TRUE))
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    dwSts = NTLMakeItem(phKey, KEY_HANDLE, (void *)pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // if 40 bit key + no mention of salt, set zeroized salt for
    // RSABase compatibility
    if ((5 == pTmpKey->cbKeyLen) && (0 == (dwFlags & CRYPT_NO_SALT)))
    {
        dwSts = MakeKeyRSABaseCompatible(hUID, *phKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }

    pTmpKey = NULL;
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    if (fAlloc && pLocalWrapKey)
        FreeNewKey(pLocalWrapKey);
    memset(rgbTmp1, 0, sizeof(rgbTmp1));
    memset(rgbTmp2, 0, sizeof(rgbTmp2));
    if (NULL != pTmpKey)
        FreeNewKey(pTmpKey);
    return dwReturn;
}


/*
 -  CPExportKey
 -
 *  Purpose:
 *                Export cryptographic keys out of a CSP in a secure manner
 *
 *
 *  Parameters:
 *               IN  hUID       - Handle to the CSP user
 *               IN  hKey       - Handle to the key to export
 *               IN  hPubKey    - Handle to the exchange public key value of
 *                                the destination user
 *               IN  dwBlobType - Type of key blob to be exported
 *               IN  dwFlags -    Flags values
 *               OUT pbData -     Key blob data
 *               OUT pdwDataLen - Length of key blob in bytes
 *
 *  Returns:
 */

BOOL WINAPI
CPExportKey(
    IN  HCRYPTPROV hUID,
    IN  HCRYPTKEY hKey,
    IN  HCRYPTKEY hPubKey,
    IN  DWORD dwBlobType,
    IN  DWORD dwFlags,
    OUT BYTE *pbData,
    OUT DWORD *pdwDataLen)
{
    // return codes
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    BOOL            fRet;

    // miscellaneous variables
    DWORD           dwLen;
    NTSimpleBlob    *pSimpleHeader;
    BLOBHEADER      *pPreHeader;
    BLOBHEADER      shScratch;
    RSAPUBKEY       *pExpPubKey;
    BSAFE_PUB_KEY   *pBsafePubKey;
    BSAFE_PUB_KEY   *pPublicKey;
    DWORD           PubKeyLen;
    BSAFE_PRV_KEY   *pPrvKey = NULL;
    DWORD           PrvKeyLen = 0;
    DWORD           cbPrivateBlob = 0;
    PBYTE           pbPrivateBlob = NULL;
    DWORD           cb = 0;
    BOOL            fExportable = FALSE;
    DWORD           dwSts;

    // temporary variables for pointing to user and key records
    PNTAGKeyList    pTmpKey;
    PNTAGKeyList    pPubKey;
    PNTAGUserList   pTmpUser;

    EntryPoint
    if (0 != (dwFlags & ~(CRYPT_SSL2_FALLBACK
                          | CRYPT_DESTROYKEY
                          | CRYPT_OAEP)))
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    if (pdwDataLen == NULL)
    {
        dwReturn = ERROR_INVALID_PARAMETER;
        goto ErrorExit;
    }

    if (((PUBLICKEYBLOB == dwBlobType) || (OPAQUEKEYBLOB == dwBlobType)
        || (PLAINTEXTKEYBLOB == dwBlobType))
        && (0 != hPubKey))
    {
        dwReturn = (DWORD)NTE_BAD_PUBLIC_KEY;
        goto ErrorExit;
    }

    // check the user identification
    pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    // check if the user is just looking for a length.  If so,
    // use a scratchpad to construct a pseudoblob.

    if ((pbData != NULL) && (*pdwDataLen > sizeof(BLOBHEADER)))
        pPreHeader = (BLOBHEADER *)pbData;
    else
        pPreHeader = &shScratch;

    pPreHeader->bType = (BYTE)(dwBlobType & 0xff);
    pPreHeader->bVersion = CUR_BLOB_VERSION;
    pPreHeader->reserved = 0;

    dwSts = NTLValidate((HNTAG)hKey, hUID, HNTAG_TO_HTYPE(hKey), &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = (NTE_FAIL == dwSts) ? (DWORD)NTE_BAD_KEY : dwSts;
        goto ErrorExit;
    }

    if ((dwBlobType != PUBLICKEYBLOB) &&
        (0 == (pTmpKey->Rights & (CRYPT_EXPORTABLE | CRYPT_ARCHIVABLE))))
    {
        dwReturn = (DWORD)NTE_BAD_KEY_STATE;
        goto ErrorExit;
    }

    pPreHeader->aiKeyAlg = pTmpKey->Algid;

    switch (dwBlobType)
    {
    case PUBLICKEYBLOB:
    {
        if ((HNTAG_TO_HTYPE(hKey) != SIGPUBKEY_HANDLE) &&
            (HNTAG_TO_HTYPE(hKey) != EXCHPUBKEY_HANDLE))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        pBsafePubKey = (BSAFE_PUB_KEY *) pTmpKey->pKeyValue;

        if (pBsafePubKey == NULL)
        {
            dwReturn = (DWORD)NTE_NO_KEY;
            goto ErrorExit;
        }

        //
        // Subtract off 2 extra DWORD needed by RSA code
        //
        dwLen = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
                ((pBsafePubKey->bitlen + 7) / 8);

        // Check user buffer size
        if (pbData == NULL || *pdwDataLen < dwLen)
        {
            *pdwDataLen = dwLen;
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pExpPubKey = (RSAPUBKEY *) (pbData + sizeof(BLOBHEADER));
        pExpPubKey->magic = pBsafePubKey->magic;
        pExpPubKey->bitlen = pBsafePubKey->bitlen;
        pExpPubKey->pubexp = pBsafePubKey->pubexp;

        memcpy((BYTE *) pbData + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY),
               (BYTE *) pBsafePubKey + sizeof(BSAFE_PUB_KEY),
               ((pBsafePubKey->bitlen + 7) / 8));
        break;
    }

    case PRIVATEKEYBLOB:
    {
        DWORD   dwBlockLen = 0;
        BOOL    fSigKey;
        LPWSTR  szPrompt;

        cb = sizeof(DWORD);
        if (HNTAG_TO_HTYPE(hKey) == SIGPUBKEY_HANDLE)
        {
            fSigKey = TRUE;
            szPrompt = g_Strings.pwszExportPrivSig;
            pPublicKey = (BSAFE_PUB_KEY*)pTmpUser->ContInfo.pbSigPub;
            PubKeyLen = pTmpUser->ContInfo.ContLens.cbSigPub;
        }
        else if (HNTAG_TO_HTYPE(hKey) == EXCHPUBKEY_HANDLE)
        {
            fSigKey = FALSE;
            szPrompt = g_Strings.pwszExportPrivExch;
            pPublicKey = (BSAFE_PUB_KEY*)pTmpUser->ContInfo.pbExchPub;
            PubKeyLen = pTmpUser->ContInfo.ContLens.cbExchPub;
        }
        else
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        // make sure the public key is available and appropriate
        if ((pPublicKey == NULL)
            || (PubKeyLen != pTmpKey->cbKeyLen)
            || (0 != memcmp((PBYTE)pPublicKey, pTmpKey->pKeyValue, PubKeyLen)))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        GetLengthOfPrivateKeyForExport(pPublicKey, &cbPrivateBlob);

        if (hPubKey)
        {
            if (!CPGetKeyParam(hUID, hPubKey, KP_BLOCKLEN,
                               (PBYTE)&dwBlockLen, &cb, 0))
            {
                dwReturn = (DWORD)NTE_BAD_KEY;
                goto ErrorExit;
            }

            // convert to byte count
            dwBlockLen /= 8;
        }

        // Check user buffer size
        if (pbData == NULL)
        {
            *pdwDataLen = sizeof(BLOBHEADER) + cbPrivateBlob + dwBlockLen;
            dwReturn = ERROR_SUCCESS;
            goto ErrorExit;
        }
        else if (*pdwDataLen < (sizeof(BLOBHEADER)
                                + cbPrivateBlob + dwBlockLen))
        {
            *pdwDataLen = sizeof(BLOBHEADER) + cbPrivateBlob + dwBlockLen;
            dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        // if the context being used is a Verify context then the key is not
        // in persisted storage and therefore is in memory
        if (0 == (pTmpUser->Rights & CRYPT_VERIFYCONTEXT))
        {
            // always read the private key from storage when exporting
            dwSts= UnprotectPrivKey(pTmpUser, szPrompt, fSigKey, TRUE);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts; // NTE_BAD_KEYSET
                goto ErrorExit;
            }
        }

        if (fSigKey)
        {
            PrvKeyLen = pTmpUser->SigPrivLen;
            pPrvKey = (BSAFE_PRV_KEY*)pTmpUser->pSigPrivKey;
            fExportable = pTmpUser->ContInfo.fSigExportable;
        }
        else
        {
            PrvKeyLen = pTmpUser->ExchPrivLen;
            pPrvKey = (BSAFE_PRV_KEY*)pTmpUser->pExchPrivKey;
            fExportable = pTmpUser->ContInfo.fExchExportable;
        }

        if (pPrvKey == NULL)
        {
            dwReturn = (DWORD)NTE_NO_KEY;
            goto ErrorExit;
        }

        if (!fExportable && (0 == (CRYPT_ARCHIVABLE & pTmpKey->Rights)))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (!PreparePrivateKeyForExport(pPrvKey, NULL, &cbPrivateBlob))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        // allocate memory for the private key blob
        cb = cbPrivateBlob + dwBlockLen;
        pbPrivateBlob = _nt_malloc(cb);
        if (NULL == pbPrivateBlob)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        if (!PreparePrivateKeyForExport(pPrvKey,
                                        pbPrivateBlob, &cbPrivateBlob))
        {
            // ?BUGBUG? Fix this
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (hPubKey)
        {
            dwSts = LocalEncrypt(hUID, hPubKey, 0, TRUE, 0, pbPrivateBlob,
                                 &cbPrivateBlob, cb, FALSE);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }

        dwLen = sizeof(BLOBHEADER) + cbPrivateBlob;
        CopyMemory(pbData + sizeof(BLOBHEADER), pbPrivateBlob, cbPrivateBlob);
        break;
    }

    case SIMPLEBLOB:
    {
        if (HNTAG_TO_HTYPE(hKey) != KEY_HANDLE)
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (0 == hPubKey)
        {
            dwReturn = (DWORD)NTE_NO_KEY;
            goto ErrorExit;
        }

        if (!FIsLegalKey(pTmpUser, pTmpKey, FALSE))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

#ifdef CSP_USE_SSL3
        // if the SSL2_FALLBACK flag is set then make sure the key
        // is an SSL2 master key
        if (CRYPT_SSL2_FALLBACK & dwFlags)
        {
            if (CALG_SSL2_MASTER != pTmpKey->Algid)
            {
                dwReturn = (DWORD)NTE_BAD_KEY;
                goto ErrorExit;
            }
        }
#endif

        dwSts = NTLValidate((HNTAG)hPubKey, hUID, EXCHPUBKEY_HANDLE,
                            &pPubKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = (NTE_FAIL == dwSts) ? (DWORD)NTE_BAD_KEY : dwSts;
            goto ErrorExit;
        }

        pBsafePubKey = (BSAFE_PUB_KEY *) pPubKey->pKeyValue;
        if (pBsafePubKey == NULL)
        {
            dwReturn = (DWORD)NTE_NO_KEY;
            goto ErrorExit;
        }

        //
        // Subtract off 8 bytes for 2 extra DWORD needed by RSA code
        dwLen = sizeof(BLOBHEADER) + sizeof(NTSimpleBlob) +
                (pBsafePubKey->bitlen + 7) / 8;

        if (pbData == NULL || *pdwDataLen < dwLen)
        {
            *pdwDataLen = dwLen;    // set what we need
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pSimpleHeader = (NTSimpleBlob *) (pbData + sizeof(BLOBHEADER));
        pSimpleHeader->aiEncAlg = CALG_RSA_KEYX;

#ifdef USE_SGC
        // if this is the schannel provider and we are a verify context and
        // the SGC flags are set and the key is large then make sure the
        // key is the same as the SGC key
        if ((PROV_RSA_SCHANNEL == pTmpUser->dwProvType) &&
            (0 != pTmpUser->dwSGCFlags) &&
            (pTmpUser->Rights & CRYPT_VERIFYCONTEXT) &&
            (pBsafePubKey->bitlen > 1024))
        {
            if (!CheckSGCSimpleForExport(pTmpUser, pBsafePubKey))
            {
                dwReturn = (DWORD)NTE_BAD_KEY;
                goto ErrorExit;
            }
        }
#endif

        // perform the RSA encryption.
        dwSts = RSAEncrypt(pTmpUser, pBsafePubKey, pTmpKey->pKeyValue,
                           pTmpKey->cbKeyLen, pTmpKey->pbParams,
                           pTmpKey->cbParams, dwFlags,
                           pbData + sizeof(BLOBHEADER) + sizeof(NTSimpleBlob));
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;
    }

    case OPAQUEKEYBLOB:
    {
        dwLen = *pdwDataLen;
        dwSts = ExportOpaqueBlob(pTmpKey, pbData, &dwLen);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        // if the destroy key flag is set then destroy the key
        if (CRYPT_DESTROYKEY & dwFlags)
        {
            if (!CPDestroyKey(hUID, hKey))
            {
                dwReturn = GetLastError();
                goto ErrorExit;
            }
        }

        break;
    }

    case SYMMETRICWRAPKEYBLOB:
    {
        // get a pointer to the symmetric key to wrap with (the variable
        // name pPubKey is a misnomer)
        dwSts = NTLValidate((HNTAG)hPubKey,
                            hUID,
                            HNTAG_TO_HTYPE(hKey),
                            &pPubKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = (NTE_FAIL == dwSts) ? (DWORD)NTE_BAD_KEY : dwSts;
            goto ErrorExit;
        }

        dwSts = WrapSymKey(pTmpUser, pTmpKey, pPubKey, pbData, pdwDataLen);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        dwLen = *pdwDataLen;
        break;
    }

    case PLAINTEXTKEYBLOB:
    {
        if (HNTAG_TO_HTYPE(hKey) != KEY_HANDLE)
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (! FIsLegalKey(pTmpUser, pTmpKey, FALSE))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        dwLen = sizeof(BLOBHEADER) + sizeof(DWORD) + pTmpKey->cbKeyLen;
        
        if (NULL == pbData || *pdwDataLen < dwLen)
        {
            *pdwDataLen = dwLen;
            if (NULL == pbData)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pbData += sizeof(BLOBHEADER);
        *((DWORD*)pbData) = pTmpKey->cbKeyLen;
        pbData += sizeof(DWORD);
        memcpy(pbData, pTmpKey->pKeyValue, pTmpKey->cbKeyLen);
        *pdwDataLen = dwLen;
        break;
    }

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    // set the size of the key blob
    *pdwDataLen = dwLen;
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (pbPrivateBlob)
        _nt_free(pbPrivateBlob, cb);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*static*/ DWORD
ImportOpaqueBlob(
    HCRYPTPROV hUID,
    CONST BYTE *pbData,
    DWORD cbData,
    HCRYPTKEY *phKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGKeyList    pTmpKey = NULL;
    PNTAGPackedKeyList pPackedKey;
    DWORD           cbRequired;
    DWORD           dwSts;

    *phKey = 0;

    // allocate a temporary key structure
    pTmpKey = (PNTAGKeyList)_nt_malloc(sizeof(NTAGKeyList));
    if (NULL == pTmpKey)
    {
        dwReturn = ERROR_NOT_ENOUGH_MEMORY;
        goto ErrorExit;
    }

    // make sure we have enough data
    cbRequired = sizeof(BLOBHEADER) +
                 sizeof(NTAGPackedKeyList);

    if (cbData < cbRequired)
    {
        dwReturn = (DWORD)NTE_BAD_DATA;
        goto ErrorExit;
    }

    // build key structure from packed key structure
    pPackedKey = (PNTAGPackedKeyList)(pbData + sizeof(BLOBHEADER));

    pTmpKey->hUID               = hUID;
    pTmpKey->Algid              = pPackedKey->Algid;
    pTmpKey->Rights             = pPackedKey->Rights;
    pTmpKey->cbKeyLen           = pPackedKey->cbKeyLen;
    pTmpKey->cbDataLen          = pPackedKey->cbDataLen;
    memcpy(pTmpKey->IV, pPackedKey->IV, sizeof(pTmpKey->IV));
    memcpy(pTmpKey->FeedBack, pPackedKey->FeedBack, sizeof(pTmpKey->FeedBack));
    pTmpKey->InProgress         = pPackedKey->InProgress;
    pTmpKey->cbSaltLen          = pPackedKey->cbSaltLen;
    memcpy(pTmpKey->rgbSalt, pPackedKey->rgbSalt, sizeof(pTmpKey->rgbSalt));
    pTmpKey->Padding            = pPackedKey->Padding;
    pTmpKey->Mode               = pPackedKey->Mode;
    pTmpKey->ModeBits           = pPackedKey->ModeBits;
    pTmpKey->Permissions        = pPackedKey->Permissions;
    pTmpKey->EffectiveKeyLen    = pPackedKey->EffectiveKeyLen;
    pTmpKey->dwBlockLen         = pPackedKey->dwBlockLen;

    // make sure we still have enough data
    cbRequired += pTmpKey->cbKeyLen +
                  pTmpKey->cbDataLen;

    if (cbData < cbRequired)
    {
        dwReturn = (DWORD)NTE_BAD_DATA;
        goto ErrorExit;
    }

    // allocate memory and copy the key value
    if (0 < pTmpKey->cbKeyLen)
    {
        pTmpKey->pKeyValue = _nt_malloc(pTmpKey->cbKeyLen);
        if (NULL == pTmpKey->pKeyValue)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        memcpy(pTmpKey->pKeyValue,
               (PBYTE)pPackedKey + sizeof(NTAGPackedKeyList),
               pTmpKey->cbKeyLen);
    }

    // allocate memory and copy the key data
    if (0 < pTmpKey->cbDataLen)
    {
        pTmpKey->pData = _nt_malloc(pTmpKey->cbDataLen);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        memcpy(pTmpKey->pData,
               (PBYTE)pPackedKey + sizeof(NTAGPackedKeyList) + pTmpKey->cbKeyLen,
               pTmpKey->cbDataLen);
    }

    dwSts = NTLMakeItem(phKey, KEY_HANDLE, (void *)pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    pTmpKey = NULL;
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    if (NULL != pTmpKey)
        FreeNewKey(pTmpKey);
    return dwReturn;
}


/*
 -  CPImportKey
 -
 *  Purpose:
 *                Import cryptographic keys
 *
 *
 *  Parameters:
 *               IN  hUID      -  Handle to the CSP user
 *               IN  pbData    -  Key blob data
 *               IN  dwDataLen -  Length of the key blob data
 *               IN  hPubKey   -  Handle to the exchange public key value of
 *                                the destination user
 *               IN  dwFlags   -  Flags values
 *               OUT phKey     -  Pointer to the handle to the key which was
 *                                Imported
 *
 *  Returns:
 */

BOOL WINAPI
CPImportKey(
    IN HCRYPTPROV hUID,
    IN CONST BYTE *pbData,
    IN DWORD dwDataLen,
    IN HCRYPTKEY hPubKey,
    IN DWORD dwFlags,
    OUT HCRYPTKEY *phKey)
{
    // Status return variables
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    BOOL            fRet;

    // miscellaneous variables
    DWORD           KeyBufLen;
    CONST BYTE      *pbEncPortion;
    BLOBHEADER      *ThisStdHeader = (BLOBHEADER *)pbData;
    BSAFE_PRV_KEY   *pBsafePrvKey = NULL;
    BYTE            *pKeyBuf = NULL;
    DWORD           dwRights = 0;
    DWORD           cbTmpLen;
    DWORD           dwSts;

    // temporary variables for pointing to user and key records
    PNTAGUserList   pTmpUser = NULL;
    PNTAGKeyList    pTmpKey = NULL;
    LPWSTR          szPrompt;
    BLOBHEADER      *pPublic;
    RSAPUBKEY       *pImpPubKey;
    BSAFE_PUB_KEY   *pBsafePubKey;
    PBYTE           pbData2 = NULL;
    DWORD           cb;
    DWORD           *pcbPub;
    BYTE            **ppbPub;
    DWORD           *pcbPrv;
    BYTE            **ppbPrv;
    BOOL            *pfExportable;
    BOOL            fExch;
    PEXPORT_PRV_KEY pExportKey;
    BOOL            fPubKey = FALSE;
    NTSimpleBlob    *ThisSB;
    PNTAGKeyList    pExPubKey = NULL;
    BOOL            fInCritSec = FALSE;
    BYTE            *pbParams = NULL;
    DWORD           cbParams = 0;
    BOOL            fAllowBigRC2Key = FALSE;

    EntryPoint
    // Validate user pointer
    //    count = *phKey;

    if ((dwFlags & ~(CRYPT_USER_PROTECTED | CRYPT_EXPORTABLE |
                     CRYPT_NO_SALT | CRYPT_SGCKEY | CRYPT_OAEP |
                     CRYPT_IPSEC_HMAC_KEY)) != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    if ((PUBLICKEYBLOB == ThisStdHeader->bType) && (0 != hPubKey))
    {
        dwReturn = ERROR_INVALID_PARAMETER;
        goto ErrorExit;
    }

    // check the user identification
    pTmpUser = (PNTAGUserList)NTLCheckList((HNTAG)hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    if (ThisStdHeader->bVersion != CUR_BLOB_VERSION)
    {
        dwReturn = (DWORD)NTE_BAD_VER;
        goto ErrorExit;
    }

    // Handy pointer for decrypting the blob...
    pbEncPortion = pbData + sizeof(BLOBHEADER) + sizeof(NTSimpleBlob);

    // determine which key blob is being imported
    switch (ThisStdHeader->bType)
    {
    case PUBLICKEYBLOB:
        pPublic = (BLOBHEADER *) pbData;
        pImpPubKey = (RSAPUBKEY *)(pbData+sizeof(BLOBHEADER));

        if ((pPublic->aiKeyAlg != CALG_RSA_KEYX) &&
            (pPublic->aiKeyAlg != CALG_RSA_SIGN))
        {
            dwReturn =(DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }

        cbTmpLen = (sizeof(DWORD) * 2) -
                   (((pImpPubKey->bitlen + 7) / 8) % (sizeof(DWORD) * 2));
        if ((sizeof(DWORD) * 2) != cbTmpLen)
            cbTmpLen += sizeof(DWORD) * 2;
        dwSts = MakeNewKey(pPublic->aiKeyAlg,
                           0,
                           sizeof(BSAFE_PUB_KEY)
                               + (pImpPubKey->bitlen / 8)
                               + cbTmpLen,
                           hUID,
                           0,
                           FALSE,
                           TRUE,
                           &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        pBsafePubKey = (BSAFE_PUB_KEY *)pTmpKey->pKeyValue;
        pBsafePubKey->magic = pImpPubKey->magic;
        pBsafePubKey->keylen = (pImpPubKey->bitlen / 8) + cbTmpLen;
        pBsafePubKey->bitlen = pImpPubKey->bitlen;
        pBsafePubKey->datalen = (pImpPubKey->bitlen+7)/8 - 1;
        pBsafePubKey->pubexp = pImpPubKey->pubexp;

        memset((BYTE *) pBsafePubKey + sizeof(BSAFE_PUB_KEY),
               '\0', pBsafePubKey->keylen);

        memcpy((BYTE *) pBsafePubKey + sizeof(BSAFE_PUB_KEY),
               (BYTE *) pPublic + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY),
               (pImpPubKey->bitlen+7)/8);

        dwSts = NTLMakeItem(phKey,
                            (BYTE) (pPublic->aiKeyAlg == CALG_RSA_KEYX
                                    ? EXCHPUBKEY_HANDLE
                                    : SIGPUBKEY_HANDLE),
                            (void *)pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case PRIVATEKEYBLOB:
        // wrap with a try since there is a critical section in here
        __try
        {
            EnterCriticalSection(&pTmpUser->CritSec);
            fInCritSec = TRUE;

            pPublic = (BLOBHEADER *) pbData;
            cb = dwDataLen - sizeof(BLOBHEADER);
            pbData2 = _nt_malloc(cb);
            if (NULL == pbData2)
            {
                dwReturn = ERROR_NOT_ENOUGH_MEMORY;
                goto ErrorExit;
            }

            CopyMemory(pbData2, pbData + sizeof(BLOBHEADER), cb);
            if (hPubKey)
            {
                dwSts = LocalDecrypt(hUID, hPubKey, 0, TRUE, 0,
                                     pbData2, &cb, FALSE);
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }
            }

            if (pPublic->aiKeyAlg == CALG_RSA_KEYX)
            {
                if (PROV_RSA_SIG == pTmpUser->dwProvType)
                {
                    dwReturn = (DWORD)NTE_BAD_DATA;
                    goto ErrorExit;
                }
                pcbPub = &pTmpUser->ContInfo.ContLens.cbExchPub;
                ppbPub = &pTmpUser->ContInfo.pbExchPub;
                pcbPrv = &pTmpUser->ExchPrivLen;
                ppbPrv = &pTmpUser->pExchPrivKey;
                pfExportable = &pTmpUser->ContInfo.fExchExportable;
                fExch = TRUE;
                szPrompt = g_Strings.pwszImportPrivExch;
            }
            else if (pPublic->aiKeyAlg == CALG_RSA_SIGN)
            {
                if (PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
                {
                    dwReturn = (DWORD)NTE_BAD_DATA;
                    goto ErrorExit;
                }
                pcbPub = &pTmpUser->ContInfo.ContLens.cbSigPub;
                ppbPub = &pTmpUser->ContInfo.pbSigPub;
                pcbPrv = &pTmpUser->SigPrivLen;
                ppbPrv = &pTmpUser->pSigPrivKey;
                fExch = FALSE;
                pfExportable = &pTmpUser->ContInfo.fSigExportable;
                szPrompt = g_Strings.pwszImportPrivSig;
            }
            else
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }

            // check the length of the key exchange key
            pExportKey = (PEXPORT_PRV_KEY)pbData2;

#ifdef USE_SGC
            // check if the provider is an SChannel provider and if so if the
            // SGC flag is set then use the FIsLegalSGCKeySize function
            if ((PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
                && (!(pTmpUser->Rights & CRYPT_VERIFYCONTEXT))
                && (0 != pTmpUser->dwSGCFlags)) // make sure this is server side
            {
                if (!FIsLegalSGCKeySize(pPublic->aiKeyAlg,
                                        pExportKey->bitlen / 8,
                                        FALSE, FALSE, &fPubKey))
                {
                    dwReturn = (DWORD)NTE_BAD_DATA;
                    goto ErrorExit;
                }
            }
            else
#endif
            {
                if (!FIsLegalKeySize(pTmpUser->dwCspTypeId,
                                     pPublic->aiKeyAlg,
                                     pExportKey->bitlen / 8,
                                     FALSE, &fPubKey))
                {
                    dwReturn = (DWORD)NTE_BAD_DATA;
                    goto ErrorExit;
                }
            }

            if (!fPubKey)
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }

            if (*ppbPub)
            {
                ASSERT(*pcbPub);
                ASSERT(*pcbPrv);
                ASSERT(*ppbPrv);

                _nt_free(*ppbPub, *pcbPub);
                *ppbPub = NULL;
                *pcbPub = 0;

                _nt_free(*ppbPrv, *pcbPrv);
                *ppbPrv = NULL;
                *pcbPrv = 0;
            }

            if (!PreparePrivateKeyForImport(pbData2, NULL, pcbPrv,
                                            NULL, pcbPub))
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }

            *ppbPub = _nt_malloc(*pcbPub);
            if (NULL == *ppbPub)
            {
                dwReturn = ERROR_NOT_ENOUGH_MEMORY;
                goto ErrorExit;
            }

            *ppbPrv = _nt_malloc(*pcbPrv);
            if (NULL == *ppbPrv)
            {
                dwReturn = ERROR_NOT_ENOUGH_MEMORY;
                goto ErrorExit;
            }

            if (!PreparePrivateKeyForImport(pbData2,
                                            (LPBSAFE_PRV_KEY)*ppbPrv,
                                            pcbPrv,
                                            (LPBSAFE_PUB_KEY)*ppbPub,
                                            pcbPub))
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }

            if (dwFlags & CRYPT_EXPORTABLE)
                *pfExportable = TRUE;
            else
                *pfExportable = FALSE;

            // test the RSA key to make sure it works
            dwSts = EncryptAndDecryptWithRSAKey(*ppbPub, *ppbPrv,
                                                TRUE, FALSE);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;   // NTE_BAD_DATA
                goto ErrorExit;
            }

            dwSts = EncryptAndDecryptWithRSAKey(*ppbPub, *ppbPrv,
                                                FALSE, FALSE);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;   // NTE_BAD_DATA
                goto ErrorExit;
            }

            // if the context being used is a Verify Context then the key
            // is not persisted to storage
            if (0 == (pTmpUser->Rights & CRYPT_VERIFYCONTEXT))
            {
                // write the new keys to the user storage file
                dwSts = ProtectPrivKey(pTmpUser, szPrompt, dwFlags, (!fExch));
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }
            }

            if (!CPGetUserKey(hUID,
                              (fExch ? AT_KEYEXCHANGE : AT_SIGNATURE),
                              phKey))
            {
                dwReturn = GetLastError();
                goto ErrorExit;
            }
        }
        __except ( EXCEPTION_EXECUTE_HANDLER )
        {
            // ?BUGBUG? No it's not!
            dwReturn = ERROR_INVALID_PARAMETER;
            goto ErrorExit;
        }
        break;

    case SIMPLEBLOB:
        ThisSB = (NTSimpleBlob *) (pbData + sizeof(BLOBHEADER));

        if (!ValidKeyAlgid(pTmpUser, ThisStdHeader->aiKeyAlg))
        {
            dwReturn = (DWORD)NTE_BAD_TYPE;
            goto ErrorExit;
        }

        if (ThisSB->aiEncAlg != CALG_RSA_KEYX)
        {
            dwReturn = (DWORD)NTE_BAD_ALGID;
            goto ErrorExit;
        }

        // if the import key handle is not zero make sure it is the
        if (0 != hPubKey)
        {
            dwSts = NTLValidate((HNTAG)hPubKey,
                                hUID,
                                HNTAG_TO_HTYPE((HNTAG)hPubKey),
                                &pExPubKey);
            if (ERROR_SUCCESS != dwSts)
            {
                // NTLValidate doesn't know what error to set
                // so it set NTE_FAIL -- fix it up.
                dwReturn = (dwSts == NTE_FAIL) ?(DWORD)NTE_BAD_KEY : dwSts;
                goto ErrorExit;
            }

            if ((pTmpUser->ContInfo.ContLens.cbExchPub != pExPubKey->cbKeyLen)
                || (0 != memcmp((PBYTE)pExPubKey->pKeyValue,
                                pTmpUser->ContInfo.pbExchPub,
                                pExPubKey->cbKeyLen)))
            {
                dwReturn = (DWORD)NTE_BAD_KEY;
                goto ErrorExit;
            }

            pbParams = pExPubKey->pbParams;
            cbParams = pExPubKey->cbParams;
        }

        pBsafePubKey = (BSAFE_PUB_KEY *)pTmpUser->ContInfo.pbExchPub;
        if (NULL == pBsafePubKey)
        {
            dwReturn = (DWORD)NTE_NO_KEY;
            goto ErrorExit;
        }

#ifdef USE_SGC
        // check if the provider is an SChannel provider and if so if the
        // SGC flag is set then use the FIsLegalSGCKeySize function
        if ((PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
            && (!(pTmpUser->Rights & CRYPT_VERIFYCONTEXT))
            && (0 != pTmpUser->dwSGCFlags)) // make sure this is server side
        {
            if (!FIsLegalSGCKeySize(CALG_RSA_KEYX,
                                    pBsafePubKey->bitlen / 8,
                                    FALSE, FALSE, &fPubKey))
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }
        }
        else
#endif
        {
            if (!FIsLegalKeySize(pTmpUser->dwCspTypeId,
                                 CALG_RSA_KEYX,
                                 pBsafePubKey->bitlen / 8,
                                 FALSE, &fPubKey))
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }
        }

        // get the key to use
        dwSts = UnprotectPrivKey(pTmpUser, g_Strings.pwszImportSimple,
                                 FALSE, FALSE);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;   // NTE_NO_KEY
            goto ErrorExit;
        }

        pBsafePrvKey = (BSAFE_PRV_KEY *)pTmpUser->pExchPrivKey;
        if (NULL == pBsafePrvKey)
        {
            dwReturn = (DWORD)NTE_NO_KEY;
            goto ErrorExit;
        }

        // Check the input data length
        if ((dwDataLen - (sizeof(BLOBHEADER) + sizeof(NTSimpleBlob)))
            < ((pBsafePrvKey->bitlen + 7) / 8))
        {
            dwReturn = (DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }

        // perform the RSA decryption
        dwSts = RSADecrypt(pTmpUser, pBsafePrvKey,
                           pbData + sizeof(BLOBHEADER) + sizeof(NTSimpleBlob),
                           dwDataLen - (sizeof(BLOBHEADER) + sizeof(NTSimpleBlob)),
                           pbParams, cbParams, dwFlags,
                           &pKeyBuf, &KeyBufLen);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        // check if the key is CRYPT_EXPORTABLE
        if (dwFlags & CRYPT_EXPORTABLE)
            dwRights = CRYPT_EXPORTABLE;

#ifdef CSP_USE_SSL3
        // if SSL3 or TLS1 master key then check the version
        if (CALG_SSL3_MASTER == ThisStdHeader->aiKeyAlg ||
            CALG_TLS1_MASTER == ThisStdHeader->aiKeyAlg)
        {
            if (MAKEWORD(pKeyBuf[1], pKeyBuf[0]) < 0x300)
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }
        }
#endif // CSP_USE_SSL3

        dwSts = MakeNewKey(ThisStdHeader->aiKeyAlg, dwRights, KeyBufLen,
                           hUID, pKeyBuf, TRUE, TRUE, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        pKeyBuf = NULL;

        // check keylength...
        if (!FIsLegalImportSymKey(pTmpUser, pTmpKey, TRUE))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        dwSts = NTLMakeItem(phKey, KEY_HANDLE, (void *)pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        // if 40 bit key + no mention of salt, set zeroized salt for
        // RSABase compatibility
        if ((5 == KeyBufLen)
            && (0 == (dwFlags & CRYPT_NO_SALT))
            && (CALG_SSL2_MASTER != ThisStdHeader->aiKeyAlg))
        {
            dwSts = MakeKeyRSABaseCompatible(hUID, *phKey);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }
        break;

    case OPAQUEKEYBLOB:
        dwSts = ImportOpaqueBlob(hUID, pbData, dwDataLen, phKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case SYMMETRICWRAPKEYBLOB:
        // get a pointer to the symmetric key to unwrap with (the variable
        // name pExPubKey is a misnomer)
        dwSts = NTLValidate((HNTAG)hPubKey, hUID,
                            HNTAG_TO_HTYPE((HNTAG)hPubKey),
                            &pExPubKey);
        if (ERROR_SUCCESS != dwSts)
        {
            // NTLValidate doesn't know what error to set
            // so it set NTE_FAIL -- fix it up.
            dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
            goto ErrorExit;
        }

        dwSts = UnWrapSymKey(hUID, pTmpUser, pExPubKey, (BYTE*)pbData,
                             dwDataLen, dwFlags, phKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        break;

    case PLAINTEXTKEYBLOB:
        if (! ValidKeyAlgid(pTmpUser, ThisStdHeader->aiKeyAlg))
        {
            dwReturn = (DWORD)NTE_BAD_ALGID;
            goto ErrorExit;
        }

        // check if the key is CRYPT_EXPORTABLE
        if (dwFlags & CRYPT_EXPORTABLE)
            dwRights = CRYPT_EXPORTABLE;

        KeyBufLen = *((DWORD*)(pbData + sizeof(BLOBHEADER)));
        dwSts = MakeNewKey( ThisStdHeader->aiKeyAlg, dwRights, 
                            KeyBufLen, hUID, 
                            (BYTE *) (pbData + sizeof(BLOBHEADER) + sizeof(DWORD)),
                            FALSE, TRUE, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        if (CRYPT_IPSEC_HMAC_KEY & dwFlags)
            fAllowBigRC2Key = TRUE;

        if (! FIsLegalImportSymKey(pTmpUser, pTmpKey, fAllowBigRC2Key))
        {
            dwReturn = (DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }
        
        dwSts = NTLMakeItem(phKey, KEY_HANDLE, (void *)pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        
        // if 40 bit key + no mention of salt, set zeroized salt for
        // RSABase compatibility
        if ((5 == KeyBufLen)
            && (0 == (dwFlags & CRYPT_NO_SALT))
            && (CALG_SSL2_MASTER != ThisStdHeader->aiKeyAlg))
        {
            dwSts = MakeKeyRSABaseCompatible(hUID, *phKey);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }
        break;

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    pTmpKey = NULL;
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (fInCritSec)
        LeaveCriticalSection(&pTmpUser->CritSec);
    if (pKeyBuf)
        _nt_free(pKeyBuf, KeyBufLen);
    if (pbData2)
        _nt_free(pbData2, dwDataLen - sizeof(BLOBHEADER));
    if (NULL != pTmpKey)
        FreeNewKey(pTmpKey);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*
 -  CPInflateKey
 -
 *  Purpose:
 *                Use to "inflate" (expand) a cryptographic key for use with
 *                the CryptEncrypt and CryptDecrypt functions
 *
 *  Parameters:
 *               IN      hUID    -  Handle to a CSP
 *               IN      hKey    -  Handle to a key
 *               IN      dwFlags -  Flags values
 *
 *  Returns:
 */

DWORD
InflateKey(
    IN PNTAGKeyList pTmpKey)
{
    DWORD   dwReturn = ERROR_INTERNAL_ERROR;
    BYTE    *pbRealKey = NULL;

    // if space for the key table has been allocated previously
    // then free it
    if (pTmpKey->pData != NULL)
    {
        ASSERT(0 != pTmpKey->cbDataLen);
        _nt_free(pTmpKey->pData, pTmpKey->cbDataLen);
        pTmpKey->cbDataLen = 0;
    }
    else
    {
        ASSERT(pTmpKey->cbDataLen == 0);
    }

    // determine the algorithm to be used
    switch (pTmpKey->Algid)
    {
#ifdef CSP_USE_RC2
    case CALG_RC2:
        pTmpKey->pData = (BYTE *)_nt_malloc(RC2_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            return NTF_FAILED;
        }

        pbRealKey = (BYTE *)_nt_malloc(pTmpKey->cbKeyLen
                                       + pTmpKey->cbSaltLen);
        if (NULL == pbRealKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        memcpy(pbRealKey, pTmpKey->pKeyValue, pTmpKey->cbKeyLen);
        memcpy(pbRealKey+pTmpKey->cbKeyLen, pTmpKey->rgbSalt,
               pTmpKey->cbSaltLen);
        pTmpKey->cbDataLen = RC2_TABLESIZE;

        RC2KeyEx((WORD *)pTmpKey->pData,
                 pbRealKey,
                 pTmpKey->cbKeyLen + pTmpKey->cbSaltLen,
                 pTmpKey->EffectiveKeyLen);
        break;
#endif

#ifdef CSP_USE_RC4
    case CALG_RC4:
        pTmpKey->pData = (BYTE *)_nt_malloc(sizeof(RC4_KEYSTRUCT));
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pbRealKey = (BYTE *)_nt_malloc(pTmpKey->cbKeyLen
                                       + pTmpKey->cbSaltLen);
        if (NULL == pbRealKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        memcpy(pbRealKey, pTmpKey->pKeyValue, pTmpKey->cbKeyLen);
        memcpy(pbRealKey+pTmpKey->cbKeyLen, pTmpKey->rgbSalt,
               pTmpKey->cbSaltLen);
        pTmpKey->cbDataLen = sizeof(RC4_KEYSTRUCT);
        rc4_key((struct RC4_KEYSTRUCT *)pTmpKey->pData,
                pTmpKey->cbKeyLen+pTmpKey->cbSaltLen,
                pbRealKey);
        break;
#endif

#ifdef CSP_USE_DES
    case CALG_DES:
        pTmpKey->pData = (BYTE *)_nt_malloc(DES_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pTmpKey->cbDataLen = DES_TABLESIZE;
        deskey((DESTable *)pTmpKey->pData, pTmpKey->pKeyValue);
        break;
#endif

#ifdef CSP_USE_3DES
    case CALG_3DES_112:
        pTmpKey->pData = (BYTE *)_nt_malloc(DES3_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pTmpKey->cbDataLen = DES3_TABLESIZE;
        tripledes2key((PDES3TABLE)pTmpKey->pData, pTmpKey->pKeyValue);
        break;

    case CALG_3DES:
        pTmpKey->pData = (BYTE *)_nt_malloc(DES3_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pTmpKey->cbDataLen = DES3_TABLESIZE;
        tripledes3key((PDES3TABLE)pTmpKey->pData, pTmpKey->pKeyValue);
        break;
#endif

#ifdef CSP_USE_SSL3
    case CALG_SSL3_MASTER:
    case CALG_PCT1_MASTER:
    case CALG_SCHANNEL_MAC_KEY:
        break;
#endif

#ifdef CSP_USE_AES
    case CALG_AES_128:
        pTmpKey->pData = (BYTE *)_nt_malloc(AES_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pTmpKey->cbDataLen = AES_TABLESIZE;
        aeskey((AESTable *)pTmpKey->pData, pTmpKey->pKeyValue, CRYPT_AES128_ROUNDS);
        break;
    
    case CALG_AES_192:
        pTmpKey->pData = (BYTE *)_nt_malloc(AES_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pTmpKey->cbDataLen = AES_TABLESIZE;
        aeskey((AESTable *)pTmpKey->pData, pTmpKey->pKeyValue, CRYPT_AES192_ROUNDS);
        break;
    
    case CALG_AES_256:
        pTmpKey->pData = (BYTE *)_nt_malloc(AES_TABLESIZE);
        if (NULL == pTmpKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pTmpKey->cbDataLen = AES_TABLESIZE;
        aeskey((AESTable *)pTmpKey->pData, pTmpKey->pKeyValue, CRYPT_AES256_ROUNDS);
        break;
#endif

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    if (pbRealKey)
        _nt_free(pbRealKey, pTmpKey->cbKeyLen + pTmpKey->cbSaltLen);
    return dwReturn;
}


/*
 -  CPDestroyKey
 -
 *  Purpose:
 *                Destroys the cryptographic key that is being referenced
 *                with the hKey parameter
 *
 *
 *  Parameters:
 *               IN      hUID   -  Handle to a CSP
 *               IN      hKey   -  Handle to a key
 *
 *  Returns:
 */

BOOL WINAPI
CPDestroyKey(
    IN HCRYPTPROV hUID,
    IN HCRYPTKEY hKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGKeyList    pTmpKey;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    // check the user identification
    if (NULL == NTLCheckList((HNTAG)hUID, USER_HANDLE))
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    dwSts = NTLValidate((HNTAG)hKey, hUID, SIGPUBKEY_HANDLE, &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwSts = NTLValidate((HNTAG)hKey, hUID, EXCHPUBKEY_HANDLE, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwSts = NTLValidate((HNTAG)hKey, hUID, KEY_HANDLE, &pTmpKey);
            if (ERROR_SUCCESS != dwSts)
            {
                // NTLValidate doesn't know what error to set
                // so it set NTE_FAIL -- fix it up.
                dwReturn = (dwSts == NTE_FAIL)
                           ? (DWORD)NTE_BAD_KEY
                           : dwSts;
                goto ErrorExit;
            }
        }
    }

    // Remove from internal list first so others can't get to it, then free.
    NTLDelete((HNTAG)hKey);

    // scrub the memory where the key information was held
    if (pTmpKey->pKeyValue)
    {
        ASSERT(pTmpKey->cbKeyLen);
        memnuke(pTmpKey->pKeyValue, pTmpKey->cbKeyLen);
        _nt_free(pTmpKey->pKeyValue, pTmpKey->cbKeyLen);
    }
    if (pTmpKey->pbParams)
        _nt_free(pTmpKey->pbParams, pTmpKey->cbParams);
    if (pTmpKey->pData)
    {
        ASSERT(pTmpKey->cbDataLen);
        if ((CALG_SSL3_MASTER == pTmpKey->Algid) ||
            (CALG_PCT1_MASTER == pTmpKey->Algid))
        {
            FreeSChKey((PSCH_KEY)pTmpKey->pData);
        }
        memnuke(pTmpKey->pData, pTmpKey->cbDataLen);
        _nt_free(pTmpKey->pData, pTmpKey->cbDataLen);
    }
    _nt_free(pTmpKey, sizeof(NTAGKeyList));

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*
 -  CPGetUserKey
 -
 *  Purpose:
 *                Gets a handle to a permanent user key
 *
 *
 *  Parameters:
 *               IN  hUID       -  Handle to the user identifcation
 *               IN  dwWhichKey -  Specification of the key to retrieve
 *               OUT phKey      -  Pointer to key handle of retrieved key
 *
 *  Returns:
 */

BOOL WINAPI
CPGetUserKey(
    IN HCRYPTPROV hUID,
    IN DWORD dwWhichKey,
    OUT HCRYPTKEY *phKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList   pUser;
    PNTAGKeyList    pTmpKey;
    ALG_ID          Algid;
    DWORD           cb;
    BYTE            *pb;
    BYTE            bType;
    DWORD           dwExportability = 0;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    // check the user identification
    pUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    switch (dwWhichKey)
    {
    case AT_KEYEXCHANGE:
        Algid = CALG_RSA_KEYX;
        cb = pUser->ContInfo.ContLens.cbExchPub;
        pb = pUser->ContInfo.pbExchPub;
        if (pUser->ContInfo.fExchExportable)
            dwExportability = CRYPT_EXPORTABLE;
        bType = EXCHPUBKEY_HANDLE;
        break;

    case AT_SIGNATURE:
        Algid = CALG_RSA_SIGN;
        cb = pUser->ContInfo.ContLens.cbSigPub;
        pb = pUser->ContInfo.pbSigPub;
        if (pUser->ContInfo.fSigExportable)
            dwExportability = CRYPT_EXPORTABLE;
        bType = SIGPUBKEY_HANDLE;
        break;

    default:
        dwReturn = (DWORD)NTE_BAD_KEY;
        goto ErrorExit;
    }

    if (!ValidKeyAlgid(pUser, Algid))
    {
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    if (0 == cb)
    {
        dwReturn = (DWORD)NTE_NO_KEY;
        goto ErrorExit;
    }

    dwSts = MakeNewKey(Algid, dwExportability, cb, hUID, pb, FALSE, FALSE,
                       &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    dwSts = NTLMakeItem(phKey, bType, (void *)pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*
 -  CPSetKeyParam
 -
 *  Purpose:
 *                Allows applications to customize various aspects of the
 *                operations of a key
 *
 *  Parameters:
 *               IN      hUID    -  Handle to a CSP
 *               IN      hKey    -  Handle to a key
 *               IN      dwParam -  Parameter number
 *               IN      pbData  -  Pointer to data
 *               IN      dwFlags -  Flags values
 *
 *  Returns:
 */

BOOL WINAPI
CPSetKeyParam(
    IN HCRYPTPROV hUID,
   IN HCRYPTKEY hKey,
   IN DWORD dwParam,
   IN CONST BYTE *pbData,
   IN DWORD dwFlags)
{
    DWORD               dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList       pTmpUser;
    PNTAGKeyList        pTmpKey;
    PCRYPT_DATA_BLOB    psData;
    DWORD               *pdw;
    DWORD               dw;
    BOOL                fRet;
    DWORD               dwSts;

    EntryPoint
    if ((dwFlags & ~CRYPT_SERVER) != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    // check the user identification
    pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    dwSts = NTLValidate((HNTAG)hKey, hUID, KEY_HANDLE, &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwSts = NTLValidate((HNTAG)hKey, hUID, SIGPUBKEY_HANDLE, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwSts = NTLValidate((HNTAG)hKey, hUID,
                                EXCHPUBKEY_HANDLE, &pTmpKey);
            if (ERROR_SUCCESS != dwSts)
            {
                // NTLValidate doesn't know what error to set
                // so it set NTE_FAIL -- fix it up.
                dwReturn = (dwSts == NTE_FAIL)
                           ? (DWORD)NTE_BAD_KEY
                           : dwSts;
                goto ErrorExit;
            }
        }
    }

    switch (dwParam)
    {
    case KP_IV:
        memcpy(pTmpKey->IV, pbData, RC2_BLOCKLEN);
        break;

    case KP_SALT:
        if ((CALG_RC2 != pTmpKey->Algid) && (CALG_RC4 != pTmpKey->Algid))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (pbData == NULL)
        {
            dwReturn = ERROR_INVALID_PARAMETER;
            goto ErrorExit;
        }

        if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
            || (POLICY_MS_STRONG == pTmpUser->dwCspTypeId))
        {
            pTmpKey->cbSaltLen = DEFAULT_WEAK_SALT_LENGTH;
        }
        else
        {
            pTmpKey->cbSaltLen = DEFAULT_STRONG_SALT_LENGTH;
        }

        if (pTmpKey->cbSaltLen)
            CopyMemory(pTmpKey->rgbSalt, pbData, pTmpKey->cbSaltLen);

        dwSts = InflateKey(pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case KP_SALT_EX:
        if ((CALG_RC2 != pTmpKey->Algid) && (CALG_RC4 != pTmpKey->Algid))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        psData = (PCRYPT_DATA_BLOB)pbData;
        if (pbData == NULL)
        {
            dwReturn = ERROR_INVALID_PARAMETER;
            goto ErrorExit;
        }

        if (psData->cbData > MAX_SALT_LEN)
        {
            dwReturn = NTE_BAD_DATA;
            goto ErrorExit;
        }

        pTmpKey->cbSaltLen = psData->cbData;
        CopyMemory(pTmpKey->rgbSalt, psData->pbData, pTmpKey->cbSaltLen);

        dwSts = InflateKey(pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case KP_PADDING:
        if (*((DWORD *) pbData) != PKCS5_PADDING)
        {
            dwReturn = (DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }
        break;

    case KP_MODE:
        if ((CALG_RSA_SIGN == pTmpKey->Algid) ||
            (CALG_RSA_KEYX == pTmpKey->Algid))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (*pbData != CRYPT_MODE_CBC &&
            *pbData != CRYPT_MODE_ECB &&
            *pbData != CRYPT_MODE_CFB &&
            *pbData != CRYPT_MODE_OFB)
        {
            dwReturn = (DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }

        pTmpKey->Mode = *((DWORD *) pbData);
        break;

    case KP_MODE_BITS:
        dw = *((DWORD *) pbData);
        if ((dw == 0) || (dw > 64)) // if 0 or larger than the blocklength
        {
            dwReturn = (DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }

        pTmpKey->ModeBits = dw;
        break;

    case KP_PERMISSIONS:
    {
        DWORD dwPerm = *(LPDWORD)pbData;

        if (0 != (dwPerm & ~(CRYPT_ENCRYPT|CRYPT_DECRYPT|CRYPT_EXPORT|
                             CRYPT_READ|CRYPT_WRITE|CRYPT_MAC|CRYPT_ARCHIVE)))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        // the exportability of a key may not be changed, but it may be ignored.
        if (0 != (dwPerm & CRYPT_EXPORT)
            && 0 == (pTmpKey->Permissions & CRYPT_EXPORT))
        {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }
        if (0 != (dwPerm & CRYPT_ARCHIVE)
            && 0 == (pTmpKey->Permissions & CRYPT_ARCHIVE))
        {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }

        dwPerm &= ~(CRYPT_ARCHIVE | CRYPT_EXPORT);
        dwPerm |= pTmpKey->Permissions & (CRYPT_ARCHIVE | CRYPT_EXPORT);
        pTmpKey->Permissions = dwPerm;
        break;
    }

    case KP_EFFECTIVE_KEYLEN:
        if (CALG_RC2 != pTmpKey->Algid)
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        pdw = (DWORD*)pbData;
        if (*pdw < RC2_MIN_EFFECTIVE_KEYLEN)
        {
            dwReturn = (DWORD)NTE_BAD_DATA;
            goto ErrorExit;
        }
        if (POLICY_MS_DEF == pTmpUser->dwCspTypeId)
        {
            if (*pdw > RC2_MAX_WEAK_EFFECTIVE_KEYLEN)
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }
        }
        else
        {
            if (*pdw > RC2_MAX_STRONG_EFFECTIVE_KEYLEN)
            {
                dwReturn = (DWORD)NTE_BAD_DATA;
                goto ErrorExit;
            }
        }

        pTmpKey->EffectiveKeyLen = *pdw;

        dwSts = InflateKey(pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

#ifdef CSP_USE_SSL3
    case KP_CLIENT_RANDOM:
    case KP_SERVER_RANDOM:
    case KP_CERTIFICATE:
    case KP_CLEAR_KEY:
    case KP_SCHANNEL_ALG:
        if (PROV_RSA_SCHANNEL != pTmpUser->dwProvType)
        {
            dwReturn = (DWORD)NTE_BAD_TYPE;
            goto ErrorExit;
        }

        dwSts = SCHSetKeyParam(pTmpUser, pTmpKey, dwParam, pbData);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;
#endif // CSP_USE_SSL3

    case KP_OAEP_PARAMS:
        if (CALG_RSA_KEYX != pTmpKey->Algid)
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        psData = (PCRYPT_DATA_BLOB)pbData;
        if (pbData == NULL)
        {
            dwReturn = ERROR_INVALID_PARAMETER;
            goto ErrorExit;
        }

        // free salt if it already exists
        if (NULL != pTmpKey->pbParams)
        {
            _nt_free(pTmpKey->pbParams, pTmpKey->cbParams);
            pTmpKey->pbParams = NULL;
        }
        pTmpKey->cbParams = psData->cbData;

        // alloc variable size
        pTmpKey->pbParams = (BYTE *)_nt_malloc(pTmpKey->cbParams);
        if (NULL == pTmpKey->pbParams)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            pTmpKey->cbParams = 0;
            goto ErrorExit;
        }

        CopyMemory(pTmpKey->pbParams, psData->pbData, pTmpKey->cbParams);
        break;

#ifdef CSP_USE_SSL3
    case KP_HIGHEST_VERSION:
        if ((CALG_SSL3_MASTER != pTmpKey->Algid) &&
            (CALG_TLS1_MASTER != pTmpKey->Algid))
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (pbData == NULL)
        {
            dwReturn = ERROR_INVALID_PARAMETER;
            goto ErrorExit;
        }

        if (dwFlags & CRYPT_SERVER)
        {
            if ((CALG_SSL3_MASTER == pTmpKey->Algid)
                && (*(DWORD *)pbData >= 0x301))
            {
                // We're a server doing SSL3, and we also support TLS1.
                // If the pre_master_secret contains a version number
                // greater than or equal to TLS1, then abort the connection.
                if (MAKEWORD(pTmpKey->pKeyValue[1], pTmpKey->pKeyValue[0]) >= 0x301)
                {
                    dwReturn = (DWORD)NTE_BAD_VER;
                    goto ErrorExit;
                }
            }
        }
        else
        {
            pTmpKey->pKeyValue[0] = HIBYTE(*(DWORD *)pbData);
            pTmpKey->pKeyValue[1] = LOBYTE(*(DWORD *)pbData);
        }
        break;
#endif // CSP_USE_SSL3

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*
 -  CPGetKeyParam
 -
 *  Purpose:
 *                Allows applications to get various aspects of the
 *                operations of a key
 *
 *  Parameters:
 *               IN      hUID       -  Handle to a CSP
 *               IN      hKey       -  Handle to a key
 *               IN      dwParam    -  Parameter number
 *               IN      pbData     -  Pointer to data
 *               IN      pdwDataLen -  Length of parameter data
 *               IN      dwFlags    -  Flags values
 *
 *  Returns:
 */

BOOL WINAPI
CPGetKeyParam(
    IN HCRYPTPROV hUID,
    IN HCRYPTKEY hKey,
    IN DWORD dwParam,
    IN BYTE *pbData,
    IN DWORD *pwDataLen,
    IN DWORD dwFlags)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList   pTmpUser;
    PNTAGKeyList    pTmpKey;
    BSAFE_PUB_KEY   *pBsafePubKey;
    DWORD           *pdw;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    if (dwFlags != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    // check the user identification
    pTmpUser = NTLCheckList((HNTAG)hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    dwSts = NTLValidate((HNTAG)hKey, hUID, KEY_HANDLE, &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwSts = NTLValidate((HNTAG)hKey, hUID, SIGPUBKEY_HANDLE, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            dwSts = NTLValidate((HNTAG)hKey, hUID,
                                EXCHPUBKEY_HANDLE, &pTmpKey);
            if (ERROR_SUCCESS != dwSts)
            {
                // NTLValidate doesn't know what error to set
                // so it set NTE_FAIL -- fix it up.
                dwReturn = (dwSts == NTE_FAIL)
                           ? (DWORD)NTE_BAD_KEY
                           : dwSts;
                goto ErrorExit;
            }
        }
    }

    if (pwDataLen == NULL)
    {
        dwReturn = ERROR_INVALID_PARAMETER;
        goto ErrorExit;
    }

    switch (dwParam)
    {
    case KP_IV:
        if (pbData == NULL || *pwDataLen < RC2_BLOCKLEN)
        {
            *pwDataLen = RC2_BLOCKLEN;
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }
        memcpy(pbData, pTmpKey->IV, RC2_BLOCKLEN);
        *pwDataLen = RC2_BLOCKLEN;
        break;

    case KP_SALT:
        if ((CALG_RC2 != pTmpKey->Algid) && (CALG_RC4 != pTmpKey->Algid))
        {
            if ((CALG_DES == pTmpKey->Algid)
                || (CALG_3DES == pTmpKey->Algid)
                || (CALG_3DES_112 == pTmpKey->Algid))
            {
                *pwDataLen = 0;
                dwReturn = ERROR_SUCCESS;
                goto ErrorExit;
            }
            else
            {
                dwReturn = (DWORD)NTE_BAD_KEY;
                goto ErrorExit;
            }
        }

        if (pbData == NULL || (*pwDataLen < pTmpKey->cbSaltLen))
        {
            *pwDataLen = pTmpKey->cbSaltLen;
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        CopyMemory(pbData, pTmpKey->rgbSalt, pTmpKey->cbSaltLen);
        *pwDataLen = pTmpKey->cbSaltLen;
        break;

    case KP_PADDING:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);

            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }
        *((DWORD *) pbData) = PKCS5_PADDING;
        *pwDataLen = sizeof(DWORD);
        break;

    case KP_MODE:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }
        *((DWORD *) pbData) = pTmpKey->Mode;
        *pwDataLen = sizeof(DWORD);
        break;

    case KP_MODE_BITS:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }
        *((DWORD *)pbData) = pTmpKey->ModeBits;
        *pwDataLen = sizeof(DWORD);
        break;

    case KP_PERMISSIONS:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }
        *((DWORD *) pbData) = pTmpKey->Permissions;
        *pwDataLen = sizeof(DWORD);
        break;

    case KP_ALGID:
        if (pbData == NULL || *pwDataLen < sizeof(ALG_ID))
        {
            *pwDataLen = sizeof(ALG_ID);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }
        *((ALG_ID *) pbData) = pTmpKey->Algid;
        *pwDataLen = sizeof(ALG_ID);
        break;

    case KP_KEYLEN:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        // ALWAYS report keylen in BITS
        if ((HNTAG_TO_HTYPE(hKey) == SIGPUBKEY_HANDLE) ||
            (HNTAG_TO_HTYPE(hKey) == EXCHPUBKEY_HANDLE))
        {
            pBsafePubKey = (BSAFE_PUB_KEY *) pTmpKey->pKeyValue;
            if (pBsafePubKey == NULL)
            {
                dwReturn = (DWORD)NTE_NO_KEY;
                goto ErrorExit;
            }
            *((DWORD *) pbData) = pBsafePubKey->bitlen;
        }
        else
        {
            switch (pTmpKey->Algid)
            {
#ifdef CSP_USE_DES
            case CALG_DES:
                *((DWORD *) pbData) = (DES_KEYSIZE) * 8;
                break;
#endif
#ifdef CSP_USE_3DES
            case CALG_3DES_112:
                *((DWORD *) pbData) = (DES2_KEYSIZE) * 8;
                break;
            case CALG_3DES:
                *((DWORD *) pbData) = (DES3_KEYSIZE) * 8;
                break;
#endif
            default:
                *((DWORD *) pbData) = pTmpKey->cbKeyLen * 8;
            }
        }
        *pwDataLen = sizeof(DWORD);
        break;

    case KP_BLOCKLEN:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        if ((HNTAG_TO_HTYPE(hKey) == SIGPUBKEY_HANDLE) ||
            (HNTAG_TO_HTYPE(hKey) == EXCHPUBKEY_HANDLE))
        {
            pBsafePubKey = (BSAFE_PUB_KEY *) pTmpKey->pKeyValue;
            if (pBsafePubKey == NULL)
            {
                dwReturn = (DWORD)NTE_NO_KEY;
                goto ErrorExit;
            }
            *(DWORD *)pbData = pBsafePubKey->bitlen;
            *pwDataLen = sizeof(DWORD);
        }
        else
        {
            switch (pTmpKey->Algid)
            {
#ifdef CSP_USE_RC2
            case CALG_RC2:
                *((DWORD *) pbData) = RC2_BLOCKLEN * 8;
                *pwDataLen = sizeof(DWORD);
                break;
#endif
#ifdef CSP_USE_DES
            case CALG_DES:
                *((DWORD *) pbData) = DES_BLOCKLEN * 8;
                *pwDataLen = sizeof(DWORD);
                break;
#endif
#ifdef CSP_USE_3DES
            case CALG_3DES_112:
            case CALG_3DES:
                *((DWORD *) pbData) = DES_BLOCKLEN * 8;
                *pwDataLen = sizeof(DWORD);
                break;
#endif
#ifdef CSP_USE_AES
            case CALG_AES_128:
            case CALG_AES_192:
            case CALG_AES_256:
                *((DWORD *) pbData) = pTmpKey->dwBlockLen * 8;
                *pwDataLen = sizeof(DWORD);
                break;
#endif
            default:
                *((DWORD *) pbData) = 0;
                *pwDataLen = sizeof(DWORD);
            }
        }
        break;

    case KP_EFFECTIVE_KEYLEN:
        if (CALG_RC2 != pTmpKey->Algid &&
            CALG_DES != pTmpKey->Algid &&
            CALG_3DES != pTmpKey->Algid &&
            CALG_3DES_112 != pTmpKey->Algid)
        {
            dwReturn = (DWORD)NTE_BAD_KEY;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            if (pbData == NULL)
                dwReturn = ERROR_SUCCESS;
            else
                dwReturn = ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        pdw = (DWORD*)pbData;
        
        switch (pTmpKey->Algid)
        {
        case CALG_RC2:
            *pdw = pTmpKey->EffectiveKeyLen;
            break;
        case CALG_DES:
            *pdw = (DES_KEYSIZE - 1) * 8;
            break;
        case CALG_3DES_112:
            *pdw = (DES2_KEYSIZE - 2) * 8;
            break;
        case CALG_3DES:
            *pdw = (DES3_KEYSIZE - 3) * 8;
            break;
        }
        
        break;

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*static*/ DWORD
DeletePersistedKey(
    PNTAGUserList pTmpUser,
    DWORD dwKeySpec)
{
    DWORD       dwReturn = ERROR_INTERNAL_ERROR;
    CHAR        *pszExport;
    CHAR        *pszPrivKey;
    CHAR        *pszPubKey;
    BOOL        fMachineKeySet = FALSE;
    DWORD       dwSts;

    if (pTmpUser->Rights & CRYPT_MACHINE_KEYSET)
        fMachineKeySet = TRUE;

    if (AT_SIGNATURE == dwKeySpec)
    {
        pszExport = "SExport";
        pszPrivKey = "SPvK";
        pszPubKey = "SPbK";
    }
    else if (AT_KEYEXCHANGE == dwKeySpec)
    {
        pszExport = "EExport";
        pszPrivKey = "EPvK";
        pszPubKey = "EPbK";
    }
    else
    {
        dwReturn = (DWORD)NTE_BAD_DATA;
        goto ErrorExit;
    }

    // if protected store is available then delete the key from there
    if (pTmpUser->pPStore)
    {
        dwSts = DeleteKeyFromProtectedStorage(
                    pTmpUser, &g_Strings, dwKeySpec,
                    fMachineKeySet, FALSE);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }

    // delete stuff from the registry
    RegDeleteValue(pTmpUser->hKeys, pszPrivKey);
    RegDeleteValue(pTmpUser->hKeys, pszPubKey);
    RegDeleteValue(pTmpUser->hKeys, pszExport);

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}


#ifdef USE_SGC
//
// Function to bring all the SGC checking together
//

/*static*/ DWORD
SetSGCInfo(
    PNTAGUserList pTmpUser,
    CONST BYTE *pbData)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PCCERT_CONTEXT  pCertContext = (PCCERT_CONTEXT)pbData;
    BSAFE_PUB_KEY   *pPubKey;
    BYTE            *pbMod;
    DWORD           dwSts;

    // make sure the root certs are loaded
    dwSts = LoadSGCRoots(&pTmpUser->CritSec);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    // verify context means that you are on the client side
    if (pTmpUser->Rights & CRYPT_VERIFYCONTEXT)
    {
        dwSts = SPQueryCFLevel(pCertContext, NULL, 0, 0,
                               (LPDWORD)&pTmpUser->dwSGCFlags);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        // set the modulus of the SGC cert so it may be checked later
        // when exporting the pre-master secret
        dwSts = SGCAssignPubKey(pCertContext,
                                &pTmpUser->pbSGCKeyMod,
                                &pTmpUser->cbSGCKeyMod,
                                &pTmpUser->dwSGCKeyExpo);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }
    else
    {
        // get a pointer to the exchange public key
        if ((0 == pTmpUser->ContInfo.ContLens.cbExchPub)
            || (NULL == pTmpUser->ContInfo.pbExchPub))
        {
            dwReturn = (DWORD)NTE_FAIL;
            goto ErrorExit;
        }

        pPubKey = (BSAFE_PUB_KEY*)pTmpUser->ContInfo.pbExchPub;
        pbMod = (BYTE*)pPubKey + sizeof(BSAFE_PUB_KEY);

        dwSts = SPQueryCFLevel(pCertContext, pbMod, pPubKey->bitlen / 8,
                               pPubKey->pubexp,
                               (LPDWORD)&pTmpUser->dwSGCFlags);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
    }

    if (0 == pTmpUser->dwSGCFlags)
    {
        dwReturn = (DWORD)NTE_FAIL;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}
#endif


/*
 -  CPSetProvParam
 -
 *  Purpose:
 *                Allows applications to customize various aspects of the
 *                operations of a provider
 *
 *  Parameters:
 *               IN      hUID    -  Handle to a CSP
 *               IN      dwParam -  Parameter number
 *               IN      pbData  -  Pointer to data
 *               IN      dwFlags -  Flags values
 *
 *  Returns:
 */

BOOL WINAPI
CPSetProvParam(
    IN HCRYPTPROV hUID,
    IN DWORD dwParam,
    IN CONST BYTE *pbData,
    IN DWORD dwFlags)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList   pTmpUser;
    HCRYPTKEY       hKey = 0;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    switch (dwParam)
    {
    case PP_KEYSET_SEC_DESCR:
        if (0 != (dwFlags & ~(OWNER_SECURITY_INFORMATION
                              | GROUP_SECURITY_INFORMATION
                              | DACL_SECURITY_INFORMATION
                              | SACL_SECURITY_INFORMATION)))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (!(dwFlags & OWNER_SECURITY_INFORMATION) &&
            !(dwFlags & GROUP_SECURITY_INFORMATION) &&
            !(dwFlags & DACL_SECURITY_INFORMATION) &&
            !(dwFlags & SACL_SECURITY_INFORMATION))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        // set the security descriptor for the hKey of the keyset
        dwSts = SetSecurityOnContainer(pTmpUser->ContInfo.rgwszFileName,
                                       pTmpUser->dwProvType,
                                       pTmpUser->Rights & CRYPT_MACHINE_KEYSET,
                                       (SECURITY_INFORMATION)dwFlags,
                                       (PSECURITY_DESCRIPTOR)pbData);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case PP_KEY_TYPE_SUBTYPE:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }
        break;

    case PP_UI_PROMPT:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pTmpUser->pPStore)
        {
            dwSts = SetUIPrompt(pTmpUser, (LPWSTR)pbData);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }
        else
        {
            dwReturn = (DWORD)NTE_BAD_TYPE;
            goto ErrorExit;
        }
        break;

    case PP_DELETEKEY:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        // check if it is a verify context
        if (pTmpUser->Rights & CRYPT_VERIFYCONTEXT)
        {
            dwReturn = (DWORD)NTE_BAD_UID;
            goto ErrorExit;
        }

        // check if the keys exists
        if (!CPGetUserKey(hUID, *((DWORD*)pbData), &hKey))
        {
            dwReturn = GetLastError();  // (DWORD)NTE_NO_KEY
            goto ErrorExit;
        }

        // destroy the key handle right away
        if (!CPDestroyKey(hUID, hKey))
        {
            dwReturn = GetLastError();
            goto ErrorExit;
        }

        // delete the key
        dwSts = DeletePersistedKey(pTmpUser, *(DWORD*)pbData);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case PP_SGC_INFO:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        // check if it is an SChannel provider
        if (PROV_RSA_SCHANNEL != pTmpUser->dwProvType)
        {
            dwReturn = (DWORD)NTE_BAD_TYPE;
            goto ErrorExit;
        }

#ifdef USE_SGC
        // check if the SGC Info (cert) is good
        dwSts = SetSGCInfo(pTmpUser, pbData);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;   // (DWORD)NTE_FAIL
            goto ErrorExit;
        }
#endif
        break;

#ifdef USE_HW_RNG
#ifdef _M_IX86
    case PP_USE_HARDWARE_RNG:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        dwSts = GetRNGDriverHandle(&(pTmpUser->hRNGDriver));
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;
#endif // _M_IX86
#endif // USE_HW_RNG

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*
 -  CPGetProvParam
 -
 *  Purpose:
 *                Allows applications to get various aspects of the
 *                operations of a provider
 *
 *  Parameters:
 *               IN      hUID       -  Handle to a CSP
 *               IN      dwParam    -  Parameter number
 *               IN      pbData     -  Pointer to data
 *               IN      pdwDataLen -  Length of parameter data
 *               IN      dwFlags    -  Flags values
 *
 *  Returns:
 */

BOOL WINAPI
CPGetProvParam(
    IN HCRYPTPROV hUID,
    IN DWORD dwParam,
    IN BYTE *pbData,
    IN DWORD *pwDataLen,
    IN DWORD dwFlags)
{
    DWORD             dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGUserList     pTmpUser;
    PROV_ENUMALGS    *pEnum = NULL;
    PROV_ENUMALGS_EX *pEnumEx = NULL;
    DWORD             cbName = 0;
    LPSTR             pszName;
    DWORD             cbTmpData;
    BOOL              fRet;
    DWORD             dwSts;

    EntryPoint
    pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
    if (NULL == pTmpUser)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    if (pwDataLen == NULL)
    {
        dwReturn = ERROR_INVALID_PARAMETER;
        goto ErrorExit;
    }

    switch (dwParam)
    {
    case PP_ENUMALGS:
        if (PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
        {
            if ((dwFlags & ~(CRYPT_FIRST | CRYPT_NEXT | CRYPT_SGC_ENUM)) != 0)
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }
        else
        {
            if ((dwFlags & ~(CRYPT_FIRST | CRYPT_NEXT)) != 0)
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }

        pEnumEx = g_AlgTables[pTmpUser->dwCspTypeId];

        if (dwFlags & CRYPT_FIRST)
            pTmpUser->dwEnumalgs = 0;
        else if (0xFFFFFFFF == pTmpUser->dwEnumalgs)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pEnumEx[pTmpUser->dwEnumalgs].aiAlgid == 0)
        {
            dwReturn = ERROR_NO_MORE_ITEMS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(PROV_ENUMALGS))
        {
            *pwDataLen = sizeof(PROV_ENUMALGS);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        // each entry in ENUMALGS is of fixed size
        pEnum = (PROV_ENUMALGS *)pbData;
        pEnum->aiAlgid = pEnumEx[pTmpUser->dwEnumalgs].aiAlgid;
        pEnum->dwBitLen = pEnumEx[pTmpUser->dwEnumalgs].dwDefaultLen;
        pEnum->dwNameLen = pEnumEx[pTmpUser->dwEnumalgs].dwNameLen;
        memcpy(pEnum->szName, pEnumEx[pTmpUser->dwEnumalgs].szName,
               sizeof(pEnum->szName));
        *pwDataLen = sizeof(PROV_ENUMALGS);

        pTmpUser->dwEnumalgs++;
        break;

    case PP_ENUMALGS_EX:
        if (PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
        {
            if ((dwFlags & ~(CRYPT_FIRST | CRYPT_NEXT | CRYPT_SGC_ENUM)) != 0)
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }
        else
        {
            if ((dwFlags & ~(CRYPT_FIRST | CRYPT_NEXT)) != 0)
            {
                dwReturn = (DWORD)NTE_BAD_FLAGS;
                goto ErrorExit;
            }
        }

        pEnumEx = g_AlgTables[pTmpUser->dwCspTypeId];

        if (dwFlags & CRYPT_FIRST)
            pTmpUser->dwEnumalgsEx = 0;
        else if (0xFFFFFFFF == pTmpUser->dwEnumalgsEx)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pEnumEx[pTmpUser->dwEnumalgsEx].aiAlgid == 0)
        {
            dwReturn = ERROR_NO_MORE_ITEMS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(pEnumEx[0]))
        {
            *pwDataLen = sizeof(pEnumEx[0]);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        // each entry in ENUMALGSEX is of fixed size
        memcpy(pbData, &pEnumEx[pTmpUser->dwEnumalgsEx], sizeof(pEnumEx[0]));
        *pwDataLen = sizeof(pEnumEx[0]);

        pTmpUser->dwEnumalgsEx++;
        break;

    case PP_ENUMCONTAINERS:
    {
        BOOL fMachineKeySet = pTmpUser->Rights & CRYPT_MACHINE_KEYSET;

        if ((dwFlags & ~(CRYPT_FIRST | CRYPT_NEXT)) != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (dwFlags & CRYPT_FIRST)
        {
            if (0 != pTmpUser->ContInfo.hFind)
            {
                FindClose(pTmpUser->ContInfo.hFind);
                pTmpUser->ContInfo.hFind = 0;
            }
            FreeEnumOldMachKeyEntries(&pTmpUser->ContInfo);
            FreeEnumRegEntries(&pTmpUser->ContInfo);

            pTmpUser->ContInfo.fCryptFirst = TRUE;
            pTmpUser->ContInfo.fNoMoreFiles = FALSE;
        }
        else if (!pTmpUser->ContInfo.fCryptFirst)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        dwSts = ERROR_SUCCESS;
        if (!pTmpUser->ContInfo.fNoMoreFiles)
        {
            dwSts = GetNextContainer(pTmpUser->dwProvType,
                                     fMachineKeySet,
                                     dwFlags,
                                     (LPSTR)pbData,
                                     pwDataLen,
                                     &pTmpUser->ContInfo.hFind);
        }

        // ?BUGBUG?  This logic needs desperate cleaning!
        if ((ERROR_SUCCESS != dwSts) || pTmpUser->ContInfo.fNoMoreFiles)
        {
            if ((ERROR_SUCCESS != dwSts) && (ERROR_NO_MORE_ITEMS != dwSts))
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }

            pTmpUser->ContInfo.fNoMoreFiles = TRUE;

            if (fMachineKeySet)
            {
                dwSts = EnumOldMachineKeys(pTmpUser->dwProvType,
                                           &pTmpUser->ContInfo);
                if (ERROR_SUCCESS != dwSts)
                {
                    if (ERROR_NO_MORE_ITEMS != dwSts)
                    {
                        dwReturn = dwSts;
                        goto ErrorExit;
                    }
                }
            }
            dwSts = EnumRegKeys(&pTmpUser->ContInfo,
                                fMachineKeySet,
                                pTmpUser->dwProvType,
                                pbData,
                                pwDataLen);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }

            cbTmpData = *pwDataLen;
            if ((!fMachineKeySet)
                || (0 != (dwSts = GetNextEnumedOldMachKeys(&pTmpUser->ContInfo,
                                                           fMachineKeySet,
                                                           pbData,
                                                           &cbTmpData))))
            {
                if (0 != (dwSts = GetNextEnumedRegKeys(&pTmpUser->ContInfo,
                                                       pbData,
                                                       pwDataLen)))
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }
            }
            else
                *pwDataLen = cbTmpData;
        }
        break;
    }

    case PP_IMPTYPE:
        if (0 != dwFlags)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        *((DWORD *) pbData) = CRYPT_IMPL_SOFTWARE;
        break;

    case PP_NAME:
        if (0 != dwFlags)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        ASSERT(NULL != pTmpUser->szProviderName);
        if (NULL != pTmpUser->szProviderName)
            cbName = (lstrlen(pTmpUser->szProviderName) + 1) * sizeof(CHAR);
        else
            cbName = 0;

        if (pbData == NULL || *pwDataLen < cbName)
        {
            *pwDataLen = cbName;
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = cbName;
        memcpy(pbData, pTmpUser->szProviderName, cbName);
        break;

    case PP_VERSION:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        *((DWORD *) pbData) = 0x200;    // ?BUGBUG? Symbolic?
        break;

    case PP_CONTAINER:
        if (0 != dwFlags)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        pszName = pTmpUser->ContInfo.pszUserName;
        if (pbData == NULL || *pwDataLen < (strlen(pszName) + 1))
        {
            *pwDataLen = strlen(pszName) + 1;
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = strlen(pszName) + 1;
        strcpy((LPSTR)pbData, pszName);
        break;

    case PP_UNIQUE_CONTAINER:
        if (0 != dwFlags)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        dwSts = GetUniqueContainerName(&pTmpUser->ContInfo,
                                       pbData, pwDataLen);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case PP_KEYSET_SEC_DESCR:
        if (0 != (dwFlags & ~(OWNER_SECURITY_INFORMATION
                              | GROUP_SECURITY_INFORMATION
                              | DACL_SECURITY_INFORMATION
                              | SACL_SECURITY_INFORMATION)))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (!(dwFlags & OWNER_SECURITY_INFORMATION) &&
            !(dwFlags & GROUP_SECURITY_INFORMATION) &&
            !(dwFlags & DACL_SECURITY_INFORMATION) &&
            !(dwFlags & SACL_SECURITY_INFORMATION))
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        dwSts = GetSecurityOnContainer(pTmpUser->ContInfo.rgwszFileName,
                                       pTmpUser->dwProvType,
                                       pTmpUser->Rights & CRYPT_MACHINE_KEYSET,
                                       (SECURITY_INFORMATION)dwFlags,
                                       (PSECURITY_DESCRIPTOR)pbData,
                                       pwDataLen);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;

    case PP_KEYSTORAGE:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        if (pTmpUser->pPStore)
            *((DWORD*)pbData) = CRYPT_PSTORE
                                | CRYPT_UI_PROMPT
                                | CRYPT_SEC_DESCR;
        else
            *((DWORD*)pbData) = CRYPT_SEC_DESCR;
        break;

    case PP_PROVTYPE:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        *((DWORD*)pbData) = pTmpUser->dwProvType;
        break;

    case PP_KEYSET_TYPE:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        if (pTmpUser->Rights & CRYPT_MACHINE_KEYSET)
            *(DWORD*)pbData = CRYPT_MACHINE_KEYSET;
        else
            *(DWORD*)pbData = 0;
        break;

    case PP_SIG_KEYSIZE_INC:
    case PP_KEYX_KEYSIZE_INC:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        *((DWORD*)pbData) = RSA_KEYSIZE_INC;
        break;

    case PP_SGC_INFO:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        // check if it is an SChannel provider
        if (PROV_RSA_SCHANNEL != pTmpUser->dwProvType)
        {
            dwReturn = (DWORD)NTE_BAD_TYPE;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        // return the SGC Flags
#ifdef USE_SGC
        *((DWORD*)pbData) = pTmpUser->dwSGCFlags;
#else
        *((DWORD*)pbData) = 0;
#endif
        break;

#ifdef USE_HW_RNG
#ifdef _M_IX86
    case PP_USE_HARDWARE_RNG:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        *pwDataLen = 0;

        // check if the hardware RNG is available for use
        dwSts = CheckIfRNGAvailable();
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }
        break;
#endif // _M_IX86
#endif // USE_HW_RNG

    case PP_KEYSPEC:
        if (dwFlags != 0)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        if (PROV_RSA_SIG == pTmpUser->dwProvType)
            *((DWORD*)pbData) = AT_SIGNATURE;
        else if (PROV_RSA_FULL == pTmpUser->dwProvType)
            *((DWORD*)pbData) = AT_SIGNATURE | AT_KEYEXCHANGE;
        else if (PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
            *((DWORD*)pbData) = AT_KEYEXCHANGE;
        break;

    case PP_ENUMEX_SIGNING_PROT:
        if (0 != dwFlags)
        {
            dwReturn = (DWORD)NTE_BAD_FLAGS;
            goto ErrorExit;
        }

        *pwDataLen = 0;
        break;

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*
 -  CPSetHashParam
 -
 *  Purpose:
 *                Allows applications to customize various aspects of the
 *                operations of a hash
 *
 *  Parameters:
 *               IN      hUID    -  Handle to a CSP
 *               IN      hHash   -  Handle to a hash
 *               IN      dwParam -  Parameter number
 *               IN      pbData  -  Pointer to data
 *               IN      dwFlags -  Flags values
 *
 *  Returns:
 */

BOOL WINAPI
CPSetHashParam(
    IN HCRYPTPROV hUID,
    IN HCRYPTHASH hHash,
    IN DWORD dwParam,
    IN CONST BYTE *pbData,
    IN DWORD dwFlags)
{
    DWORD               dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGHashList       pTmpHash;
    PNTAGKeyList        pTmpKey;
    MD4_object          *pMD4Hash;
    MD5_object          *pMD5Hash;
    A_SHA_CTX           *pSHAHash;
    MACstate            *pMAC;
    PHMAC_INFO          pHMACInfo;
    BYTE                *pb;
    BOOL                fRet;
    DWORD               dwSts;

    EntryPoint
    if (dwFlags != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    // check the user identification
    if (NTLCheckList((HNTAG)hUID, USER_HANDLE) == NULL)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    dwSts = NTLValidate(hHash, hUID, HASH_HANDLE, &pTmpHash);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_HASH : dwSts;
        goto ErrorExit;
    }

    switch (dwParam)
    {
    case HP_HASHVAL:
        switch (pTmpHash->Algid)
        {
#ifdef CSP_USE_MD2
        case CALG_MD2:
        {
            MD2_object      *pMD2Hash;

            pMD2Hash = (MD2_object *) pTmpHash->pHashData;
            if (pMD2Hash->FinishFlag == TRUE)
            {
                dwReturn = (DWORD)NTE_BAD_HASH_STATE;
                goto ErrorExit;
            }

            memcpy(&pMD2Hash->MD.state, pbData, MD2DIGESTLEN);
            break;
        }
#endif
#ifdef CSP_USE_MD4
        case CALG_MD4:
            pMD4Hash = (MD4_object *) pTmpHash->pHashData;
            if (pMD4Hash->FinishFlag == TRUE)
            {
                dwReturn = (DWORD)NTE_BAD_HASH_STATE;
                goto ErrorExit;
            }

            memcpy (&pMD4Hash->MD, pbData, MD4DIGESTLEN);
            break;
#endif
#ifdef CSP_USE_MD5
        case CALG_MD5:
            pMD5Hash = (MD5_object *) pTmpHash->pHashData;
            if (pMD5Hash->FinishFlag == TRUE)
            {
                dwReturn = (DWORD)NTE_BAD_HASH_STATE;
                goto ErrorExit;
            }

            memcpy (pMD5Hash->digest, pbData, MD5DIGESTLEN);
            break;
#endif
#ifdef CSP_USE_SHA
        case CALG_SHA:
            pSHAHash = (A_SHA_CTX *) pTmpHash->pHashData;
            if (pSHAHash->FinishFlag == TRUE)
            {
                dwReturn = (DWORD)NTE_BAD_HASH_STATE;
                goto ErrorExit;
            }

            memcpy (pSHAHash->HashVal, pbData, A_SHA_DIGEST_LEN);
            break;
#endif
#ifdef CSP_USE_SSL3SHAMD5
        case CALG_SSL3_SHAMD5:
            memcpy (pTmpHash->pHashData, pbData, SSL3_SHAMD5_LEN);
            break;
#endif
#ifdef CSP_USE_MAC
        case CALG_MAC:
            pMAC = (MACstate *)pTmpHash->pHashData;
            dwSts = NTLValidate(pTmpHash->hKey, hUID, KEY_HANDLE, &pTmpKey);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
                goto ErrorExit;
            }

            if (pMAC->FinishFlag == TRUE)
            {
                dwReturn = (DWORD)NTE_BAD_HASH_STATE;
                goto ErrorExit;
            }

            memcpy(pTmpKey->FeedBack, pbData, pTmpKey->dwBlockLen);
            break;
#endif
        default:
            dwReturn = (DWORD)NTE_BAD_ALGID;
            goto ErrorExit;
        }
        pTmpHash->dwHashState |= DATA_IN_HASH;
        break;

    case HP_HMAC_INFO:
        if (CALG_HMAC != pTmpHash->Algid)
        {
            dwReturn = (DWORD)NTE_BAD_TYPE;
            goto ErrorExit;
        }

        pHMACInfo = (PHMAC_INFO)pbData;
        pTmpHash->HMACAlgid = pHMACInfo->HashAlgid;

        // now that we know the type of hash we can create it
        dwSts = LocalCreateHash(pTmpHash->HMACAlgid,
                                (BYTE**)&pTmpHash->pHashData,
                                &pTmpHash->dwDataLen);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        // if the length of the inner string is 0 then use the default string
        if (0 == pHMACInfo->cbInnerString)
            pTmpHash->cbHMACInner = HMAC_DEFAULT_STRING_LEN;
        else
            pTmpHash->cbHMACInner = pHMACInfo->cbInnerString;

        pb = _nt_malloc(pTmpHash->cbHMACInner);
        if (NULL == pb)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        if (0 == pHMACInfo->cbInnerString)
            memset(pb, 0x36, pTmpHash->cbHMACInner);
        else
            memcpy(pb, pHMACInfo->pbInnerString, pTmpHash->cbHMACInner);

        if (pTmpHash->pbHMACInner)
            _nt_free(pTmpHash->pbHMACInner, pTmpHash->cbHMACInner);
        pTmpHash->pbHMACInner = pb;

        // if the length of the outer string is 0 then use the default string
        if (0 == pHMACInfo->cbOuterString)
            pTmpHash->cbHMACOuter = HMAC_DEFAULT_STRING_LEN;
        else
            pTmpHash->cbHMACOuter = pHMACInfo->cbOuterString;

        pb = _nt_malloc(pTmpHash->cbHMACOuter);
        if (NULL == pb)
        {
            _nt_free(pTmpHash->pbHMACInner, pTmpHash->cbHMACInner);
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        if (0 == pHMACInfo->cbOuterString)
            memset(pb, 0x5C, pTmpHash->cbHMACOuter);
        else
            memcpy(pb, pHMACInfo->pbOuterString, pTmpHash->cbHMACOuter);
        if (pTmpHash->pbHMACOuter)
            _nt_free(pTmpHash->pbHMACOuter, pTmpHash->cbHMACOuter);
        pTmpHash->pbHMACOuter = pb;
        break;

#ifdef CSP_USE_SSL3
    case HP_TLS1PRF_LABEL:
    case HP_TLS1PRF_SEED:
    {
        if (CALG_TLS1PRF != pTmpHash->Algid)
        {
            dwReturn = (DWORD)NTE_BAD_HASH;
            goto ErrorExit;
        }

        dwSts = SetPRFHashParam((PRF_HASH*)pTmpHash->pHashData,
                                dwParam, pbData);
        if (ERROR_SUCCESS != dwSts)
        {
            dwReturn = dwSts;
            goto ErrorExit;
        }

        pTmpHash->dwHashState |= DATA_IN_HASH;
        break;
    }
#endif

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    if (dwParam == HP_HASHVAL)
        pTmpHash->HashFlags |= HF_VALUE_SET;

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*static*/ DWORD
LocalGetHashVal(
    IN ALG_ID Algid,
    IN DWORD dwHashFlags,
    IN OUT BYTE *pbHashData,
    OUT BYTE *pbHashVal,
    OUT DWORD *pcbHashVal)
{
    DWORD       dwReturn = ERROR_INTERNAL_ERROR;
    MD2_object  *pMD2Hash;
    MD4_object  *pMD4Hash;
    MD5_object  *pMD5Hash;
    A_SHA_CTX   *pSHAHash;

    switch (Algid)
    {
#ifdef CSP_USE_MD2
    case CALG_MD2:
        // make sure there's enough room.
        if (pbHashVal == NULL || *pcbHashVal < MD2DIGESTLEN)
        {
            *pcbHashVal = MD2DIGESTLEN;
            dwReturn = (pbHashVal == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pMD2Hash = (MD2_object *)pbHashData;
        if ((dwHashFlags & HF_VALUE_SET) == 0)
        {
            if (pMD2Hash->FinishFlag == TRUE)
            {
                *pcbHashVal = MD2DIGESTLEN;
                memcpy(pbHashVal, pMD2Hash->MD.state, MD2DIGESTLEN);
                break;
            }

            // set the finish flag on the hash and
            // process what's left in the buffer.
            pMD2Hash->FinishFlag = TRUE;

            // Finish offthe hash
            MD2Final(&pMD2Hash->MD);
        }

        *pcbHashVal = MD2DIGESTLEN;
        memcpy (pbHashVal, pMD2Hash->MD.state, MD2DIGESTLEN);
        break;
#endif

#ifdef CSP_USE_MD4
    case CALG_MD4:
        // make sure there's enough room.
        if (pbHashVal == NULL || *pcbHashVal < MD4DIGESTLEN)
        {
            *pcbHashVal = MD4DIGESTLEN;
            dwReturn = (pbHashVal == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pMD4Hash = (MD4_object *)pbHashData;
        if ((dwHashFlags & HF_VALUE_SET) == 0)
        {
            if (pMD4Hash->FinishFlag == TRUE)
            {
                *pcbHashVal = MD4DIGESTLEN;
                memcpy(pbHashVal, &pMD4Hash->MD, *pcbHashVal);
                break;
            }

            // set the finish flag on the hash and
            // process what's left in the buffer.
            pMD4Hash->FinishFlag = TRUE;
            if (MD4_SUCCESS != MDupdate(&pMD4Hash->MD, pMD4Hash->Buf,
                                        MD4BYTESTOBITS(pMD4Hash->BufLen)))
            {
                dwReturn = (DWORD)NTE_FAIL;
                goto ErrorExit;
            }
        }

        *pcbHashVal = MD4DIGESTLEN;
        memcpy(pbHashVal, &pMD4Hash->MD, *pcbHashVal);
        break;
#endif

#ifdef CSP_USE_MD5
    case CALG_MD5:
        // make sure there's enough room.
        if (pbHashVal == NULL || *pcbHashVal < MD5DIGESTLEN)
        {
            *pcbHashVal = MD5DIGESTLEN;
            dwReturn = (pbHashVal == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pMD5Hash = (MD5_object *)pbHashData;
        if ((dwHashFlags & HF_VALUE_SET) == 0)
        {
            if (pMD5Hash->FinishFlag == TRUE)
            {
                *pcbHashVal = MD5DIGESTLEN;
                memcpy (pbHashVal, pMD5Hash->digest, MD5DIGESTLEN);
                break;
            }

            // set the finish flag on the hash and
            // process what's left in the buffer.
            pMD5Hash->FinishFlag = TRUE;

            // Finish offthe hash
            MD5Final(pMD5Hash);
        }

        *pcbHashVal = MD5DIGESTLEN;
        memcpy (pbHashVal, pMD5Hash->digest, MD5DIGESTLEN);
        break;
#endif

#ifdef CSP_USE_SHA
    case CALG_SHA:
        // make sure there's enough room.
        if (pbHashVal == NULL || *pcbHashVal < A_SHA_DIGEST_LEN)
        {
            *pcbHashVal = A_SHA_DIGEST_LEN;
            dwReturn = (pbHashVal == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        pSHAHash = (A_SHA_CTX *)pbHashData;
        if ((dwHashFlags & HF_VALUE_SET) == 0)
        {
            if (pSHAHash->FinishFlag == TRUE)
            {
                *pcbHashVal = A_SHA_DIGEST_LEN;
                memcpy (pbHashVal, pSHAHash->HashVal, A_SHA_DIGEST_LEN);
                break;
            }

            // set the finish flag on the hash and
            // process what's left in the buffer.
            pSHAHash->FinishFlag = TRUE;

            // Finish off the hash
            A_SHAFinal(pSHAHash, pSHAHash->HashVal);
        }

        *pcbHashVal = A_SHA_DIGEST_LEN;
        memcpy (pbHashVal, pSHAHash->HashVal, A_SHA_DIGEST_LEN);
        break;
#endif

    default:
        dwReturn = (DWORD)NTE_BAD_ALGID;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}


/*static*/ DWORD
GetHashLength(
    IN ALG_ID Algid)
{
    DWORD cbLen;

    switch (Algid)
    {
#ifdef CSP_USE_MD2
    case CALG_MD2:
        cbLen = MD2DIGESTLEN;
        break;
#endif
#ifdef CSP_USE_MD4
    case CALG_MD4:
        cbLen = MD4DIGESTLEN;
        break;
#endif
#ifdef CSP_USE_MD5
    case CALG_MD5:
        cbLen = MD5DIGESTLEN;
        break;
#endif
#ifdef CSP_USE_SHA
    case CALG_SHA:
        cbLen = A_SHA_DIGEST_LEN;
        break;
#endif
    default:
        ASSERT(FALSE);
        cbLen = 0;
    }

    return cbLen;
}


/*
 -  CPGetHashParam
 -
 *  Purpose:
 *                Allows applications to get various aspects of the
 *                operations of a key
 *
 *  Parameters:
 *               IN      hUID       -  Handle to a CSP
 *               IN      hHash      -  Handle to a hash
 *               IN      dwParam    -  Parameter number
 *               IN      pbData     -  Pointer to data
 *               IN      pdwDataLen -  Length of parameter data
 *               IN      dwFlags    -  Flags values
 *
 *  Returns:
 */

BOOL WINAPI
CPGetHashParam(
    IN HCRYPTPROV hUID,
    IN HCRYPTHASH hHash,
    IN DWORD dwParam,
    IN BYTE *pbData,
    IN DWORD *pwDataLen,
    IN DWORD dwFlags)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGHashList   pTmpHash;
    MACstate        *pMAC;
    BYTE            MACbuf[2*MAX_BLOCKLEN];
    PNTAGKeyList    pTmpKey;
    BYTE            rgbFinalHash[A_SHA_DIGEST_LEN];
    DWORD           cbFinalHash;
    DWORD           cb;
    BYTE            *pb = NULL;
    DWORD           cbHashData;
    BYTE            *pbHashData = NULL;
    DWORD           i;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    if (dwFlags != 0)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    // check the user identification
    if (NTLCheckList ((HNTAG)hUID, USER_HANDLE) == NULL)
    {
        dwReturn = (DWORD)NTE_BAD_UID;
        goto ErrorExit;
    }

    if (pwDataLen == NULL)
    {
        dwReturn = ERROR_INVALID_PARAMETER;
        goto ErrorExit;
    }

    dwSts = NTLValidate(hHash, hUID, HASH_HANDLE, &pTmpHash);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_HASH : dwSts;
        goto ErrorExit;
    }

    switch (dwParam)
    {
    case HP_ALGID:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        *((DWORD *) pbData) = pTmpHash->Algid;
        *pwDataLen = sizeof(DWORD);
        break;

    case HP_HASHSIZE:
        if (pbData == NULL || *pwDataLen < sizeof(DWORD))
        {
            *pwDataLen = sizeof(DWORD);
            dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
            goto ErrorExit;
        }

        switch (pTmpHash->Algid)
        {
        case CALG_MD2:
        case CALG_MD4:
        case CALG_MD5:
        case CALG_SHA:
            *(DWORD *)pbData = GetHashLength(pTmpHash->Algid);
            break;
#ifdef CSP_USE_MAC
        case CALG_MAC:
            *(DWORD *)pbData = MAX_BLOCKLEN;
            break;
#endif // CSP_USE_MAC
        case CALG_HMAC:
            *(DWORD *)pbData = GetHashLength(pTmpHash->HMACAlgid);
            break;
#ifdef CSP_USE_SSL3SHAMD5
        case CALG_SSL3_SHAMD5:
            *((DWORD *) pbData) = SSL3_SHAMD5_LEN;
            break;
#endif
        default:
            dwReturn = (DWORD)NTE_BAD_ALGID;
            goto ErrorExit;
        }

        *pwDataLen = sizeof(DWORD);
        break;

    case HP_HASHVAL:
        switch (pTmpHash->Algid)
        {
#ifdef CSP_USE_SSL3SHAMD5
        case CALG_SSL3_SHAMD5:
            // make sure there's enough room.
            if (pbData == NULL || *pwDataLen < SSL3_SHAMD5_LEN)
            {
                *pwDataLen = SSL3_SHAMD5_LEN;
                dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
                goto ErrorExit;
            }

            // Hash value must have already been set.
            if ((pTmpHash->HashFlags & HF_VALUE_SET) == 0)
            {
                dwReturn = (DWORD)NTE_BAD_HASH_STATE;
                goto ErrorExit;
            }

            *pwDataLen = SSL3_SHAMD5_LEN;
            memcpy (pbData, pTmpHash->pHashData, SSL3_SHAMD5_LEN);
            break;
#endif
#ifdef CSP_USE_MAC
        case CALG_MAC:
            pMAC = (MACstate *)pTmpHash->pHashData;
            dwSts = NTLValidate(pTmpHash->hKey, hUID, KEY_HANDLE, &pTmpKey);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
                goto ErrorExit;
            }

            // make sure there is enough room.
            if (pbData == NULL || (*pwDataLen < pTmpKey->dwBlockLen))
            {
                *pwDataLen = pTmpKey->dwBlockLen;
                dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
                goto ErrorExit;
            }

            if (pMAC->FinishFlag == TRUE)
            {
                *pwDataLen = pTmpKey->dwBlockLen;
                memcpy(pbData, pTmpKey->FeedBack, pTmpKey->dwBlockLen);
                break;
            }

            // set the finish flag on the hash and
            // process what's left in the buffer.
            pMAC->FinishFlag = TRUE;

            if (pMAC->dwBufLen)
            {
                memset(MACbuf, 0, 2*MAX_BLOCKLEN);
                memcpy(MACbuf, pMAC->Buffer, pMAC->dwBufLen);

                switch (pTmpKey->Algid)
                {
                case CALG_RC2:
                    dwSts = BlockEncrypt(RC2, pTmpKey, RC2_BLOCKLEN, TRUE,
                                         MACbuf, &pMAC->dwBufLen,
                                         2*MAX_BLOCKLEN);
                    if (ERROR_SUCCESS != dwSts)
                    {
                        dwReturn = dwSts;
                        goto ErrorExit;
                    }
                    break;

                case CALG_DES:
                    dwSts = BlockEncrypt(des, pTmpKey, DES_BLOCKLEN, TRUE,
                                         MACbuf, &pMAC->dwBufLen,
                                         2*MAX_BLOCKLEN);
                    if (ERROR_SUCCESS != dwSts)
                    {
                        dwReturn = dwSts;
                        goto ErrorExit;
                    }
                    break;
#ifdef CSP_USE_3DES
                case CALG_3DES_112:
                case CALG_3DES:
                    dwSts = BlockEncrypt(tripledes, pTmpKey, DES_BLOCKLEN,
                                         TRUE, MACbuf, &pMAC->dwBufLen,
                                         2*MAX_BLOCKLEN);
                    if (ERROR_SUCCESS != dwSts)
                    {
                        dwReturn = dwSts;
                        goto ErrorExit;
                    }
                    break;
#endif
#ifdef CSP_USE_AES
                case CALG_AES_128:
                case CALG_AES_192:
                case CALG_AES_256:
                    dwSts = BlockEncrypt(aes, pTmpKey, pTmpKey->dwBlockLen,
                                         TRUE, MACbuf, &pMAC->dwBufLen,
                                         2*MAX_BLOCKLEN);
                    if (ERROR_SUCCESS != dwSts)
                    {
                        dwReturn = dwSts;
                        goto ErrorExit;
                    }
                    break;
#endif
                // default: It's not a block cipher.
                }
            }

            *pwDataLen = pTmpKey->dwBlockLen;
            memcpy(pbData, pTmpKey->FeedBack, pTmpKey->dwBlockLen);
            break;
#endif
        case CALG_HMAC:
            if (!(pTmpHash->HMACState & HMAC_FINISHED))
            {
                cbFinalHash = sizeof(rgbFinalHash);
                dwSts = LocalGetHashVal(pTmpHash->HMACAlgid,
                                        pTmpHash->HashFlags,
                                        pTmpHash->pHashData,
                                        rgbFinalHash,
                                        &cbFinalHash);
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }

                // now XOR the outer string with the key and hash
                // over this and the inner hash
                dwSts = NTLValidate(pTmpHash->hKey, hUID,
                                    KEY_HANDLE, &pTmpKey);
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = (dwSts == NTE_FAIL)
                               ? (DWORD)NTE_BAD_KEY
                               : dwSts;
                    goto ErrorExit;
                }

                if (pTmpKey->cbKeyLen < pTmpHash->cbHMACOuter)
                    cb = pTmpHash->cbHMACOuter;
                else
                    cb = pTmpKey->cbKeyLen;

                pb = (BYTE *)_nt_malloc(cb);
                if (NULL == pb)
                {
                    dwReturn = ERROR_NOT_ENOUGH_MEMORY;
                    goto ErrorExit;
                }

                memcpy(pb, pTmpHash->pbHMACOuter, pTmpHash->cbHMACOuter);

                // currently no support for byte reversed keys with HMAC
                for (i=0;i<pTmpKey->cbKeyLen;i++)
                    pb[i] ^= (pTmpKey->pKeyValue)[i];

                dwSts = LocalCreateHash(pTmpHash->HMACAlgid,
                                        &pbHashData,
                                        &cbHashData);
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }

                dwSts = LocalHashData(pTmpHash->HMACAlgid, pbHashData,
                                      pb, cb);
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }

                dwSts = LocalHashData(pTmpHash->HMACAlgid, pbHashData,
                                      rgbFinalHash, cbFinalHash);
                if (ERROR_SUCCESS != dwSts)
                {
                    dwReturn = dwSts;
                    goto ErrorExit;
                }

                _nt_free(pTmpHash->pHashData, pTmpHash->dwDataLen);
                pTmpHash->dwDataLen = cbHashData;
                pTmpHash->pHashData = pbHashData;
                pbHashData = NULL;
                pTmpHash->HMACState |= HMAC_FINISHED;
            }

            dwSts = LocalGetHashVal(pTmpHash->HMACAlgid, pTmpHash->HashFlags,
                                    pTmpHash->pHashData, pbData, pwDataLen);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
            break;
#ifdef CSP_USE_SSL3
        case CALG_TLS1PRF:
        {
            dwSts = CalculatePRF((PRF_HASH*)pTmpHash->pHashData,
                                 pbData, pwDataLen);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
            break;
        }
#endif
        default:
            dwSts = LocalGetHashVal(pTmpHash->Algid, pTmpHash->HashFlags,
                                    pTmpHash->pHashData, pbData, pwDataLen);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = dwSts;
                goto ErrorExit;
            }
        }
        break;

    default:
        dwReturn = (DWORD)NTE_BAD_TYPE;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (pb)
        _nt_free(pb, cb);
    if (pbHashData)
        _nt_free(pbHashData, cbHashData);
    if (!fRet)
        SetLastError(dwReturn);
    return fRet;
}


/*static*/ DWORD
CopyKey(
    IN PNTAGKeyList pOldKey,
    OUT PNTAGKeyList *ppNewKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGKeyList    pNewKey;

    pNewKey = (PNTAGKeyList)_nt_malloc(sizeof(NTAGKeyList));
    if (NULL == pNewKey)
    {
        dwReturn = ERROR_NOT_ENOUGH_MEMORY;
        goto ErrorExit;
    }

    memcpy(pNewKey, pOldKey, sizeof(NTAGKeyList));
    pNewKey->Rights &= ~CRYPT_ARCHIVABLE;
    pNewKey->Permissions &= ~CRYPT_ARCHIVE;
    pNewKey->pKeyValue = NULL;
    pNewKey->cbDataLen = 0;
    pNewKey->pData = NULL;
    pNewKey->cbSaltLen = 0;

    pNewKey->cbKeyLen = pOldKey->cbKeyLen;
    if (pNewKey->cbKeyLen)
    {
        pNewKey->pKeyValue = (BYTE*)_nt_malloc(pNewKey->cbKeyLen);
        if (NULL == pNewKey->pKeyValue)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }
    }

    memcpy(pNewKey->pKeyValue, pOldKey->pKeyValue, pNewKey->cbKeyLen);
    pNewKey->cbDataLen = pOldKey->cbDataLen;
    if (pNewKey->cbDataLen)
    {
        pNewKey->pData = (BYTE*)_nt_malloc(pNewKey->cbDataLen);
        if (NULL == pNewKey->pData)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }
    }

    memcpy(pNewKey->pData, pOldKey->pData, pNewKey->cbDataLen);
    if (pOldKey->Algid == CALG_PCT1_MASTER)
    {
        // This is a PCT master key, and so it might have some certificate
        // data attached to it. If this is the case, then make a copy
        // of the certificate data and attach it to the new key.
        if (pOldKey->cbDataLen == sizeof(SCH_KEY))
        {
            PSCH_KEY pOldSChKey = (PSCH_KEY)pOldKey->pData;
            PSCH_KEY pNewSChKey = (PSCH_KEY)pNewKey->pData;

            if (pOldSChKey->pbCertData && pOldSChKey->cbCertData)
            {
                pNewSChKey->pbCertData = (BYTE*)_nt_malloc(pOldSChKey->cbCertData);
                if (NULL == pNewSChKey->pbCertData)
                {
                    dwReturn = ERROR_NOT_ENOUGH_MEMORY;
                    goto ErrorExit;
                }

                memcpy(pNewSChKey->pbCertData,
                       pOldSChKey->pbCertData,
                       pOldSChKey->cbCertData);
            }
        }
    }

    pNewKey->cbSaltLen = pOldKey->cbSaltLen;
    memcpy(pNewKey->rgbSalt, pOldKey->rgbSalt, pNewKey->cbSaltLen);

    *ppNewKey = pNewKey;
    pNewKey = NULL;
    dwReturn = ERROR_SUCCESS;

ErrorExit:
    if (NULL != pNewKey)
        FreeNewKey(pNewKey);
    return dwReturn;
}


/*
 -  CPDuplicateKey
 -
 *  Purpose:
 *                Duplicates the state of a key and returns a handle to it
 *
 *  Parameters:
 *               IN      hUID           -  Handle to a CSP
 *               IN      hKey           -  Handle to a key
 *               IN      pdwReserved    -  Reserved
 *               IN      dwFlags        -  Flags
 *               IN      phKey          -  Handle to the new key
 *
 *  Returns:
 */

BOOL WINAPI
CPDuplicateKey(
    IN HCRYPTPROV hUID,
    IN HCRYPTKEY hKey,
    IN DWORD *pdwReserved,
    IN DWORD dwFlags,
    IN HCRYPTKEY *phKey)
{
    DWORD           dwReturn = ERROR_INTERNAL_ERROR;
    PNTAGKeyList    pTmpKey;
    PNTAGKeyList    pNewKey = NULL;
    BYTE            bType = KEY_HANDLE;
    BOOL            fRet;
    DWORD           dwSts;

    EntryPoint
    if (NULL != pdwReserved)
    {
        dwReturn = ERROR_INVALID_PARAMETER;
        goto ErrorExit;
    }

    if (0 != dwFlags)
    {
        dwReturn = (DWORD)NTE_BAD_FLAGS;
        goto ErrorExit;
    }

    dwSts = NTLValidate((HNTAG)hKey, hUID, bType, &pTmpKey);
    if (ERROR_SUCCESS != dwSts)
    {
        bType = SIGPUBKEY_HANDLE;
        dwSts = NTLValidate((HNTAG)hKey, hUID, bType, &pTmpKey);
        if (ERROR_SUCCESS != dwSts)
        {
            bType = EXCHPUBKEY_HANDLE;
            dwSts = NTLValidate((HNTAG)hKey, hUID, bType, &pTmpKey);
            if (ERROR_SUCCESS != dwSts)
            {
                dwReturn = (NTE_FAIL == dwSts)
                           ? (DWORD)NTE_BAD_KEY
                           : dwSts;
                goto ErrorExit;
            }
        }
    }

    dwSts = CopyKey(pTmpKey, &pNewKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }


    dwSts = NTLMakeItem(phKey, bType, (void *)pNewKey);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    fRet = (ERROR_SUCCESS == dwReturn);
    if (!fRet)
    {
        if (NULL != pNewKey)
            FreeNewKey(pNewKey);
        SetLastError(dwReturn);
    }
    return fRet;
}


//
// Function : TestEncDec
//
// Description : This function expands the passed in key buffer for the
//               appropriate algorithm, and then either encryption or
//               decryption is performed.  A comparison is then made to see
//               if the ciphertext or plaintext matches the expected value.
//               The function only uses ECB mode for block ciphers and the
//               plaintext buffer must be the same length as the ciphertext
//               buffer.  The length of the plaintext must be either the
//               block length of the cipher if it is a block cipher or less
//               than MAX_BLOCKLEN if a stream cipher is being used.
//

/*static*/ DWORD
TestEncDec(
    IN ALG_ID Algid,
    IN BYTE *pbKey,
    IN DWORD cbKey,
    IN BYTE *pbPlaintext,
    IN DWORD cbPlaintext,
    IN BYTE *pbCiphertext,
    IN BYTE *pbIV,
    IN int iOperation)
{
    DWORD   dwReturn = ERROR_INTERNAL_ERROR;
    BYTE    *pbExpandedKey = NULL;
    BYTE    rgbBuffIn[MAX_BLOCKLEN];
    BYTE    rgbBuffOut[MAX_BLOCKLEN];
    DWORD   i;

    memset(rgbBuffIn, 0, sizeof(rgbBuffIn));
    memset(rgbBuffOut, 0, sizeof(rgbBuffOut));

    // length of data to encrypt must be < MAX_BLOCKLEN
    if (cbPlaintext > MAX_BLOCKLEN)
    {
        dwReturn = (DWORD)NTE_BAD_LEN;
        goto ErrorExit;
    }

    // alloc for and expand the key
    switch (Algid)
    {
#ifdef CSP_USE_RC4
    case (CALG_RC4):
        pbExpandedKey = _nt_malloc(sizeof(RC4_KEYSTRUCT));
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        rc4_key((RC4_KEYSTRUCT*)pbExpandedKey, cbKey, pbKey);
        break;
#endif // CSP_USE_RC4

#ifdef CSP_USE_RC2
    case (CALG_RC2):
        pbExpandedKey = _nt_malloc(RC2_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        RC2KeyEx((WORD*)pbExpandedKey, pbKey, cbKey, cbKey * 8);
        break;
#endif // CSP_USE_RC2

#ifdef CSP_USE_DES40
    case (CALG_DES40):
        pbExpandedKey = _nt_malloc(DES_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        pbKey[0] &= 0x0F;    // set 4 leftmost bits of first byte to zero
        pbKey[2] &= 0x0F;    // set 4 leftmost bits of third byte to zero
        pbKey[4] &= 0x0F;    // set 4 leftmost bits of fifth byte to zero
        pbKey[6] &= 0x0F;    // set 4 leftmost bits of seventh byte to zero
        desparityonkey(pbKey, cbKey);
        deskey((DESTable*)pbExpandedKey, pbKey);
        break;
#endif // CSP_USE_DES40

#ifdef CSP_USE_DES
    case (CALG_DES):
        pbExpandedKey = _nt_malloc(DES_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        desparityonkey(pbKey, cbKey);
        deskey((DESTable*)pbExpandedKey, pbKey);
        break;
#endif // CSP_USE_DES

#ifdef CSP_USE_3DES
    case (CALG_3DES):
        pbExpandedKey = _nt_malloc(DES3_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        desparityonkey(pbKey, cbKey);
        tripledes3key((PDES3TABLE)pbExpandedKey, pbKey);
        break;

    case (CALG_3DES_112):
        pbExpandedKey = _nt_malloc(DES3_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        desparityonkey(pbKey, cbKey);
        tripledes2key((PDES3TABLE)pbExpandedKey, pbKey);
        break;
#endif // CSP_USE_3DES

#ifdef CSP_USE_AES
    case CALG_AES_128:
        pbExpandedKey = _nt_malloc(AES_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        aeskey((AESTable *) pbExpandedKey, pbKey, CRYPT_AES128_ROUNDS);
        break;

    case CALG_AES_192:
        pbExpandedKey = _nt_malloc(AES_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        aeskey((AESTable *) pbExpandedKey, pbKey, CRYPT_AES192_ROUNDS);
        break;

    case CALG_AES_256:
        pbExpandedKey = _nt_malloc(AES_TABLESIZE);
        if (NULL == pbExpandedKey)
        {
            dwReturn = ERROR_NOT_ENOUGH_MEMORY;
            goto ErrorExit;
        }

        aeskey((AESTable *) pbExpandedKey, pbKey, CRYPT_AES256_ROUNDS);
        break;
#endif

    default:
        dwReturn = (DWORD)NTE_BAD_ALGID;
        goto ErrorExit;
    }

    // if encrypting and there is an IV then use it
    if ((ENCRYPT == iOperation) && (CALG_RC4 != Algid))
    {
        memcpy(rgbBuffIn, pbPlaintext, cbPlaintext);

        if (NULL != pbIV)
        {
            for (i = 0; i < cbPlaintext; i++)
                rgbBuffIn[i] = (BYTE)(rgbBuffIn[i] ^ pbIV[i]);
        }
    }

    // encrypt the plaintext
    switch (Algid)
    {
#ifdef CSP_USE_RC4
    case (CALG_RC4):
        if (ENCRYPT == iOperation)
            memcpy(rgbBuffOut, pbPlaintext, cbPlaintext);
        else
            memcpy(rgbBuffOut, pbCiphertext, cbPlaintext);
        rc4((RC4_KEYSTRUCT*)pbExpandedKey, cbPlaintext, rgbBuffOut);
        break;
#endif // CSP_USE_RC4

#ifdef CSP_USE_RC2
    case (CALG_RC2):
        if (ENCRYPT == iOperation)
            RC2(rgbBuffOut, rgbBuffIn, pbExpandedKey, ENCRYPT);
        else
            RC2(rgbBuffOut, pbCiphertext, pbExpandedKey, DECRYPT);
        break;
#endif // CSP_USE_RC2

#ifdef CSP_USE_DES40
    case (CALG_DES40):
        if (ENCRYPT == iOperation)
            des(rgbBuffOut, rgbBuffIn, pbExpandedKey, ENCRYPT);
        else
            des(rgbBuffOut, pbCiphertext, pbExpandedKey, DECRYPT);
        break;
#endif // CSP_USE_DES40

#ifdef CSP_USE_DES
    case (CALG_DES):
        if (ENCRYPT == iOperation)
            des(rgbBuffOut, rgbBuffIn, pbExpandedKey, ENCRYPT);
        else
            des(rgbBuffOut, pbCiphertext, pbExpandedKey, DECRYPT);
        break;
#endif // CSP_USE_DES

#ifdef CSP_USE_3DES
    case (CALG_3DES):
    case (CALG_3DES_112):
        if (ENCRYPT == iOperation)
            tripledes(rgbBuffOut, rgbBuffIn, pbExpandedKey, ENCRYPT);
        else
            tripledes(rgbBuffOut, pbCiphertext, pbExpandedKey, DECRYPT);
        break;
#endif // CSP_USE_3DES

#ifdef CSP_USE_AES
    case CALG_AES_128:
    case CALG_AES_192:
    case CALG_AES_256:
        if (ENCRYPT == iOperation)
            aes(rgbBuffOut, rgbBuffIn, pbExpandedKey, ENCRYPT);
        else
            aes(rgbBuffOut, pbCiphertext, pbExpandedKey, DECRYPT);
        break;
#endif

    default:
        dwReturn = (DWORD)NTE_BAD_ALGID;
        goto ErrorExit;
    }

    if (ENCRYPT == iOperation)
    {
        // compare the encrypted plaintext with the passed in ciphertext
        if (0 != memcmp(pbCiphertext, rgbBuffOut, cbPlaintext))
        {
            dwReturn = (DWORD)NTE_FAIL;
            goto ErrorExit;
        }
    }
    else
    {
        // if there is an IV then use it
        if (NULL != pbIV)
        {
            for (i = 0; i < cbPlaintext; i++)
                rgbBuffOut[i] = (BYTE)(rgbBuffOut[i] ^ pbIV[i]);
        }

        // compare the decrypted ciphertext with the passed in plaintext
        if (0 != memcmp(pbPlaintext, rgbBuffOut, cbPlaintext))
        {
            dwReturn = (DWORD)NTE_FAIL;
            goto ErrorExit;
        }
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    if (pbExpandedKey)
        _nt_free(pbExpandedKey, 0);
    return dwReturn;
}


//
// Function : TestSymmetricAlgorithm
//
// Description : This function expands the passed in key buffer for the
//               appropriate algorithm, encrypts the plaintext buffer with the
//               same algorithm and key, and the compares the passed in
//               expected ciphertext with the calculated ciphertext to make
//               sure they are the same.  The opposite is then done with
//               decryption.
//
//               The function only uses ECB mode for block ciphers and the
//               plaintext buffer must be the same length as the ciphertext
//               buffer.  The length of the plaintext must be either the block
//               length of the cipher if it is a block cipher or less than
//               MAX_BLOCKLEN if a stream cipher is being used.
//

DWORD
TestSymmetricAlgorithm(
    IN ALG_ID Algid,
    IN BYTE *pbKey,
    IN DWORD cbKey,
    IN BYTE *pbPlaintext,
    IN DWORD cbPlaintext,
    IN BYTE *pbCiphertext,
    IN BYTE *pbIV)
{
    DWORD   dwReturn = ERROR_INTERNAL_ERROR;
    DWORD   dwSts;

    dwSts = TestEncDec(Algid, pbKey, cbKey, pbPlaintext, cbPlaintext,
                       pbCiphertext, pbIV, ENCRYPT);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }
    dwSts = TestEncDec(Algid, pbKey, cbKey, pbPlaintext, cbPlaintext,
                       pbCiphertext, pbIV, DECRYPT);
    if (ERROR_SUCCESS != dwSts)
    {
        dwReturn = dwSts;
        goto ErrorExit;
    }

    dwReturn = ERROR_SUCCESS;

ErrorExit:
    return dwReturn;
}