/*++ Copyright (C) Microsoft Corporation, 2001 Module Name: Windows for Smart Cards Base CSP Abstract: Author: Dan Griffin Notes: --*/ #include #include #include #include #include #pragma warning(push) #pragma warning(disable:4201) // Disable error C4201 in public header // nonstandard extension used : nameless struct/union #include #pragma warning(pop) #include #include #include #include "basecsp.h" #include "cardmod.h" #include "datacach.h" #include "pincache.h" #include "pinlib.h" #include "resource.h" #include "debug.h" #include "compress.h" extern DWORD WINAPI Asn1UtilAdjustEncodedLength( IN const BYTE *pbDER, IN DWORD cbDER ); // // Debugging Macros // #define LOG_BEGIN_FUNCTION(x) \ { DebugLog((DEB_TRACE_CSP, "%s: Entering\n", #x)); } #define LOG_END_FUNCTION(x, y) \ { DebugLog((DEB_TRACE_CSP, "%s: Leaving, status: 0x%x\n", #x, y)); } #define LOG_BEGIN_CRYPTOAPI(x) \ { DebugLog((DEB_TRACE_CRYPTOAPI, "%s: Entering\n", #x)); } #define LOG_END_CRYPTOAPI(x, y) \ { DebugLog((DEB_TRACE_CRYPTOAPI, "%s: Leaving, status: 0x%x\n", #x, y)); } // // When receiving an encoded certificate from the calling application, // the current interface doesn't include a length, so we have to try // to determine the length of the encoded blob ourselves. If there's an // encoding error, we'll just walk off the end of the buffer, so set this // maximum. // #define cbENCODED_CERT_OVERFLOW 5000 // Bytes #define PROVPATH "SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider\\" #define PROVPATH_LEN sizeof(PROVPATH) // // Local structure definitions // // // This is a node for the list of algorithms supported by this CSP and a given // card. // typedef struct _SUPPORTED_ALGORITHM { struct _SUPPORTED_ALGORITHM *pNext; PROV_ENUMALGS_EX EnumalgsEx; } SUPPORTED_ALGORITHM, *PSUPPORTED_ALGORITHM; // // Type: LOCAL_USER_CONTEXT // // This is the HCRYPTPROV type for the base CSP. // #define LOCAL_USER_CONTEXT_CURRENT_VERSION 1 typedef struct _LOCAL_USER_CONTEXT { DWORD dwVersion; PCARD_STATE pCardState; CSP_REG_SETTINGS RegSettings; BOOL fHoldingTransaction; BYTE bContainerIndex; // This is a multi-string of all of the container names present on // the card associated with this context. This member is only used // by CryptGetProvParam PP_ENUMCONTAINERS. Access is not synchronized. LPSTR mszEnumContainers; LPSTR mszCurrentEnumContainer; // This is a list of algorithms supported by this CSP and card. This is // only accessed via CryptGetProvParam PP_ENUMALGS and PP_ENUMALGS_EX. // Access is not synchronized. PSUPPORTED_ALGORITHM pSupportedAlgs; PSUPPORTED_ALGORITHM pCurrentAlg; } LOCAL_USER_CONTEXT, *PLOCAL_USER_CONTEXT; // // Type: LOCAL_KEY_CONTEXT // // This is the HCRYPTKEY type for the base CSP. // typedef struct _LOCAL_KEY_CONTEXT { PBYTE pbArchivablePrivateKey; DWORD cbArchivablePrivateKey; } LOCAL_KEY_CONTEXT, *PLOCAL_KEY_CONTEXT; // // Type: LOCAL_HASH_CONTEXT // // This is the HCRYPTHASH type for the base CSP. // /* typedef struct _LOCAL_HASH_CONTEXT { // // Don't need anything here yet. // } LOCAL_HASH_CONTEXT, *PLOCAL_HASH_CONTEXT; */ // // Global variables // CSP_STRING g_Strings [] = { { NULL, IDS_PINDIALOG_NEWPIN_MISMATCH }, { NULL, IDS_PINDIALOG_MSGBOX_TITLE }, { NULL, IDS_PINDIALOG_WRONG_PIN }, { NULL, IDS_PINDIALOG_PIN_RETRIES } }; CSP_STATE g_CspState; CARD_KEY_SIZES DefaultCardKeySizes = { CARD_KEY_SIZES_CURRENT_VERSION, 1024, 1024, 1024, 0 }; // // Registry Initialization // // // Function: RegConfigAddEntries // DWORD WINAPI RegConfigAddEntries( IN HKEY hKey) { DWORD dwSts = ERROR_SUCCESS; DWORD iEntry = 0; for ( iEntry = 0; iEntry < sizeof(RegConfigValues) / sizeof(RegConfigValues[0]); iEntry++) { dwSts = RegSetValueExW( hKey, RegConfigValues[iEntry].wszValueName, 0L, REG_DWORD, (LPBYTE) &RegConfigValues[iEntry].dwDefValue, sizeof(DWORD)); if (ERROR_SUCCESS != dwSts) goto Ret; } Ret: return dwSts; } // // Function: RegConfigGetSettings // DWORD WINAPI RegConfigGetSettings( IN OUT PCSP_REG_SETTINGS pRegSettings) { DWORD dwSts = ERROR_SUCCESS; HKEY hKey = 0; DWORD dwVal = 0; DWORD cbVal = sizeof(DWORD); dwSts = RegOpenProviderKey(&hKey, KEY_READ); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = RegQueryValueExW( hKey, wszREG_DEFAULT_KEY_LEN, NULL, NULL, (LPBYTE) &dwVal, &cbVal); if (ERROR_SUCCESS != dwSts) goto Ret; pRegSettings->cDefaultPrivateKeyLenBits = dwVal; dwVal = 0; cbVal = sizeof(DWORD); dwSts = RegQueryValueExW( hKey, wszREG_REQUIRE_CARD_KEY_GEN, NULL, NULL, (LPBYTE) &dwVal, &cbVal); if (ERROR_SUCCESS != dwSts) goto Ret; pRegSettings->fRequireOnCardPrivateKeyGen = (BOOL) dwVal; dwVal = 0; cbVal = sizeof(DWORD); Ret: if (hKey) RegCloseKey(hKey); return dwSts; } // // CSP State Management Routines // // // Function: DeleteCspState // // Purpose: Delete the global state data structure for this CSP. // Should be called during DLL_PROCESS_DETACH. // DWORD DeleteCspState(void) { CspDeleteCriticalSection(&g_CspState.cs); if (0 != g_CspState.hCache) CacheDeleteCache(g_CspState.hCache); memset(&g_CspState, 0, sizeof(g_CspState)); return ERROR_SUCCESS; } // // Function: InitializeCspState // // Purpose: Setup the global state data structure for this CSP. // Should be called during DLL_PROCESS_ATTACH. // DWORD InitializeCspState( IN HMODULE hCspModule) { DWORD dwSts = ERROR_SUCCESS; CACHE_INITIALIZE_INFO CacheInitInfo; BOOL fSuccess = FALSE; memset(&g_CspState, 0, sizeof(g_CspState)); memset(&CacheInitInfo, 0, sizeof(CacheInitInfo)); g_CspState.hCspModule = hCspModule; dwSts = CspInitializeCriticalSection( &g_CspState.cs); if (ERROR_SUCCESS != dwSts) goto Ret; CacheInitInfo.dwType = CACHE_TYPE_IN_PROC; dwSts = CacheInitializeCache( &g_CspState.hCache, &CacheInitInfo); if (ERROR_SUCCESS != dwSts) goto Ret; fSuccess = TRUE; Ret: if (FALSE == fSuccess) DeleteCspState(); return dwSts; } // // Function: GetCspState // // Purpose: Acquire a pointer to the global state data structure // for this CSP. This function should always be used, // rather than referring to the global object directly. // DWORD GetCspState( IN OUT PCSP_STATE *ppCspState) { DWORD dwSts; dwSts = CspEnterCriticalSection( &g_CspState.cs); if (ERROR_SUCCESS != dwSts) return dwSts; g_CspState.dwRefCount++; CspLeaveCriticalSection( &g_CspState.cs); *ppCspState = &g_CspState; return ERROR_SUCCESS; } // // Function: ReleaseCspState // // Purpose: Signal that the caller's pointer to the global // state data structure is no longer being used. // DWORD ReleaseCspState( IN OUT PCSP_STATE *ppCspState) { DWORD dwSts; dwSts = CspEnterCriticalSection( &g_CspState.cs); if (ERROR_SUCCESS != dwSts) return dwSts; g_CspState.dwRefCount--; CspLeaveCriticalSection( &g_CspState.cs); *ppCspState = NULL; return ERROR_SUCCESS; } // // Pin Management Routines // // // Struct: VERIFY_PIN_CALLBACK_DATA // typedef struct _VERIFY_PIN_CALLBACK_DATA { PUSER_CONTEXT pUserCtx; LPWSTR wszUserId; } VERIFY_PIN_CALLBACK_DATA, *PVERIFY_PIN_CALLBACK_DATA; // // Callback for verifying a submitted pin, or requested pin change, from the // user via the pin prompt UI. // DWORD WINAPI VerifyPinFromUICallback( IN PPINCACHE_PINS pPins, IN PVOID pvCallbackCtx) { PPIN_SHOW_GET_PIN_UI_INFO pInfo = (PPIN_SHOW_GET_PIN_UI_INFO) pvCallbackCtx; PVERIFY_PIN_CALLBACK_DATA pData = (PVERIFY_PIN_CALLBACK_DATA) pInfo->pvCallbackContext; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pData->pUserCtx->pvLocalUserContext; // Determine if a Change Pin operation has been requested if (NULL != pPins->pbNewPin) { // A pin change needs to go through the caching layer to ensure that // the cache and associated counters get updated. return CspChangeAuthenticator( pLocal->pCardState, pData->wszUserId, pPins->pbCurrentPin, pPins->cbCurrentPin, pPins->pbNewPin, pPins->cbNewPin, 0, &pInfo->cAttemptsRemaining); } // For the simple submit pin, this pin is directly from the user, so we // pass it directly to the card rather than going through the caching // layer. return pLocal->pCardState->pCardData->pfnCardSubmitPin( pLocal->pCardState->pCardData, pData->wszUserId, pPins->pbCurrentPin, pPins->cbCurrentPin, &pInfo->cAttemptsRemaining); } // // Function: VerifyPinCallback // DWORD WINAPI VerifyPinCallback( IN PPINCACHE_PINS pPins, IN PVOID pvCallbackCtx) { PVERIFY_PIN_CALLBACK_DATA pData = (PVERIFY_PIN_CALLBACK_DATA) pvCallbackCtx; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pData->pUserCtx->pvLocalUserContext; // This pin is from the pin cache, so filter it through the caching layer. return CspSubmitPin( pLocal->pCardState, pData->wszUserId, pPins->pbCurrentPin, pPins->cbCurrentPin, NULL); } // // Function: CspAuthenticateUser // DWORD WINAPI CspAuthenticateUser( IN PUSER_CONTEXT pUserCtx) { VERIFY_PIN_CALLBACK_DATA CallbackCtx; DWORD dwError = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserContext = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; PIN_SHOW_GET_PIN_UI_INFO PinUIInfo; PINCACHE_PINS Pins; LOG_BEGIN_FUNCTION(CspAuthenticateUser); memset(&Pins, 0, sizeof(Pins)); memset(&PinUIInfo, 0, sizeof(PinUIInfo)); memset(&CallbackCtx, 0, sizeof(CallbackCtx)); CallbackCtx.pUserCtx = pUserCtx; CallbackCtx.wszUserId = wszCARD_USER_USER; dwError = PinCachePresentPin( pLocalUserContext->pCardState->hPinCache, VerifyPinCallback, (PVOID) &CallbackCtx); if (SCARD_W_CARD_NOT_AUTHENTICATED == dwError || ERROR_EMPTY == dwError || SCARD_W_WRONG_CHV == dwError) { PinCacheFlush(&pLocalUserContext->pCardState->hPinCache); // No pin is cached. Is the context "silent"? if (CRYPT_VERIFYCONTEXT & pUserCtx->dwFlags || CRYPT_SILENT & pUserCtx->dwFlags) { dwError = (DWORD) NTE_SILENT_CONTEXT; goto Ret; } // Context is not silent. Show UI to let the user enter a pin. pUserCtx->pVTableW->FuncReturnhWnd(&PinUIInfo.hClientWindow); PinUIInfo.pStrings = g_Strings; PinUIInfo.hDlgResourceModule = GetModuleHandle(L"basecsp.dll"); PinUIInfo.wszPrincipal = wszCARD_USER_USER; PinUIInfo.pfnVerify = VerifyPinFromUICallback; PinUIInfo.pvCallbackContext = (PVOID) &CallbackCtx; PinUIInfo.wszCardName = pLocalUserContext->pCardState->wszSerialNumber; dwError = PinShowGetPinUI(&PinUIInfo); if (ERROR_SUCCESS != dwError || NULL == PinUIInfo.pbPin) goto Ret; Pins.cbCurrentPin = PinUIInfo.cbPin; Pins.pbCurrentPin = PinUIInfo.pbPin; // // The user entered a pin that has been successfully verified by the // card. The Pin UI should have already converted the pin from // string form into bytes that we can send to the card. // Cache the pin. // dwError = PinCacheAdd( &pLocalUserContext->pCardState->hPinCache, &Pins, VerifyPinCallback, (PVOID) &CallbackCtx); if (ERROR_SUCCESS != dwError) goto Ret; } pLocalUserContext->pCardState->fAuthenticated = TRUE; Ret: if (PinUIInfo.pbPin) { RtlSecureZeroMemory(PinUIInfo.pbPin, PinUIInfo.cbPin); CspFreeH(PinUIInfo.pbPin); } LOG_END_FUNCTION(CspAuthenticateUser, dwError); return dwError; } // // Frees the memory consumed by the supported algorithms list. // DWORD FreeSupportedAlgorithmsList(PLOCAL_USER_CONTEXT pLocal) { DWORD dwError = ERROR_SUCCESS; PSUPPORTED_ALGORITHM pCurrent = NULL; DsysAssert(NULL != pLocal->pSupportedAlgs); while (NULL != pLocal->pSupportedAlgs) { pCurrent = pLocal->pSupportedAlgs; pLocal->pSupportedAlgs = pCurrent->pNext; CspFreeH(pCurrent); } return ERROR_SUCCESS; } // // Builds a list of algorithms supported by this CSP and smartcard. // DWORD BuildSupportedAlgorithmsList(PUSER_CONTEXT pUserCtx) { PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; DWORD dwSts = ERROR_SUCCESS; CARD_KEY_SIZES CardKeySizes; PROV_ENUMALGS_EX EnumalgsEx; DWORD cbData = sizeof(EnumalgsEx); PSUPPORTED_ALGORITHM pCurrent = NULL; DWORD dwFlag = CRYPT_FIRST; DsysAssert(NULL != pLocal); DsysAssert(NULL == pLocal->pSupportedAlgs); memset(&CardKeySizes, 0, sizeof(CardKeySizes)); memset(&EnumalgsEx, 0, sizeof(EnumalgsEx)); while (TRUE == CryptGetProvParam( pUserCtx->hSupportProv, PP_ENUMALGS_EX, (PBYTE) &EnumalgsEx, &cbData, dwFlag)) { dwFlag = CRYPT_NEXT; if (NULL == pCurrent) { // First item pLocal->pSupportedAlgs = (PSUPPORTED_ALGORITHM) CspAllocH( sizeof(SUPPORTED_ALGORITHM)); LOG_CHECK_ALLOC(pLocal->pSupportedAlgs); pCurrent = pLocal->pSupportedAlgs; } else { // Adding an item pCurrent->pNext = (PSUPPORTED_ALGORITHM) CspAllocH( sizeof(SUPPORTED_ALGORITHM)); LOG_CHECK_ALLOC(pCurrent->pNext); pCurrent = pCurrent->pNext; } memcpy( &pCurrent->EnumalgsEx, &EnumalgsEx, sizeof(EnumalgsEx)); memset(&EnumalgsEx, 0, sizeof(EnumalgsEx)); // Special handling for public key algs since they depend on what the // target card actually supports switch (pCurrent->EnumalgsEx.aiAlgid) { case CALG_RSA_KEYX: // If there's no CARD_STATE in this context, that should mean this // is a VerifyContext with no card inserted. MMC expects that // algorithm enumeration is successful without a card, so provide // some default public key values in that case. if (NULL == pLocal->pCardState) { DsysAssert(CRYPT_VERIFYCONTEXT & pUserCtx->dwFlags); memcpy( &CardKeySizes, &DefaultCardKeySizes, sizeof(CARD_KEY_SIZES)); break; } dwSts = CspQueryKeySizes( pLocal->pCardState, AT_KEYEXCHANGE, 0, &CardKeySizes); if (ERROR_SUCCESS != dwSts) goto Ret; break; case CALG_RSA_SIGN: if (NULL == pLocal->pCardState) { DsysAssert(CRYPT_VERIFYCONTEXT & pUserCtx->dwFlags); memcpy( &CardKeySizes, &DefaultCardKeySizes, sizeof(CARD_KEY_SIZES)); break; } dwSts = CspQueryKeySizes( pLocal->pCardState, AT_SIGNATURE, 0, &CardKeySizes); if (ERROR_SUCCESS != dwSts) goto Ret; break; default: // Go to the next alg continue; } pCurrent->EnumalgsEx.dwDefaultLen = CardKeySizes.dwDefaultBitlen; pCurrent->EnumalgsEx.dwMaxLen = CardKeySizes.dwMaximumBitlen; pCurrent->EnumalgsEx.dwMinLen = CardKeySizes.dwMinimumBitlen; memset(&CardKeySizes, 0, sizeof(CardKeySizes)); } if (ERROR_NO_MORE_ITEMS == (dwSts = GetLastError())) dwSts = ERROR_SUCCESS; Ret: if (ERROR_SUCCESS != dwSts && NULL != pLocal->pSupportedAlgs) FreeSupportedAlgorithmsList(pLocal); return dwSts; } // // Function: DeleteLocalUserContext // void DeleteLocalUserContext(PLOCAL_USER_CONTEXT pLocalUserCtx) { if (NULL != pLocalUserCtx->mszEnumContainers) { CspFreeH(pLocalUserCtx->mszEnumContainers); pLocalUserCtx->mszEnumContainers = NULL; } if (NULL != pLocalUserCtx->pSupportedAlgs) FreeSupportedAlgorithmsList(pLocalUserCtx); // Don't free the card state here, since those structures are shared. pLocalUserCtx->pCardState = NULL; } // // Function: CleanupContainerInfo // void CleanupContainerInfo( IN OUT PCONTAINER_INFO pContainerInfo) { if (pContainerInfo->pbKeyExPublicKey) { CspFreeH(pContainerInfo->pbKeyExPublicKey); pContainerInfo->pbKeyExPublicKey = NULL; } if (pContainerInfo->pbSigPublicKey) { CspFreeH(pContainerInfo->pbSigPublicKey); pContainerInfo->pbSigPublicKey = NULL; } } // // Function: GetKeyModulusLength // DWORD GetKeyModulusLength( IN PUSER_CONTEXT pUserCtx, IN DWORD dwKeySpec, OUT PDWORD pcbModulus) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; CONTAINER_MAP_RECORD ContainerRecord; *pcbModulus = 0; memset(&ContainerRecord, 0, sizeof(ContainerRecord)); wcscpy(ContainerRecord.wszGuid, pUserCtx->wszBaseContainerName); dwSts = ContainerMapFindContainer( pLocal->pCardState, &ContainerRecord, NULL); if (ERROR_SUCCESS != dwSts) goto Ret; switch (dwKeySpec) { case AT_SIGNATURE: if (0 == ContainerRecord.wSigKeySizeBits) { dwSts = (DWORD) NTE_NO_KEY; goto Ret; } *pcbModulus = ContainerRecord.wSigKeySizeBits / 8; break; case AT_KEYEXCHANGE: if (0 == ContainerRecord.wKeyExchangeKeySizeBits) { dwSts = (DWORD) NTE_NO_KEY; goto Ret; } *pcbModulus = ContainerRecord.wKeyExchangeKeySizeBits / 8; break; default: dwSts = (DWORD) NTE_BAD_ALGID; goto Ret; } Ret: return dwSts; } // // Finds the index corresponding to a specified container in the card // container map file. Returns the contents the container map file. // // The container name can optionally be omitted, in which case the map file // is simply read and returned. // DWORD I_ContainerMapFind( IN PCARD_STATE pCardState, IN OPTIONAL LPWSTR wszContainerGuid, OUT OPTIONAL PBYTE pbIndex, OUT PCONTAINER_MAP_RECORD *ppContainerMapFile, OUT PBYTE pcContainerMapFile) { DWORD dwSts = ERROR_SUCCESS; DATA_BLOB dbContainerMap; BYTE iContainer = 0; memset(&dbContainerMap, 0, sizeof(dbContainerMap)); *ppContainerMapFile = NULL; *pcContainerMapFile = 0; // Read the container map file from the card dwSts = CspReadFile( pCardState, wszCONTAINER_MAP_FILE_FULL_PATH, 0, &dbContainerMap.pbData, &dbContainerMap.cbData); if (ERROR_SUCCESS != dwSts) goto Ret; if (0 != dbContainerMap.cbData) { // Expect that the file contains an exact multiple of the record size DsysAssert(0 == (dbContainerMap.cbData % sizeof(CONTAINER_MAP_RECORD))); *ppContainerMapFile = (PCONTAINER_MAP_RECORD) dbContainerMap.pbData; *pcContainerMapFile = (BYTE) (dbContainerMap.cbData / sizeof(CONTAINER_MAP_RECORD)); dbContainerMap.pbData = NULL; } // See if caller just wanted us to return the map file contents if (NULL == wszContainerGuid) goto Ret; for ( iContainer = 0; iContainer < *pcContainerMapFile; iContainer++) { if (0 == wcscmp( wszContainerGuid, (*ppContainerMapFile)[iContainer].wszGuid) && (CONTAINER_MAP_VALID_CONTAINER & (*ppContainerMapFile)[iContainer].bFlags)) { *pbIndex = iContainer; goto Ret; } } dwSts = NTE_BAD_KEYSET; Ret: if (dbContainerMap.pbData) CspFreeH(dbContainerMap.pbData); return dwSts; } // // Returns the number of valid containers present on the card. Optionally, // returns a list of the container names in a multi-string. // // The returned *mwszContainers pointer must be freed by the caller. // DWORD ContainerMapEnumContainers( IN PCARD_STATE pCardState, OUT PBYTE pcContainers, OUT OPTIONAL LPWSTR *mwszContainers) { DWORD dwSts = ERROR_SUCCESS; PCONTAINER_MAP_RECORD pContainerMap = NULL; BYTE cContainerMap = 0; BYTE iContainer = 0; DWORD cchCurrent = 0; DWORD cchCumulative = 0; *pcContainers = 0; if (NULL != mwszContainers) *mwszContainers = NULL; dwSts = I_ContainerMapFind( pCardState, NULL, NULL, &pContainerMap, &cContainerMap); if (ERROR_SUCCESS != dwSts) goto Ret; // // We'll make two passes through the container map file. The first pass // counts the number of valid containers present. This allows us to make // a single allocation large enough for a multi-string consisting of all // of the valid container names. We'll pick up the names in the second // pass through the file. // // Pass 1 for (iContainer = 0; iContainer < cContainerMap; iContainer++) { if (CONTAINER_MAP_VALID_CONTAINER & pContainerMap[iContainer].bFlags) *pcContainers += 1; } if (0 == *pcContainers || NULL == mwszContainers) goto Ret; // Build a big enough buffer *mwszContainers = (LPWSTR) CspAllocH( ((*pcContainers * (1 + MAX_CONTAINER_NAME_LEN)) + 1) * sizeof(WCHAR)); // Pass 2 for (iContainer = 0; iContainer < cContainerMap; iContainer++) { if (CONTAINER_MAP_VALID_CONTAINER & pContainerMap[iContainer].bFlags) { cchCurrent = wcslen(pContainerMap[iContainer].wszGuid); memcpy( *mwszContainers + cchCumulative, pContainerMap[iContainer].wszGuid, cchCurrent * sizeof(WCHAR)); cchCumulative += cchCurrent + 1; } } Ret: if (pContainerMap) CspFreeH(pContainerMap); return dwSts; } // // Searches for the named container on the card. The container to look for // must be in pContainer->wszGuid. If the container is not found, // NTE_BAD_KEYSET is returned. // DWORD ContainerMapFindContainer( IN PCARD_STATE pCardState, IN OUT PCONTAINER_MAP_RECORD pContainer, OUT OPTIONAL PBYTE pbContainerIndex) { DWORD dwSts = ERROR_SUCCESS; PCONTAINER_MAP_RECORD pContainerMap = NULL; BYTE bIndex = 0; BYTE cContainerMap = 0; dwSts = I_ContainerMapFind( pCardState, pContainer->wszGuid, &bIndex, &pContainerMap, &cContainerMap); if (ERROR_SUCCESS != dwSts) goto Ret; memcpy( (PBYTE) pContainer, (PBYTE) (pContainerMap + bIndex), sizeof(CONTAINER_MAP_RECORD)); if (NULL != pbContainerIndex) *pbContainerIndex = bIndex; Ret: if (pContainerMap) CspFreeH(pContainerMap); return dwSts; } // // Searches for the default container on the card. If no default container is // found, NTE_BAD_KEYSET is returned. // DWORD ContainerMapGetDefaultContainer( IN PCARD_STATE pCardState, OUT PCONTAINER_MAP_RECORD pContainer, OUT OPTIONAL PBYTE pbContainerIndex) { DWORD dwSts = ERROR_SUCCESS; PCONTAINER_MAP_RECORD pContainerMap = NULL; BYTE cContainerMap = 0; BYTE iContainer = 0; dwSts = I_ContainerMapFind( pCardState, NULL, NULL, &pContainerMap, &cContainerMap); if (ERROR_SUCCESS != dwSts) goto Ret; for (iContainer = 0; iContainer < cContainerMap; iContainer++) { if ((pContainerMap[iContainer].bFlags & CONTAINER_MAP_VALID_CONTAINER) && (pContainerMap[iContainer].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER)) { memcpy( (PBYTE) pContainer, (PBYTE) (pContainerMap + iContainer), sizeof(CONTAINER_MAP_RECORD)); if (NULL != pbContainerIndex) *pbContainerIndex = iContainer; goto Ret; } } dwSts = NTE_BAD_KEYSET; Ret: if (pContainerMap) CspFreeH(pContainerMap); return dwSts; } // // Sets the default container on the card. // DWORD ContainerMapSetDefaultContainer( IN PCARD_STATE pCardState, IN LPWSTR wszContainerGuid) { DWORD dwSts = ERROR_SUCCESS; PCONTAINER_MAP_RECORD pContainerMap = NULL; BYTE bIndex = 0; BYTE cContainerMap = 0; BYTE iContainer = 0; DATA_BLOB dbContainerMap; memset(&dbContainerMap, 0, sizeof(dbContainerMap)); dwSts = I_ContainerMapFind( pCardState, wszContainerGuid, &bIndex, &pContainerMap, &cContainerMap); if (ERROR_SUCCESS != dwSts) goto Ret; // // If some other container is currently marked as the default, unmark it. // for (iContainer = 0; iContainer < cContainerMap; iContainer++) { if (pContainerMap[iContainer].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER) pContainerMap[iContainer].bFlags &= ~CONTAINER_MAP_DEFAULT_CONTAINER; } pContainerMap[bIndex].bFlags |= CONTAINER_MAP_DEFAULT_CONTAINER; dbContainerMap.pbData = (PBYTE) pContainerMap; dbContainerMap.cbData = cContainerMap * sizeof(CONTAINER_MAP_RECORD); // Write the updated map file to the card dwSts = CspWriteFile( pCardState, wszCONTAINER_MAP_FILE_FULL_PATH, 0, dbContainerMap.pbData, dbContainerMap.cbData); Ret: if (pContainerMap) CspFreeH(pContainerMap); return dwSts; } // // Adds a new container record to the container map. If the specified // container already exists, replaces the existing keyset (if any) with // the one provided. // // If cKeySizeBits is zero, assumes that a container with no keys is being // added. // DWORD ContainerMapAddContainer( IN PCARD_STATE pCardState, IN LPWSTR pwszContainerGuid, IN DWORD cKeySizeBits, IN DWORD dwKeySpec, IN BOOL fGetNameOnly, OUT PBYTE pbContainerIndex) { DWORD dwSts = ERROR_SUCCESS; PCONTAINER_MAP_RECORD pContainerMap = NULL; BYTE cContainerMap = 0; BYTE iContainer = 0; DATA_BLOB dbContainerMap; PCONTAINER_MAP_RECORD pNewMap = NULL; BOOL fExistingContainer = FALSE; memset(&dbContainerMap, 0, sizeof(dbContainerMap)); // See if this container already exists dwSts = I_ContainerMapFind( pCardState, pwszContainerGuid, &iContainer, &pContainerMap, &cContainerMap); switch (dwSts) { case NTE_BAD_KEYSET: // // This is a new container that does not already exist. // Look for an existing "empty" slot in the container map file // for (iContainer = 0; iContainer < cContainerMap; iContainer++) { if (0 == (pContainerMap[iContainer].bFlags & CONTAINER_MAP_VALID_CONTAINER)) break; } break; case ERROR_SUCCESS: // // This container already exists. The new keyset will be added to it, // possibly replacing an existing keyset of the same type. // fExistingContainer = TRUE; break; default: goto Ret; } // // Pass the container index that we're using back to the caller; that may // be all that was requested. // *pbContainerIndex = iContainer; if (fGetNameOnly) goto Ret; if (iContainer == cContainerMap) { // // No empty slot was found in the container map. We'll have to grow // the map and add the new container to the end. // pNewMap = (PCONTAINER_MAP_RECORD) CspAllocH( (cContainerMap + 1) * sizeof(CONTAINER_MAP_RECORD)); LOG_CHECK_ALLOC(pNewMap); memcpy( (PBYTE) pNewMap, (PBYTE) pContainerMap, cContainerMap * sizeof(CONTAINER_MAP_RECORD)); CspFreeH(pContainerMap); pContainerMap = pNewMap; pNewMap = NULL; cContainerMap++; } // // Update the container map file and write it to the card // pContainerMap[iContainer].bFlags |= CONTAINER_MAP_VALID_CONTAINER; if (0 != cKeySizeBits) { switch (dwKeySpec) { case AT_KEYEXCHANGE: pContainerMap[iContainer].wKeyExchangeKeySizeBits = (WORD) cKeySizeBits; break; case AT_SIGNATURE: pContainerMap[iContainer].wSigKeySizeBits = (WORD) cKeySizeBits; break; default: dwSts = NTE_BAD_ALGID; goto Ret; } } if (FALSE == fExistingContainer) { wcscpy( pContainerMap[iContainer].wszGuid, pwszContainerGuid); } dbContainerMap.pbData = (PBYTE) pContainerMap; dbContainerMap.cbData = cContainerMap * sizeof(CONTAINER_MAP_RECORD); dwSts = CspWriteFile( pCardState, wszCONTAINER_MAP_FILE_FULL_PATH, 0, dbContainerMap.pbData, dbContainerMap.cbData); Ret: if (pContainerMap) CspFreeH(pContainerMap); if (pNewMap) CspFreeH(pNewMap); return dwSts; } // // Deletes a container record from the container map. Returns NTE_BAD_KEYSET // if the specified container does not exist. // // If the deleted container was the default, finds the first valid container // remaining on the card and marks it as the new default. // DWORD ContainerMapDeleteContainer( IN PCARD_STATE pCardState, IN LPWSTR pwszContainerGuid, OUT PBYTE pbContainerIndex) { DWORD dwSts = ERROR_SUCCESS; PCONTAINER_MAP_RECORD pContainerMap = NULL; BYTE dwIndex = 0; BYTE cContainerMap = 0; DATA_BLOB dbContainerMap; BYTE iContainer = 0; memset(&dbContainerMap, 0, sizeof(dbContainerMap)); // // See if this container already exists. If it does, invalidate its entry // in the map file and write the file back to the card. // dwSts = I_ContainerMapFind( pCardState, pwszContainerGuid, &dwIndex, &pContainerMap, &cContainerMap); if (ERROR_SUCCESS != dwSts) goto Ret; if (CONTAINER_MAP_DEFAULT_CONTAINER & pContainerMap[dwIndex].bFlags) { // Find a valid container to mark as the new default for (iContainer = 0; iContainer < cContainerMap; iContainer++) { if (CONTAINER_MAP_VALID_CONTAINER & pContainerMap[iContainer].bFlags) { pContainerMap[iContainer].bFlags |= CONTAINER_MAP_DEFAULT_CONTAINER; break; } } } memset( (PBYTE) (pContainerMap + dwIndex), 0, sizeof(CONTAINER_MAP_RECORD)); *pbContainerIndex = dwIndex; // // Update the container map file and write it to the card // dbContainerMap.pbData = (PBYTE) pContainerMap; dbContainerMap.cbData = cContainerMap * sizeof(CONTAINER_MAP_RECORD); dwSts = CspWriteFile( pCardState, wszCONTAINER_MAP_FILE_FULL_PATH, 0, dbContainerMap.pbData, dbContainerMap.cbData); Ret: if (pContainerMap) CspFreeH(pContainerMap); return dwSts; } // // Function: ValidateCardHandle // // Purpose: Verify that the provided SCARDHANDLE is valid. If the handle // is not valid, disconnect and flush the pin cache. // // Assume: pCardState critical section should currently be held by caller. // DWORD ValidateCardHandle( IN PCARD_STATE pCardState, IN BOOL fMayReleaseContextHandle, OUT OPTIONAL BOOL *pfFlushPinCache) { DWORD dwSts = ERROR_SUCCESS; DWORD dwState = 0; DWORD dwProtocol = 0; DWORD cch = 0; LPWSTR mszReaders = NULL; BYTE rgbAtr [cbATR_BUFFER]; DWORD cbAtr = sizeof(rgbAtr); PCARD_DATA pCardData = pCardState->pCardData; LOG_BEGIN_FUNCTION(ValidateCardHandle); cch = SCARD_AUTOALLOCATE; dwSts = SCardStatusW( pCardData->hScard, (LPWSTR) &mszReaders, &cch, &dwState, &dwProtocol, rgbAtr, &cbAtr); if (mszReaders) SCardFreeMemory(pCardData->hScard, mszReaders); switch (dwSts) { case SCARD_W_RESET_CARD: dwSts = SCardReconnect( pCardData->hScard, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD, &dwProtocol); if (ERROR_SUCCESS == dwSts) // We're done if reconnect succeeded goto Ret; break; case ERROR_SUCCESS: if (SCARD_SPECIFIC == dwState) // The card handle is still valid and ready to use. We're done. goto Ret; break; default: // This includes the case where the card status SCARD_W_REMOVED was // returned. Nothing to do but disconnect, below. break; } // // The card appears to have been withdrawan at some point, or some // other problem occurred. // // We should not attempt to reconnect the card in any case other than // RESET, since we wouldn't be sure that it's the same card without // further checks. Instead just close the card handle and let the caller // find the correct card again. // SCardDisconnect( pCardData->hScard, SCARD_LEAVE_CARD); pCardData->hScard = 0; dwSts = (DWORD) SCARD_E_INVALID_HANDLE; if (fMayReleaseContextHandle) { SCardReleaseContext( pCardData->hSCardCtx); pCardData->hSCardCtx = 0; } if (NULL != pfFlushPinCache) *pfFlushPinCache = TRUE; Ret: LOG_END_FUNCTION(ValidateCardHandle, dwSts); return dwSts; } // // Function: FindCardBySerialNumber // // Purpose: Search for an existing card whose serial number is already // known. This is necessary for a User Context associated with // a card whose handle failed to reconnect. // DWORD FindCardBySerialNumber( IN PUSER_CONTEXT pUserCtx) { DWORD dwSts = ERROR_SUCCESS; CARD_MATCH_DATA CardMatchData; PCSP_STATE pCspState = NULL; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; memset(&CardMatchData, 0, sizeof(CardMatchData)); dwSts = GetCspState(&pCspState); if (ERROR_SUCCESS != dwSts) goto Ret; CardMatchData.pwszSerialNumber = pLocalUserCtx->pCardState->wszSerialNumber; CardMatchData.dwCtxFlags = pUserCtx->dwFlags; CardMatchData.dwMatchType = CARD_MATCH_TYPE_SERIAL_NUMBER; CardMatchData.cchMatchedCard = MAX_PATH; CardMatchData.cchMatchedReader = MAX_PATH; CardMatchData.cchMatchedSerialNumber = MAX_PATH; CardMatchData.pCspState = pCspState; CardMatchData.dwShareMode = SCARD_SHARE_SHARED; CardMatchData.dwPreferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; // As a sanity check, disassociate this user context with its current // Card State structure. If we find a match, it will be the same one, // because we already know the serial number, but the user context // shouldn't maintain this state until then. pLocalUserCtx->pCardState = NULL; // Try to find a matching card. dwSts = FindCard(&CardMatchData); if (ERROR_SUCCESS == dwSts) pLocalUserCtx->pCardState = CardMatchData.pCardState; Ret: if (pCspState) ReleaseCspState(&pCspState); return dwSts; } // // Function: BuildCertificateFilename // DWORD WINAPI BuildCertificateFilename( IN PUSER_CONTEXT pUserCtx, IN DWORD dwKeySpec, OUT LPWSTR *ppszFilename) { DWORD dwSts = ERROR_SUCCESS; BYTE bContainerIndex = 0; DWORD cchFilename = 0; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; CONTAINER_MAP_RECORD ContainerRecord; LPWSTR wszPrefix = NULL; switch(dwKeySpec) { case AT_SIGNATURE: wszPrefix = wszUSER_SIGNATURE_CERT_PREFIX; break; case AT_KEYEXCHANGE: wszPrefix = wszUSER_KEYEXCHANGE_CERT_PREFIX; break; default: dwSts = (DWORD) NTE_BAD_ALGID; goto Ret; } memset(&ContainerRecord, 0, sizeof(ContainerRecord)); wcscpy(ContainerRecord.wszGuid, pUserCtx->wszBaseContainerName); dwSts = ContainerMapFindContainer( pLocal->pCardState, &ContainerRecord, &bContainerIndex); cchFilename = wcslen(wszPrefix) + 2 + 1; *ppszFilename = (LPWSTR) CspAllocH(sizeof(WCHAR) * cchFilename); LOG_CHECK_ALLOC(*ppszFilename); wsprintf( *ppszFilename, L"%s%02X", wszPrefix, bContainerIndex); Ret: if (ERROR_SUCCESS != dwSts && *ppszFilename) { CspFreeH(*ppszFilename); *ppszFilename = NULL; } return dwSts; } // // Function: CspBeginTransaction // DWORD CspBeginTransaction( IN PCARD_STATE pCardState) { DWORD dwSts = ERROR_SUCCESS; dwSts = CspEnterCriticalSection(&pCardState->cs); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = SCardBeginTransaction(pCardState->pCardData->hScard); Ret: if (ERROR_SUCCESS != dwSts) CspLeaveCriticalSection(&pCardState->cs); return dwSts; } // // Function: CspEndTransaction // DWORD CspEndTransaction( IN PCARD_STATE pCardState) { DWORD dwSts = ERROR_SUCCESS; DWORD dwAction = pCardState->fAuthenticated ? SCARD_RESET_CARD : SCARD_LEAVE_CARD; if (pCardState->fAuthenticated && pCardState->pCardData->pfnCardDeauthenticate) { // The card is currently in an authenticated state, and the card // module has its own Deauthenticate function. Try to use the // cardmod function instead using RESET_CARD in the // SCardEndTransaction call. This can be a big perf improvement. dwSts = pCardState->pCardData->pfnCardDeauthenticate( pCardState->pCardData, wszCARD_USER_USER, 0); // If the Deauthenticate succeeded, all that's left to do is release // the transaction. If the Deauthenticate failed, we'll try one // more time to RESET the card. if (ERROR_SUCCESS == dwSts) dwAction = SCARD_LEAVE_CARD; } dwSts = SCardEndTransaction( pCardState->pCardData->hScard, dwAction); if (ERROR_SUCCESS != dwSts) { // Bad news if we got here. Better try to close the card handle // to cleanup. SCardDisconnect(pCardState->pCardData->hScard, SCARD_RESET_CARD); pCardState->pCardData->hScard = 0; SCardReleaseContext(pCardState->pCardData->hSCardCtx); pCardState->pCardData->hSCardCtx = 0; } else pCardState->fAuthenticated = FALSE; // We've left the transaction, so the "cached" cache file info is no // longer reliable. pCardState->fCacheFileValid = FALSE; CspLeaveCriticalSection(&pCardState->cs); return dwSts; } // // Function: BeginCardCapiCall // // Purpose: Setup the user context and associated card context for a new // Crypto API call. This includes: // 1) Reconnecting to the card, if necessary. // 2) Begin transaction. // DWORD BeginCardCapiCall( IN PUSER_CONTEXT pUserCtx) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; BOOL fFlushPinCache = FALSE; LOG_BEGIN_FUNCTION(BeginCardCapiCall); DsysAssert(FALSE == pLocalUserCtx->fHoldingTransaction); dwSts = CspEnterCriticalSection( &pLocalUserCtx->pCardState->cs); if (ERROR_SUCCESS != dwSts) return dwSts; dwSts = ValidateCardHandle( pLocalUserCtx->pCardState, TRUE, &fFlushPinCache); if (ERROR_SUCCESS != dwSts || TRUE == fFlushPinCache) // Flush the pin cache for this card. Not checking error code // since we'll keep processing, anyway. CspRemoveCachedPin(pLocalUserCtx->pCardState, wszCARD_USER_USER); if (ERROR_SUCCESS != dwSts) { // // Could not connect to the card. // CspLeaveCriticalSection( &pLocalUserCtx->pCardState->cs); // Attempt to find the card again, // possibly in a different reader. dwSts = FindCardBySerialNumber(pUserCtx); if (ERROR_SUCCESS != dwSts) goto Ret; } else CspLeaveCriticalSection( &pLocalUserCtx->pCardState->cs); // If the reconnect failed, bail now. if (ERROR_SUCCESS != dwSts) goto Ret; // Now Begin Transaction on the card. dwSts = CspBeginTransaction(pLocalUserCtx->pCardState); if (ERROR_SUCCESS == dwSts) pLocalUserCtx->fHoldingTransaction = TRUE; Ret: LOG_END_FUNCTION(BeginCardCapiCall, dwSts); return dwSts; } // // Function: EndCardCapiCall // // Purpose: Cleanup the user context and associated card context following // the completion of a Crypto API call. This includes ending // the transaction on the card. // DWORD EndCardCapiCall( IN PUSER_CONTEXT pUserCtx) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; LOG_BEGIN_FUNCTION(EndCardCapiCall); if (TRUE == pLocal->fHoldingTransaction) { dwSts = CspEndTransaction(pLocal->pCardState); if (ERROR_SUCCESS != dwSts) { // Something got screwed up and we weren't able to EndTransaction // correctly. Expect that the SCard handles got released as a // result. DsysAssert(0 == pLocal->pCardState->pCardData->hScard); DsysAssert(0 == pLocal->pCardState->pCardData->hSCardCtx); } // Even if the EndTransaction failed, we expect that the card handles // got closed, and we expect that the Card State critsec is no // longer held. pLocal->fHoldingTransaction = FALSE; } LOG_END_FUNCTION(EndCardCapiCall, dwSts); return dwSts; } // // Function: DeleteContainer // DWORD DeleteContainer(PUSER_CONTEXT pUserCtx) { PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; DWORD dwSts = ERROR_SUCCESS; LPWSTR wszFilename = NULL; BYTE bContainerIndex = 0; // // Delete the certificate (if any) associated with the signature key // (if any). // dwSts = BuildCertificateFilename( pUserCtx, AT_SIGNATURE, &wszFilename); if (ERROR_SUCCESS != dwSts) goto Ret; // Ignore error on this call - there may not be a cert associated // with this container and we're just trying to cleanup as much as // we can. CspDeleteFile( pLocal->pCardState, 0, wszFilename); CspFreeH(wszFilename); wszFilename = NULL; // // Delete the key exchange cert (if any) // dwSts = BuildCertificateFilename( pUserCtx, AT_KEYEXCHANGE, &wszFilename); if (ERROR_SUCCESS != dwSts) goto Ret; CspDeleteFile( pLocal->pCardState, 0, wszFilename); // // Perform the delete operation on the card // dwSts = CspDeleteContainer( pLocal->pCardState, pLocal->bContainerIndex, 0); if (ERROR_SUCCESS != dwSts) goto Ret; // // Remove this container from the container map // dwSts = ContainerMapDeleteContainer( pLocal->pCardState, pUserCtx->wszBaseContainerName, &bContainerIndex); DsysAssert(bContainerIndex == pLocal->bContainerIndex); Ret: if (wszFilename) CspFreeH(wszFilename); return dwSts; } // // Determines if the current context was acquired using CRYPT_VERIFYCONTEXT // semantics. // // A VerifyContext is allowed for some calls if and only if the // context has been associated with a specific card. The fAllowWithCardAccess // param should be set to TRUE in those cases. // DWORD CheckForVerifyContext( IN PUSER_CONTEXT pUserContext, IN BOOL fAllowOnlyWithCardAccess) { PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserContext->pvLocalUserContext; if (CRYPT_VERIFYCONTEXT & pUserContext->dwFlags) { if (fAllowOnlyWithCardAccess) { if (NULL != pLocal && NULL != pLocal->pCardState) return ERROR_SUCCESS; } return NTE_BAD_FLAGS; } return ERROR_SUCCESS; } // // Function: LocalAcquireContext // DWORD LocalAcquireContext( PUSER_CONTEXT pUserContext, PLOCAL_CALL_INFO pLocalCallInfo) { PLOCAL_USER_CONTEXT pLocalUserContext = NULL; DWORD dwSts; CARD_MATCH_DATA CardMatchData; LPWSTR pwsz = NULL; PCSP_STATE pCspState = NULL; DWORD cch = 0; CONTAINER_MAP_RECORD ContainerRecord; LOG_BEGIN_CRYPTOAPI(LocalAcquireContext); memset(&CardMatchData, 0, sizeof(CardMatchData)); memset(&ContainerRecord, 0, sizeof(ContainerRecord)); SetLocalCallInfo(pLocalCallInfo, FALSE); // Determine if any container/reader information // has been specified. if (pUserContext->wszContainerNameFromCaller) { pwsz = (LPWSTR) pUserContext->wszContainerNameFromCaller; if (0 == wcsncmp(L"\\\\.\\", pwsz, 4)) { // A reader has been specified pwsz += wcslen(L"\\\\.\\"); // pwsz now points to the reader name CardMatchData.pwszContainerName = wcschr(pwsz, L'\\') + 1; cch = (DWORD) (CardMatchData.pwszContainerName - pwsz - 1); CardMatchData.pwszReaderName = (LPWSTR) CspAllocH( sizeof(WCHAR) * (1 + cch)); LOG_CHECK_ALLOC(CardMatchData.pwszReaderName); memcpy( CardMatchData.pwszReaderName, pwsz, sizeof(WCHAR) * cch); } else { CardMatchData.pwszContainerName = pwsz; } // Check and cleanup the specified container name if (CardMatchData.pwszContainerName) { // Additional backslashes are not allowed. if (wcschr( CardMatchData.pwszContainerName, L'\\')) { dwSts = (DWORD) NTE_BAD_KEYSET; goto Ret; } // There may have just been a trailing '\' // with no following container name, or the // specified name may have just been the NULL string. if (L'\0' == CardMatchData.pwszContainerName[0]) { CardMatchData.pwszContainerName = NULL; } } // Verify that the final container name isn't too long if (NULL != CardMatchData.pwszContainerName && MAX_CONTAINER_NAME_LEN < wcslen(CardMatchData.pwszContainerName)) { dwSts = (DWORD) NTE_BAD_KEYSET; goto Ret; } } // Get pointer to global CSP data; includes // list of cached card information. dwSts = GetCspState(&pCspState); if (ERROR_SUCCESS != dwSts) goto Ret; // Setup the caller's cryptographic context. pLocalUserContext = (PLOCAL_USER_CONTEXT) CspAllocH(sizeof(LOCAL_USER_CONTEXT)); LOG_CHECK_ALLOC(pLocalUserContext); pLocalUserContext->dwVersion = LOCAL_USER_CONTEXT_CURRENT_VERSION; // Prepare info for matching an available // smart card to the caller's request. CardMatchData.dwCtxFlags = pUserContext->dwFlags; CardMatchData.dwMatchType = CARD_MATCH_TYPE_READER_AND_CONTAINER; CardMatchData.cchMatchedCard = MAX_PATH; CardMatchData.cchMatchedReader = MAX_PATH; CardMatchData.cchMatchedSerialNumber = MAX_PATH; CardMatchData.pCspState = pCspState; CardMatchData.dwShareMode = SCARD_SHARE_SHARED; CardMatchData.dwPreferredProtocols = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; // Try to find a matching card. dwSts = FindCard(&CardMatchData); // // Check for a VERIFYCONTEXT request in which no container specification // has been made. A failed card match is fine in that case since // this context will only be used to query generic CSP information. // if (ERROR_SUCCESS != dwSts && (CRYPT_VERIFYCONTEXT & pUserContext->dwFlags) && NULL == pUserContext->wszContainerNameFromCaller) { pUserContext->pvLocalUserContext = (PVOID) pLocalUserContext; pLocalUserContext = NULL; dwSts = ERROR_SUCCESS; goto Ret; } // Any other non-success case is fatal if (ERROR_SUCCESS != dwSts) goto Ret; pLocalUserContext->pCardState = CardMatchData.pCardState; pLocalUserContext->bContainerIndex = CardMatchData.bContainerIndex; // Read configuration information out of the registry. dwSts = RegConfigGetSettings( &pLocalUserContext->RegSettings); if (ERROR_SUCCESS != dwSts) goto Ret; // // If the caller requested a new container but didn't give a container // name, create a Guid name now. // if (NULL == CardMatchData.pwszContainerName && (CRYPT_NEWKEYSET & pUserContext->dwFlags)) { dwSts = CreateUuidContainerName(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; } else if (NULL != CardMatchData.pwszContainerName) { // Caller did give a container name, or the default container is // being used and we queried the name from the card during // our search. Copy it into the user context. pUserContext->wszBaseContainerName = (LPWSTR) CspAllocH( sizeof(WCHAR) * (1 + wcslen(CardMatchData.pwszContainerName))); LOG_CHECK_ALLOC(pUserContext->wszBaseContainerName); wcscpy( pUserContext->wszBaseContainerName, CardMatchData.pwszContainerName); } // // Associate context information for this CSP // pUserContext->pvLocalUserContext = (PVOID) pLocalUserContext; if (NULL != pUserContext->wszBaseContainerName) { // // Make a copy of the base container name to use as the "unique" container // name, since for this CSP they're the same. // // The only reason we should skip this step is for VERIFY_CONTEXT. // pUserContext->wszUniqueContainerName = (LPWSTR) CspAllocH( sizeof(WCHAR) * (1 + wcslen(pUserContext->wszBaseContainerName))); LOG_CHECK_ALLOC(pUserContext->wszUniqueContainerName); wcscpy( pUserContext->wszUniqueContainerName, pUserContext->wszBaseContainerName); if (CRYPT_NEWKEYSET & pUserContext->dwFlags) { // // Add-container requires us to // authenticate to the card, since we'll need to write the updated // container map. // dwSts = BeginCardCapiCall(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspAuthenticateUser(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = ContainerMapAddContainer( pLocalUserContext->pCardState, pUserContext->wszBaseContainerName, 0, 0, FALSE, &pLocalUserContext->bContainerIndex); // // Determine if there is already a "default" container on this // card. If not, mark the new one as default. // dwSts = ContainerMapGetDefaultContainer( pLocalUserContext->pCardState, &ContainerRecord, NULL); if (NTE_BAD_KEYSET == dwSts) { dwSts = ContainerMapSetDefaultContainer( pLocalUserContext->pCardState, pUserContext->wszBaseContainerName); } if (ERROR_SUCCESS != dwSts) goto Ret; } } else { DsysAssert(CRYPT_VERIFYCONTEXT & pUserContext->dwFlags); } // // If caller has requested a Delete Keyset, then do that work now // and cleanup the local user context at the end. // if (CRYPT_DELETEKEYSET & pUserContext->dwFlags) { dwSts = BeginCardCapiCall(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspAuthenticateUser(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = DeleteContainer(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; } else { pLocalUserContext = NULL; } Ret: if (pUserContext->pvLocalUserContext) EndCardCapiCall(pUserContext); if (pCspState) ReleaseCspState(&pCspState); if (pLocalUserContext) { pUserContext->pvLocalUserContext = NULL; DeleteLocalUserContext(pLocalUserContext); CspFreeH(pLocalUserContext); } if (CardMatchData.pwszReaderName) CspFreeH(CardMatchData.pwszReaderName); if (CardMatchData.fFreeContainerName && CardMatchData.pwszContainerName) CspFreeH(CardMatchData.pwszContainerName); LOG_END_CRYPTOAPI(LocalAcquireContext, dwSts); return dwSts; } // // Function: LocalReleaseContext // DWORD WINAPI LocalReleaseContext( IN PUSER_CONTEXT pUserCtx, IN DWORD dwFlags, OUT PLOCAL_CALL_INFO pLocalCallInfo) { PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; DWORD dwSts = ERROR_SUCCESS; UNREFERENCED_PARAMETER(dwFlags); LOG_BEGIN_CRYPTOAPI(LocalReleaseContext); if (pLocal) { DeleteLocalUserContext(pLocal); CspFreeH(pLocal); pUserCtx->pvLocalUserContext = NULL; } LOG_END_CRYPTOAPI(LocalReleaseContext, dwSts); return dwSts; } // // Function: LocalGenKey // DWORD WINAPI LocalGenKey( IN PKEY_CONTEXT pKeyCtx, OUT PLOCAL_CALL_INFO pLocalCallInfo) { PUSER_CONTEXT pUserCtx = pKeyCtx->pUserContext; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; DWORD dwSts = ERROR_SUCCESS; CARD_CAPABILITIES CardCapabilities; HCRYPTKEY hKey = 0; PBYTE pbKey = NULL; DWORD cbKey = 0; PLOCAL_KEY_CONTEXT pLocalKeyCtx = NULL; BYTE bContainerIndex = 0; LOG_BEGIN_CRYPTOAPI(LocalGenKey); memset(&CardCapabilities, 0, sizeof(CardCapabilities)); if (CALG_RSA_KEYX == pKeyCtx->Algid) pKeyCtx->Algid = AT_KEYEXCHANGE; else if (CALG_RSA_SIGN == pKeyCtx->Algid) pKeyCtx->Algid = AT_SIGNATURE; if (AT_SIGNATURE == pKeyCtx->Algid || AT_KEYEXCHANGE == pKeyCtx->Algid) { // Public key call. Handle this here since we have to talk to the // card. All other key algs will be handled in the support CSP. SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pKeyCtx->pUserContext, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; if (CRYPT_EXPORTABLE & pKeyCtx->dwFlags) { dwSts = NTE_BAD_FLAGS; goto Ret; } dwSts = BeginCardCapiCall(pUserCtx); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspQueryCapabilities( pLocalUserCtx->pCardState, &CardCapabilities); if (ERROR_SUCCESS != dwSts) goto Ret; if (0 == pKeyCtx->cKeyBits) pKeyCtx->cKeyBits = pLocalUserCtx->RegSettings.cDefaultPrivateKeyLenBits; // // If ARCHIVABLE is set, we don't gen the key on the card, since we // don't want to force cards to support exportable private keys. // if ( CardCapabilities.fKeyGen && 0 == (CRYPT_ARCHIVABLE & pKeyCtx->dwFlags)) { dwSts = CspAuthenticateUser(pUserCtx); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspCreateContainer( pLocalUserCtx->pCardState, pLocalUserCtx->bContainerIndex, CARD_CREATE_CONTAINER_KEY_GEN, pKeyCtx->Algid, pKeyCtx->cKeyBits, NULL); if (ERROR_SUCCESS != dwSts) goto Ret; } else { // This card does not support on-card key generation. See // if we're allowed to create our own key blob and import // it. if (pLocalUserCtx->RegSettings.fRequireOnCardPrivateKeyGen) { dwSts = (DWORD) SCARD_E_UNSUPPORTED_FEATURE; goto Ret; } // // Create a new, exportable private key in the software CSP. Then // export it and import it onto the card. // if (! CryptGenKey( pUserCtx->hSupportProv, pKeyCtx->Algid, CRYPT_EXPORTABLE | (pKeyCtx->cKeyBits << 16), &hKey)) { dwSts = GetLastError(); goto Ret; } if (! CryptExportKey( hKey, 0, PRIVATEKEYBLOB, 0, NULL, &cbKey)) { dwSts = GetLastError(); goto Ret; } pbKey = (PBYTE) CspAllocH(cbKey); LOG_CHECK_ALLOC(pbKey); if (! CryptExportKey( hKey, 0, PRIVATEKEYBLOB, 0, pbKey, &cbKey)) { dwSts = GetLastError(); goto Ret; } dwSts = CspAuthenticateUser(pUserCtx); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspCreateContainer( pLocalUserCtx->pCardState, pLocalUserCtx->bContainerIndex, CARD_CREATE_CONTAINER_KEY_IMPORT, pKeyCtx->Algid, cbKey, pbKey); if (ERROR_SUCCESS != dwSts) goto Ret; // // Check for CRYPT_ARCHIVABLE. If it's set, we'll keep a copy of // the private key for the lifetime of this key context for the // caller to export it. // if (CRYPT_ARCHIVABLE & pKeyCtx->dwFlags) { pLocalKeyCtx = (PLOCAL_KEY_CONTEXT) CspAllocH( sizeof(LOCAL_KEY_CONTEXT)); LOG_CHECK_ALLOC(pLocalKeyCtx); pLocalKeyCtx->pbArchivablePrivateKey = pbKey; pLocalKeyCtx->cbArchivablePrivateKey = cbKey; pbKey = NULL; pKeyCtx->pvLocalKeyContext = (PVOID) pLocalKeyCtx; } } // // Add the new key information for this container to the map file // dwSts = ContainerMapAddContainer( pLocalUserCtx->pCardState, pUserCtx->wszBaseContainerName, pKeyCtx->cKeyBits, pKeyCtx->Algid, FALSE, &bContainerIndex); DsysAssert(bContainerIndex == pLocalUserCtx->bContainerIndex); } else { // Not a public key call, so handle in the support CSP. SetLocalCallInfo(pLocalCallInfo, TRUE); } Ret: EndCardCapiCall(pUserCtx); if (pbKey) { RtlSecureZeroMemory(pbKey, cbKey); CspFreeH(pbKey); } if (hKey) CryptDestroyKey(hKey); LOG_END_CRYPTOAPI(LocalGenKey, dwSts); return dwSts; } // // Function: LocalDestroyKey // DWORD WINAPI LocalDestroyKey( IN OUT PKEY_CONTEXT pKeyContext, OUT PLOCAL_CALL_INFO pLocalCallInfo) { PLOCAL_KEY_CONTEXT pLocalKeyCtx = (PLOCAL_KEY_CONTEXT) pKeyContext->pvLocalKeyContext; LOG_BEGIN_CRYPTOAPI(LocalDestroyKey); if (NULL != pLocalKeyCtx) { if (NULL != pLocalKeyCtx->pbArchivablePrivateKey) { RtlSecureZeroMemory( pLocalKeyCtx->pbArchivablePrivateKey, pLocalKeyCtx->cbArchivablePrivateKey); CspFreeH(pLocalKeyCtx->pbArchivablePrivateKey); pLocalKeyCtx->pbArchivablePrivateKey = NULL; } CspFreeH(pLocalKeyCtx); pKeyContext->pvLocalKeyContext = NULL; } LOG_END_CRYPTOAPI(LocalDestroyKey, ERROR_SUCCESS); return ERROR_SUCCESS; } // // Determines if an encoded certificate blob contains certain Enhanced Key // Usage OIDs. The target OIDs are SmartCard Logon and Enrollment Agent. // If either OID is present, the key container associated with this // certificate should be considered the new default container on the target // card. // DWORD CheckCertUsageForDefaultContainer( PBYTE pbEncodedCert, DWORD cbEncodedCert, BOOL *pfMakeDefault) { DWORD dwSts = 0; PCCERT_CONTEXT pCertCtx = NULL; PCERT_ENHKEY_USAGE pUsage = NULL; DWORD cbUsage = 0; *pfMakeDefault = FALSE; // // Build a cert context from the encoded blob // pCertCtx = CertCreateCertificateContext( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbEncodedCert, cbEncodedCert); if (NULL == pCertCtx) { dwSts = GetLastError(); goto Ret; } // // Get an array of the EKU OIDs present in this cert // if (! CertGetEnhancedKeyUsage( pCertCtx, 0, NULL, &cbUsage)) { dwSts = GetLastError(); goto Ret; } pUsage = (PCERT_ENHKEY_USAGE) CspAllocH(cbUsage); if (NULL == pUsage) { dwSts = ERROR_NOT_ENOUGH_MEMORY; goto Ret; } if (! CertGetEnhancedKeyUsage( pCertCtx, 0, pUsage, &cbUsage)) { dwSts = GetLastError(); goto Ret; } // // Look for the two specific OIDs that would make this the new default // cert/container // while (pUsage->cUsageIdentifier) { pUsage->cUsageIdentifier -= 1; if (0 == strcmp( szOID_KP_SMARTCARD_LOGON, pUsage->rgpszUsageIdentifier[pUsage->cUsageIdentifier]) || 0 == strcmp( szOID_ENROLLMENT_AGENT, pUsage->rgpszUsageIdentifier[pUsage->cUsageIdentifier])) { *pfMakeDefault = TRUE; } } Ret: if (pUsage) CspFreeH(pUsage); if (pCertCtx) CertFreeCertificateContext(pCertCtx); return dwSts; } // // Function: LocalSetKeyParam // DWORD WINAPI LocalSetKeyParam( IN PKEY_CONTEXT pKeyCtx, IN DWORD dwParam, IN CONST BYTE *pbData, IN DWORD dwFlags, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pKeyCtx->pUserContext->pvLocalUserContext; DWORD cbCert = 0; DWORD cbCompressed = 0; PBYTE pbCompressed = NULL; LPWSTR wszCertFilename = NULL; CARD_FILE_ACCESS_CONDITION Acl = EveryoneReadUserWriteAc; DATA_BLOB CertData; CARD_CAPABILITIES CardCapabilities; BOOL fMakeDefault = FALSE; UNREFERENCED_PARAMETER(dwFlags); LOG_BEGIN_CRYPTOAPI(LocalSetKeyParam); memset(&CertData, 0, sizeof(CertData)); memset(&CardCapabilities, 0, sizeof(CardCapabilities)); switch (dwParam) { case KP_CERTIFICATE: SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pKeyCtx->pUserContext, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; // // Determine how long the encoded cert blob is. // __try { cbCert = Asn1UtilAdjustEncodedLength( pbData, (DWORD) cbENCODED_CERT_OVERFLOW); if (0 == cbCert || cbENCODED_CERT_OVERFLOW == cbCert) { dwSts = (DWORD) NTE_BAD_DATA; goto Ret; } } __except(EXCEPTION_ACCESS_VIOLATION == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { dwSts = (DWORD) NTE_BAD_DATA; goto Ret; } // Begin a transaction and reconnect the card if necessary dwSts = BeginCardCapiCall(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; // // Build the filename we'll use for this cert // dwSts = BuildCertificateFilename( pKeyCtx->pUserContext, pKeyCtx->Algid, &wszCertFilename); if (ERROR_SUCCESS != dwSts) goto Ret; // // Determine if this certificate contains OIDs that should make the // associated key container the new default. // dwSts = CheckCertUsageForDefaultContainer( (PBYTE) pbData, cbCert, &fMakeDefault); if (ERROR_SUCCESS != dwSts) goto Ret; // // Determine the capabilities of the target card - we want to know // whether it (or its card module) implements its own data // compression. // dwSts = CspQueryCapabilities( pLocalUserCtx->pCardState, &CardCapabilities); if (ERROR_SUCCESS != dwSts) goto Ret; if (FALSE == CardCapabilities.fCertificateCompression) { // // If this card doesn't implement its own certificate compression // then we will compress the cert. // // Find out how big the compressed cert will be // dwSts = CompressData(cbCert, NULL, &cbCompressed, NULL); if (ERROR_SUCCESS != dwSts) goto Ret; pbCompressed = CspAllocH(cbCompressed); LOG_CHECK_ALLOC(pbCompressed); // Compress the cert dwSts = CompressData( cbCert, (PBYTE) pbData, &cbCompressed, pbCompressed); if (ERROR_SUCCESS != dwSts) goto Ret; CertData.cbData = cbCompressed; CertData.pbData = pbCompressed; } else { CertData.cbData = cbCert; CertData.pbData = (PBYTE) pbData; } // // Authenticate to the card as User // dwSts = CspAuthenticateUser(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; // // Write the cert to the card. // dwSts = CspCreateFile( pLocalUserCtx->pCardState, wszCertFilename, Acl); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspWriteFile( pLocalUserCtx->pCardState, wszCertFilename, 0, CertData.pbData, CertData.cbData); if (ERROR_SUCCESS != dwSts) goto Ret; if (fMakeDefault) { dwSts = ContainerMapSetDefaultContainer( pLocalUserCtx->pCardState, pKeyCtx->pUserContext->wszBaseContainerName); } break; default: SetLocalCallInfo(pLocalCallInfo, TRUE); break; } Ret: EndCardCapiCall(pKeyCtx->pUserContext); if (pbCompressed) CspFreeH(pbCompressed); if (wszCertFilename) CspFreeH(wszCertFilename); LOG_END_CRYPTOAPI(LocalSetKeyParam, dwSts); return dwSts; } // // Function: LocalGetKeyParam // DWORD WINAPI LocalGetKeyParam( IN PKEY_CONTEXT pKeyCtx, IN DWORD dwParam, OUT LPBYTE pbData, IN OUT LPDWORD pcbDataLen, IN DWORD dwFlags, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pKeyCtx->pUserContext->pvLocalUserContext; LPWSTR wszCertFilename = NULL; DATA_BLOB CertData; DWORD cbUncompressed = 0; CARD_CAPABILITIES CardCapabilities; LOG_BEGIN_CRYPTOAPI(LocalGetKeyParam); memset(&CertData, 0, sizeof(CertData)); memset(&CardCapabilities, 0, sizeof(CardCapabilities)); switch (dwParam) { case KP_CERTIFICATE: SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pKeyCtx->pUserContext, TRUE); if (ERROR_SUCCESS != dwSts) goto Ret; // Note, reading the certificate files should not require // authentication to the card, but we will Enter Transaction // to be safe. dwSts = BeginCardCapiCall(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; // // Get the name of the certificate file to read from the card. // dwSts = BuildCertificateFilename( pKeyCtx->pUserContext, pKeyCtx->Algid, &wszCertFilename); if (ERROR_SUCCESS != dwSts) goto Ret; // // Read the file from the card. // dwSts = CspReadFile( pLocalUserCtx->pCardState, wszCertFilename, 0, &CertData.pbData, &CertData.cbData); if (ERROR_SUCCESS != dwSts) { if (SCARD_E_FILE_NOT_FOUND == dwSts) dwSts = SCARD_E_NO_SUCH_CERTIFICATE; goto Ret; } dwSts = CspQueryCapabilities( pLocalUserCtx->pCardState, &CardCapabilities); if (ERROR_SUCCESS != dwSts) goto Ret; if (FALSE == CardCapabilities.fCertificateCompression) { // // If this card doesn't implement its own certificate compression, // then we expect the cert was compressed by the CSP. // // Find out how big the uncompressed cert will be // dwSts = UncompressData( CertData.cbData, CertData.pbData, &cbUncompressed, NULL); if (ERROR_SUCCESS != dwSts) goto Ret; // // Check the length of the caller's buffer, or if the caller is just // querying for size. // if (*pcbDataLen < cbUncompressed || NULL == pbData) { *pcbDataLen = cbUncompressed; if (NULL != pbData) dwSts = ERROR_MORE_DATA; goto Ret; } *pcbDataLen = cbUncompressed; // Uncompress the cert into the caller's buffer dwSts = UncompressData( CertData.cbData, CertData.pbData, &cbUncompressed, pbData); if (ERROR_SUCCESS != dwSts) { if (ERROR_INTERNAL_ERROR == dwSts) dwSts = NTE_BAD_DATA; goto Ret; } } else { // // This card does implement its own compression, so assume that // we have received the cert uncompressed. // if (*pcbDataLen < CertData.cbData || NULL == pbData) { *pcbDataLen = CertData.cbData; if (NULL != pbData) dwSts = ERROR_MORE_DATA; goto Ret; } *pcbDataLen = CertData.cbData; memcpy(pbData, CertData.pbData, CertData.cbData); } break; default: SetLocalCallInfo(pLocalCallInfo, TRUE); break; } Ret: EndCardCapiCall(pKeyCtx->pUserContext); if (CertData.pbData) CspFreeH(CertData.pbData); if (wszCertFilename) CspFreeH(wszCertFilename); LOG_END_CRYPTOAPI(LocalGetKeyParam, dwSts); return dwSts; } // // Function: LocalSetProvParam // DWORD WINAPI LocalSetProvParam( IN PUSER_CONTEXT pUserCtx, IN DWORD dwParam, IN CONST BYTE *pbData, IN DWORD dwFlags, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pUserCtx->pvLocalUserContext; PINCACHE_PINS Pins; PFN_VERIFYPIN_CALLBACK pfnVerify = VerifyPinCallback; VERIFY_PIN_CALLBACK_DATA CallbackCtx; DWORD iChar = 0; LPSTR szPin = NULL; LOG_BEGIN_CRYPTOAPI(LocalSetProvParam); memset(&Pins, 0, sizeof(Pins)); memset(&CallbackCtx, 0, sizeof(CallbackCtx)); switch (dwParam) { case PP_KEYEXCHANGE_PIN: case PP_SIGNATURE_PIN: SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pUserCtx, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = PinStringToBytesA( (LPSTR) pbData, &Pins.cbCurrentPin, &Pins.pbCurrentPin); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = BeginCardCapiCall(pUserCtx); if (ERROR_SUCCESS != dwSts) goto Ret; // Remove any existing cached pin, just in case CspRemoveCachedPin( pLocalUserCtx->pCardState, wszCARD_USER_USER); CallbackCtx.pUserCtx = pUserCtx; CallbackCtx.wszUserId = wszCARD_USER_USER; dwSts = PinCacheAdd( &pLocalUserCtx->pCardState->hPinCache, &Pins, pfnVerify, (PVOID) &CallbackCtx); // We're now authenticated to the card if the pin was correct. Make // sure we deauthenticate below. if (ERROR_SUCCESS == dwSts) pLocalUserCtx->pCardState->fAuthenticated = TRUE; break; default: SetLocalCallInfo(pLocalCallInfo, TRUE); break; } Ret: EndCardCapiCall(pUserCtx); if (Pins.pbCurrentPin) CspFreeH(Pins.pbCurrentPin); LOG_END_CRYPTOAPI(LocalSetProvParam, dwSts); return dwSts; } // // Function: LocalGetProvParam // DWORD WINAPI LocalGetProvParam( IN PUSER_CONTEXT pUserContext, IN DWORD dwParam, OUT PBYTE pbData, IN OUT PDWORD pcbDataLen, IN DWORD dwFlags, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pUserContext->pvLocalUserContext; BYTE cContainers = 0; LPWSTR mwszContainers = NULL; DWORD cbContainers = 0; DWORD cchContainers = 0; DWORD cbCurrent = 0; BOOL fTransacted = FALSE; PROV_ENUMALGS *pEnumAlgs = NULL; LOG_BEGIN_CRYPTOAPI(LocalGetProvParam); switch (dwParam) { case PP_ENUMALGS_EX: case PP_ENUMALGS: SetLocalCallInfo(pLocalCallInfo, FALSE); // Build the list of supported algorithms if we haven't done so already // for this context. if (NULL == pLocalUserCtx->pSupportedAlgs) { dwSts = BuildSupportedAlgorithmsList(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; } // Reset the enumeration if requested. if (CRYPT_FIRST == dwFlags) pLocalUserCtx->pCurrentAlg = pLocalUserCtx->pSupportedAlgs; // Is the enumeration already done? if (NULL == pLocalUserCtx->pCurrentAlg) { dwSts = ERROR_NO_MORE_ITEMS; goto Ret; } cbCurrent = (PP_ENUMALGS_EX == dwParam) ? sizeof(PROV_ENUMALGS_EX) : sizeof(PROV_ENUMALGS); // Check the size of the caller's buffer or if caller is merely // requesting size info. if (NULL == pbData || *pcbDataLen < cbCurrent) { *pcbDataLen = cbCurrent; if (NULL != pbData) dwSts = ERROR_MORE_DATA; goto Ret; } *pcbDataLen = cbCurrent; if (PP_ENUMALGS_EX == dwParam) { memcpy(pbData, &pLocalUserCtx->pCurrentAlg->EnumalgsEx, cbCurrent); } else { // Have to do a member-wise copy of the PROV_ENUMALGS struct since // the list we maintain is PROV_ENUMALGS_EX. pEnumAlgs = (PROV_ENUMALGS *) pbData; pEnumAlgs->aiAlgid = pLocalUserCtx->pCurrentAlg->EnumalgsEx.aiAlgid; pEnumAlgs->dwBitLen = pLocalUserCtx->pCurrentAlg->EnumalgsEx.dwDefaultLen; pEnumAlgs->dwNameLen = pLocalUserCtx->pCurrentAlg->EnumalgsEx.dwNameLen; memcpy( pEnumAlgs->szName, pLocalUserCtx->pCurrentAlg->EnumalgsEx.szName, pEnumAlgs->dwNameLen); } pLocalUserCtx->pCurrentAlg = pLocalUserCtx->pCurrentAlg->pNext; break; case PP_ENUMCONTAINERS: SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pUserContext, TRUE); if (ERROR_SUCCESS != dwSts) goto Ret; if (NULL == pLocalUserCtx->mszEnumContainers || CRYPT_FIRST == dwFlags) { // // We need to build a new list of containers on this card if we // haven't already done so, or if the caller is starting a new // container enumeration. // if (NULL != pLocalUserCtx->mszEnumContainers) { CspFreeH(pLocalUserCtx->mszEnumContainers); pLocalUserCtx->mszEnumContainers = NULL; pLocalUserCtx->mszCurrentEnumContainer = NULL; } dwSts = BeginCardCapiCall(pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; fTransacted = TRUE; dwSts = ContainerMapEnumContainers( pLocalUserCtx->pCardState, &cContainers, &mwszContainers); if (ERROR_SUCCESS != dwSts) goto Ret; cchContainers = CountCharsInMultiSz(mwszContainers); cbContainers = WideCharToMultiByte( CP_ACP, 0, mwszContainers, cchContainers, NULL, 0, NULL, NULL); if (0 == cbContainers) { dwSts = GetLastError(); goto Ret; } pLocalUserCtx->mszEnumContainers = (LPSTR) CspAllocH(cbContainers); LOG_CHECK_ALLOC(pLocalUserCtx->mszEnumContainers); cbContainers = WideCharToMultiByte( CP_ACP, 0, mwszContainers, cchContainers, pLocalUserCtx->mszEnumContainers, cbContainers, NULL, NULL); if (0 == cbContainers) { dwSts = GetLastError(); goto Ret; } pLocalUserCtx->mszCurrentEnumContainer = pLocalUserCtx->mszEnumContainers; } if (NULL == pLocalUserCtx->mszCurrentEnumContainer || '\0' == pLocalUserCtx->mszCurrentEnumContainer[0]) { dwSts = ERROR_NO_MORE_ITEMS; goto Ret; } cbCurrent = (strlen( pLocalUserCtx->mszCurrentEnumContainer) + 1) * sizeof(CHAR); if (NULL == pbData || *pcbDataLen < cbCurrent) { *pcbDataLen = cbCurrent; if (NULL != pbData) dwSts = ERROR_MORE_DATA; goto Ret; } *pcbDataLen = cbCurrent; memcpy( pbData, (PBYTE) pLocalUserCtx->mszCurrentEnumContainer, cbCurrent); ((PBYTE) pLocalUserCtx->mszCurrentEnumContainer) += cbCurrent; break; default: SetLocalCallInfo(pLocalCallInfo, TRUE); break; } Ret: if (fTransacted) EndCardCapiCall(pUserContext); LOG_END_CRYPTOAPI(LocalGetProvParam, dwSts); return dwSts; } // // Function: LocalExportKey // DWORD WINAPI LocalExportKey( IN PKEY_CONTEXT pKeyCtx, IN PKEY_CONTEXT pPubKeyCtx, IN DWORD dwBlobType, IN DWORD dwFlags, OUT LPBYTE pbData, IN OUT LPDWORD pcbDataLen, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocalUserCtx = (PLOCAL_USER_CONTEXT) pKeyCtx->pUserContext->pvLocalUserContext; CONTAINER_INFO ContainerInfo; DWORD cbKey = 0; PBYTE pbKey = 0; PLOCAL_KEY_CONTEXT pLocalKeyCtx = NULL; LOG_BEGIN_CRYPTOAPI(LocalExportKey); memset(&ContainerInfo, 0, sizeof(ContainerInfo)); SetLocalCallInfo(pLocalCallInfo, TRUE); switch (dwBlobType) { case PRIVATEKEYBLOB: SetLocalCallInfo(pLocalCallInfo, FALSE); if (NULL != pKeyCtx->pvLocalKeyContext) { pLocalKeyCtx = (PLOCAL_KEY_CONTEXT) pKeyCtx->pvLocalKeyContext; if (NULL != pLocalKeyCtx->pbArchivablePrivateKey) { if ( *pcbDataLen < pLocalKeyCtx->cbArchivablePrivateKey || NULL == pbData) { *pcbDataLen = pLocalKeyCtx->cbArchivablePrivateKey; if (NULL != pbData) dwSts = ERROR_MORE_DATA; goto Ret; } *pcbDataLen = pLocalKeyCtx->cbArchivablePrivateKey; memcpy( pbData, pLocalKeyCtx->pbArchivablePrivateKey, pLocalKeyCtx->cbArchivablePrivateKey); break; } } dwSts = (DWORD) NTE_BAD_TYPE; break; case PUBLICKEYBLOB: SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = BeginCardCapiCall(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspGetContainerInfo( pLocalUserCtx->pCardState, pLocalUserCtx->bContainerIndex, 0, &ContainerInfo); if (ERROR_SUCCESS != dwSts) goto Ret; switch (pKeyCtx->Algid) { case AT_SIGNATURE: cbKey = ContainerInfo.cbSigPublicKey; pbKey = ContainerInfo.pbSigPublicKey; break; case AT_KEYEXCHANGE: cbKey = ContainerInfo.cbKeyExPublicKey; pbKey = ContainerInfo.pbKeyExPublicKey; break; default: dwSts = (DWORD) NTE_BAD_KEY; goto Ret; } if (*pcbDataLen < cbKey || NULL == pbData) { *pcbDataLen = cbKey; if (NULL != pbData) dwSts = ERROR_MORE_DATA; goto Ret; } *pcbDataLen = cbKey; memcpy(pbData, pbKey, cbKey); break; } Ret: EndCardCapiCall(pKeyCtx->pUserContext); CleanupContainerInfo(&ContainerInfo); LOG_END_CRYPTOAPI(LocalExportKey, dwSts); return dwSts; } // // Function: LocalImportKey // DWORD WINAPI LocalImportKey( IN PKEY_CONTEXT pKeyContext, IN PBYTE pbData, IN DWORD cbDataLen, IN PKEY_CONTEXT pPubKey, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; BLOBHEADER *pBlobHeader = (BLOBHEADER *) pbData; PBYTE pbDecrypted = NULL; DWORD cbDecrypted = 0; CARD_PRIVATE_KEY_DECRYPT_INFO DecryptInfo; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pKeyContext->pUserContext->pvLocalUserContext; PBYTE pbPlaintextBlob = NULL; DWORD cbPlaintextBlob = 0; memset(&DecryptInfo, 0, sizeof(DecryptInfo)); LOG_BEGIN_CRYPTOAPI(LocalImportKey); switch (pBlobHeader->bType) { case SIMPLEBLOB: SetLocalCallInfo(pLocalCallInfo, FALSE); // Only allow Key Exchange type keys to decrypt other keys if (AT_SIGNATURE == pPubKey->Algid) { dwSts = (DWORD) NTE_BAD_TYPE; goto Ret; } if (CALG_RSA_KEYX != *(ALG_ID *) (pbData + sizeof(BLOBHEADER))) { dwSts = (DWORD) NTE_BAD_ALGID; goto Ret; } if (pPubKey->pUserContext != pKeyContext->pUserContext) { dwSts = (DWORD) NTE_BAD_UID; goto Ret; } dwSts = CheckForVerifyContext(pKeyContext->pUserContext, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; // // Decrypt a session key blob using the private key. // dwSts = BeginCardCapiCall(pKeyContext->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspAuthenticateUser(pKeyContext->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; DecryptInfo.cbData = cbDataLen - sizeof(BLOBHEADER) - sizeof(ALG_ID); DecryptInfo.dwKeySpec = AT_KEYEXCHANGE; DecryptInfo.dwVersion = CARD_PRIVATE_KEY_DECRYPT_INFO_CURRENT_VERSION; DecryptInfo.pbData = pbData + sizeof(BLOBHEADER) + sizeof(ALG_ID); DecryptInfo.bContainerIndex = pLocal->bContainerIndex; dwSts = CspPrivateKeyDecrypt( pLocal->pCardState, &DecryptInfo); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = VerifyPKCS2Padding( DecryptInfo.pbData, DecryptInfo.cbData, &pbDecrypted, &cbDecrypted); if (ERROR_SUCCESS != dwSts) goto Ret; // // Now we can build a PLAINTEXTKEYBLOB with the decrypted session key // and import it into the helper CSP. // cbPlaintextBlob = sizeof(BLOBHEADER) + sizeof(DWORD) + cbDecrypted; pbPlaintextBlob = CspAllocH(cbPlaintextBlob); LOG_CHECK_ALLOC(pbPlaintextBlob); ((BLOBHEADER *) pbPlaintextBlob)->aiKeyAlg = pBlobHeader->aiKeyAlg; ((BLOBHEADER *) pbPlaintextBlob)->bType = PLAINTEXTKEYBLOB; ((BLOBHEADER *) pbPlaintextBlob)->bVersion = CUR_BLOB_VERSION; *(DWORD *) (pbPlaintextBlob + sizeof(BLOBHEADER)) = cbDecrypted; memcpy( pbPlaintextBlob + sizeof(BLOBHEADER) + sizeof(DWORD), pbDecrypted, cbDecrypted); if (! CryptImportKey( pKeyContext->pUserContext->hSupportProv, pbPlaintextBlob, cbPlaintextBlob, 0, pKeyContext->dwFlags, &pKeyContext->hSupportKey)) { dwSts = GetLastError(); goto Ret; } pKeyContext->Algid = pBlobHeader->aiKeyAlg; pKeyContext->cKeyBits = cbDecrypted * 8; break; case PRIVATEKEYBLOB: // We don't allow importing privatekey blobs into the smartcard // CSP, and it doesn't make sense to use the helper CSP for this, // so fail. SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = (DWORD) NTE_BAD_TYPE; break; default: // For all other blob types, let the helper CSP take a shot. SetLocalCallInfo(pLocalCallInfo, TRUE); } Ret: EndCardCapiCall(pKeyContext->pUserContext); if (pbDecrypted) CspFreeH(pbDecrypted); if (pbPlaintextBlob) CspFreeH(pbPlaintextBlob); LOG_END_CRYPTOAPI(LocalImportKey, dwSts); return dwSts; } // // Function: LocalEncrypt // DWORD WINAPI LocalEncrypt( IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN HCRYPTHASH hHash, IN BOOL fFinal, IN DWORD dwFlags, IN OUT LPBYTE pbData, IN OUT LPDWORD pcbDataLen, IN DWORD cbBufLen) { *pcbDataLen = 0; // // TODO // return ERROR_CALL_NOT_IMPLEMENTED; } // // Function: LocalDecrypt // DWORD WINAPI LocalDecrypt( IN PKEY_CONTEXT pKeyCtx, IN PHASH_CONTEXT pHashCtx, IN BOOL fFinal, IN DWORD dwFlags, IN OUT LPBYTE pbData, IN OUT LPDWORD pcbDataLen, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PBYTE pbDecrypted = NULL; DWORD cbDecrypted = 0; CARD_PRIVATE_KEY_DECRYPT_INFO DecryptInfo; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pKeyCtx->pUserContext->pvLocalUserContext; LOG_BEGIN_CRYPTOAPI(LocalDecrypt); memset(&DecryptInfo, 0, sizeof(DecryptInfo)); dwSts = CheckForVerifyContext(pKeyCtx->pUserContext, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; if (AT_KEYEXCHANGE == pKeyCtx->Algid) { SetLocalCallInfo(pLocalCallInfo, FALSE); } else { SetLocalCallInfo(pLocalCallInfo, TRUE); goto Ret; } dwSts = BeginCardCapiCall(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = CspAuthenticateUser(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; DecryptInfo.cbData = *pcbDataLen; DecryptInfo.dwKeySpec = AT_KEYEXCHANGE; DecryptInfo.dwVersion = CARD_PRIVATE_KEY_DECRYPT_INFO_CURRENT_VERSION; DecryptInfo.pbData = pbData; DecryptInfo.bContainerIndex = pLocal->bContainerIndex; dwSts = CspPrivateKeyDecrypt( pLocal->pCardState, &DecryptInfo); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = VerifyPKCS2Padding( DecryptInfo.pbData, DecryptInfo.cbData, &pbDecrypted, &cbDecrypted); if (ERROR_SUCCESS != dwSts) goto Ret; memcpy( pbData, pbDecrypted, cbDecrypted); *pcbDataLen = cbDecrypted; Ret: EndCardCapiCall(pKeyCtx->pUserContext); if (pbDecrypted) CspFreeH(pbDecrypted); LOG_END_CRYPTOAPI(LocalDecrypt, dwSts); return dwSts; } // // Function: LocalSignHash // DWORD WINAPI LocalSignHash( IN PHASH_CONTEXT pHashCtx, IN DWORD dwKeySpec, IN DWORD dwFlags, OUT LPBYTE pbSignature, IN OUT LPDWORD pcbSigLen, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pHashCtx->pUserContext->pvLocalUserContext; PBYTE pbHash = NULL; PBYTE pbSig = NULL; DWORD cbHash = 0; DWORD cbPrivateKey = 0; ALG_ID aiHash = 0; DWORD cbData = 0; CARD_PRIVATE_KEY_DECRYPT_INFO DecryptInfo; RSAPUBKEY *pPubKey = NULL; LOG_BEGIN_CRYPTOAPI(LocalSignHash); SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pHashCtx->pUserContext, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; memset(&DecryptInfo, 0, sizeof(DecryptInfo)); dwSts = BeginCardCapiCall(pHashCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; // // Get the size of the private key that will be used for signing // dwSts = GetKeyModulusLength( pHashCtx->pUserContext, dwKeySpec, &cbPrivateKey); if (ERROR_SUCCESS != dwSts) goto Ret; if (*pcbSigLen < cbPrivateKey || NULL == pbSignature) { *pcbSigLen = cbPrivateKey; if (NULL != pbSignature) dwSts = ERROR_MORE_DATA; goto Ret; } // // Get the hash value we're going to sign // if (! CryptGetHashParam( pHashCtx->hSupportHash, HP_HASHVAL, NULL, &cbHash, 0)) { dwSts = GetLastError(); goto Ret; } pbHash = (PBYTE) CspAllocH(cbHash); LOG_CHECK_ALLOC(pbHash); if (! CryptGetHashParam( pHashCtx->hSupportHash, HP_HASHVAL, pbHash, &cbHash, 0)) { dwSts = GetLastError(); goto Ret; } // // Get the algid of this hash so the correct encoding can be applied // cbData = sizeof(aiHash); if (! CryptGetHashParam( pHashCtx->hSupportHash, HP_ALGID, (PBYTE) &aiHash, &cbData, 0)) { dwSts = GetLastError(); goto Ret; } // // Apply PKCS1 encoding to this hash data. It gets padded out to the // length of the key modulus. The padded buffer is allocated for us. // dwSts = ApplyPKCS1SigningFormat( aiHash, pbHash, cbHash, 0, cbPrivateKey, &pbSig); if (ERROR_SUCCESS != dwSts) goto Ret; // // Private key decrypt the formatted data // dwSts = CspAuthenticateUser(pHashCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; DecryptInfo.cbData = cbPrivateKey; DecryptInfo.pbData = pbSig; DecryptInfo.dwKeySpec = dwKeySpec; DecryptInfo.dwVersion = CARD_PRIVATE_KEY_DECRYPT_INFO_CURRENT_VERSION; DecryptInfo.bContainerIndex = pLocal->bContainerIndex; dwSts = CspPrivateKeyDecrypt( pLocal->pCardState, &DecryptInfo); if (ERROR_SUCCESS != dwSts) goto Ret; // // Copy the completed signature into the caller's buffer // memcpy( pbSignature, pbSig, cbPrivateKey); *pcbSigLen = cbPrivateKey; Ret: EndCardCapiCall(pHashCtx->pUserContext); if (pbSig) CspFreeH(pbSig); if (pbHash) CspFreeH(pbHash); LOG_END_CRYPTOAPI(LocalSignHash, dwSts); return dwSts; } // // Function: LocalVerifySignature // DWORD WINAPI LocalVerifySignature( IN PHASH_CONTEXT pHashCtx, IN CONST BYTE *pbSignature, IN DWORD cbSigLen, IN PKEY_CONTEXT pKeyCtx, IN DWORD dwFlags, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; PLOCAL_USER_CONTEXT pLocal = (PLOCAL_USER_CONTEXT) pHashCtx->pUserContext->pvLocalUserContext; CONTAINER_INFO ContainerInfo; HCRYPTKEY hKey = 0; BOOL fSignature = (AT_SIGNATURE == pKeyCtx->Algid); LOG_BEGIN_CRYPTOAPI(LocalVerifySignature); memset(&ContainerInfo, 0, sizeof(ContainerInfo)); SetLocalCallInfo(pLocalCallInfo, FALSE); dwSts = CheckForVerifyContext(pHashCtx->pUserContext, FALSE); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = BeginCardCapiCall(pHashCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; // // Get the public key // dwSts = CspGetContainerInfo( pLocal->pCardState, pLocal->bContainerIndex, 0, &ContainerInfo); if (ERROR_SUCCESS != dwSts) goto Ret; // // Import the public key into the helper CSP // if (! CryptImportKey( pHashCtx->pUserContext->hSupportProv, fSignature ? ContainerInfo.pbSigPublicKey : ContainerInfo.pbKeyExPublicKey, fSignature ? ContainerInfo.cbSigPublicKey : ContainerInfo.cbKeyExPublicKey, 0, 0, &hKey)) { dwSts = GetLastError(); goto Ret; } // // Use the helper CSP to verify the signature // if (! CryptVerifySignature( pHashCtx->hSupportHash, pbSignature, cbSigLen, hKey, NULL, dwFlags)) { dwSts = GetLastError(); goto Ret; } Ret: EndCardCapiCall(pHashCtx->pUserContext); CleanupContainerInfo(&ContainerInfo); if (hKey) CryptDestroyKey(hKey); LOG_END_CRYPTOAPI(LocalVerifySignature, dwSts); return dwSts; } // // Function: LocalGetUserKey // DWORD WINAPI LocalGetUserKey( IN PKEY_CONTEXT pKeyCtx, OUT PLOCAL_CALL_INFO pLocalCallInfo) { DWORD dwSts = ERROR_SUCCESS; DWORD cbKey = 0; LOG_BEGIN_CRYPTOAPI(LocalGetUserKey); if (AT_SIGNATURE == pKeyCtx->Algid || AT_KEYEXCHANGE == pKeyCtx->Algid) { SetLocalCallInfo(pLocalCallInfo, FALSE); } else { SetLocalCallInfo(pLocalCallInfo, TRUE); goto Ret; } dwSts = CheckForVerifyContext(pKeyCtx->pUserContext, TRUE); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = BeginCardCapiCall(pKeyCtx->pUserContext); if (ERROR_SUCCESS != dwSts) goto Ret; dwSts = GetKeyModulusLength( pKeyCtx->pUserContext, pKeyCtx->Algid, &cbKey); if (ERROR_SUCCESS != dwSts) goto Ret; if (cbKey) { pKeyCtx->cKeyBits = cbKey * 8; } else { dwSts = (DWORD) NTE_NO_KEY; goto Ret; } Ret: EndCardCapiCall(pKeyCtx->pUserContext); LOG_END_CRYPTOAPI(LocalGetUserKey, dwSts); return dwSts; } // // Function: LocalDuplicateKey // DWORD WINAPI LocalDuplicateKey( IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN LPDWORD pdwReserved, IN DWORD dwFlags, OUT HCRYPTKEY *phKey) { return ERROR_CALL_NOT_IMPLEMENTED; } // // Function: UnloadStrings // void UnloadStrings( IN PCSP_STRING pStrings, IN DWORD cStrings) { DWORD iString = 0; for ( iString = 0; iString < cStrings; iString++) { if (NULL != pStrings[iString].wszString) { CspFreeH(pStrings[iString].wszString); pStrings[iString].wszString = NULL; } } } // // Function: LoadStrings // DWORD LoadStrings( IN HMODULE hMod, IN PCSP_STRING pStrings, IN DWORD cStrings) { DWORD dwSts = ERROR_INTERNAL_ERROR; DWORD cch; WCHAR wsz[MAX_PATH]; DWORD iString = 0; for ( iString = 0; iString < cStrings; iString++) { cch = LoadStringW( hMod, pStrings[iString].dwResource, wsz, sizeof(wsz) / sizeof(wsz[0])); if (0 == cch) { dwSts = GetLastError(); goto ErrorExit; } pStrings[iString].wszString = (LPWSTR) CspAllocH(sizeof(WCHAR) * (cch + 1)); if (NULL == pStrings[iString].wszString) { dwSts = ERROR_NOT_ENOUGH_MEMORY; goto ErrorExit; } wcscpy( pStrings[iString].wszString, wsz); } return ERROR_SUCCESS; ErrorExit: UnloadStrings(pStrings, cStrings); return dwSts; } // // Function: LocalDllInitialize // BOOL WINAPI LocalDllInitialize( IN PVOID hmod, IN ULONG Reason, IN PCONTEXT Context) { DWORD dwLen = 0; static BOOL fLoadedStrings = FALSE; static BOOL fInitializedCspState = FALSE; BOOL fSuccess = FALSE; switch (Reason) { case DLL_PROCESS_ATTACH: // load strings if (ERROR_SUCCESS != LoadStrings( hmod, g_Strings, sizeof(g_Strings) / sizeof(g_Strings[0]))) goto Cleanup; fLoadedStrings = TRUE; // Initialize global CSP data for this process if (ERROR_SUCCESS != InitializeCspState(hmod)) goto Cleanup; fInitializedCspState = TRUE; CspInitializeDebug(); fSuccess = TRUE; break; case DLL_PROCESS_DETACH: CspUnloadDebug(); fSuccess = TRUE; goto Cleanup; } return fSuccess; Cleanup: if (fLoadedStrings) { UnloadStrings( g_Strings, sizeof(g_Strings) / sizeof(g_Strings[0])); fLoadedStrings = FALSE; } if (fInitializedCspState) { DeleteCspState(); fInitializedCspState = FALSE; } return fSuccess; } // // Function: LocalDllRegisterServer // DWORD WINAPI LocalDllRegisterServer(void) { HKEY hKey = 0; DWORD dwSts = ERROR_SUCCESS; dwSts = RegOpenProviderKey(&hKey, KEY_ALL_ACCESS); if (ERROR_SUCCESS != dwSts) goto Ret; // // Add CSP default configuration // dwSts = RegConfigAddEntries(hKey); if (ERROR_SUCCESS != dwSts) goto Ret; Ret: if (hKey) RegCloseKey(hKey); return dwSts; } // // Declaration of the LOCAL_CSP_INFO structure required by the // CSP lib. // LOCAL_CSP_INFO LocalCspInfo = { LocalAcquireContext, //pfnLocalAcquireContext; LocalReleaseContext, //pfnLocalReleaseContext; LocalGenKey, //pfnLocalGenKey; NULL, //pfnLocalDeriveKey; LocalDestroyKey, //pfnLocalDestroyKey; LocalSetKeyParam, //pfnLocalSetKeyParam; LocalGetKeyParam, //pfnLocalGetKeyParam; LocalSetProvParam, //pfnLocalSetProvParam; LocalGetProvParam, //pfnLocalGetProvParam; NULL, //pfnLocalSetHashParam; NULL, //pfnLocalGetHashParam; LocalExportKey, //pfnLocalExportKey; LocalImportKey, //pfnLocalImportKey; NULL, //pfnLocalEncrypt; LocalDecrypt, //pfnLocalDecrypt; NULL, //pfnLocalCreateHash; NULL, //pfnLocalHashData; NULL, //pfnLocalHashSessionKey; LocalSignHash, //pfnLocalSignHash; NULL, //pfnLocalDestroyHash; LocalVerifySignature, //pfnLocalVerifySignature; NULL, //pfnLocalGenRandom; LocalGetUserKey, //pfnLocalGetUserKey; NULL, //pfnLocalDuplicateHash; NULL, //pfnLocalDuplicateKey; LocalDllInitialize, //pfnLocalDllInitialize; LocalDllRegisterServer, //pfnLocalDllRegisterServer; NULL, //pfnLocalDllUnregisterServer; MS_SCARD_PROV_W, //wszProviderName; PROV_RSA_FULL, //dwProviderType; CRYPT_IMPL_REMOVABLE, MS_STRONG_PROV, //wszSupportProviderName; PROV_RSA_FULL //dwSupportProviderType; };