Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4145 lines
106 KiB

/*++
Copyright (C) Microsoft Corporation, 2001
Module Name:
Windows for Smart Cards Base CSP
Abstract:
Author:
Dan Griffin
Notes:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <wincrypt.h>
#pragma warning(push)
#pragma warning(disable:4201)
// Disable error C4201 in public header
// nonstandard extension used : nameless struct/union
#include <winscard.h>
#pragma warning(pop)
#include <cspdk.h>
#include <md5.h>
#include <stdlib.h>
#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;
};