// Cspi.cpp -- Schlumberger Cryptographic Service Provider Interface definition // (c) Copyright Schlumberger Technology Corp., unpublished work, created // 1999. This computer program includes Confidential, Proprietary // Information and is a Trade Secret of Schlumberger Technology Corp. All // use, disclosure, and/or reproduction is prohibited unless authorized // in writing. All Rights Reserved. // Don't allow the min & max macros in WINDEF.H to be defined so the // min/max methods declared in limits are accessible. #define NOMINMAX #if defined(_UNICODE) #if !defined(UNICODE) #define UNICODE #endif //!UNICODE #endif //_UNICODE #if defined(UNICODE) #if !defined(_UNICODE) #define _UNICODE #endif //!_UNICODE #endif //UNICODE #include "stdafx.h" #include // for auto_ptr #include #include #include #include #include #include #include #include #include #include #include #include "Guard.h" #include "slbCsp.h" #include "CspProfile.h" #include "CryptCtx.h" #include "SesKeyCtx.h" #include "PubKeyCtx.h" #include "HashCtx.h" #include "CSpec.h" #include "Blob.h" #include "Cspi.h" #include "StResource.h" #include "scarderr.h" // must be last for now using namespace std; using namespace scu; #define HANDLEID_CRYPT_CONTEXT 17 static CHandleList hlCryptContexts(HANDLEID_CRYPT_CONTEXT); #if defined(_DEBUG) #define CSPI_DEFINE_ROUTINE_NAME(sRoutine) \ LPCTSTR cspi_sRoutine = sRoutine #define CSPI_TRACE_ROUTINE(sFlag) \ TRACE(TEXT("%s %s, Thread Id %08X\n"), cspi_sRoutine, \ sFlag, GetCurrentThreadId()); #else #define CSPI_DEFINE_ROUTINE_NAME(sRoutine) #define CSPI_TRACE_ROUTINE(sFlag) #endif // defined(_DEBUG) typedef DWORD CapiError; #define CSPI_TRY(Routine) \ { \ bool cspi_fExceptionCaught = false; \ CapiError cspi_ce = AsCapiError(ERROR_SUCCESS); \ \ { \ AFX_MANAGE_STATE(AfxGetStaticModuleState()); \ \ CSPI_DEFINE_ROUTINE_NAME(TEXT(#Routine)); \ CSPI_TRACE_ROUTINE(TEXT("Begin")); \ \ CWaitCursor cspi_wc; \ \ try #define CSPI_CATCH(fStatus) \ catch (scu::Exception const &rExc) \ { \ cspi_fExceptionCaught = true; \ cspi_ce = AsCapiError(rExc); \ } \ \ catch (std::bad_alloc const &rExc) \ { \ cspi_fExceptionCaught = true; \ cspi_ce = AsCapiError(rExc); \ } \ \ catch (DWORD dwError) \ { \ cspi_fExceptionCaught = true; \ cspi_ce = AsCapiError(dwError); \ } \ \ catch (...) \ { \ cspi_fExceptionCaught = true; \ cspi_ce = AsCapiError(NTE_FAIL); \ } \ \ CSPI_TRACE_ROUTINE(TEXT("End")); \ \ (fStatus) = cspi_fExceptionCaught \ ? CRYPT_FAILED \ : CRYPT_SUCCEED; \ } \ \ SetLastError(cspi_ce); \ \ } namespace { // Helper routines template struct ErrorCodeMap { typename CauseCode m_cc; DWORD m_dwErrorCode; }; template DWORD FindErrorCode(ErrorCodeMap const *pFirst, ErrorCodeMap const *pLast, typename CauseCode cc) { bool fFound = false; DWORD dwErrorCode = NTE_FAIL; for (ErrorCodeMap const *p = pFirst; !fFound && (p < pLast); p++) { if (p->m_cc == cc) { dwErrorCode = p->m_dwErrorCode; fFound = true; } } return dwErrorCode; } ErrorCodeMap CciErrorMap[] = { { cci::ccBadKeySpec, SCARD_E_INVALID_VALUE }, { cci::ccBadPinLength, SCARD_E_INVALID_VALUE }, { cci::ccNoCertificate, SCARD_E_NO_SUCH_CERTIFICATE }, { cci::ccNotPersonalized, SCARD_E_UNSUPPORTED_FEATURE }, { cci::ccOutOfPrivateKeySlots, NTE_TOKEN_KEYSET_STORAGE_FULL }, { cci::ccOutOfSymbolTableSpace, NTE_TOKEN_KEYSET_STORAGE_FULL }, { cci::ccOutOfSymbolTableEntries, NTE_TOKEN_KEYSET_STORAGE_FULL }, }; ErrorCodeMap SmartCardErrorMap[] = { { iop::CSmartCard::ccAccessConditionsNotMet, SCARD_W_SECURITY_VIOLATION }, { iop::CSmartCard::ccAlgorithmIdNotSupported, CRYPT_E_UNKNOWN_ALGO }, { iop::CSmartCard::ccAuthenticationFailed, SCARD_W_WRONG_CHV }, { iop::CSmartCard::ccChvVerificationFailedMoreAttempts, SCARD_W_WRONG_CHV }, { iop::CSmartCard::ccDataPossiblyCorrupted, ERROR_FILE_CORRUPT }, { iop::CSmartCard::ccFileExists, ERROR_FILE_EXISTS }, { iop::CSmartCard::ccInsufficientSpace, NTE_TOKEN_KEYSET_STORAGE_FULL }, { iop::CSmartCard::ccOutOfSpaceToCreateFile, NTE_TOKEN_KEYSET_STORAGE_FULL }, { iop::CSmartCard::ccKeyBlocked, SCARD_W_CHV_BLOCKED }, { iop::CSmartCard::ccNoAccess, SCARD_W_SECURITY_VIOLATION }, { iop::CSmartCard::ccReturnedDataCorrupted, ERROR_FILE_CORRUPT }, { iop::CSmartCard::ccTimeOut, E_UNEXPECTED }, { iop::CSmartCard::ccUnidentifiedTechnicalProblem, E_UNEXPECTED }, { iop::CSmartCard::ccVerificationFailed, SCARD_W_WRONG_CHV }, }; ErrorCodeMap IopErrorMap[] = { { iop::ccAlgorithmIdNotSupported, CRYPT_E_UNKNOWN_ALGO }, { iop::ccInvalidParameter, E_INVALIDARG }, { iop::ccNoResponseAvailable, E_UNEXPECTED }, { iop::ccResourceManagerDisabled, SCARD_E_NO_SERVICE }, { iop::ccUnknownCard, SCARD_E_UNKNOWN_CARD }, { iop::ccUnsupportedCommand, SCARD_E_UNSUPPORTED_FEATURE }, }; bool IsHResult(DWORD dwError) { return (0 != HRESULT_SEVERITY(static_cast(dwError))) ? true : false; } CapiError AsCapiError(HRESULT hr) { // If the HRESULT has been converted from a Win32 error code // (WIN32 facility), then convert it back to a Win32 error // code. These types of HRESULTs confuse WinLogon, according // to Doug Barlow (Microsoft) return (FACILITY_WIN32 == HRESULT_FACILITY(hr)) ? HRESULT_CODE(hr) : static_cast(hr); } CapiError AsCapiError(DWORD dwError) { if (IsHResult(dwError)) dwError = AsCapiError(static_cast(dwError)); return dwError; } CapiError AsCapiError(cci::Exception const &rExc) { return AsCapiError(FindErrorCode(CciErrorMap, (CciErrorMap + (sizeof CciErrorMap / sizeof *CciErrorMap)), rExc.Cause())); } CapiError AsCapiError(iop::Exception const &rExc) { return AsCapiError(FindErrorCode(IopErrorMap, (IopErrorMap + (sizeof IopErrorMap / sizeof *IopErrorMap)), rExc.Cause())); } CapiError AsCapiError(scu::OsException const &rExc) { return AsCapiError(rExc.Cause()); } CapiError AsCapiError(iop::CSmartCard::Exception const &rExc) { return AsCapiError(FindErrorCode(SmartCardErrorMap, (SmartCardErrorMap + (sizeof SmartCardErrorMap / sizeof *SmartCardErrorMap)), rExc.Cause())); } CapiError AsCapiError(pki::Exception const &rExc) { return AsCapiError(ERROR_INVALID_PARAMETER); } CapiError AsCapiError(scu::Exception const &rExc) { using namespace scu; CapiError ce; switch (rExc.Facility()) { case Exception::fcCCI: ce = AsCapiError(static_cast(rExc)); break; case Exception::fcIOP: ce = AsCapiError(static_cast(rExc)); break; case Exception::fcOS: ce = AsCapiError(static_cast(rExc)); break; case Exception::fcPKI: ce = AsCapiError(static_cast(rExc)); break; case Exception::fcSmartCard: ce = AsCapiError(static_cast(rExc)); break; default: ce = AsCapiError(E_UNEXPECTED); break; } return ce; } CapiError AsCapiError(std::bad_alloc const &rExc) { return AsCapiError(NTE_NO_MEMORY); } void Assign(void *pvDestination, DWORD *pcbDestinationLength, void const *pvSource, size_t cSourceLength) { DWORD dwError = ERROR_SUCCESS; if (pcbDestinationLength) { if (numeric_limits::max() >= cSourceLength) { if (pvSource) { if (pvDestination) { if (*pcbDestinationLength >= cSourceLength) CopyMemory(pvDestination, pvSource, cSourceLength); else dwError = ERROR_MORE_DATA; } } *pcbDestinationLength = cSourceLength; } else dwError = ERROR_INVALID_PARAMETER; } else dwError = ERROR_INVALID_PARAMETER; if (ERROR_SUCCESS != dwError) throw scu::OsException(dwError); } void Assign(void *pvDestination, DWORD *pcbDestinationLength, LPCTSTR pvSource ) { DWORD dwError = ERROR_SUCCESS; if (pcbDestinationLength) { // We want the number of characters including NULL DWORD cSourceLength = _tcslen(pvSource) + 1; if (numeric_limits::max() >= cSourceLength) { if (pvSource) { if (pvDestination) { if (*pcbDestinationLength >= cSourceLength) { char *sTarget = (char *)pvDestination; for(int i =0; i(*(pvSource+i)); } else dwError = ERROR_MORE_DATA; } } *pcbDestinationLength = cSourceLength; } else dwError = ERROR_INVALID_PARAMETER; } else dwError = ERROR_INVALID_PARAMETER; if (ERROR_SUCCESS != dwError) throw scu::OsException(dwError); } void Assign(void *pvDestination, DWORD *pcbDestinationLength, Blob const &rblob) { Assign(pvDestination, pcbDestinationLength, rblob.data(), rblob.length()); } void Assign(void *pvDestination, DWORD cbDestinationLength, Blob const &rblob) { if (cbDestinationLength < rblob.length()) throw scu::OsException(ERROR_INTERNAL_ERROR); Assign(pvDestination, &cbDestinationLength, rblob.data(), rblob.length()); } // Helper to BufferLengthRequired to compare length of strings. struct LengthIsLess : public binary_function { public: explicit LengthIsLess() {}; result_type operator()(first_argument_type lhs, second_argument_type rhs) const { return lhs.length() < rhs.length(); } }; // Return the data buffer length required to hold the largest // string in the vector. DWORD BufferLengthRequired(vector const &rvs) { DWORD dwRequiredLength = 0; vector::const_iterator itLongestString(max_element(rvs.begin(), rvs.end(), LengthIsLess())); if (rvs.end() != itLongestString) dwRequiredLength = itLongestString->length() + 1; return dwRequiredLength; } // Helper to enumerate the container names for the given context, // returning the result in user parameters pbData and pdwDataLen. void EnumContainers(Guarded &rgpCtx, BYTE *pbData, DWORD *pdwDataLen, bool fFirst) { DWORD dwError = ERROR_SUCCESS; string sName; DWORD dwReturnLength = 0; void const *pvReturnData = 0; if (!pbData) { // Return the buffer size required for the longest string auto_ptr apce; if (!fFirst) apce = auto_ptr(new ContainerEnumerator(rgpCtx->CntrEnumerator(false))); else apce = auto_ptr(new ContainerEnumerator(rgpCtx->CntrEnumerator(true))); vector::const_iterator &rit = apce->Iterator(); if(apce->Names().end() != rit) { dwReturnLength = BufferLengthRequired(apce->Names()); if (0 == dwReturnLength) dwError = ERROR_NO_MORE_ITEMS; } else dwError = ERROR_NO_MORE_ITEMS; } else { ContainerEnumerator ce(rgpCtx->CntrEnumerator(fFirst)); vector::const_iterator &rit = ce.Iterator(); if (ce.Names().end() != rit) { sName = *rit++; dwReturnLength = sName.length() + 1; pvReturnData = sName.c_str(); if (dwReturnLength > *pdwDataLen) { // tell'em the size required for the longest name pbData = 0; dwReturnLength = BufferLengthRequired(ce.Names()); dwError = ERROR_MORE_DATA; } else rgpCtx->CntrEnumerator(ce); } else dwError = ERROR_NO_MORE_ITEMS; } if ((ERROR_SUCCESS != dwError) && (ERROR_MORE_DATA != dwError)) throw scu::OsException(dwError); Assign(pbData, pdwDataLen, pvReturnData, dwReturnLength); } bool FlagsAreSet(DWORD dwFlags, DWORD dwFlagsToTestFor) { return (dwFlags & dwFlagsToTestFor) ? true : false; } void Pin(Guarded const &rgpCtx, char const *pszPin) { // TO DO: Should forward PIN setting to aux context // when this context is ephemeral. if (rgpCtx->IsEphemeral()) throw scu::OsException(ERROR_INVALID_PARAMETER); // TO DO: UNICODE ? rgpCtx->Pin(User, pszPin); } // Throw if more than dwValidFlags are set in dwFlags void ValidateFlags(DWORD dwFlags, DWORD dwValidFlags) { if (dwFlags & ~dwValidFlags) throw scu::OsException(NTE_BAD_FLAGS); } class StaleContainerKeyAccumulator : public binary_function, AdaptiveContainerRegistrar::RegistryType::CollectionType::value_type, vector > { public: explicit StaleContainerKeyAccumulator() {} result_type operator()(first_argument_type &rvStaleCntrs, second_argument_type const &rvt) const { if (!rvt.second->CardContext(false)) rvStaleCntrs.push_back(rvt.second); return rvStaleCntrs; } private: }; bool FindCryptCtxForACntr(HAdaptiveContainer &rhAdptCntr) { bool fRetValue = false; for(int i = 0; i< hlCryptContexts.Count();i++) { CryptContext *pCryptCtx = static_cast (hlCryptContexts.GetQuietly(hlCryptContexts.IndexHandle(i))); if( pCryptCtx && pCryptCtx->AdaptiveContainer() == rhAdptCntr) { fRetValue = true; } } return fRetValue; } void CollectRegistryGarbage() { Guarded guard(&AdaptiveContainerRegistrar::Registry()); // serialize registry access AdaptiveContainerRegistrar::ConstRegistryType &rRegistry = AdaptiveContainerRegistrar::Registry(); AdaptiveContainerRegistrar::ConstRegistryType::CollectionType &rcollection = rRegistry(); vector vStaleCntrs(accumulate(rcollection.begin(), rcollection.end(), vector(), StaleContainerKeyAccumulator())); for (vector::iterator iCurrent(vStaleCntrs.begin()); iCurrent != vStaleCntrs.end(); ++iCurrent) { //Lookup the CryptContext list to see if any of these //stale container are referenced. If not, remove them //to avoid memory leaks. if(!FindCryptCtxForACntr(HAdaptiveContainer(*iCurrent))) { //Remove the adaptive container from the registry AdaptiveContainerKey aKey(HCardContext(0), (*iCurrent)->Name()); AdaptiveContainerRegistrar::Discard(aKey); } } } } // namespace ////////////////////////// BEGIN CSP INTERFACE ///////////////////////////// // // See MSDN for documentation on these interfaces. // SLBCSPAPI CPAcquireContext(OUT HCRYPTPROV *phProv, IN LPCTSTR pszContainer, IN DWORD dwFlags, IN PVTableProvStruc pVTable) { BOOL fSts = CRYPT_FAILED; SecureArray sac(80); CSPI_TRY(CPAcquireContext) { Guard grdMaster(TheMasterLock()); CSpec cspec(pszContainer ? AsCCharP(pszContainer) : ""); ValidateFlags(dwFlags, (CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET | CRYPT_DELETEKEYSET | CRYPT_SILENT)); if (!phProv) throw scu::OsException(ERROR_INVALID_PARAMETER); bool const fMakeEphemeral = FlagsAreSet(dwFlags, CRYPT_VERIFYCONTEXT); bool const fGuiEnabled = !(FlagsAreSet(dwFlags, CRYPT_SILENT) || (cspec.CardId().empty() && fMakeEphemeral)); bool const fCreateContainer = FlagsAreSet(dwFlags, CRYPT_NEWKEYSET); auto_ptr apCtx(new CryptContext(cspec, pVTable, fGuiEnabled, fCreateContainer, fMakeEphemeral)); if (FlagsAreSet(dwFlags, CRYPT_DELETEKEYSET)) apCtx->RemoveContainer(); else { *phProv = static_cast(hlCryptContexts.Add(apCtx.get())); apCtx.release(); } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPGetProvParam(IN HCRYPTPROV hProv, IN DWORD dwParam, IN BYTE *pbData, IN OUT DWORD *pdwDataLen, IN DWORD dwFlags) { using namespace ProviderProfile; BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPGetProvParam) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); switch (dwParam) { case PP_CONTAINER: case PP_UNIQUE_CONTAINER: { ValidateFlags(dwFlags, 0); HAdaptiveContainer hacntr = gpCtx->AdaptiveContainer(); if (!hacntr) throw scu::OsException(ERROR_INVALID_PARAMETER); string sName(hacntr->TheCContainer()->Name()); Assign(pbData, pdwDataLen, sName.c_str(), sName.length() + 1); } break; case PP_ENUMALGS: case PP_ENUMALGS_EX: { ValidateFlags(dwFlags, CRYPT_FIRST); AlignedBlob abAlgInfo; gpCtx->EnumAlgorithms(dwParam, dwFlags, (0 != pbData), abAlgInfo); Assign(pbData, pdwDataLen, abAlgInfo.Data(), abAlgInfo.Length()); } break; case PP_ENUMCONTAINERS: ValidateFlags(dwFlags, CRYPT_FIRST | CRYPT_MACHINE_KEYSET); EnumContainers(gpCtx, pbData, pdwDataLen, (CRYPT_FIRST & dwFlags)); break; case PP_IMPTYPE: { ValidateFlags(dwFlags, 0); DWORD const dwImplType = CRYPT_IMPL_MIXED | CRYPT_IMPL_REMOVABLE; Assign(pbData, pdwDataLen, &dwImplType, sizeof dwImplType); } break; case PP_NAME: { ValidateFlags(dwFlags, 0); CString sName(CspProfile::Instance().Name()); Assign(pbData, pdwDataLen, (LPCTSTR)sName); } break; case PP_VERSION: { ValidateFlags(dwFlags, 0); VersionInfo const &ver = CspProfile::Instance().Version(); DWORD dwVersion = (ver.m_dwMajor << 8) | ver.m_dwMinor; Assign(pbData, pdwDataLen, &dwVersion, sizeof dwVersion); } break; case PP_PROVTYPE: { ValidateFlags(dwFlags, 0); DWORD const dwType = CspProfile::Instance().Type(); Assign(pbData, pdwDataLen, &dwType, sizeof dwType); } break; case PP_KEYX_KEYSIZE_INC: // fall-through case PP_SIG_KEYSIZE_INC: { ValidateFlags(dwFlags, 0); DWORD const dwIncrement = 0; Assign(pbData, pdwDataLen, &dwIncrement, sizeof dwIncrement); } break; case PP_ENUMEX_SIGNING_PROT: ValidateFlags(dwFlags, 0); if (CryptGetProvParam(gpCtx->AuxContext(), dwParam, pbData, pdwDataLen, dwFlags)) throw scu::OsException(GetLastError()); break; case PP_KEYSPEC: { ValidateFlags(dwFlags, 0); DWORD const dwKeySpec = AT_SIGNATURE | AT_KEYEXCHANGE; Assign(pbData, pdwDataLen, &dwKeySpec, sizeof dwKeySpec); } break; default: throw scu::OsException(NTE_BAD_TYPE); break; } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPReleaseContext(IN HCRYPTPROV hProv, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPReleaseContext) { ValidateFlags(dwFlags, 0); Guard grdMaster(TheMasterLock()); auto_ptr apCtx(static_cast(hlCryptContexts.Close(hProv))); // TO DO: Verify current thread is this context's owning // thread *and* not currently in use; if not, return // ERROR_BUSY. //Garbage collection of unusable adaptive containers //that this or other threads may have left behind. An unusable //container is one which is not referenced by any of the //existing active crypt contexts. try { CollectRegistryGarbage(); } catch(...) { //Don't let exceptions during garbage collection //propagate outside. These are likely due to other //problems which are better exposed with proper error //codes from other parts of the CSP. } } CSPI_CATCH(fSts); if ((CRYPT_SUCCEED == fSts) && (0 != dwFlags)) fSts = false; return fSts; } SLBCSPAPI CPSetProvParam(IN HCRYPTPROV hProv, IN DWORD dwParam, IN BYTE *pbData, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPSetProvParam) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); switch (dwParam) { case PP_KEYEXCHANGE_PIN: // fall-through case PP_SIGNATURE_PIN: Pin(gpCtx, reinterpret_cast(pbData)); break; case PP_KEYSET_SEC_DESCR: // Ignore this option and return success. break; case PP_USE_HARDWARE_RNG: if (!CryptSetProvParam(gpCtx->AuxContext(), dwParam, pbData, dwFlags)) throw scu::OsException(GetLastError()); break; default: throw scu::OsException(ERROR_NOT_SUPPORTED); break; } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPDeriveKey(IN HCRYPTPROV hProv, IN ALG_ID Algid, IN HCRYPTHASH hHash, IN DWORD dwFlags, OUT HCRYPTKEY *phKey) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPDeriveKey) { Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); auto_ptr apSessionKey(new CSessionKeyContext(gpCtx->AuxContext())); apSessionKey->Derive(Algid, pHash->HashHandleInAuxCSP(), dwFlags); *phKey = gpCtx->Add(apSessionKey); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPDestroyKey(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey) { BOOL fSts = CRYPT_FAILED; // TO DO: Throw ERROR_BUSY if destroying thread is not the owning // thread OR some other thread has a handle to this key. CSPI_TRY(CPDestroyKey) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); // TO DO: Deleting the first handle usually fails for some reason. // For now, protect against the exception and carry on. try { auto_ptr apKey(gpCtx->CloseKey(hKey)); } catch (...) {} } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPDuplicateHash(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN DWORD *pdwReserved, IN DWORD dwFlags, OUT HCRYPTHASH *phDupHash) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPDuplicateHash) { ValidateFlags(dwFlags, 0); Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); auto_ptr apDupHash(pHash->Clone(pdwReserved, dwFlags)); *phDupHash = gpCtx->Add(apDupHash); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPDuplicateKey(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN DWORD *pdwReserved, IN DWORD dwFlags, OUT HCRYPTKEY *phDupKey) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPDuplicateKey) { ValidateFlags(dwFlags, 0); Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CKeyContext *pKey = gpCtx->LookupKey(hKey); auto_ptr apDupKey(pKey->Clone(pdwReserved, dwFlags)); *phDupKey = gpCtx->Add(apDupKey); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPExportKey(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN HCRYPTKEY hExpKey, IN DWORD dwBlobType, IN DWORD dwFlags, OUT BYTE *pbData, IN OUT DWORD *pdwDataLen) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPExportKey) { ValidateFlags(dwFlags, 0); if (PRIVATEKEYBLOB == dwBlobType) throw scu::OsException(ERROR_INVALID_PARAMETER); Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CKeyContext *pKey = gpCtx->LookupKey(hKey); if (KT_SESSIONKEY == pKey->TypeOfKey()) { if ((SIMPLEBLOB != dwBlobType) && (SYMMETRICWRAPKEYBLOB != dwBlobType)) throw scu::OsException(NTE_BAD_TYPE); } else if (PUBLICKEYBLOB != dwBlobType) throw scu::OsException(NTE_BAD_TYPE); if (hExpKey && (PUBLICKEYBLOB == dwBlobType)) throw scu::OsException(ERROR_INVALID_PARAMETER); CKeyContext *pExpKey = 0; if (SIMPLEBLOB != dwBlobType) { pExpKey = hExpKey ? gpCtx->LookupKey(hExpKey) : 0; } else { CPublicKeyContext *pPubExpKey = hExpKey ? gpCtx->LookupPublicKey(hExpKey) : 0; if (pPubExpKey) { if (!pPubExpKey->AuxKeyLoaded()) pPubExpKey->AuxPublicKey(pPubExpKey->AsAlignedBlob(0, dwBlobType)); } pExpKey = pPubExpKey; } HCRYPTKEY hAuxExpKey = pExpKey ? pExpKey->KeyHandleInAuxCSP() : 0; SecureArray abKey(pKey->AsAlignedBlob(hAuxExpKey, dwBlobType)); Assign(pbData, pdwDataLen, abKey.data(), abKey.length()); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPGenKey(IN HCRYPTPROV hProv, IN ALG_ID Algid, IN DWORD dwFlags, OUT HCRYPTKEY *phKey) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPGenKey) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); if (FlagsAreSet(dwFlags, CRYPT_USER_PROTECTED) && !gpCtx->GuiEnabled()) throw scu::OsException(NTE_SILENT_CONTEXT); *phKey = gpCtx->GenerateKey(Algid, dwFlags); fSts = CRYPT_SUCCEED; } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPGetKeyParam(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN DWORD dwParam, OUT BYTE *pbData, IN DWORD *pdwDataLen, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPGetKeyParam) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CKeyContext *pKey = gpCtx->LookupKey(hKey); ValidateFlags(dwFlags, 0); if (KT_PUBLICKEY == pKey->TypeOfKey()) { // Public key CPublicKeyContext *pPubKey = static_cast(pKey); switch (dwParam) { case KP_ALGID: { pPubKey->VerifyKeyExists(); ALG_ID algid; if (pPubKey->KeySpec() == AT_KEYEXCHANGE) algid = CALG_RSA_KEYX; else algid = CALG_RSA_SIGN; Assign(pbData, pdwDataLen, &algid, sizeof algid); } break; case KP_BLOCKLEN: { pPubKey->VerifyKeyExists(); CPublicKeyContext::StrengthType stBlockLen = pPubKey->MaxStrength(); Assign(pbData, pdwDataLen, &stBlockLen, sizeof stBlockLen); } break; case KP_KEYLEN: { pPubKey->VerifyKeyExists(); DWORD dwKeyLen = pPubKey->MaxStrength(); // must be DWORD Assign(pbData, pdwDataLen, &dwKeyLen, sizeof dwKeyLen); } break; case KP_PERMISSIONS: { pPubKey->VerifyKeyExists(); BYTE bPermissions = pPubKey->Permissions(); Assign(pbData, pdwDataLen, &bPermissions, sizeof bPermissions); } break; case KP_CERTIFICATE: { CWaitCursor waitCursor; Blob const blob(pPubKey->Certificate()); Assign(pbData, pdwDataLen, blob); } break; default: throw scu::OsException(NTE_BAD_TYPE); } } else { // session key CSessionKeyContext *pSessionKey = static_cast(pKey); if (!CryptGetKeyParam(pSessionKey->KeyHandleInAuxCSP(), dwParam, pbData, pdwDataLen, dwFlags)) throw scu::OsException(GetLastError()); } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPGenRandom(IN HCRYPTPROV hProv, IN DWORD dwLen, IN OUT BYTE *pbBuffer) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPGenRandom) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); if (!pbBuffer || (0 == dwLen)) throw scu::OsException(ERROR_INVALID_PARAMETER); HAdaptiveContainer hacntr = gpCtx->AdaptiveContainer(); if (!hacntr) { if (!CryptGenRandom(gpCtx->AuxContext(), dwLen, pbBuffer)) throw scu::OsException(GetLastError()); } else { Secured hsacntr(hacntr); hsacntr->TheCContainer()->Card()->GenRandom(dwLen, pbBuffer); } fSts = CRYPT_SUCCEED; } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPGetUserKey(IN HCRYPTPROV hProv, IN DWORD dwKeySpec, OUT HCRYPTKEY *phUserKey) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPGetUserKey) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); // TO DO: This should really be a key pair, not public key. CKeyContext *pKey = 0; auto_ptr apKey(new CPublicKeyContext(gpCtx->AuxContext(), **gpCtx, dwKeySpec)); *phUserKey = gpCtx->Add(apKey); fSts = CRYPT_SUCCEED; } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPImportKey(IN HCRYPTPROV hProv, IN CONST BYTE *pbData, IN DWORD dwDataLen, IN HCRYPTKEY hImpKey, IN DWORD dwFlags, OUT HCRYPTKEY *phKey) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPImportKey) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); if (!phKey) throw scu::OsException(ERROR_INVALID_PARAMETER); // Conversion to do here PUBLICKEYSTRUC const *pPubKey = reinterpret_cast(pbData); if (CUR_BLOB_VERSION != pPubKey->bVersion) // 2 throw scu::OsException(NTE_BAD_VER); switch(pPubKey->bType) { case PRIVATEKEYBLOB: // fall-through intentional case PUBLICKEYBLOB: { DWORD dwKeySpec; switch (pPubKey->aiKeyAlg) { case CALG_RSA_SIGN: dwKeySpec = AT_SIGNATURE; break; case CALG_RSA_KEYX: dwKeySpec = AT_KEYEXCHANGE; break; default: throw scu::OsException(NTE_BAD_ALGID); } SecureArray blbMsKey(pbData, dwDataLen); auto_ptr apKey; if (PRIVATEKEYBLOB == pPubKey->bType) { HCRYPTKEY hEncKey = hImpKey ? gpCtx->LookupSessionKey(hImpKey)->KeyHandleInAuxCSP() : 0; ValidateFlags(dwFlags, CRYPT_EXPORTABLE); apKey = gpCtx->ImportPrivateKey(blbMsKey, dwKeySpec, (dwFlags & CRYPT_EXPORTABLE) != 0, hEncKey); } else { if (0 != hImpKey) throw scu::OsException(ERROR_INVALID_PARAMETER); ValidateFlags(dwFlags, 0); apKey = gpCtx->ImportPublicKey(blbMsKey, dwKeySpec); } *phKey = gpCtx->Add(apKey); } break; case SIMPLEBLOB: { auto_ptr apKey; ALG_ID const *pAlgId = reinterpret_cast(&pbData[sizeof BLOBHEADER]); if (CALG_RSA_KEYX == *pAlgId) { // ignore hImp apKey = gpCtx->UseSessionKey(pbData, dwDataLen, 0, dwFlags); } else { // if other algo then hImp shall specify a session key if (!hImpKey) throw scu::OsException(ERROR_INVALID_PARAMETER); // Find the handle in the Aux CSP corresponding // to hImpKey which should have been previously // imported in the CSP CSessionKeyContext *pSessionKey = gpCtx->LookupSessionKey(hImpKey); apKey = gpCtx->UseSessionKey(pbData, dwDataLen, pSessionKey->KeyHandleInAuxCSP(), dwFlags); } *phKey = gpCtx->Add(apKey); } } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPSetKeyParam(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN DWORD dwParam, IN BYTE *pbData, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPSetKeyParam) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CKeyContext *pKey = gpCtx->LookupKey(hKey); switch (pKey->TypeOfKey()) { case KT_PUBLICKEY: { CPublicKeyContext *pPubKey = static_cast(pKey); // Error return is a special case for KP_CERTIFICATE, // see below. if (KP_CERTIFICATE != dwParam) ValidateFlags(dwFlags, 0); switch(dwParam) { case KP_CERTIFICATE: try { ValidateFlags(dwFlags, 0); pPubKey->Certificate(pbData); } // Xenroll provided by Microsoft only recognizes // SCARD_ errors when writing a certificate. It // does, however, recognize all other errors when // *not* writing a certificate. The MS // OS/Security group recognizes Xenroll is in // error but it will be some time, if ever, before // it will be fixed. So, all errors are // translated into SCARD_ errors at this point. catch (scu::Exception const &rExc) { CapiError ce(AsCapiError(rExc)); if (NTE_TOKEN_KEYSET_STORAGE_FULL == ce) ce = SCARD_E_WRITE_TOO_MANY; else if (FACILITY_SCARD != HRESULT_FACILITY(ce)) ce = SCARD_E_UNEXPECTED; throw scu::OsException(ce); } catch (...) { throw scu::OsException(SCARD_E_UNEXPECTED); } break; case KP_PERMISSIONS: pPubKey->Permissions(*pbData); break; case PP_KEYEXCHANGE_PIN: // fall-through case PP_SIGNATURE_PIN: Pin(gpCtx, reinterpret_cast(pbData)); break; default: throw scu::OsException(ERROR_NOT_SUPPORTED); break; } break; } case KT_SESSIONKEY: { CSessionKeyContext *pSessionKey = static_cast(pKey); if (!CryptSetKeyParam(pSessionKey->KeyHandleInAuxCSP(), dwParam, pbData, dwFlags)) throw scu::OsException(GetLastError()); break; } default: throw scu::OsException(ERROR_NOT_SUPPORTED); break; } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPEncrypt(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN HCRYPTHASH hHash, IN BOOL Final, IN DWORD dwFlags, IN OUT BYTE *pbData, IN OUT DWORD *pdwDataLen, IN DWORD dwBufLen) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPEncrypt) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CKeyContext *pKeyCtx = gpCtx->LookupKey(hKey); ValidateFlags(dwFlags, CRYPT_OAEP); HCRYPTHASH hAuxHash = hHash ? gpCtx->LookupHash(hHash)->HashHandleInAuxCSP() : NULL; pKeyCtx->Encrypt(hAuxHash, Final, dwFlags, pbData, pdwDataLen, dwBufLen); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPDecrypt(IN HCRYPTPROV hProv, IN HCRYPTKEY hKey, IN HCRYPTHASH hHash, IN BOOL Final, IN DWORD dwFlags, IN OUT BYTE *pbData, IN OUT DWORD *pdwDataLen) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPDecrypt) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CSessionKeyContext *pSessionKey = gpCtx->LookupSessionKey(hKey); ValidateFlags(dwFlags, CRYPT_OAEP); if (hHash) { CHashContext *pHash = gpCtx->LookupHash(hHash); pSessionKey->Decrypt(pHash->HashHandleInAuxCSP(), Final, dwFlags, pbData, pdwDataLen); pHash->ExportFromAuxCSP(); } else pSessionKey->Decrypt(0, Final, dwFlags, pbData, pdwDataLen); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPCreateHash( IN HCRYPTPROV hProv, IN ALG_ID Algid, IN HCRYPTKEY hKey, IN DWORD dwFlags, OUT HCRYPTHASH *phHash) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPCreateHash) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); ValidateFlags(dwFlags, 0); auto_ptr apHash(CHashContext::Make(Algid, **gpCtx)); apHash->ImportToAuxCSP(); *phHash = gpCtx->Add(apHash); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPDestroyHash(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPDestroyHash) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); auto_ptr apHash(gpCtx->CloseHash(hHash)); apHash->Close(); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPGetHashParam(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN DWORD dwParam, OUT BYTE *pbData, IN DWORD *pdwDataLen, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPGetHashParam) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); ValidateFlags(dwFlags, 0); switch (dwParam) { case HP_ALGID: { ALG_ID const algid = pHash->AlgId(); Assign(pbData, pdwDataLen, &algid, sizeof algid); } break; case HP_HASHSIZE: { CHashContext::SizeType const cLength = pHash->Length(); Assign(pbData, pdwDataLen, &cLength, sizeof cLength); } break; case HP_HASHVAL: { Blob const blob(pHash->Value()); Assign(pbData, pdwDataLen, blob); } break; default: throw scu::OsException(ERROR_INVALID_PARAMETER); break; } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPHashData(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN CONST BYTE *pbData, IN DWORD dwDataLen, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPHashData) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); if (!CryptHashData(pHash->HashHandleInAuxCSP(), pbData, dwDataLen, dwFlags)) throw scu::OsException(GetLastError()); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPHashSessionKey(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN HCRYPTKEY hKey, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPHashSessionKey) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CSessionKeyContext *pSessionKey = gpCtx->LookupSessionKey(hKey); CHashContext *pHash = gpCtx->LookupHash(hHash); if (!CryptHashSessionKey(pHash->HashHandleInAuxCSP(), pSessionKey->KeyHandleInAuxCSP(), dwFlags)) throw scu::OsException(GetLastError()); pHash->ExportFromAuxCSP(); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPSetHashParam(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN DWORD dwParam, IN BYTE *pbData, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPSetHashParam) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); if (!CryptSetHashParam(pHash->HashHandleInAuxCSP(), dwParam, pbData, dwFlags)) throw scu::OsException(GetLastError()); } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPSignHash(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN DWORD dwKeySpec, IN LPCTSTR szDescription, IN DWORD dwFlags, OUT BYTE *pbSignature, IN OUT DWORD *pdwSigLen) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPSignHash) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); if (!pdwSigLen) throw scu::OsException(ERROR_INVALID_PARAMETER); ValidateFlags(dwFlags, CRYPT_NOHASHOID); // TO DO: This should really be a private key and // avoid having to fetch the public key to get the modulus CPublicKeyContext Key(gpCtx->AuxContext(), **gpCtx, dwKeySpec); DWORD cSignatureLength = Key.Strength() / numeric_limits::digits; if (!pbSignature) { *pdwSigLen = cSignatureLength; } else if (*pdwSigLen < cSignatureLength) { *pdwSigLen = cSignatureLength; throw scu::OsException(ERROR_MORE_DATA); } else { // Security: Support for the description parameter is // removed due to security vulnerability. if (szDescription) throw scu::OsException(ERROR_INVALID_PARAMETER); Blob SignedHash(Key.Sign(pHash, dwFlags & CRYPT_NOHASHOID)); memcpy(pbSignature, SignedHash.data(), SignedHash.length()); *pdwSigLen = SignedHash.length(); } } CSPI_CATCH(fSts); return fSts; } SLBCSPAPI CPVerifySignature(IN HCRYPTPROV hProv, IN HCRYPTHASH hHash, IN CONST BYTE *pbSignature, IN DWORD dwSigLen, IN HCRYPTKEY hPubKey, IN LPCTSTR szDescription, IN DWORD dwFlags) { BOOL fSts = CRYPT_FAILED; CSPI_TRY(CPVerifySignature) { Guard grdMaster(TheMasterLock()); Guarded gpCtx(static_cast(hlCryptContexts[hProv])); CHashContext *pHash = gpCtx->LookupHash(hHash); CPublicKeyContext *pKey = gpCtx->LookupPublicKey(hPubKey); ValidateFlags(dwFlags, CRYPT_NOHASHOID); pKey->VerifySignature(pHash->HashHandleInAuxCSP(), pbSignature, dwSigLen, szDescription, dwFlags); } CSPI_CATCH(fSts); return fSts; }