windows-nt/Source/XPSP1/NT/ds/security/csps/cryptoflex/slbcsp/cryptctx.cpp
2020-09-26 16:20:57 +08:00

785 lines
22 KiB
C++

// CryptCtx.cpp -- Cryptographic Context class implementation
// (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.
#include "stdafx.h"
#include <algorithm>
#include <malloc.h> // for _alloca
#include <scuOsExc.h>
#include <cciPubKey.h>
#include <cciPriKey.h>
#include <cciKeyPair.h>
#include "LoginId.h"
#include "ACntrFinder.h"
#include "Secured.h"
#include "ILoginTask.h"
#include "NILoginTsk.h"
#include "SesKeyCtx.h"
#include "PubKeyCtx.h"
#include "HashCtx.h"
#include "CryptCtx.h"
#include "Uuid.h"
#include "PromptUser.h"
#include "AlignedBlob.h"
#include "scarderr.h" // must be last for now
using namespace std;
using namespace scu;
using namespace cci;
/////////////////////////// LOCAL/HELPER /////////////////////////////////
namespace
{
WORD const dwHandleIdKeyContext = 13;
WORD const dwHandleIdHashContext = 7;
template<class T>
HANDLE_TYPE
AddHandle(auto_ptr<T> &rapObject,
CHandleList &rhl)
{
HANDLE_TYPE handle = rhl.Add(rapObject.get());
rapObject.release();
return handle;
}
CardFinder::DialogDisplayMode
DefaultDialogMode(bool fGuiEnabled)
{
using CardFinder::DialogDisplayMode;
return fGuiEnabled
? CardFinder::DialogDisplayMode::ddmIfNecessary
: CardFinder::DialogDisplayMode::ddmNever;
}
bool
IsEmpty(CContainer &rcntr)
{
return !rcntr->KeyPairExists(ksExchange) &&
!rcntr->KeyPairExists(ksSignature);
}
bool
IsProtected(CKeyPair const &rhkp)
{
bool fIsProtected = false;
CCard hcard(rhkp->Card());
if (hcard->IsProtectedMode())
fIsProtected = true;
else
{
if (hcard->IsPKCS11Enabled())
{
CPrivateKey hprikey(rhkp->PrivateKey());
if (hprikey && hprikey->Private())
fIsProtected = true;
else
{
CPublicKey hpubkey(rhkp->PublicKey());
if (hpubkey && hpubkey->Private())
fIsProtected = true;
else
{
CCertificate hcert(rhkp->Certificate());
fIsProtected = (hcert && hcert->Private());
}
}
}
}
return fIsProtected;
}
bool
IsProtected(CContainer &rhcntr)
{
return IsProtected(rhcntr->GetKeyPair(ksExchange)) ||
IsProtected(rhcntr->GetKeyPair(ksSignature));
}
} // namespace
/////////////////////////// PUBLIC /////////////////////////////////
// Types
// C'tors/D'tors
CryptContext::CryptContext(CSpec const &rcspec,
PVTableProvStruc const pVTable,
bool fGuiEnabled,
bool fCreateContainer,
bool fEphemeralContainer)
: CHandle(),
m_dwOwnerThreadId(GetCurrentThreadId()),
m_hacntr(),
m_fEphemeralContainer(fEphemeralContainer),
m_fGuiEnabled(fGuiEnabled),
m_hwnd(0),
m_hlKeys(dwHandleIdKeyContext),
m_hlHashes(dwHandleIdHashContext),
m_auxcontext(),
m_ce(),
m_apabCachedAlg()
{
if (pVTable && pVTable->FuncReturnhWnd)
(reinterpret_cast<CRYPT_RETURN_HWND>(pVTable->FuncReturnhWnd))(&m_hwnd);
// An ephemeral container cannot be "created"
if (m_fEphemeralContainer && fCreateContainer)
throw scu::OsException(ERROR_INVALID_PARAMETER);
if (fCreateContainer)
CreateNewContainer(rcspec);
else
OpenExistingContainer(rcspec);
}
CryptContext::~CryptContext()
{
if (m_hacntr)
{
try
{
m_hacntr = 0;
}
catch (...)
{
// don't allow exceptions to propagate out of destructor
}
}
}
// Operators
// Operations
HCRYPTHASH
CryptContext::Add(auto_ptr<CHashContext> &rapHashCtx)
{
return AddHandle(rapHashCtx, m_hlHashes);
}
HCRYPTKEY
CryptContext::Add(auto_ptr<CKeyContext> &rapKeyCtx)
{
return AddHandle(rapKeyCtx, m_hlKeys);
}
HCRYPTKEY
CryptContext::Add(auto_ptr<CPublicKeyContext> &rapPublicKeyCtx)
{
return AddHandle(rapPublicKeyCtx, m_hlKeys);
}
HCRYPTKEY
CryptContext::Add(auto_ptr<CSessionKeyContext> &rapSessionKeyCtx)
{
return AddHandle(rapSessionKeyCtx, m_hlKeys);
}
auto_ptr<CHashContext>
CryptContext::CloseHash(HCRYPTHASH const hHash)
{
return auto_ptr<CHashContext>(reinterpret_cast<CHashContext *>(m_hlHashes.Close(hHash)));
}
auto_ptr<CKeyContext>
CryptContext::CloseKey(HCRYPTKEY const hKey)
{
return auto_ptr<CKeyContext>(reinterpret_cast<CKeyContext *>(m_hlKeys.Close(hKey)));
}
void
CryptContext::CntrEnumerator(ContainerEnumerator const &rce)
{
m_ce = rce;
}
void
CryptContext::EnumAlgorithms(DWORD dwParam,
DWORD dwFlags,
bool fPostAdvanceIterator,
AlignedBlob &rabAlgInfo)
{
bool fFirst = dwFlags & CRYPT_FIRST;
if (fFirst)
m_apabCachedAlg = auto_ptr<AlignedBlob>(0);
if (!m_apabCachedAlg.get())
{
DWORD dwDataLen;
bool bSkip;
do
{
if (CryptGetProvParam(m_auxcontext(),
dwParam,
NULL,
&dwDataLen,
dwFlags) == CRYPT_FAILED)
throw scu::OsException(GetLastError());
BYTE *pbAlgInfo = reinterpret_cast<BYTE *>(_alloca(dwDataLen));
if (CryptGetProvParam(m_auxcontext(),
dwParam,
pbAlgInfo,
&dwDataLen,
dwFlags) == CRYPT_FAILED)
throw scu::OsException(GetLastError());
m_apabCachedAlg =
auto_ptr<AlignedBlob>(new AlignedBlob(pbAlgInfo, dwDataLen));
// Override SIGN and KEYX and algorithms not suported
ALG_ID algid = (PP_ENUMALGS == dwParam)
? reinterpret_cast<PROV_ENUMALGS *>(m_apabCachedAlg->Data())->aiAlgid
: reinterpret_cast<PROV_ENUMALGS_EX *>(m_apabCachedAlg->Data())->aiAlgid;
switch (GET_ALG_CLASS(algid))
{
case ALG_CLASS_SIGNATURE: // fall-through intentional
case ALG_CLASS_KEY_EXCHANGE:
if (PP_ENUMALGS == dwParam)
{
PROV_ENUMALGS *pAlgEnum =
reinterpret_cast<PROV_ENUMALGS *>(m_apabCachedAlg->Data());
pAlgEnum->dwBitLen = 1024;
}
else
{
PROV_ENUMALGS_EX *pAlgEnum =
reinterpret_cast<PROV_ENUMALGS_EX *>(m_apabCachedAlg->Data());
pAlgEnum->dwDefaultLen =
pAlgEnum->dwMinLen =
pAlgEnum->dwMaxLen = 1024;
}
bSkip = false;
break;
case ALG_CLASS_HASH:
bSkip = (!CHashContext::IsSupported(algid));
break;
case ALG_CLASS_DATA_ENCRYPT:
bSkip = false;
break;
default:
m_apabCachedAlg = auto_ptr<AlignedBlob>(0);
bSkip = true;
break;
}
dwFlags = dwFlags & ~CRYPT_FIRST;
} while (bSkip);
}
rabAlgInfo = m_apabCachedAlg.get()
? *m_apabCachedAlg
: AlignedBlob();
if (fPostAdvanceIterator)
m_apabCachedAlg = auto_ptr<AlignedBlob>(0);
}
auto_ptr<CPublicKeyContext>
CryptContext::ImportPrivateKey(Blob const &rblbMsPrivateKey,
DWORD dwKeySpec,
bool fExportable,
HCRYPTKEY hEncKey)
{
Secured<HAdaptiveContainer> hsacntr(m_hacntr);
auto_ptr<CPublicKeyContext>
apKeyCtx(ImportPublicKey(rblbMsPrivateKey, dwKeySpec));
BYTE const *pbKeyData = 0;
DWORD dwKeyDataLen = 0;
if (hEncKey || m_fEphemeralContainer)
{
// Export the key in plain text by importing to the aux provider
// and then exporting.
HCRYPTKEY hAuxKey;
AlignedBlob abMsPrivateKey(rblbMsPrivateKey);
if (!CryptImportKey(m_auxcontext(),
abMsPrivateKey.Data(),
abMsPrivateKey.Length(), hEncKey,
CRYPT_EXPORTABLE, &hAuxKey))
throw scu::OsException(GetLastError());
if (!m_fEphemeralContainer)
{
// Export the key in plain text
if (!CryptExportKey(m_auxcontext(), NULL, PRIVATEKEYBLOB, 0, NULL,
&dwKeyDataLen))
throw scu::OsException(GetLastError());
// Define pb to avoid "const" cast problems due to
// rblbMsPrivateKey.data below
BYTE *pb = reinterpret_cast<BYTE *>(_alloca(dwKeyDataLen));
if (!CryptExportKey(m_auxcontext(), NULL, PRIVATEKEYBLOB, 0, pb,
&dwKeyDataLen))
throw scu::OsException(GetLastError());
pbKeyData = pb;
// Scrub the key imported into the aux provider. To do this,
// the auxillary key must be destroyed and another key be put
// (generated) in its place.
if (!CryptDestroyKey(hAuxKey))
throw scu::OsException(GetLastError());
hAuxKey = NULL;
if (!CryptGenKey(m_auxcontext(), dwKeySpec, 0, &hAuxKey))
throw scu::OsException(GetLastError());
if (!CryptDestroyKey(hAuxKey))
throw scu::OsException(GetLastError());
}
}
else
{
pbKeyData = rblbMsPrivateKey.data();
dwKeyDataLen = rblbMsPrivateKey.length();
}
if (!m_fEphemeralContainer)
{
// Now continue importing the key that's now in plain text.
MsRsaPrivateKeyBlob msprikb(pbKeyData, dwKeyDataLen);
apKeyCtx->ImportPrivateKey(msprikb, fExportable);
}
return apKeyCtx;
}
auto_ptr<CPublicKeyContext>
CryptContext::ImportPublicKey(Blob const &rblbMsPublicKey,
DWORD dwKeySpec)
{
Secured<HAdaptiveContainer> hsacntr(m_hacntr);
auto_ptr<CPublicKeyContext>
apKeyCtx(new CPublicKeyContext(m_auxcontext(), *this,
dwKeySpec, false));
if (m_fEphemeralContainer)
apKeyCtx->AuxPublicKey(AlignedBlob(rblbMsPublicKey));
else
{
MsRsaPublicKeyBlob mspubkb(rblbMsPublicKey.data(),
rblbMsPublicKey.length());
apKeyCtx->ImportPublicKey(mspubkb);
}
return apKeyCtx;
}
void
CryptContext::Login(LoginIdentity const &rlid)
{
Secured<HCardContext> hscardctx(AdaptiveContainer()->CardContext());
Login(rlid, hscardctx);
}
void
CryptContext::Pin(LoginIdentity const &rlid,
char const *pszPin)
{
Secured<HCardContext> hscardctx(AdaptiveContainer()->CardContext());
// TO DO: Support Entrust
if (pszPin)
hscardctx->Login(rlid, NonInteractiveLoginTask(string(pszPin)));
else
hscardctx->ClearLogin(rlid);
}
// Remove (destroy) the container from the card
void
CryptContext::RemoveContainer()
{
Secured<HCardContext> hscardctx(AdaptiveContainer()->CardContext());
CContainer hcntr(m_hacntr->TheCContainer());
DeleteContainer(hscardctx, hcntr);
m_hacntr = 0; // disconnect from container
}
// Generate a key, string it in the context
HCRYPTKEY
CryptContext::GenerateKey(ALG_ID algid,
DWORD dwFlags)
{
// TO DO: Revisit this method, implement as a manager/factory?
HCRYPTKEY hKey = 0;
auto_ptr<CKeyContext> apKey;
bool bError = false;
DWORD dwErrorCode = NO_ERROR;
//
// Verify the parameters.
//
switch(algid)
{
case AT_KEYEXCHANGE:
case AT_SIGNATURE:
{
if (dwFlags & (CRYPT_CREATE_SALT | CRYPT_NO_SALT | CRYPT_PREGEN))
throw scu::OsException(NTE_BAD_FLAGS);
Secured<HAdaptiveContainer> hsacntr(m_hacntr);
apKey =
auto_ptr<CKeyContext>(new CPublicKeyContext(m_auxcontext(),
*this,
algid,
false));
apKey->Generate(algid, dwFlags);
}
break;
default:
apKey =
auto_ptr<CKeyContext>(new CSessionKeyContext(m_auxcontext()));
apKey->Generate(algid, dwFlags);
break;
}
hKey = Add(apKey);
return hKey;
}
// Load an external Session Key.
auto_ptr<CSessionKeyContext>
CryptContext::UseSessionKey(BYTE const *pbKeyBlob,
DWORD cbKeyBlobLen,
HCRYPTKEY hAuxImpKey,
DWORD dwFlags)
{
// TO DO: Revisit this method, really necessary??
auto_ptr<CSessionKeyContext>
apKeyCtx(new CSessionKeyContext(m_auxcontext()));
if (!apKeyCtx.get())
throw scu::OsException(NTE_NO_MEMORY);
// Decrypt key blob if encrypted with Key Exchange Key
// otherwise forward blob to Auxiliary CSP directly
ALG_ID const *pAlgId =
reinterpret_cast<ALG_ID const *>(&pbKeyBlob[sizeof(BLOBHEADER)]);
if (CALG_RSA_KEYX == *pAlgId)
{
// Get Key exchange key
// TO DO: Shouldn't this be getting a private key?
auto_ptr<CPublicKeyContext>
apXKey(new CPublicKeyContext(m_auxcontext(), *this,
AT_KEYEXCHANGE));
// Decrypt key blob
// TO DO: Support multiple key sizes
Blob EncryptedKey(pbKeyBlob + sizeof BLOBHEADER + sizeof ALG_ID,
128);
Blob DecryptedKey(apXKey->Decrypt(EncryptedKey));
// Recreate the blob
Blob DecryptedBlob(pbKeyBlob, sizeof BLOBHEADER + sizeof ALG_ID);
// we must trim out 64 bytes of the random data from the simple
// blob and then terminate it. (Termination occurs by making the
// n-1 byte = 0x02 and the nth byte = 0x00.) This is necessary
// in order to import this blob into the CSP.
DecryptedBlob.append(DecryptedKey.data(),
(DecryptedKey.length() / 2) - 2);
BYTE bTerminationBytes[] = { 0x02, 0x00 };
DecryptedBlob.append(bTerminationBytes, sizeof bTerminationBytes);
// Load Decrypted blob into key context
apKeyCtx->LoadKey(DecryptedBlob.data(),
DecryptedBlob.length(), 0, dwFlags);
}
else
{
// Load Encrypted blob into key context
apKeyCtx->LoadKey(pbKeyBlob, cbKeyBlobLen, hAuxImpKey, dwFlags);
}
// Import decrypted blob into Auxiliary CSP
apKeyCtx->ImportToAuxCSP();
return apKeyCtx;
}
// Access
HAdaptiveContainer
CryptContext::AdaptiveContainer() const
{
if (!m_hacntr)
throw scu::OsException(ERROR_INVALID_PARAMETER);
return m_hacntr;
}
HCRYPTPROV
CryptContext::AuxContext() const
{
return m_auxcontext();
}
HCardContext
CryptContext::CardContext() const
{
return AdaptiveContainer()->CardContext();
}
ContainerEnumerator
CryptContext::CntrEnumerator(bool fReset)
{
if (fReset)
{
if (m_hacntr)
m_ce = ContainerEnumerator(list<HCardContext>(1, m_hacntr->CardContext()));
else
{
CardEnumerator ce;
m_ce = ContainerEnumerator(*(ce.Cards()));
}
}
return m_ce;
}
CHashContext *
CryptContext::LookupHash(HCRYPTHASH hHash)
{
return reinterpret_cast<CHashContext *>(m_hlHashes[hHash]);
}
CKeyContext *
CryptContext::LookupKey(HCRYPTKEY hKey)
{
return reinterpret_cast<CKeyContext *>(m_hlKeys[hKey]);
}
CPublicKeyContext *
CryptContext::LookupPublicKey(HCRYPTKEY hKey)
{
return reinterpret_cast<CPublicKeyContext *>(LookupChecked(hKey, KT_PUBLICKEY));
}
CSessionKeyContext *
CryptContext::LookupSessionKey(HCRYPTKEY hKey)
{
return reinterpret_cast<CSessionKeyContext *>(LookupChecked(hKey, KT_SESSIONKEY));
}
HWND
CryptContext::Window() const
{
HWND hwndActive = m_hwnd;
// Find a window if the designated one isn't valid. If the
// designated one is NULL, don't use the result of GetActiveWindow
// because the mouse is locked when displaying a dialog box using
// that as the parent window from certain applications (IE and
// Outlook Express).
return (m_hwnd && !IsWindow(m_hwnd))
? GetActiveWindow()
: m_hwnd;
}
// Predicates
bool
CryptContext::GuiEnabled() const
{
return m_fGuiEnabled;
}
bool
CryptContext::IsEphemeral() const
{
return m_fEphemeralContainer;
}
// Static Variables
/////////////////////////// PROTECTED /////////////////////////////////
// C'tors/D'tors
// Operators
// Operations
// Access
// Predicates
// Static Variables
/////////////////////////// PRIVATE /////////////////////////////////
// C'tors/D'tors
// Operators
// Operations
// Create and open a new container (named by rcspec). If the
// container does exist, then it must be empty.
void
CryptContext::CreateNewContainer(CSpec const &rcspec)
{
ASSERT (!m_hacntr);
// Find the card in the reader specified.
CardFinder cardfinder(DefaultDialogMode(GuiEnabled()), Window());
CSpec csReader(rcspec);
csReader.SetReader(rcspec.Reader());
Secured<HCardContext> hscardctx(cardfinder.Find(csReader));
// Default the container name a UUID (GUID) if it wasn't supplied.
string sCntrToCreate(rcspec.CardId());
if (sCntrToCreate.empty())
{
Uuid uuid;
sCntrToCreate = AsString(uuid.AsUString());
}
AdaptiveContainerKey Key(hscardctx, sCntrToCreate);
m_hacntr = AdaptiveContainer::Find(Key); // find the existing one
if (m_hacntr && !IsEmpty(m_hacntr->TheCContainer()))
throw scu::OsException(NTE_TOKEN_KEYSET_STORAGE_FULL);
if (hscardctx->Card()->IsProtectedMode())
Login(User, hscardctx);
if (!m_hacntr)
m_hacntr = HAdaptiveContainer(Key);
}
void
CryptContext::DeleteContainer(Secured<HCardContext> &rhscardctx,
CContainer &rhcntr)
{
if (IsProtected(rhcntr))
Login(User, rhscardctx);
AdaptiveContainer::Discard(AdaptiveContainerKey(rhscardctx,
rhcntr->Name()));
rhcntr->Delete();
}
void
CryptContext::Login(LoginIdentity const &rlid,
Secured<HCardContext> &rhscardctx)
{
// TO DO: Support Entrust
if (m_fGuiEnabled)
rhscardctx->Login(rlid, InteractiveLoginTask(Window()));
else
rhscardctx->Login(rlid, LoginTask());
}
void
CryptContext::OkDeletingCredentials() const
{
if (GuiEnabled())
{
UINT uiResponse = PromptUser(Window(),
IDS_DELETE_CREDENTIALS,
MB_OKCANCEL | MB_ICONWARNING);
switch (uiResponse)
{
case IDCANCEL:
throw scu::OsException(ERROR_CANCELLED);
break;
case IDOK:
break;
default:
throw scu::OsException(ERROR_INTERNAL_ERROR);
break;
};
}
else
throw scu::OsException(NTE_EXISTS);
}
// Access
CKeyContext *
CryptContext::LookupChecked(HCRYPTKEY hKey,
DWORD const dwKeyType)
{
CKeyContext *pKeyCtx = LookupKey(hKey);
if (dwKeyType != pKeyCtx->TypeOfKey())
throw scu::OsException(ERROR_INVALID_PARAMETER);
return pKeyCtx;
}
// Open to an existing container specified by the container
// specification rcspec. If container name is empty, then open the
// default container.
void
CryptContext::OpenExistingContainer(CSpec const &rcspec)
{
if (rcspec.CardId().empty())
{
if (!m_fEphemeralContainer)
{
CardFinder cardfinder(DefaultDialogMode(GuiEnabled()), Window());
Secured<HCardContext> hscardctx(cardfinder.Find(rcspec));
CContainer hcntr(hscardctx->Card()->DefaultContainer());
if (hcntr)
m_hacntr =
HAdaptiveContainer(AdaptiveContainerKey(hscardctx,
hcntr->Name()));
}
}
else
{
AContainerFinder cntrfinder(DefaultDialogMode(GuiEnabled()), Window());
m_hacntr = cntrfinder.Find(rcspec);
}
if (!m_hacntr && (!rcspec.CardId().empty() || !m_fEphemeralContainer))
throw scu::OsException(NTE_BAD_KEYSET);
}
// Predicates
// Static Variables