diff options
author | rrelyea <rrelyea@fba4d07e-fe0f-4d7f-8147-e0026e666dc0> | 2006-07-27 22:23:07 +0000 |
---|---|---|
committer | rrelyea <rrelyea@fba4d07e-fe0f-4d7f-8147-e0026e666dc0> | 2006-07-27 22:23:07 +0000 |
commit | 1708a8e1c28b839ebe54829604ca16389c8411e2 (patch) | |
tree | ac3e04eaa50a9b77c6d0405fa913d5a3ec36535e | |
parent | 65a0b88f4bb833e8178b407951dfe137bc635767 (diff) |
Put the CSP up in open source
git-svn-id: http://svn.fedorahosted.org/svn/coolkey/trunk@16 fba4d07e-fe0f-4d7f-8147-e0026e666dc0
-rw-r--r-- | src/windows/csp/BinStr.h | 145 | ||||
-rw-r--r-- | src/windows/csp/Error.h | 75 | ||||
-rw-r--r-- | src/windows/csp/Key.cpp | 50 | ||||
-rw-r--r-- | src/windows/csp/Key.h | 74 | ||||
-rw-r--r-- | src/windows/csp/RegCerts.cpp | 69 | ||||
-rw-r--r-- | src/windows/csp/RegDll.cpp | 282 | ||||
-rw-r--r-- | src/windows/csp/Session.cpp | 114 | ||||
-rw-r--r-- | src/windows/csp/Session.h | 84 | ||||
-rw-r--r-- | src/windows/csp/State.cpp | 414 | ||||
-rw-r--r-- | src/windows/csp/State.h | 114 | ||||
-rw-r--r-- | src/windows/csp/csp.cpp | 2692 | ||||
-rw-r--r-- | src/windows/csp/csp.h | 146 | ||||
-rw-r--r-- | src/windows/csp/csp.rc | 163 | ||||
-rw-r--r-- | src/windows/csp/cspx.cpp | 1342 | ||||
-rw-r--r-- | src/windows/csp/gui.cpp | 220 | ||||
-rw-r--r-- | src/windows/csp/resource.h | 19 | ||||
-rw-r--r-- | src/windows/csp/uuid.cpp | 53 |
17 files changed, 6056 insertions, 0 deletions
diff --git a/src/windows/csp/BinStr.h b/src/windows/csp/BinStr.h new file mode 100644 index 0000000..82f64f2 --- /dev/null +++ b/src/windows/csp/BinStr.h @@ -0,0 +1,145 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : BinStr.h +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#ifndef __INCLUDE_BINSTR_H__ +#define __INCLUDE_BINSTR_H__ + +#include <vector> + +namespace MCSP { + +// Special tag used to identify binary strings that have been converted to +// ASCII hex. This allows us to recognize them and turn them back to raw +// binary when needed. This is used with the container name mapping to +// CKA_ID's. +static const char* PREFIX = "BINCODED:"; +static const size_t PREFIXLEN = strlen(PREFIX); + +class BinStr : public std::vector<BYTE> +{ +public: + BinStr() + : std::vector<BYTE>() {} + + BinStr(size_type size) + : std::vector<BYTE>(size) {} + + BinStr(const char* str) + { *this = str; } + + BinStr(const std::string& str) + { *this = str; } + + // Helper for the common case of returning a DWORD/CK_ULONG size + unsigned long size() const + { return static_cast<unsigned long>(std::vector<BYTE>::size()); } + + // If the string has non-printable characters then it is converted to a hex + // string of the binary data prefixed with PREFIX: (see definition above), + // otherwise it is left alone. + bool BinToHex() + { + iterator itr = begin(); + for (; itr != end(); itr++) + { + if (!isgraph(*itr) && *itr != ' ') + break; + } + + if (itr == end()) + return false; + + // Need to convert string to ASCII hex + BinStr temp; + temp = PREFIX; + temp.resize(size() * 2 + temp.size()); + + size_type pos = PREFIXLEN; + itr = begin(); + for (; itr != end(); itr++, pos += 2) + sprintf((char*)&temp[pos], "%.2x", *itr); + + swap(temp); + return true; + } + + // If the string has been encoded to hex with PREFIX: then this converts it + // back to raw binary, otherwise it is left alone. + bool HexToBin() + { + if (size() < PREFIXLEN) + return false; + if (memcmp(&(*this)[0], PREFIX, PREFIXLEN) != 0) + return false; + + BinStr::size_type newSize = size() - PREFIXLEN; + if (newSize % 2) + return false; + + newSize /= 2; + BinStr temp(newSize); + size_type pos_in = PREFIXLEN, pos_out = 0; + + for (; pos_in < size(); pos_in += 2, pos_out++) + temp[pos_out] = BinFromHexChars(&(*this)[pos_in]); + + swap(temp); + return true; + } + + // Helper for the common case of setting a BinStr to a char string value. + // Note that this DOES include the NULL at the end. + // FIXME: resizing is wierd, what if the BinStr is longer than the assigned value? + void operator =(const char* str) + { + if (size() < strlen(str) + 1) + resize(strlen(str) + 1); + strcpy((char*)&(*this)[(size_type)0], str); + } + + void operator =(const std::string& str) + { + resize(str.size()); + memcpy((char*)&(*this)[(size_type)0], &str[0], size()); + } + + void assign(const BYTE* data, size_t len) + { + resize(len); + memcpy(&(*this)[0], data, len); + } + +protected: + static BYTE BinFromHexChars(const BYTE* hex) + { + char temp[3] = { hex[0], hex[1], 0 }; + return static_cast<BYTE>(strtoul(temp, 0, 16)); + } }; + +} // namespace MCSP + +#endif // __INCLUDE_BINSTR_H__ diff --git a/src/windows/csp/Error.h b/src/windows/csp/Error.h new file mode 100644 index 0000000..437859d --- /dev/null +++ b/src/windows/csp/Error.h @@ -0,0 +1,75 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : Error.h +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#ifndef __INCLUDE_ERROR_H__ +#define __INCLUDE_ERROR_H__ + +#include <string> + +namespace MCSP { + +/////////////////////////////////////////////////////////////////////////////// +// Error handling +/////////////////////////////////////////////////////////////////////////////// +class Error +{ +public: + DWORD code_; + int line_; + std::string file_; + std::string func_; + std::string msg_; + +public: + Error(DWORD code, int line, const char* file, const char* func, const char* msg) + : code_(code), line_(line), file_(file), func_(func), msg_(msg) {} + + void log() + { + LOG("Exception: 0x%X at %s:%d in %s() \"%s\"\n", + code_, file_.c_str(), line_, func_.c_str(), msg_.c_str()); + } +}; + +// Utility template so we can catch errors of a specific type +// Example: catch(ErrorT<NTE_NO_MEMORY>& e) +// Will catch a NTE_NO_MEMORY error +template<DWORD> +class ErrorT : public Error +{ +public: + ErrorT(DWORD code, int line, const char* file, const char* func, const char* msg) + : Error(code, line, file, func, msg) {} +}; + +} // namespace MCSP + +// Utility macros +#define Throw(x) throw ErrorT<x>(x,__LINE__,__FILE__,__FUNCTION__,"") +#define ThrowMsg(x,y) throw ErrorT<x>(x,__LINE__,__FILE__,__FUNCTION__,y) + +#endif // __INCLUDE_ERROR_H__ diff --git a/src/windows/csp/Key.cpp b/src/windows/csp/Key.cpp new file mode 100644 index 0000000..eb73d74 --- /dev/null +++ b/src/windows/csp/Key.cpp @@ -0,0 +1,50 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : Key.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include "csp.h" +#include "Key.h" + +namespace MCSP { + +Key::Key() + : algId_(0), sessionKey_(true), hPublicKey_(-1), hPrivateKey_(-1), hFakeSessionKey_(0) +{ + lock_ = ::CreateMutex(NULL, FALSE, NULL); +} + +Key::Key(bool sessionKey) + : algId_(0), sessionKey_(sessionKey), hPublicKey_(-1), hPrivateKey_(-1), hFakeSessionKey_(0) +{ + lock_ = ::CreateMutex(NULL, FALSE, NULL); +} + +Key::~Key() +{ + ::CloseHandle(lock_); +} + +} // namespace MCSP diff --git a/src/windows/csp/Key.h b/src/windows/csp/Key.h new file mode 100644 index 0000000..4aca46b --- /dev/null +++ b/src/windows/csp/Key.h @@ -0,0 +1,74 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : Key.h +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#ifndef __INCLUDE_CSPKEY_H__ +#define __INCLUDE_CSPKEY_H__ + +#include "csp.h" + +namespace MCSP { + +class Key +{ +private: + HANDLE lock_; + +public: + // FIXME: make these private and add accessors... + ALG_ID algId_; + bool sessionKey_; + CK_OBJECT_HANDLE hPublicKey_; + CK_OBJECT_HANDLE hPrivateKey_; + HCRYPTKEY hFakeSessionKey_; + + Key(); + Key(bool sessionKey); + ~Key(); + + void lock() + { ::WaitForSingleObject(lock_, INFINITE); } + + void unlock() + { ::ReleaseMutex(lock_); } + + // Little helper that performs automatic thread locking (see csp.cpp for usage) + class Ptr + { + private: + Key *k_; + public: + Ptr(Key* k) { k_ = k; k_->lock(); } + ~Ptr() { k_->unlock(); } + Key* operator ->() { return k_; } + operator Key*() { return k_; } + }; +}; + +} // namespace MCSP + +#endif // __INCLUDE_CSPKEY_H__ + diff --git a/src/windows/csp/RegCerts.cpp b/src/windows/csp/RegCerts.cpp new file mode 100644 index 0000000..f31bbb5 --- /dev/null +++ b/src/windows/csp/RegCerts.cpp @@ -0,0 +1,69 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : RegCerts.cpp +/ Date : July 5, 2003 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include <stdio.h> +#include "csp.h" + +int main(int argc, char* argv[]) +{ + HCRYPTPROV hProv; + + if (argc < 2) + { + printf("usage: %s [CSP NAME]\n", argv[0]); + exit(1); + } + + if (!CryptAcquireContext(&hProv, NULL, argv[1], PROV_RSA_FULL, 0)) + { + printf("CryptAcquireContext failed (0x%X)\n", GetLastError()); + exit(1); + } + + printf("Got context\n"); + + BYTE name[4096]; + DWORD nameSize = sizeof(name); + DWORD flags = CRYPT_FIRST; + + while (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, name, &nameSize, flags)) + { + printf("While\n"); + flags = 0; + nameSize = sizeof(name); + + if (!CryptSetProvParam(hProv, PP_REGISTER_CERTIFICATE, name, 0)) + printf("Error registering container (0x%X): \"%s\"\n", GetLastError(), name); + + printf("Registered container: \"%s\"\n", name); + } + + printf("Done\n"); + CryptReleaseContext(hProv, 0); + + return 0; +} diff --git a/src/windows/csp/RegDll.cpp b/src/windows/csp/RegDll.cpp new file mode 100644 index 0000000..fcc1abd --- /dev/null +++ b/src/windows/csp/RegDll.cpp @@ -0,0 +1,282 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2006 Red Hat, Inc. +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : RegDll.cpp +/ Date : July 20, 2006 +/ Purpose: Register our Capi provider +/ +******************************************************************/ + +#include "csp.h" +#include "windows.h" +#include "winreg.h" +#include "fcntl.h" +#include "io.h" + +extern HINSTANCE g_hModule; + + +#define WINDOWS_CSP_PROVIDER \ + "SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider" +// Windows key values +#define TYPE_KEY "Type" +#define IMAGE_KEY "ImagePath" +#define SIG_KEY "Signature" + +// CSP specific key values +#define LOG_KEY "Logging" +#define KEYGEN_KEY "KeyGenHack" +#define PIN_KEY "PIN" +#define MODULE_KEY "PKCS11Module" +#define DEFAULT_PKCS11_MODULE "coolkey.dll" +#define DEFAULT_PIN "1234" + + +// +// set the key value if it doesn't exist +// +static LONG +regSetValueIf(HKEY hKey, LPCTSTR lpSubKey, + DWORD dwType, const BYTE *lpData, DWORD cbData) +{ + DWORD size; + LONG wrc = RegQueryValueEx(hKey,lpSubKey, 0, NULL, NULL, &size); + if (wrc == ERROR_SUCCESS) { + return wrc; + } + return RegSetValueEx(hKey, lpSubKey, 0, dwType, lpData, cbData); +} + +static LONG +getThisLibraryName(char **returnedLibName, DWORD *returnedLibLen) +{ + char *cspLibraryName; + DWORD cspLibraryLen; + char myModuleName[MAX_PATH]; + + *returnedLibName = NULL; + *returnedLibLen = 0; + + cspLibraryLen = GetModuleFileName(g_hModule, + myModuleName, sizeof(myModuleName)); + if (cspLibraryLen == 0) { + return GetLastError(); + } + cspLibraryName = (char *)malloc(cspLibraryLen); + if (cspLibraryName == NULL) { + return ERROR_NOT_ENOUGH_MEMORY; + } + memcpy(cspLibraryName, myModuleName, cspLibraryLen); + *returnedLibName = cspLibraryName; + *returnedLibLen = cspLibraryLen; + return ERROR_SUCCESS; +} + +#define SIG_SUFFIX ".sig" + +static char * +getSigFileName(const char *libName) +{ + int libLen = strlen(libName); + char *sigFile = (char *)malloc(libLen+sizeof(SIG_SUFFIX)); + char *ext; + + if (sigFile == NULL) { + return NULL; + } + + ext = strrchr(libName, '.'); + if (ext) { + libLen = ext - libName; + } + memcpy(sigFile,libName,libLen); + memcpy(&sigFile[libLen],SIG_SUFFIX,sizeof(SIG_SUFFIX)); + return sigFile; +} + +static DWORD +getFileSize(int fd) +{ + unsigned long offset; + unsigned long current; + + current = lseek(fd, 0, SEEK_CUR); + offset = lseek(fd, 0, SEEK_END); + lseek(fd, current, SEEK_SET); + return offset; +} + +static LONG +getSignature(const char *cspLibrary, unsigned char **returnedSig, + DWORD *returnedSigLen) +{ + char *sigFile = getSigFileName(cspLibrary); + int fd; + unsigned char *signature = NULL; + DWORD signatureLen; + int error; + LONG wrc = ERROR_SUCCESS; + + *returnedSig = NULL; + *returnedSigLen = 0; + + if (sigFile == NULL) { + return ERROR_NOT_ENOUGH_MEMORY; + } + + fd = open (sigFile, O_RDONLY); + free(sigFile); + if (fd < 0) { + return GetLastError(); + } + signatureLen = getFileSize(fd); + + signature = (unsigned char *)malloc(signatureLen); + if (signature == NULL) { + wrc = ERROR_NOT_ENOUGH_MEMORY; + goto loser; + } + error = read(fd, signature, signatureLen); + if (error != signatureLen) { + wrc = (error < 0) ? GetLastError() : ERROR_FILE_NOT_FOUND; + goto loser; + } + + *returnedSig = signature; + *returnedSigLen = signatureLen; + +loser: + close(fd); + if (signature && (wrc != ERROR_SUCCESS) ) { + free(signature); + } + return wrc; +} + + + + + +STDAPI +DllUnregisterServer(void) +{ + HKEY provKey; + DWORD disp; + LONG wrc; + + wrc = RegCreateKeyEx(HKEY_LOCAL_MACHINE, WINDOWS_CSP_PROVIDER, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, + &provKey, &disp); + if (wrc != ERROR_SUCCESS) { + return HRESULT_FROM_WIN32(wrc); + } + RegDeleteKey(provKey, PROVIDER_NAME); + RegCloseKey(provKey); + return S_OK; +} + + +STDAPI +DllRegisterServer(void) +{ + HKEY provKey = NULL; + HKEY cspKey = NULL; + char *cspLibrary = NULL; + unsigned char *signature = NULL; + DWORD cspLibraryLen, signatureLen; + DWORD dvalue; + DWORD disp; + LONG wrc; + + wrc = RegCreateKeyEx(HKEY_LOCAL_MACHINE, WINDOWS_CSP_PROVIDER, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, + &provKey, &disp); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = RegCreateKeyEx(provKey, PROVIDER_NAME, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, + &cspKey, &disp); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + dvalue = PROVIDER_TYPE; + wrc = RegSetValueEx(cspKey, TYPE_KEY, 0, REG_DWORD, + (BYTE *)&dvalue, sizeof(dvalue)); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + dvalue = 0; + wrc = regSetValueIf(cspKey, LOG_KEY, REG_DWORD, + (BYTE *)&dvalue, sizeof(dvalue)); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + dvalue = 1; + wrc = regSetValueIf(cspKey, KEYGEN_KEY, REG_DWORD, + (BYTE *)&dvalue, sizeof(dvalue)); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = regSetValueIf(cspKey, PIN_KEY, REG_DWORD, + (BYTE *)DEFAULT_PIN, sizeof(DEFAULT_PIN)); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = regSetValueIf(cspKey, MODULE_KEY, REG_SZ, + (BYTE *)DEFAULT_PKCS11_MODULE, sizeof(DEFAULT_PKCS11_MODULE)); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = getThisLibraryName(&cspLibrary, &cspLibraryLen); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = RegSetValueEx(cspKey, IMAGE_KEY, 0, REG_SZ, + (BYTE *)cspLibrary, cspLibraryLen); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = getSignature(cspLibrary, &signature, &signatureLen); + if (wrc != ERROR_SUCCESS) { + goto loser; + } + wrc = RegSetValueEx(cspKey, SIG_KEY, 0, REG_BINARY, + signature, signatureLen); + if (wrc != ERROR_SUCCESS) { + goto loser; + } +loser: + if (signature) { + free(signature); + } + if (cspLibrary) { + free(cspLibrary); + } + if (cspKey) { + RegCloseKey(cspKey); + if (wrc != ERROR_SUCCESS) { + RegDeleteKey(provKey, PROVIDER_NAME); + } + } + if (provKey) { + RegCloseKey(provKey); + } + return HRESULT_FROM_WIN32(wrc); +} diff --git a/src/windows/csp/Session.cpp b/src/windows/csp/Session.cpp new file mode 100644 index 0000000..a910d62 --- /dev/null +++ b/src/windows/csp/Session.cpp @@ -0,0 +1,114 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : Session.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include "csp.h" +#include "Session.h" + +namespace MCSP { + +Session::Session(bool init/*= true*/) + : doInit_(init), p11_(0), silent_(false), verifyContext_(false), + newKeyset_(false), machineKeyset_(false) +{ + if (doInit_) + { + lock_ = ::CreateMutex(NULL, FALSE, NULL); + + // We generate a unique container for all of our attachments to the default + // MS provider. It gets deleted when this session is closed. + BinStr uuid0; + GenUUID(&uuid0); + + size_t provNameLen = strlen(PROVIDER_NAME); + cryptProvUUID_.resize(provNameLen); + memcpy(&cryptProvUUID_[0], PROVIDER_NAME, provNameLen); + cryptProvUUID_.push_back('_'); cryptProvUUID_.push_back('_'); + cryptProvUUID_.resize(cryptProvUUID_.size() + uuid0.size()); + memcpy(&cryptProvUUID_[provNameLen+2], &uuid0[0], uuid0.size()); + cryptProvUUID_.push_back(0); + + if (!CryptAcquireContext(&cryptProv_, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + Throw(NTE_PROVIDER_DLL_FAIL); + + if (g_state.p11->C_OpenSession(g_state.slot(), CKF_SERIAL_SESSION | CKF_RW_SESSION, 0, 0, &p11_) != CKR_OK) + { + // Try one more time in case the card was removed then put back + if (g_state.p11->C_OpenSession(g_state.slot(), CKF_SERIAL_SESSION | CKF_RW_SESSION, 0, 0, &p11_) != CKR_OK) + ThrowMsg(NTE_FAIL, "PKCS#11 session could not be opened"); + } + + LOG("PKCS#11 session: 0x%X\n", p11_); + } +} + +Session::~Session() +{ + if (doInit_) + { + LOG("Closing crypt session: 0x%X\n", cryptProv_); + LOG("Closing P11 session: 0x%X\n", p11_); + + CryptReleaseContext(cryptProv_, 0); + g_state.p11->C_CloseSession(p11_); // FIXME: check error? + ::CloseHandle(lock_); + } +} + +void Session::parseFQCN(const char* fqcn0, BinStr* container_name, BinStr* reader_name) +{ + container_name->clear(); + reader_name->clear(); + + if (fqcn0 == 0 || fqcn0[0] == 0) + { + container_name->clear(); + container_name->push_back(0); + return; + } + + BinStr fqcn = fqcn0; + + if (fqcn[0] == '\\' && fqcn[1] == '\\' && fqcn[2] == '.' && fqcn[3] == '\\') + { + char* c = strchr((char*)&fqcn[4], '\\'); + if (c != 0) + { + *c = 0; + c++; + (*container_name) = c; + } + + (*reader_name) = (char*)&fqcn[4]; + } + else + (*container_name) = fqcn; + + LOG("ParseFQCN: container_name: \"%s\"\n", StringifyBin(*container_name, false).c_str()); + LOG("ParseFQCN: reader_name: \"%s\"\n", StringifyBin(*reader_name, false).c_str()); +} + +} // namespace MCSP diff --git a/src/windows/csp/Session.h b/src/windows/csp/Session.h new file mode 100644 index 0000000..25b271a --- /dev/null +++ b/src/windows/csp/Session.h @@ -0,0 +1,84 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : Session.h +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#ifndef __INCLUDE_SESSION_H__ +#define __INCLUDE_SESSION_H__ + +#include "BinStr.h" +#include <set> + +namespace MCSP { + +class Session +{ +private: + HANDLE lock_; + +public: + // FIXME: make these private and add accessors... + bool doInit_; + CK_SESSION_HANDLE p11_; + HCRYPTPROV cryptProv_; + bool silent_; + bool verifyContext_; + bool newKeyset_; + bool machineKeyset_; + BinStr readerName_; // NULL terminated; CSP friendly + BinStr containerName_; // NULL terminated; CSP friendly + BinStr CKAID_; // Real container name; could be binary; not NULL terminated + BinStr cryptProvUUID_; + + std::set<BinStr> containers_; + std::set<BinStr>::iterator containerItr_; + + Session(bool init = true); + ~Session(); + + void lock() + { ::WaitForSingleObject(lock_, INFINITE); } + + void unlock() + { ::ReleaseMutex(lock_); } + + static void parseFQCN(const char* fqcn, BinStr* container_name, BinStr* reader_name); + + // Little helper that performs automatic thread locking (see csp.cpp for usage) + class Ptr + { + private: + Session *s_; + public: + Ptr(Session* s) { s_ = s; s_->lock(); } + ~Ptr() { s_->unlock(); } + Session* operator ->() { return s_; } + operator Session*() { return s_; } + }; +}; + +} // namespace MCSP + +#endif // __INCLUDE_SESSION_H__ diff --git a/src/windows/csp/State.cpp b/src/windows/csp/State.cpp new file mode 100644 index 0000000..eba3e9a --- /dev/null +++ b/src/windows/csp/State.cpp @@ -0,0 +1,414 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : State.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include "csp.h" +#include "State.h" +#include <winscard.h> + +using namespace std; + +namespace MCSP { + +State::State() + : init_(false), logging_(false), logFilename_("C:\\CSPDEBUG.log"), slot_(0), keyGenHack_(false), pkcs11dllname_("PKCS11.dll") +{ + lock_ = ::CreateMutex(NULL, FALSE, NULL); + + HKEY hKey = NULL; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, + TEXT("SOFTWARE\\Microsoft\\Cryptography\\Defaults\\Provider\\"PROVIDER_NAME), + 0, + KEY_READ, + &hKey) == ERROR_SUCCESS) + { + DWORD value = 0; + DWORD size = sizeof(value); + + if (RegQueryValueEx(hKey, TEXT("Logging"), 0, 0, (LPBYTE)&value, &size) == ERROR_SUCCESS) + { + if (value) + logging(true); + } + + size = 0; + if (RegQueryValueEx(hKey, TEXT("LogFilename"), 0, 0, 0, &size) == ERROR_SUCCESS) + { + LOG("LogFilename size is: %u\n", size); + std::string value; + value.resize(size); + + if (RegQueryValueEx(hKey, TEXT("LogFilename"), 0, 0, (LPBYTE)&value[0], &size) == ERROR_SUCCESS) + { + // Remove trailing null + value.resize(value.size() - 1); + logFilename_ = value; + } + LOG("LogFilename value is: %s\n", &value[0]); + } + + size = sizeof(value); + if (RegQueryValueEx(hKey, TEXT("KeyGenHack"), 0, 0, (LPBYTE)&value, &size) == ERROR_SUCCESS) + { + if (value) + keyGenHack(true); + } + + if (RegQueryValueEx(hKey, TEXT("PKCS11Module"), 0, 0, 0, &size) == ERROR_SUCCESS) + { + LOG("PKCS11Module size is: %u\n", size); + std::string value; + value.resize(size); + + if (RegQueryValueEx(hKey, TEXT("PKCS11Module"), 0, 0, (LPBYTE)&value[0], &size) == ERROR_SUCCESS) + { + // Remove trailing null + value.resize(value.size() - 1); + pkcs11dllname_ = value; + } + LOG("PKCS11Module value is: %s\n", &value[0]); + } + + RegCloseKey(hKey); + } +} + +State::~State() +{ + shutdown(); + ::CloseHandle(lock_); +} + +bool State::sessionExists(Session* session) +{ + bool rv = false; + + lock(); + set<Session*>::iterator itr = sessions_.find(session); + if (itr != sessions_.end()) + rv = true; + + unlock(); + return rv; +} + +void State::removeSession(Session* session) +{ + lock(); + sessions_.erase(session); + delete session; + + if (sessions_.empty()) + shutdown(); + unlock(); +} + +Session* State::checkValidSession(HCRYPTPROV hProv) +{ + //LOG("Checking 0x%X as a valid session handle\n", hProv); + + if (!sessionExists(reinterpret_cast<Session*>(hProv))) + Throw(NTE_BAD_UID); + + return reinterpret_cast<Session*>(hProv); +} + +bool State::keyExists(Key* key) +{ + bool rv = false; + + lock(); + set<Key*>::iterator itr = keys_.find(key); + if (itr != keys_.end()) + rv = true; + + unlock(); + return rv; +} + +Key* State::checkValidKey(HCRYPTKEY hKey) +{ + //LOG("Checking 0x%X as a valid key handle\n", hKey); + + if (!keyExists(reinterpret_cast<Key*>(hKey))) + Throw(NTE_BAD_UID); + + return reinterpret_cast<Key*>(hKey); +} + +bool State::shutdown() +{ + if (init()) + { + lock(); + + LOG("Shutting down CSP\n"); + + { + set<Session*>::iterator itr = sessions_.begin(); + for (; itr != sessions_.end(); itr++) + delete *itr; + + sessions_.clear(); + } + + { + set<Key*>::iterator itr = keys_.begin(); + for (; itr != keys_.end(); itr++) + { + LOG("Destroying key: 0x%X\n", *itr); + delete *itr; + } + + keys_.clear(); + } + + g_state.p11->C_Finalize(0); + init(false); + + unlock(); + } + + return true; +} + +bool State::initP11(const BinStr& reader_name0, DWORD dwFlags) +{ + bool rv = true; + CK_RV ck_rv; + CK_SLOT_ID slot = 0; + BinStr reader_name = reader_name0; // We may need to modify the value + bool silent = false; + + lock(); + + if ((dwFlags & CRYPT_SILENT) || (dwFlags & CRYPT_VERIFYCONTEXT)) + silent = true; + + try + { + HMODULE p11lib = LoadLibrary(pkcs11dllname_.c_str()); + if (p11lib == NULL) + { + LOG("Failed to load PKCS11 library \"%s\"\n", pkcs11dllname_.c_str()); + SetLastError(NTE_FAIL); + throw(false); + } + + CK_RV (*getfunc)(CK_FUNCTION_LIST_PTR_PTR ppFunctionList); + getfunc = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR ppFunctionList))GetProcAddress(p11lib, "C_GetFunctionList"); + if (getfunc == NULL) + { + LOG("Failed to find C_GetFunctionList\n"); + SetLastError(NTE_FAIL); + throw(false); + } + + CK_RV rv = getfunc(&p11); + if (rv != CKR_OK) + { + LOG("Failed to get PKCS11 function list\n"); + SetLastError(NTE_FAIL); + throw(false); + } + + ck_rv = p11->C_Initialize(0); + + LOG("C_Initialize: 0x%X\n", ck_rv); + if (ck_rv != CKR_OK && ck_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) + { + LOG("C_Initialize() failed: 0x%X (%u)\n", ck_rv, ck_rv); + SetLastError(NTE_FAIL); + throw(false); + } + + CK_ULONG ulSlotCount; + if (p11->C_GetSlotList(FALSE, 0, &ulSlotCount) != CKR_OK) + { + LOG("C_GetSlotList() failed\n"); + SetLastError(NTE_FAIL); + throw(false); + } + + LOG("There are %d slots on this machine\n", ulSlotCount); + + if (ulSlotCount < 1) + { + LOG("No slots detected\n"); + SetLastError(NTE_FAIL); + throw(false); + } + + vector<CK_SLOT_ID> slotList(ulSlotCount); + + if (p11->C_GetSlotList(FALSE, &slotList[0], &ulSlotCount) != CKR_OK) + { + LOG("C_GetSlotList() failed (second call)\n"); + SetLastError(NTE_FAIL); + throw(false); + } + + CK_SLOT_INFO slotInfo; + BinStr current_reader; + vector<CK_SLOT_ID>::iterator itr; + bool found_slot = false; + + // FIXME: Look for the specified reader or if not specified then + // the first reader with a card present. Should probably + // search for first valid token and use MS smartcard select + // dialog. + while (!found_slot) + { + LOG("Looking for a valid token\n"); + + CK_ULONG token_count = 0; + itr = slotList.begin(); + for (; itr != slotList.end(); itr++) + { + p11->C_GetSlotInfo(*itr, &slotInfo); + + CK_TOKEN_INFO tokenInfo; + CK_RV ck_rv = p11->C_GetTokenInfo(*itr, &tokenInfo); + + // Chop off trailing spaces in P11 slot name + current_reader.assign(slotInfo.slotDescription, sizeof(slotInfo.slotDescription)); + while (current_reader[current_reader.size()-1] == 0x20) + current_reader.resize(current_reader.size() - 1); + current_reader.push_back(0); + + LOG("Slot %d: %s (looking for reader: %s)\n", *itr, ¤t_reader[0], reader_name.empty() ? "" : (char*)&reader_name[0]); + + if (!(slotInfo.flags & CKF_TOKEN_PRESENT)) + { + LOG("^^^^^ (No card present)\n"); + + if (reader_name == current_reader) + break; + } + else + { + string infoString((char*)tokenInfo.label, sizeof(tokenInfo.label)); + LOG("^^^^^ (%s)\n", infoString.c_str()); + + token_count++; + + if (reader_name.empty()) + { + // If multiple tokens, ask user + if (token_count > 1 && !silent) + break; + + found_slot = true; + slot = *itr; + } + else if (reader_name == current_reader) + { + found_slot = true; + slot = *itr; + break; + } + } + } + + if (token_count > 1 && !silent) + { + SCARDCONTEXT hSC; + OPENCARDNAME_EX dlgStruct; + char szReader[256]; + char szCard[256]; + + if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hSC) != SCARD_S_SUCCESS) + { + LOG("Failed SCardEstablishContext\n"); + SetLastError(NTE_FAIL); + throw(false); + } + + memset(&dlgStruct, 0, sizeof(dlgStruct)); + dlgStruct.dwStructSize = sizeof(dlgStruct); + dlgStruct.hSCardContext = hSC; + dlgStruct.dwFlags = SC_DLG_FORCE_UI; + dlgStruct.lpstrRdr = szReader; + dlgStruct.nMaxRdr = 256; + dlgStruct.lpstrCard = szCard; + dlgStruct.nMaxCard = 256; + //dlgStruct.lpstrTitle = "Select Card:"; + + // FIXME: Will this work during login? + if (SCardUIDlgSelectCard(&dlgStruct) != SCARD_S_SUCCESS) + { + SCardReleaseContext(hSC); + LOG("Failed SCardUIDlgSelectCard\n"); + SetLastError(NTE_FAIL); + throw(false); + } + else + { + SCardReleaseContext(hSC); + LOG("User selected reader: %s card: %s\n", szReader, szCard); + reader_name = (char*)szReader; + slot = 0; + continue; // This will restart the search loop to find the selected reader + } + } + + if (!found_slot) + { + if (silent) + { + LOG("ERROR: Can't find a card in any reader and silent mode is set"); + SetLastError(NTE_FAIL); + throw(false); + } + + // FIXME: will this work during login? + int result = MessageBox(NULL, "Please insert a supported smartcard", + "Insert Card", MB_ICONEXCLAMATION | MB_RETRYCANCEL); + + if (result == IDCANCEL) + { + SetLastError(NTE_FAIL); + throw(false); + } + } + } + + LOG("Using slot %d\n", slot); + + g_state.slot(slot); + } + catch (bool rv0) + { + rv = rv0; + } + + unlock(); + + return rv; +} + +} // namespace MCSP diff --git a/src/windows/csp/State.h b/src/windows/csp/State.h new file mode 100644 index 0000000..c1535d4 --- /dev/null +++ b/src/windows/csp/State.h @@ -0,0 +1,114 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : State.h +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#ifndef __INCLUDE_STATE_H__ +#define __INCLUDE_STATE_H__ + +#include "csp.h" + +namespace MCSP { + +// Global state; only one instance of this +class State +{ +private: + HANDLE lock_; + bool init_; + bool logging_; + std::string logFilename_; + CK_SLOT_ID slot_; + bool keyGenHack_; + std::set<Session*> sessions_; + std::set<Key*> keys_; + std::string pkcs11dllname_; + +public: + CK_FUNCTION_LIST_PTR p11; + +public: + State(); + ~State(); + + bool init() const + { return init_; } + + void init(bool init) + { init_ = init; } + + bool logging() const + { return logging_; } + + void logging(bool logging) + { logging_ = logging; } + + std::string logFilename() const + { return logFilename_; } + + void logFilename(std::string logFilename) + { logFilename_ = logFilename; } + + CK_SLOT_ID slot() const + { return slot_; } + + void slot(CK_SLOT_ID slot) + { slot_ = slot; } + + bool keyGenHack() const + { return keyGenHack_; } + + void keyGenHack(bool keyGenHack) + { keyGenHack_ = keyGenHack; } + + void addSession(Session* session) + { lock(); sessions_.insert(session); unlock(); } + + void removeSession(Session* session); + bool sessionExists(Session* session); + + Session* checkValidSession(HCRYPTPROV hProv); + + void addKey(Key* key) + { lock(); keys_.insert(key); unlock(); } + + void removeKey(Key* key) + { lock(); keys_.erase(key); unlock(); } + + bool keyExists(Key* key); + Key* checkValidKey(HCRYPTKEY hKey); + bool shutdown(); + + void lock() + { ::WaitForSingleObject(lock_, INFINITE); } + + void unlock() + { ::ReleaseMutex(lock_); } + + bool initP11(const BinStr& reader_name, DWORD dwFlags); +}; + +} // namespace MCSP +#endif // __INCLUDE_STATE_H__ diff --git a/src/windows/csp/csp.cpp b/src/windows/csp/csp.cpp new file mode 100644 index 0000000..7bea72f --- /dev/null +++ b/src/windows/csp/csp.cpp @@ -0,0 +1,2692 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : csp.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include "csp.h" +#include "IDARES.h" + +using namespace std; +using namespace MCSP; + +// Globals +HINSTANCE g_hModule = NULL; +MCSP::State MCSP::g_state; + +BOOL WINAPI +DllMain( + HINSTANCE hinstDLL, // handle to the DLL module + DWORD fdwReason, // reason for calling function + LPVOID lpvReserved) // reserved +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + LOG("Dllmain: DLL_PROCESS_ATTACH\n"); + DisableThreadLibraryCalls(hinstDLL); + g_hModule = hinstDLL; + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + LOG("Dllmain: DLL_PROCESS_DETACH\n"); + g_state.shutdown(); + } + + return TRUE; +} + + +/* + - CPAcquireContext + - + * Purpose: + * The CPAcquireContext function is used to acquire a context + * handle to a cryptographic service provider (CSP). + * + * + * Parameters: + * OUT phProv - Handle to a CSP + * IN szContainer - Pointer to a string which is the + * identity of the logged on user + * IN dwFlags - Flags values + * IN pVTable - Pointer to table of function pointers + * + * Returns: + */ + +BOOL WINAPI +CPAcquireContext( + OUT HCRYPTPROV *phProv, + IN LPCSTR szContainer, + IN DWORD dwFlags, + IN PVTableProvStruc pVTable) +{ + BEGIN_API_CALL; + LOG("Build: %s\n", "$Id$"); + LOG("Executable: \"%s\" (%s)\n", GetCurrentExecutable().c_str(), GetCurrentDLL().c_str()); + LOG("Container: \"%s\" Flags: %s (0x%X)\n", + szContainer, StringifyAquireFlags(dwFlags).c_str(), dwFlags); + + BOOL rv = TRUE; + Session* context = 0; + +#ifdef CSP_PASSTHROUGH + rv = CryptAcquireContext(phProv, szContainer, MS_ENHANCED_PROV, PROV_RSA_FULL, dwFlags); +#else + try + { + BinStr container_name, reader_name; + Session::parseFQCN(szContainer, &container_name, &reader_name); + + if (!phProv && !(dwFlags & CRYPT_DELETEKEYSET)) + ThrowMsg(NTE_FAIL, "Can't return context, phProv is invalid"); + + if (g_state.init()) + LOG("CSP already initialized\n"); + else + { + LOG("Initializing CSP\n"); + + // Initialize PKCS11 + if (!g_state.initP11(reader_name, dwFlags)) // LastError set by InitP11() + ThrowMsg(0, "PKCS#11 initialization failed"); + + g_state.init(true); + } + + context = new Session; + if (!context) + Throw(NTE_NO_MEMORY); + + context->readerName_ = reader_name; + + if (dwFlags & CRYPT_SILENT) + context->silent_ = true; + if (dwFlags & CRYPT_VERIFYCONTEXT) + context->verifyContext_ = true; + if (dwFlags & CRYPT_MACHINE_KEYSET) + context->machineKeyset_ = true; + if (dwFlags & CRYPT_NEWKEYSET) + context->newKeyset_ = true; + + // Set container name (either default or specified) + if (strlen((char*)&container_name[0]) && !context->machineKeyset_) + { + if (context->verifyContext_) + Throw(NTE_BAD_FLAGS); + + context->containerName_ = container_name; + context->CKAID_ = context->containerName_; + context->CKAID_.pop_back(); + } + else if (!context->verifyContext_) // default container + { + CK_OBJECT_HANDLE hCert; + BinStr ckaid; + if (FindDefaultCert(context, &hCert, &ckaid) || FindLastContainer(context, &hCert, &ckaid)) + { + LOG("Found default certificate or key-pair"); + context->CKAID_ = ckaid; + context->containerName_ = ckaid; + context->containerName_.BinToHex(); + context->containerName_.push_back(0); + } + else if (!strlen((char*)&container_name[0])) + { + LOG("Using UUID default container"); + context->containerName_ = context->cryptProvUUID_; + context->containerName_.push_back(0); + context->CKAID_ = context->cryptProvUUID_; + } + } + + context->CKAID_.HexToBin(); + + LOG("Container name: \"%s\"\n", &context->containerName_[0]); + LOG("CKA_ID: %s \"%s\"\n", StringifyBin(context->CKAID_).c_str(), + StringifyBin(context->CKAID_, false).c_str()); + + if (!context->silent_ && !context->verifyContext_) + { + CK_SESSION_INFO info; + if (g_state.p11->C_GetSessionInfo(context->p11_, &info) == CKR_OK && + ((info.state == CKS_RO_USER_FUNCTIONS) || (info.state == CKS_RW_USER_FUNCTIONS))) + { + LOG("PKCS#11 module in user mode, PIN verification skipped"); + } + else + { + int pin_size; + BinStr userPIN; + userPIN.resize(256); + if (!(pin_size = IDADisplayPinDialog((char*)&userPIN[0], userPIN.size()))) + ThrowMsg(SCARD_W_CANCELLED_BY_USER, "PIN dialog cancelled"); + + userPIN.resize(pin_size); + + CK_RV ck_rv = g_state.p11->C_Login(context->p11_, CKU_USER, + (CK_UTF8CHAR*)&userPIN[0], (CK_ULONG)userPIN.size()); + + if (ck_rv != CKR_OK) + { + DisplayError(context, "Error during PIN verification"); + Throw(NTE_FAIL); + } + else + LOG("PIN Verification Successful\n"); + } + } + + if (!context->verifyContext_) + { + if (FindObject(context, 0, CKO_PRIVATE_KEY)) + { + if (context->newKeyset_) + ThrowMsg(NTE_EXISTS, "Container already exists and trying CRYPT_NEWKEYSET"); + } + else if (!context->newKeyset_) + { + if (g_state.logging()) + DisplayError(context, "Could not find matching key-pair. This may just mean you are trying to use a certificate that does not have a matching key.\n\nIf you are attempting to install a certificate then it will not function properly. Your card may also be corrupt."); + + ThrowMsg(NTE_KEYSET_NOT_DEF, "Invalid container name and not CRYPT_NEWKEYSET"); + } + } + + if (!(dwFlags & CRYPT_DELETEKEYSET)) + { + g_state.addSession(context); + *phProv = (HCRYPTPROV)context; + LOG("New CSP session handle: 0x%X\n", context); + } + else + { + CK_ATTRIBUTE search = { CKA_ID, &context->CKAID_[0], context->CKAID_.size() }; + + if (g_state.p11->C_FindObjectsInit(context->p11_, &search, 1) != CKR_OK) + ThrowMsg(NTE_FAIL, "C_FindObjectsInit failed"); + + vector<CK_OBJECT_HANDLE> deleted; + CK_ULONG count; + CK_OBJECT_HANDLE hObj; + while (g_state.p11->C_FindObjects(context->p11_, &hObj, 1, &count) == CKR_OK && count > 0) + deleted.push_back(hObj); + + // This attempts to delete everything we can, even if some things fail + bool failed = false; + vector<CK_OBJECT_HANDLE>::iterator itr = deleted.begin(); + for (; itr != deleted.end(); itr++) + { + if (g_state.p11->C_DestroyObject(context->p11_, *itr) != CKR_OK) + failed = true; + } + + if (failed) + Throw(NTE_FAIL); + + delete context; + } + } + catch(Error& e) + { + e.log(); + + if (context) + delete context; + + if (e.code_ != 0) + SetLastError(e.code_); + + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPReleaseContext + - + * Purpose: + * The CPReleaseContext function is used to release a + * context created by CryptAcquireContext. + * + * Parameters: + * IN phProv - Handle to a CSP + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPReleaseContext( + IN HCRYPTPROV hProv, + IN DWORD dwFlags) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptReleaseContext(hProv, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + g_state.removeSession(context); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPGenKey + - + * Purpose: + * Generate cryptographic keys + * + * + * Parameters: + * IN hProv - Handle to a CSP + * IN Algid - Algorithm identifier + * IN dwFlags - Flags values + * OUT phKey - Handle to a generated key + * + * Returns: + */ + +BOOL WINAPI +CPGenKey( + IN HCRYPTPROV hProv, + IN ALG_ID Algid, + IN DWORD dwFlags, + OUT HCRYPTKEY *phKey) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + + Key* key = 0; + +#ifdef CSP_PASSTHROUGH + rv = CryptGenKey(hProv, Algid, dwFlags, phKey); + key = (Key*)*phKey; +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + + LOG("Algid:%s (0x%X) dwFlags:0x%X\n", StringifyCALG(Algid).c_str(), Algid, dwFlags); + + if (context->verifyContext_) + Throw(NTE_PERM); + + if (dwFlags & CRYPT_USER_PROTECTED) + { + if (context->silent_) + Throw(NTE_SILENT_CONTEXT); + + if (MessageBox(NULL, + "An application is attempting to generate a keypair. Do you want to allow this?", + PROVIDER_NAME, + MB_OKCANCEL | MB_ICONQUESTION | MB_TASKMODAL) == IDCANCEL) + { + Throw(NTE_FAIL); + } + } + + key = new Key; + if (key == 0) + Throw(NTE_NO_MEMORY); + + switch (Algid) + { + case CALG_DES: + case CALG_3DES: + case CALG_RC2: + key->sessionKey_ = true; + key->algId_ = Algid; + if (!CryptGenKey(context->cryptProv_, Algid, dwFlags, &key->hFakeSessionKey_)) + ThrowMsg(0, "CryptGenKey failed"); + break; + case CALG_RSA_SIGN: + case CALG_RSA_KEYX: + case AT_KEYEXCHANGE: + case AT_SIGNATURE: + { + // FIXME: when doing on-card key operations, we may want to be able to + // export at least bulk keys. We may want to store some sort of flag + // that allows us to export bulk keys, so this parameter may need to + // be handled at some point in the future + + // FIXME: EXPORTABLE check removed so the Wave test application will work (see README) + //if (dwFlags & CRYPT_EXPORTABLE) + // ThrowMsg(NTE_BAD_FLAGS, "ERROR: Can't do CRYPT_EXPORTABLE"); + if (dwFlags & CRYPT_CREATE_SALT) + ThrowMsg(NTE_BAD_FLAGS, "ERROR: Can't do CRYPT_CREATE_SALT"); + if (dwFlags & CRYPT_NO_SALT) + ThrowMsg(NTE_BAD_FLAGS, "ERROR: Can't do CRYPT_NO_SALT"); + if (dwFlags & CRYPT_PREGEN) + ThrowMsg(NTE_BAD_FLAGS, "ERROR: Can't do CRYPT_PREGEN"); + + CK_OBJECT_HANDLE hPrivKey, hPubKey; + key->sessionKey_ = false; + + if (Algid == AT_KEYEXCHANGE) + key->algId_ = CALG_RSA_KEYX; + else if (Algid == AT_SIGNATURE) + key->algId_ = CALG_RSA_SIGN; + else + key->algId_ = Algid; + + if (FindObject(context, &hPrivKey, CKO_PRIVATE_KEY)) + { + if (!FindObject(context, &hPubKey, CKO_PUBLIC_KEY)) + { + hPubKey = -1; + LOG("WARNING: Found private key but no matching public key (will attempt to use cert)\n"); + } + + key->hPrivateKey_ = hPrivKey; + key->hPublicKey_ = hPubKey; + LOG("KeyPair already on card; returning them as a \"new\" key pair\n"); + } + else + { + CK_MECHANISM mechanism; + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + mechanism.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + + CK_ULONG modulusBits = (dwFlags & 0xFFFF0000) >> 16; + if (modulusBits == 0) + modulusBits = 1024; + + CK_BYTE publicExponent[3] = { 1, 0, 1}; + CK_BBOOL bTrue = TRUE; + CK_ATTRIBUTE publicKeyTemplate[] = + { + {CKA_ENCRYPT, &bTrue, sizeof(bTrue)}, + {CKA_VERIFY, &bTrue, sizeof(bTrue)}, + {CKA_TOKEN, &bTrue, sizeof(bTrue)}, + //Setting the CKA_ID here won't work without P11 module changes + //{CKA_ID, &context->CKAID_[0], (CK_ULONG)context->CKAID_.size()}, + {CKA_WRAP, &bTrue, sizeof(bTrue)}, + {CKA_MODULUS_BITS, &modulusBits, sizeof(modulusBits)}, + {CKA_PUBLIC_EXPONENT, publicExponent, sizeof(publicExponent)} + }; + + CK_ATTRIBUTE privateKeyTemplate[] = + { + {CKA_TOKEN, &bTrue, sizeof(bTrue)}, + {CKA_PRIVATE, &bTrue, sizeof(bTrue)}, + {CKA_TOKEN, &bTrue, sizeof(bTrue)}, + //Setting the CKA_ID here won't work without P11 module changes + //{CKA_ID, &context->CKAID_[0], (CK_ULONG)context->CKAID_.size()}, + {CKA_SENSITIVE, &bTrue, sizeof(bTrue)}, + {CKA_DECRYPT, &bTrue, sizeof(bTrue)}, + {CKA_SIGN, &bTrue, sizeof(bTrue)}, + {CKA_UNWRAP, &bTrue, sizeof(bTrue)} + }; + + LOG("Modulus length: %d\n", modulusBits); + + if (!g_state.keyGenHack()) // Normal key generation mode + { + CK_OBJECT_HANDLE hPubKey, hPrivKey; + CK_RV ck_rv; + + ck_rv = g_state.p11->C_GenerateKeyPair( + context->p11_, + &mechanism, + publicKeyTemplate, + sizeof(publicKeyTemplate) / sizeof(CK_ATTRIBUTE), + privateKeyTemplate, + sizeof(privateKeyTemplate) / sizeof(CK_ATTRIBUTE), + &hPubKey, + &hPrivKey); + + if (ck_rv != CKR_OK) + { + DisplayError(context, "Error generating key pair\n"); + Throw(NTE_FAIL); + } + + key->hPrivateKey_ = hPrivKey; + key->hPublicKey_ = hPubKey; + + // Set the CKA_ID + CK_ATTRIBUTE pValueTemplate = + { CKA_ID, &context->CKAID_[0], context->CKAID_.size() }; + + ck_rv = g_state.p11->C_SetAttributeValue(context->p11_, hPrivKey, &pValueTemplate, 1); + if (ck_rv != CKR_OK) + LOG("ERROR: Could not set the private key's CKA_ID\n"); + + ck_rv = g_state.p11->C_SetAttributeValue(context->p11_, hPubKey, &pValueTemplate, 1); + if (ck_rv != CKR_OK) + LOG("ERROR: Could not set the public key's CKA_ID\n"); + } + else // Key generation hack (does key generation off-card, then imports) + { + LOG("*********************** Using key generation hack ***********************\n"); + + HCRYPTKEY hKey; + if (!CryptGenKey(context->cryptProv_, Algid, CRYPT_EXPORTABLE, &hKey)) + ThrowMsg(NTE_FAIL, "CryptGenKey failed"); + + DWORD dwKeyLen; + if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, 0, &dwKeyLen)) + ThrowMsg(NTE_FAIL, "CryptExport key failed"); + + BinStr pbKey(dwKeyLen); + if (!CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, &pbKey[0], &dwKeyLen)) + ThrowMsg(NTE_FAIL, "CryptExport key failed"); + + if (key) + { + delete key; + key = 0; + } + + // We import the new keys into _this_ CSP + rv = CPImportKey(hProv, &pbKey[0], dwKeyLen, 0, 0, (HCRYPTKEY*)&key); + } + } + } + break; + default: + ThrowMsg(NTE_BAD_ALGID, "Unsupported algorithm"); + break; + } + + *phKey = (HCRYPTKEY)key; + g_state.addKey(key); + } + catch(Error& e) + { + if (key) + delete key; + + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + // Logging only + { + if (key->sessionKey_) + LOG("Generated session key handle: 0x%X\n", key); + else + LOG("Generated key pair handle: 0x%X\n", key); + + LOG("algId:%s sessionKey:%s hPublicKey:0x%X hPrivateKey:0x%X hFakeKey:0x%X\n", + StringifyCALG(key->algId_).c_str(), + key->sessionKey_ ? "true" : "false", + key->hPublicKey_, + key->hPrivateKey_, + key->hFakeSessionKey_); + } + + END_API_CALL; + return rv; +} + + +/* + - CPDeriveKey + - + * Purpose: + * Derive cryptographic keys from base data + * + * + * Parameters: + * IN hProv - Handle to a CSP + * IN Algid - Algorithm identifier + * IN hBaseData - Handle to base data + * IN dwFlags - Flags values + * OUT phKey - Handle to a generated key + * + * Returns: + */ + +BOOL WINAPI +CPDeriveKey( + IN HCRYPTPROV hProv, + IN ALG_ID Algid, + IN HCRYPTHASH hHash, + IN DWORD dwFlags, + OUT HCRYPTKEY *phKey) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptDeriveKey(hProv, Algid, hHash, dwFlags, phKey); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + rv = CryptDeriveKey(context->cryptProv_, Algid, hHash, dwFlags, phKey); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPDestroyKey + - + * Purpose: + * Destroys the cryptographic key that is being referenced + * with the hKey parameter + * + * + * Parameters: + * IN hProv - Handle to a CSP + * IN hKey - Handle to a key + * + * Returns: + */ + +BOOL WINAPI +CPDestroyKey( + IN HCRYPTPROV hProv, + IN HCRYPTKEY hKey) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptDestroyKey(hKey); +#else + try + { + g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + { + if (!CryptDestroyKey(key->hFakeSessionKey_)) + { + LOG("CryptDestroyKey failed for key handle: 0x%X (MS default CSP handle: 0x%X)\n", + key, key->hFakeSessionKey_); + Throw(0); + } + } + + g_state.removeKey(key); + delete key; + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPSetKeyParam + - + * Purpose: + * Allows applications to customize various aspects of the + * operations of a key + * + * Parameters: + * IN hProv - Handle to a CSP + * IN hKey - Handle to a key + * IN dwParam - Parameter number + * IN pbData - Pointer to data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPSetKeyParam( + IN HCRYPTPROV hProv, + IN HCRYPTKEY hKey, + IN DWORD dwParam, + IN CONST BYTE *pbData, + IN DWORD dwFlags) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + LOG("hKey:0x%X dwParam:0x%X dwFlags:0x%X\n", hKey, dwParam, dwFlags); + +#ifdef CSP_PASSTHROUGH + rv = CryptSetKeyParam(hKey, dwParam, pbData, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + { + if (!CryptSetKeyParam(key->hFakeSessionKey_, dwParam, pbData, dwFlags)) + Throw(0); + } + else if (dwParam == KP_CERTIFICATE) + { + LOG("Adding certificate; CKA_ID: %s\n", StringifyBin(context->CKAID_).c_str()); + + PCCERT_CONTEXT certContext = + CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbData, ASN1Len(pbData)); + if (certContext == 0) + ThrowMsg(NTE_FAIL, "CertCreateCertificateContext failed"); + + BinStr modulus, exp, cert2; + cert2.resize(ASN1Len(pbData)); + memcpy(&cert2[0], pbData, cert2.size()); + GetModulusFromCert(context, &modulus, &exp, cert2); + + DWORD labelSize = + CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &certContext->pCertInfo->Subject, + CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, 0, 0); + BinStr label(labelSize); + CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &certContext->pCertInfo->Subject, + CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, (char*)&label[0], label.size()); + + CertFreeCertificateContext(certContext); + LOG("Certificate label is: \"%s\"\n", &label[0]); + + CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; + CK_BBOOL bTrue = TRUE; + CK_CERTIFICATE_TYPE certType = CKC_X_509; + + CK_ATTRIBUTE atrTemplate[] = { + { CKA_CLASS, &objClass, sizeof(objClass) }, + { CKA_VALUE, (CK_VOID_PTR)pbData, ASN1Len(pbData) }, + { CKA_TOKEN, &bTrue, sizeof(bTrue) }, + { CKA_ID, &context->CKAID_[0], context->CKAID_.size() }, + { CKA_LABEL, &label[0], label.size() - 1 }, + { CKA_CERTIFICATE_TYPE, &certType, sizeof(certType) } + }; + + CK_OBJECT_HANDLE cert; + + if (FindObject(context, &cert, CKO_CERTIFICATE)) + { + LOG("Warning: trying to overwrite existing certificate... ignoring request\n"); + + // This won't work unless the token supports deleting objects and may crash + //if (g_state.p11->C_SetAttributeValue(context->p11_, cert, &atrTemplate[0], atrTemplate.size()) != CKR_OK) + //{ + // ThrowMsg(NTE_FAIL, "C_SetAttributeValue failed"); + //} + } + else if (g_state.p11->C_CreateObject(context->p11_, atrTemplate, + sizeof(atrTemplate) / sizeof(CK_ATTRIBUTE), &cert) != CKR_OK) + { + ThrowMsg(NTE_FAIL, "Certificate creation failed\n"); + } + } + else + ThrowMsg(NTE_BAD_TYPE, "Can't handle dwParam type"); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPGetKeyParam + - + * Purpose: + * Allows applications to get various aspects of the + * operations of a key + * + * Parameters: + * IN hProv - Handle to a CSP + * IN hKey - Handle to a key + * IN dwParam - Parameter number + * OUT pbData - Pointer to data + * IN pdwDataLen - Length of parameter data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPGetKeyParam( + IN HCRYPTPROV hProv, + IN HCRYPTKEY hKey, + IN DWORD dwParam, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen, + IN DWORD dwFlags) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + LOG("dwParam:0x%X dwFlags:0x%X\n", dwParam, dwFlags); + +#ifdef CSP_PASSTHROUGH + rv = CryptGetKeyParam(hKey, dwParam, pbData, pcbDataLen, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + rv = CryptGetKeyParam(key->hFakeSessionKey_, dwParam, pbData, pcbDataLen, dwFlags); + else if (dwParam == KP_CERTIFICATE) + { + CK_OBJECT_HANDLE hCert; + if (!FindObject(context, &hCert, CKO_CERTIFICATE)) + ThrowMsg(NTE_FAIL, "Couldn't get certificate"); + + CK_ATTRIBUTE pTemplate = { CKA_VALUE, 0, 0 }; + if (g_state.p11->C_GetAttributeValue(context->p11_, hCert, &pTemplate, 1) != CKR_OK) + ThrowMsg(NTE_FAIL, "C_GetAttributeValue failed"); + + if (!pbData) + *pcbDataLen = pTemplate.ulValueLen; + else if (*pcbDataLen < pTemplate.ulValueLen) + Throw(ERROR_MORE_DATA); + else + { + *pcbDataLen = pTemplate.ulValueLen; + pTemplate.pValue = pbData; + if (g_state.p11->C_GetAttributeValue(context->p11_, hCert, &pTemplate, 1) != CKR_OK) + ThrowMsg(NTE_FAIL, "C_GetAttributeValue failed"); + } + } + else + ThrowMsg(NTE_BAD_TYPE, "Can't handle dwParam type"); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + // Logging only + { + if (rv && pbData) + LOG("Returning %u (0x%X) bytes data:\n%s\n\"%s\"\n", *pcbDataLen, *pcbDataLen, + StringifyBin(pbData, *pcbDataLen).c_str(), + StringifyBin(pbData, *pcbDataLen, false).c_str()); + } + + END_API_CALL; + return rv; +} + + +/* + - CPSetProvParam + - + * Purpose: + * Allows applications to customize various aspects of the + * operations of a provider + * + * Parameters: + * IN hProv - Handle to a CSP + * IN dwParam - Parameter number + * IN pbData - Pointer to data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPSetProvParam( + IN HCRYPTPROV hProv, + IN DWORD dwParam, + IN CONST BYTE *pbData, + IN DWORD dwFlags) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + LOG("dwParam:0x%X dwFlags:0x%X\n", dwParam, dwFlags); + +#ifdef CSP_PASSTHROUGH + rv = CryptSetProvParam(hProv, dwParam, pbData, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + + switch (dwParam) + { + case PP_ADMIN_PIN: + case PP_KEYEXCHANGE_PIN: + case PP_SIGNATURE_PIN: + { + CK_SESSION_INFO info; + if (g_state.p11->C_GetSessionInfo(context->p11_, &info) == CKR_OK && + ((info.state == CKS_RO_USER_FUNCTIONS) || (info.state == CKS_RW_USER_FUNCTIONS))) + { + LOG("PKCS#11 module in user mode, PIN verification skipped"); + } + else + { + CK_RV ck_rv = g_state.p11->C_Login(context->p11_, CKU_USER, + (CK_UTF8CHAR*)pbData, (CK_ULONG)strlen((char*)pbData)); + + if (ck_rv != CKR_OK) + ThrowMsg(NTE_FAIL, "Error during PIN verification"); + else + LOG("PIN Verification Successful: 0x%X\n", dwParam); + } + } + break; + case PP_REGISTER_CERTIFICATE: + { + if (context->verifyContext_) + Throw(NTE_PERM); + + CK_OBJECT_HANDLE hCert; + + if (!pbData) + { + if (!FindObject(context, &hCert, CKO_CERTIFICATE)) + { + LOG("Could not find a certificate in container"); + Throw(NTE_FAIL); + } + } + else + { + Session temp(false); + temp.p11_ = context->p11_; + temp.containerName_ = (char*)pbData; + temp.CKAID_ = (char*)pbData; + temp.CKAID_.pop_back(); // remove null + temp.CKAID_.HexToBin(); + if (!FindObject(&temp, &hCert, CKO_CERTIFICATE)) + { + LOG("Could not find a certificate in container: \"%s\"", &temp.containerName_[0]); + Throw(NTE_FAIL); + } + } + + CK_ATTRIBUTE attrib = { CKA_VALUE, 0, 0 }; + + if (g_state.p11->C_GetAttributeValue(context->p11_, hCert, &attrib, 1) != CKR_OK) + Throw(NTE_FAIL); + + BinStr cert(attrib.ulValueLen); + attrib.pValue = &cert[0]; + if (g_state.p11->C_GetAttributeValue(context->p11_, hCert, &attrib, 1) != CKR_OK) + Throw(NTE_FAIL); + + HCERTSTORE certStore = CertOpenSystemStore(hProv, "MY"); + if (certStore == 0) + ThrowMsg(NTE_FAIL, "CertOpenSystemStore failed"); + + PCCERT_CONTEXT certContext = + CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &cert[0], cert.size()); + + if (certContext == 0) + ThrowMsg(NTE_FAIL, "CertCreateCertificateContext failed"); + + PCCERT_CONTEXT newCert; + if (!CertAddCertificateContextToStore(certStore, certContext, CERT_STORE_ADD_REPLACE_EXISTING, &newCert)) + { + CertFreeCertificateContext(certContext); + ThrowMsg(NTE_FAIL, "CertAddCertificateContextToStore failed"); + } + + BinStr containerName = (char*)pbData; + CRYPT_KEY_PROV_INFO provInfo; + provInfo.pwszContainerName = new unsigned short[containerName.size()]; + provInfo.pwszProvName = new unsigned short[strlen(PROVIDER_NAME) + 1];; + provInfo.dwProvType = PROVIDER_TYPE; + provInfo.dwFlags = 0; + provInfo.cProvParam = 0; + provInfo.rgProvParam = 0; + provInfo.dwKeySpec = AT_SIGNATURE; + + mbstowcs(provInfo.pwszContainerName, (char*)&containerName[0], containerName.size()); + mbstowcs(provInfo.pwszProvName, PROVIDER_NAME, strlen(PROVIDER_NAME) + 1); + + CertSetCertificateContextProperty(newCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &provInfo); + + delete [] provInfo.pwszContainerName; + delete [] provInfo.pwszProvName; + + CertFreeCertificateContext(certContext); + CertFreeCertificateContext(newCert); + CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG); + } + break; + default: + ThrowMsg(NTE_BAD_TYPE, "Unknown parameter"); + break; + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPGetProvParam + - + * Purpose: + * Allows applications to get various aspects of the + * operations of a provider + * + * Parameters: + * IN hProv - Handle to a CSP + * IN dwParam - Parameter number + * OUT pbData - Pointer to data + * IN OUT pdwDataLen - Length of parameter data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPGetProvParam( + IN HCRYPTPROV hProv, + IN DWORD dwParam, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen, + IN DWORD dwFlags) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + LOG("dwParam = %s (%d), dwFlags = %d\n", + StringifyProvParam(dwParam).c_str(), dwParam, dwFlags); + +#ifdef CSP_PASSTHROUGH + if (dwParam == PP_NAME) + { + if (pbData) + strcpy((char*)pbData, PROVIDER_NAME); + *pcbDataLen = (DWORD)strlen(PROVIDER_NAME) + 1; + rv = TRUE; + } + else if (dwParam == PP_KEYSET_SEC_DESCR) + { + rv = FALSE; + SetLastError(NTE_BAD_TYPE); + } + else + rv = CryptGetProvParam(hProv, dwParam, pbData, pcbDataLen, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + + if ((dwFlags & CRYPT_FIRST) && (dwParam != PP_ENUMALGS) + && (dwParam != PP_ENUMALGS) && (dwParam != PP_ENUMCONTAINERS) + && (dwParam != PP_ENUMALGS_EX)) + { + Throw(NTE_BAD_FLAGS); + } + + switch (dwParam) + { + case PP_CONTAINER: + if (context->verifyContext_) + Throw(ERROR_INVALID_PARAMETER); + + PutDataIntoBuffer(pbData, pcbDataLen, &context->containerName_[0], + context->containerName_.size()); + break; + case PP_ENUMALGS: + GetProvParam_PP_ENUMALGS(context, dwFlags, pbData, pcbDataLen); + break; + case PP_ENUMALGS_EX: + GetProvParam_PP_ENUMALGS_EX(context, dwFlags, pbData, pcbDataLen); + break; + case PP_ENUMCONTAINERS: + GetProvParam_PP_ENUMCONTAINERS(context, dwFlags, pbData, pcbDataLen); + break; + case PP_IMPTYPE: + { + int type = CRYPT_IMPL_MIXED | CRYPT_IMPL_REMOVABLE; + PutDataIntoBuffer(pbData, pcbDataLen, (LPBYTE)&type, sizeof(type)); + } + break; + case PP_NAME: + PutDataIntoBuffer(pbData, pcbDataLen, (LPBYTE)PROVIDER_NAME, + (unsigned long)strlen(PROVIDER_NAME) + 1); + break; + case PP_VERSION: + { + DWORD version = PROVIDER_MAJOR_VERSION << 8 || PROVIDER_MINOR_VERSION; + PutDataIntoBuffer(pbData, pcbDataLen, (LPBYTE)&version, sizeof(version)); + } + break; + case PP_SIG_KEYSIZE_INC: + { + DWORD increment = 8; + PutDataIntoBuffer(pbData, pcbDataLen, (LPBYTE)&increment, sizeof(increment)); + } + break; + case PP_KEYX_KEYSIZE_INC: + { + DWORD increment = 8; + PutDataIntoBuffer(pbData, pcbDataLen, (LPBYTE)&increment, sizeof(increment)); + } + break; + case PP_UNIQUE_CONTAINER: + PutDataIntoBuffer(pbData, pcbDataLen, &context->containerName_[0], + context->containerName_.size()); + break; + case PP_PROVTYPE: + { + DWORD providerType = PROVIDER_TYPE; + PutDataIntoBuffer(pbData, pcbDataLen, (LPBYTE)&providerType, sizeof(providerType)); + } + break; + case PP_KEYSET_SEC_DESCR: + default: + Throw(NTE_BAD_TYPE); + break; + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + // Logging only + { + if (rv && pbData) + LOG("Returning %u (0x%X) bytes data:\n%s\n\"%s\"\n", *pcbDataLen, *pcbDataLen, + StringifyBin(pbData, *pcbDataLen).c_str(), + StringifyBin(pbData, *pcbDataLen, false).c_str()); + } + + END_API_CALL; + return rv; +} + + +/* + - CPSetHashParam + - + * Purpose: + * Allows applications to customize various aspects of the + * operations of a hash + * + * Parameters: + * IN hProv - Handle to a CSP + * IN hHash - Handle to a hash + * IN dwParam - Parameter number + * IN pbData - Pointer to data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPSetHashParam( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN DWORD dwParam, + IN CONST BYTE *pbData, + IN DWORD dwFlags) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptSetHashParam(hHash, dwParam, pbData, dwFlags); +#else + try + { + g_state.checkValidSession(hProv); + rv = CryptSetHashParam(hHash, dwParam, pbData, dwFlags); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPGetHashParam + - + * Purpose: + * Allows applications to get various aspects of the + * operations of a hash + * + * Parameters: + * IN hProv - Handle to a CSP + * IN hHash - Handle to a hash + * IN dwParam - Parameter number + * OUT pbData - Pointer to data + * IN pdwDataLen - Length of parameter data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPGetHashParam( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN DWORD dwParam, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen, + IN DWORD dwFlags) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptGetHashParam(hHash, dwParam, pbData, pcbDataLen, dwFlags); +#else + try + { + g_state.checkValidSession(hProv); + rv = CryptGetHashParam(hHash, dwParam, pbData, pcbDataLen, dwFlags); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPExportKey + - + * Purpose: + * Export cryptographic keys out of a CSP in a secure manner + * + * + * Parameters: + * IN hProv - Handle to the CSP user + * IN hKey - Handle to the key to export + * IN hPubKey - Handle to exchange public key value of + * the destination user + * IN dwBlobType - Type of key blob to be exported + * IN dwFlags - Flags values + * OUT pbData - Key blob data + * IN OUT pdwDataLen - Length of key blob in bytes + * + * Returns: + */ + +BOOL WINAPI +CPExportKey( + IN HCRYPTPROV hProv, + IN HCRYPTKEY hKey, + IN HCRYPTKEY hPubKey, + IN DWORD dwBlobType, + IN DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptExportKey(hKey, hPubKey, dwBlobType, dwFlags, pbData, pcbDataLen); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + LOG("hKey:0x%X hPubKey:0x%X dwBlobType:0x%X dwFlags:0x%X\n", + hKey, hPubKey, dwBlobType, dwFlags); + + if (key->sessionKey_) + { + LOG("Trying to export hFakeSessionKey: 0x%X\n", key->hFakeSessionKey_); + + if (dwBlobType == PUBLICKEYBLOB) + rv = CryptExportKey(key->hFakeSessionKey_, hPubKey, dwBlobType, dwFlags, pbData, pcbDataLen); + else + { + DWORD publicKeyLen; + g_state.checkValidKey(hPubKey); + + // Prevent infinite loop + if (((Key*)hPubKey)->sessionKey_) + { + rv = CryptExportKey( + key->hFakeSessionKey_, + ((Key*)hPubKey)->hFakeSessionKey_, + dwBlobType, dwFlags, pbData, pcbDataLen); + } + else + { + if (!CPExportKey(hProv, hPubKey, 0, PUBLICKEYBLOB, 0, NULL, &publicKeyLen)) + ThrowMsg(0, "CPExportKey failed"); + + BinStr keyBlob(publicKeyLen); + if (!CPExportKey(hProv, hPubKey, 0, PUBLICKEYBLOB, 0, &keyBlob[0], &publicKeyLen)) + ThrowMsg(0, "CPExportKey failed"); + + HCRYPTKEY hPublicKey; + if (!CryptImportKey(context->cryptProv_, &keyBlob[0], publicKeyLen, 0, CRYPT_NO_SALT, &hPublicKey)) + ThrowMsg(0, "CryptImportKey failed"); + + rv = CryptExportKey(key->hFakeSessionKey_, hPublicKey, dwBlobType, dwFlags, pbData, pcbDataLen); + + CryptDestroyKey(hPubKey); + } + } + } + else // non-session key + { + LOG("Signature/Exchange Key Looking for: %x\n", hKey); + LOG("KeyAlg:%x AlgRSA:%x\n", key->algId_, CALG_RSA_KEYX); + + switch (dwBlobType) + { + case SIMPLEBLOB: + ThrowMsg(NTE_BAD_TYPE, "Unknown: SIMPLEBLOB\n"); + break; + case PUBLICKEYBLOB: + { + CK_ATTRIBUTE atrTemplate[] = { + { CKA_MODULUS, 0, 0 }, + { CKA_PUBLIC_EXPONENT, 0, 0 }, + }; + + BinStr modulus, exponent; + + if (key->hPublicKey_ == -1) // No public key on card + { + CK_OBJECT_HANDLE hObj; + if (!FindObject(context, &hObj, CKO_CERTIFICATE)) + ThrowMsg(NTE_FAIL, "No public key and no certificate; bailing out"); + + CK_ATTRIBUTE attrib = { CKA_VALUE, 0, 0 }; + if (g_state.p11->C_GetAttributeValue(context->p11_, hObj, &attrib, 1) != CKR_OK) + Throw(NTE_FAIL); + + BinStr certData(attrib.ulValueLen); + attrib.pValue = &certData[0]; + if (g_state.p11->C_GetAttributeValue(context->p11_, hObj, &attrib, 1) != CKR_OK) + Throw(NTE_FAIL); + + if (!GetModulusFromCert(context, &modulus, &exponent, certData)) + Throw(NTE_FAIL); + } + else + { + CK_RV ck_rv = g_state.p11->C_GetAttributeValue(context->p11_, key->hPublicKey_, + atrTemplate, sizeof(atrTemplate) / sizeof(CK_ATTRIBUTE)); + + if (ck_rv != CKR_OK) + ThrowMsg(NTE_FAIL, "Could not get the attribute values"); + + modulus.resize(atrTemplate[0].ulValueLen); + exponent.resize(atrTemplate[1].ulValueLen); + } + + if (pbData == NULL) + { + *pcbDataLen = sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY) + modulus.size(); + LOG("Length: %d\n", *pcbDataLen); + goto ExportKeyDone; + } + + if (key->hPublicKey_ != -1) + { + LOG("Modulus ulValueLen:%u Exponent: %u\n", + modulus.size(), exponent.size()); + + atrTemplate[0].pValue = &modulus[0]; + atrTemplate[1].pValue = &exponent[0]; + + CK_RV ck_rv = g_state.p11->C_GetAttributeValue(context->p11_, key->hPublicKey_, + atrTemplate, sizeof(atrTemplate) / sizeof(CK_ATTRIBUTE)); + + if (ck_rv != CKR_OK) + ThrowMsg(NTE_FAIL, "Could not read the attributes"); + } + + PUBLICKEYSTRUC header; + + // build the blob header + header.bType = (BYTE)dwBlobType; + header.bVersion = 2; + header.reserved = 0; + header.aiKeyAlg = CALG_RSA_KEYX; + + LPBYTE pos = pbData; + // put the blob header into the char array + memcpy(pos, &header, sizeof(header)); + pos += sizeof(header); + + // fill in the RSA structure + RSAPUBKEY rsaPubKey; + rsaPubKey.magic = 0x31415352; + rsaPubKey.bitlen = atrTemplate[0].ulValueLen * 8; //bit length + rsaPubKey.pubexp = 0; + + Reverse(&exponent); + + if (exponent.size() <= 4) + memcpy(&rsaPubKey.pubexp, &exponent[0], exponent.size()); + else + ThrowMsg(NTE_FAIL, "Can't handle exponent sizes more than 4"); + + LOG("rsaPubKey.pubexp = 0x%X\n", rsaPubKey.pubexp); + + //put the rsaPubKey data in the BYTE array + memcpy(pos, &rsaPubKey, sizeof(rsaPubKey)); + pos += sizeof(rsaPubKey); + + LOG("Public exponent: %02x %02x %02x %02x\n", + pbData[16], pbData[17], pbData[18], pbData[19]); + + memcpy(pos, &modulus[0], modulus.size()); + Reverse(pos, modulus.size()); + pos += modulus.size(); + } + break; + case PRIVATEKEYBLOB: + ThrowMsg(NTE_BAD_TYPE, "Unknown: PRIVATEKEYBLOB\n"); + break; + case OPAQUEKEYBLOB: + ThrowMsg(NTE_BAD_TYPE, "Unknown: OPAQUEKEYBLOB\n"); + break; + } + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + +ExportKeyDone: + // Logging only + { + if (rv && pbData) + LOG("Returning %u (0x%X) bytes data:\n%s\n\"%s\"\n", *pcbDataLen, *pcbDataLen, + StringifyBin(pbData, *pcbDataLen).c_str(), + StringifyBin(pbData, *pcbDataLen, false).c_str()); + } + + END_API_CALL; + return rv; +} + + +/* + - CPImportKey + - + * Purpose: + * Import cryptographic keys + * + * + * Parameters: + * IN hProv - Handle to the CSP user + * IN pbData - Key blob data + * IN dwDataLen - Length of the key blob data + * IN hPubKey - Handle to the exchange public key value of + * the destination user + * IN dwFlags - Flags values + * OUT phKey - Pointer to the handle to the key which was + * Imported + * + * Returns: + */ + +BOOL WINAPI +CPImportKey( + IN HCRYPTPROV hProv, + IN CONST BYTE *pbData, + IN DWORD cbDataLen, + IN HCRYPTKEY hPubKey, + IN DWORD dwFlags, + OUT HCRYPTKEY *phKey) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptImportKey(hProv, pbData, cbDataLen, hPubKey, dwFlags, phKey); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + Key* pubKey = reinterpret_cast<Key*>(hPubKey); + + BLOBHEADER* header = (BLOBHEADER*)pbData; + + switch(header->bType) + { + case SIMPLEBLOB: + // What a terrible name, this is the most complicated of blob types + LOG("Trying to import SIMPLEBLOB\n"); + { + ALG_ID id = *((ALG_ID*)&pbData[sizeof(BLOBHEADER)]); + DWORD headerSize = sizeof(BLOBHEADER) + sizeof(ALG_ID); + BinStr data(cbDataLen - headerSize); + memcpy(&data[0], &pbData[headerSize], data.size()); + + CK_OBJECT_HANDLE hPrivKey; + if (!FindObject(context, &hPrivKey, CKO_PRIVATE_KEY)) + ThrowMsg(NTE_FAIL, "Could not find private key"); + + CK_MECHANISM mechanism; + mechanism.mechanism = CKM_RSA_PKCS; + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + CK_RV ck_rv = g_state.p11->C_DecryptInit(context->p11_, &mechanism, hPrivKey); + if (ck_rv != CKR_OK) + ThrowMsg(NTE_FAIL, "C_DecryptInit failed"); + + Reverse(&data); + + BinStr decrypted(data.size()); + CK_ULONG decrypt_size = static_cast<CK_ULONG>(decrypted.size()); + ck_rv = g_state.p11->C_Decrypt(context->p11_, &data[0], (CK_ULONG)data.size(), &decrypted[0], &decrypt_size); + if (ck_rv != CKR_OK) + ThrowMsg(NTE_FAIL, "C_Decrypt failed"); + + decrypted.resize(decrypt_size); + Reverse(&decrypted); + + LOG("Session key is (LEN:%u ALG:%s): %s\n", + decrypted.size(), StringifyCALG(header->aiKeyAlg).c_str(), StringifyBin(decrypted).c_str()); + + HCRYPTKEY hPubPrivKey; + if (!CryptoHelper::CreatePrivateExponentOneKey( + context->cryptProv_, AT_KEYEXCHANGE, &hPubPrivKey)) + { + ThrowMsg(NTE_FAIL, "CryptoHelper::CreatePrivateExponentOneKey failed"); + } + + // We reverse it again here because ImportPlainSessionBlob will reverse + // it once more (!) FIXME: please + Reverse(&decrypted); + + HCRYPTKEY hKey; + if (!CryptoHelper::ImportPlainSessionBlob(context->cryptProv_, hPubPrivKey, + header->aiKeyAlg, &decrypted[0], decrypted.size(), &hKey)) + { + ThrowMsg(NTE_FAIL, "CryptoHelper::ImportPlainSessionBlob failed"); + } + + CryptDestroyKey(hPubPrivKey); + + Key* newKey = new Key(true); + if (!newKey) + Throw(NTE_NO_MEMORY); + + newKey->algId_ = header->aiKeyAlg; + newKey->hFakeSessionKey_ = hKey; + + *phKey = reinterpret_cast<HCRYPTKEY>(newKey); + g_state.addKey(newKey); + } + break; + case PUBLICKEYBLOB: + // FIXME: import to P11 module not CryptoAPI? + LOG("Trying to import PUBLICKEYBLOB\n"); + { + Key* key = new Key(true); + if (!key) + Throw(NTE_NO_MEMORY); + + key->algId_ = header->aiKeyAlg; + if (!CryptImportKey(context->cryptProv_, pbData, cbDataLen, + hPubKey, dwFlags, &key->hFakeSessionKey_)) + { + delete key; + Throw(NTE_FAIL); + } + + *phKey = (HCRYPTKEY)key; + g_state.addKey(key); + } + break; + case PRIVATEKEYBLOB: + LOG("Trying to import PRIVATEKEYBLOB\n"); + { + BinStr data(cbDataLen); + memcpy(&data[0], pbData, cbDataLen); + + BLOBHEADER* header = (BLOBHEADER*)&data[0]; + RSAPUBKEY* rsakey = (RSAPUBKEY*)&data[sizeof(BLOBHEADER)]; + BYTE* pos = &data[0] + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY); + + CK_ULONG bitLen8 = rsakey->bitlen / 8; + CK_ULONG bitLen16 = rsakey->bitlen / 16; + + BYTE* modulus = pos; + pos += bitLen8; + BYTE* prime1 = pos; + pos += bitLen16; + BYTE* prime2 = pos; + pos += bitLen16; + BYTE* exponent1 = pos; + pos += bitLen16; + BYTE* exponent2 = pos; + pos += bitLen16; + BYTE* coefficient = pos; + pos += bitLen16; + BYTE* privateExponent = pos; + pos += bitLen8; + + BinStr pubExp(4); + memcpy(&pubExp[0], &rsakey->pubexp, sizeof(rsakey->pubexp)); + + // Shrink the exponent + while (pubExp.size() > 1 && pubExp[pubExp.size()-1] == 0x00) + pubExp.pop_back(); + + Reverse(&pubExp); + Reverse(modulus, bitLen8); + Reverse(prime1, bitLen16); + Reverse(prime2, bitLen16); + Reverse(exponent1, bitLen16); + Reverse(exponent2, bitLen16); + Reverse(coefficient, bitLen16); + Reverse(privateExponent, bitLen8); + + CK_ULONG cls = 0x03; + CK_ULONG keyType = 0x00; + CK_BYTE bTrue = 0x01; + CK_ATTRIBUTE pTemplate[] = { + { CKA_CLASS, &cls, sizeof(cls) }, + { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, + { CKA_TOKEN, &bTrue, sizeof(bTrue) }, + { CKA_PRIVATE, &bTrue, sizeof(bTrue) }, + { CKA_SENSITIVE, &bTrue, sizeof(bTrue) }, + { CKA_ID, &context->containerName_[0], (CK_ULONG)context->containerName_.size() - 1 }, + { CKA_MODULUS, modulus, bitLen8 }, + { CKA_PRIVATE_EXPONENT, privateExponent, bitLen8 }, + { CKA_PUBLIC_EXPONENT, &pubExp[0], (CK_ULONG)pubExp.size() }, + { CKA_PRIME_1, prime1, bitLen16 }, + { CKA_PRIME_2, prime2, bitLen16 }, + { CKA_EXPONENT_1, exponent1, bitLen16 }, + { CKA_EXPONENT_2, exponent2, bitLen16 }, + { CKA_COEFFICIENT, coefficient, bitLen16 } }; + + CK_OBJECT_HANDLE hPrivKey, hPubKey; + CK_RV ck_rv; + ck_rv = g_state.p11->C_CreateObject(context->p11_, &pTemplate[0], + sizeof(pTemplate) / sizeof(CK_ATTRIBUTE), &hPrivKey); + if (ck_rv != CKR_OK) + ThrowMsg(NTE_FAIL, "C_CreateObject failed"); + + if (!FindObject(context, &hPubKey, CKO_PUBLIC_KEY)) + ThrowMsg(NTE_FAIL, "Could not find public key"); + + Key* key = new Key(false); + if (!key) + Throw(NTE_NO_MEMORY); + + key->hPrivateKey_ = hPrivKey; + key->hPublicKey_ = hPubKey; + + *phKey = (HCRYPTKEY)key; + g_state.addKey(key); + } + break; + case PLAINTEXTKEYBLOB: + LOG("Trying to import PLAINTEXTKEYBLOB\n"); + Throw(NTE_BAD_TYPE); + break; + case OPAQUEKEYBLOB: + LOG("Trying to import OPAQUEKEYBLOB\n"); + Throw(NTE_BAD_TYPE); + break; + case PUBLICKEYBLOBEX: + LOG("Trying to import PUBLICKEYBLOBEX\n"); + Throw(NTE_BAD_TYPE); + break; + case SYMMETRICWRAPKEYBLOB: + LOG("Trying to import SYMMETRICWRAPKEYBLOB\n"); + Throw(NTE_BAD_TYPE); + break; + default: + LOG("Trying to import UNKNOWN blob type\n"); + Throw(NTE_BAD_TYPE); + break; + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPEncrypt + - + * Purpose: + * Encrypt data + * + * + * Parameters: + * IN hProv - Handle to the CSP user + * IN hKey - Handle to the key + * IN hHash - Optional handle to a hash + * IN Final - Boolean indicating if this is the final + * block of plaintext + * IN dwFlags - Flags values + * IN OUT pbData - Data to be encrypted + * IN OUT pdwDataLen - Pointer to the length of the data to be + * encrypted + * IN dwBufLen - Size of Data buffer + * + * Returns: + */ + +BOOL WINAPI +CPEncrypt( + 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) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptEncrypt(hKey, hHash, fFinal, dwFlags, pbData, pcbDataLen, cbBufLen); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + { + LOG("Input data: %s\n", StringifyBin(pbData, *pcbDataLen).c_str()); + LOG("Input data: %s\n", StringifyBin(pbData, *pcbDataLen, false).c_str()); + rv = CryptEncrypt(key->hFakeSessionKey_, hHash, fFinal, dwFlags, pbData, pcbDataLen, cbBufLen); + LOG("Outut data: %s\n", StringifyBin(pbData, *pcbDataLen).c_str()); + LOG("Outut data: %s\n", StringifyBin(pbData, *pcbDataLen, false).c_str()); + } + else + { + // FIXME: Encrypt at PKCS#11 module + Throw(NTE_BAD_ALGID); + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPDecrypt + - + * Purpose: + * Decrypt data + * + * + * Parameters: + * IN hProv - Handle to the CSP user + * IN hKey - Handle to the key + * IN hHash - Optional handle to a hash + * IN Final - Boolean indicating if this is the final + * block of ciphertext + * IN dwFlags - Flags values + * IN OUT pbData - Data to be decrypted + * IN OUT pdwDataLen - Pointer to the length of the data to be + * decrypted + * + * Returns: + */ + +BOOL WINAPI +CPDecrypt( + IN HCRYPTPROV hProv, + IN HCRYPTKEY hKey, + IN HCRYPTHASH hHash, + IN BOOL fFinal, + IN DWORD dwFlags, + IN OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptDecrypt(hKey, hHash, fFinal, dwFlags, pbData, pcbDataLen); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + { + LOG("Decrypting with default MS provider\n"); + LOG("Input data: %s\n", StringifyBin(pbData, *pcbDataLen, false).c_str()); + rv = CryptDecrypt(key->hFakeSessionKey_, hHash, fFinal, dwFlags, pbData, pcbDataLen); + LOG("Outut data: %s\n", StringifyBin(pbData, *pcbDataLen, false).c_str()); + } + else + { + LOG("Decrypting with PKCS#11\n"); + CK_MECHANISM decryptMechanism; + decryptMechanism.mechanism = CKM_RSA_PKCS; + decryptMechanism.pParameter = 0; + decryptMechanism.ulParameterLen = 0; + + CK_RV ck_rv = g_state.p11->C_DecryptInit(context->p11_, &decryptMechanism, key->hPrivateKey_); + if (ck_rv == CKR_OK) + { + LOG("Datalen: %d\n", *pcbDataLen); + Reverse(pbData, *pcbDataLen); + LOG("Data Reversed: %s\n", StringifyBin(pbData, *pcbDataLen).c_str()); + + BinStr cleartext; + cleartext.resize(128); + DWORD cleartextLen = cleartext.size(); + + ck_rv = g_state.p11->C_Decrypt(context->p11_, pbData, *pcbDataLen, &cleartext[0], &cleartextLen); + if (ck_rv != CKR_OK) + { + LOG("Could not perform the decryption, ck_rv: %x\n", ck_rv); + Throw(NTE_FAIL); + } + + memcpy(pbData, &cleartext[0], cleartext.size()); + + *pcbDataLen = cleartextLen; + } + else + ThrowMsg(NTE_FAIL, "Could not initialize the decryption"); + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPCreateHash + - + * Purpose: + * initate the hashing of a stream of data + * + * + * Parameters: + * IN hUID - Handle to the user identifcation + * IN Algid - Algorithm identifier of the hash algorithm + * to be used + * IN hKey - Optional handle to a key + * IN dwFlags - Flags values + * OUT pHash - Handle to hash object + * + * Returns: + */ + +BOOL WINAPI +CPCreateHash( + IN HCRYPTPROV hProv, + IN ALG_ID Algid, + IN HCRYPTKEY hKey, + IN DWORD dwFlags, + OUT HCRYPTHASH *phHash) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptCreateHash(hProv, Algid, hKey, dwFlags, phHash); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + // FIXME: Keyed hash algorithms can not be handled because this assumes + // hKey is valid even though it's probably not a default crypto + // provider key + rv = CryptCreateHash(context->cryptProv_, Algid, hKey, dwFlags, phHash); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPHashData + - + * Purpose: + * Compute the cryptograghic hash on a stream of data + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN hHash - Handle to hash object + * IN pbData - Pointer to data to be hashed + * IN dwDataLen - Length of the data to be hashed + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPHashData( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN CONST BYTE *pbData, + IN DWORD cbDataLen, + IN DWORD dwFlags) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptHashData(hHash, pbData, cbDataLen, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + rv = CryptHashData(hHash, pbData, cbDataLen, dwFlags); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + // Logging only + { + if (pbData) + LOG("Hashing %u (0x%X) bytes data:\n%s\n\"%s\"\n", cbDataLen, cbDataLen, + StringifyBin((LPBYTE)pbData, cbDataLen).c_str(), + StringifyBin((LPBYTE)pbData, cbDataLen, false).c_str()); + } + + END_API_CALL; + return rv; +} + + +/* + - CPHashSessionKey + - + * Purpose: + * Compute the cryptograghic hash on a key object. + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN hHash - Handle to hash object + * IN hKey - Handle to a key object + * IN dwFlags - Flags values + * + * Returns: + * CRYPT_FAILED + * CRYPT_SUCCEED + */ + +BOOL WINAPI +CPHashSessionKey( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN HCRYPTKEY hKey, + IN DWORD dwFlags) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptHashSessionKey(hHash, hKey, dwFlags); +#else + try + { + g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + rv = CryptHashSessionKey(hHash, key->hFakeSessionKey_, dwFlags); + else + ThrowMsg(NTE_FAIL, "ERROR: Non-session key"); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPSignHash + - + * Purpose: + * Create a digital signature from a hash + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN hHash - Handle to hash object + * IN dwKeySpec - Key pair to that is used to sign with + * IN sDescription - Description of data to be signed + * IN dwFlags - Flags values + * OUT pbSignature - Pointer to signature data + * IN OUT dwHashLen - Pointer to the len of the signature data + * + * Returns: + */ + +BOOL WINAPI +CPSignHash( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN DWORD dwKeySpec, + IN LPCWSTR szDescription, + IN DWORD dwFlags, + OUT LPBYTE pbSignature, + IN OUT LPDWORD pcbSigLen) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptSignHash(hHash, dwKeySpec, 0, dwFlags, pbSignature, pcbSigLen); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + + // Find the key we want to sign with + CK_OBJECT_HANDLE hPrivKey; + if (!FindObject(context, &hPrivKey, CKO_PRIVATE_KEY)) + { + LOG("Could not find key; container is: %s\n", &context->containerName_[0]); + Throw(NTE_NO_KEY); + } + + DWORD dwHashLen; + if (!CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashLen, 0)) + { + DisplayError(context, "Could not get length using getHashParam\n"); + Throw(NTE_BAD_HASH); + } + + LOG("Hash len: %d\n", dwHashLen); + BinStr pbHash(dwHashLen); + + // Get the hash itself + if (!CryptGetHashParam(hHash, HP_HASHVAL, &pbHash[0], &dwHashLen, 0)) + { + DisplayError(context, "Error during reading hash value."); + Throw(NTE_BAD_HASH); + } + + DWORD hashAlg; + DWORD hashAlgSize = sizeof(hashAlg); + if (!CryptGetHashParam(hHash, HP_ALGID, (BYTE*)&hashAlg, &hashAlgSize, 0)) + { + DisplayError(context, "Error during reading hash ALGID value."); + Throw(NTE_BAD_HASH); + } + + if (!(dwFlags & CRYPT_NOHASHOID)) + { + // Add PKCS#7 header + BinStr temp; + if (hashAlg == CALG_MD5) + { + LOG("CALG_MD5 hash\n"); + BYTE pkcs7[] = { 0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00, + 0x04, 0x10 }; + temp.resize(sizeof(pkcs7) + pbHash.size()); + memcpy(&temp[0], pkcs7, sizeof(pkcs7)); + memcpy(&temp[sizeof(pkcs7)], &pbHash[0], pbHash.size()); + pbHash.swap(temp); + } + else if (hashAlg == CALG_SHA1) + { + LOG("CALG_SHA1 hash\n"); + BYTE pkcs7[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, + 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 }; + temp.resize(sizeof(pkcs7) + pbHash.size()); + memcpy(&temp[0], pkcs7, sizeof(pkcs7)); + memcpy(&temp[sizeof(pkcs7)], &pbHash[0], pbHash.size()); + pbHash.swap(temp); + } + else if (hashAlg == CALG_SSL3_SHAMD5) + { + // Intentionally blank; should not need to do anything else + } + else + { + LOG("Unsupported hash type: 0x%X\n", hashAlg); + Throw(NTE_BAD_HASH); + } + } + + if (pbSignature == NULL) + { + CK_ATTRIBUTE privKeyMod = { CKA_MODULUS, 0, 0 }; + + if (g_state.p11->C_GetAttributeValue(context->p11_, hPrivKey, &privKeyMod, 1) != CKR_OK || privKeyMod.ulValueLen == 0) + { + LOG("C_GetAttributeValue failed; using (slow) C_Sign to get signature length\n"); + + CK_MECHANISM signingMechanism; + signingMechanism.mechanism = CKM_RSA_PKCS; + signingMechanism.pParameter = NULL; + signingMechanism.ulParameterLen = 0; + + CK_RV ck_rv = g_state.p11->C_SignInit(context->p11_, &signingMechanism, hPrivKey); + if (ck_rv != CKR_OK) + { + LOG("Error during SignInit, errorcode 0x%X\n", ck_rv); + Throw(NTE_FAIL); + } + + ck_rv = g_state.p11->C_Sign(context->p11_, &pbHash[0], pbHash.size(), 0, &privKeyMod.ulValueLen); + if (ck_rv != CKR_OK) + { + LOG("Error during C_Sign to get signature length %x\n", ck_rv); + Throw(NTE_FAIL); + } + + // We now must actually do the C_Sign to finalize the session + BinStr temp_buf(privKeyMod.ulValueLen); + ck_rv = g_state.p11->C_Sign(context->p11_, &pbHash[0], pbHash.size(), &temp_buf[0], &privKeyMod.ulValueLen); + if (ck_rv != CKR_OK) + { + LOG("Error during C_Sign %x\n", ck_rv); + Throw(NTE_FAIL); + } + } + + *pcbSigLen = privKeyMod.ulValueLen; + } + else + { + CK_MECHANISM signingMechanism; + signingMechanism.mechanism = CKM_RSA_PKCS; + signingMechanism.pParameter = NULL; + signingMechanism.ulParameterLen = 0; + + CK_RV ck_rv = g_state.p11->C_SignInit(context->p11_, &signingMechanism, hPrivKey); + if (ck_rv != CKR_OK) + { + LOG("Error during SignInit, errorcode 0x%Xcd \n", ck_rv); + Throw(NTE_FAIL); + } + + LOG("Buffer size: %d\t\n", *pcbSigLen); + LOG("C_Sign called with data: %s\n", StringifyBin(pbHash, true).c_str()); + ck_rv = g_state.p11->C_Sign(context->p11_, &pbHash[0], pbHash.size(), pbSignature, pcbSigLen); + if (ck_rv == CKR_BUFFER_TOO_SMALL) + { + // We need to do this to close the sign session. There is no other way :( + LOG("Buffer too small, calling C_Sign to finalize session\n"); + BinStr temp_buf(*pcbSigLen); + g_state.p11->C_Sign(context->p11_, &pbHash[0], pbHash.size(), &temp_buf[0], pcbSigLen); + Throw(ERROR_MORE_DATA); + } + else if (ck_rv != CKR_OK) + { + LOG("Error during Sign %x\n", ck_rv); + Throw(NTE_FAIL); + } + + LOG("The signature is (len %d): %s\n", + *pcbSigLen, StringifyBin(pbSignature, *pcbSigLen).c_str()); + + Reverse(pbSignature, *pcbSigLen); + } + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + // Logging only + if (rv) + { + DWORD dwHashLen = 1024; + BinStr pbHash(dwHashLen); + if (CryptGetHashParam(hHash, HP_HASHVAL, &pbHash[0], &dwHashLen, 0)) + { + pbHash.resize(dwHashLen); + LOG("The hash is: %s\n", StringifyBin(pbHash).c_str()); + Reverse(&pbHash); + LOG("The hash is (reversed): %s\n", StringifyBin(pbHash).c_str()); + } + + if (pbSignature) + LOG("Returning %u (0x%X) bytes data:\n%s\n\"%s\"\n", *pcbSigLen, *pcbSigLen, + StringifyBin(pbSignature, *pcbSigLen).c_str(), + StringifyBin(pbSignature, *pcbSigLen, false).c_str()); + } + + END_API_CALL; + return rv; +} + + +/* + - CPDestroyHash + - + * Purpose: + * Destroy the hash object + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN hHash - Handle to hash object + * + * Returns: + */ + +BOOL WINAPI +CPDestroyHash( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptDestroyHash(hHash); +#else + try + { + g_state.checkValidSession(hProv); + rv = CryptDestroyHash(hHash); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPVerifySignature + - + * Purpose: + * Used to verify a signature against a hash object + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN hHash - Handle to hash object + * IN pbSignture - Pointer to signature data + * IN dwSigLen - Length of the signature data + * IN hPubKey - Handle to the public key for verifying + * the signature + * IN sDescription - String describing the signed data + * IN dwFlags - Flags values + * + * Returns: + */ + +BOOL WINAPI +CPVerifySignature( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN CONST BYTE *pbSignature, + IN DWORD cbSigLen, + IN HCRYPTKEY hPubKey, + IN LPCWSTR szDescription, + IN DWORD dwFlags) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptVerifySignature(hHash, pbSignature, cbSigLen, hPubKey, 0, dwFlags); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + + LOG("Must first import the public key to the default CSP\n"); + DWORD publicKeyLen; + if (CPExportKey( + hProv, + hPubKey, + 0, + PUBLICKEYBLOB, + 0, + NULL, + &publicKeyLen)) + { + LOG("Got Key length successfully, %d\n", publicKeyLen); + } + else + ThrowMsg(0, "Could not get keylength"); + + BinStr keyBlob; + keyBlob.resize(publicKeyLen); + + if (CPExportKey( + hProv, + hPubKey, + 0, + PUBLICKEYBLOB, + 0, + &keyBlob[0], + &publicKeyLen)) + { + LOG("Got the public key successfully\n"); + } + else + ThrowMsg(0, "Could not get public key"); + + LOG("The key blob data is (len %d): %s\n", + keyBlob.size(), StringifyBin(keyBlob).c_str()); + + HCRYPTKEY hKey; + if (CryptImportKey( + context->cryptProv_, + &keyBlob[0], + keyBlob.size(), + 0, + CRYPT_NO_SALT, + &hKey)) + { + LOG("Imported key to CSP successfully\n"); + } + else + ThrowMsg(0, "Could not import the key to the CSP"); + + rv = CryptVerifySignature(hHash, pbSignature, cbSigLen, hKey, NULL, dwFlags); + + CryptDestroyKey(hKey); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPGenRandom + - + * Purpose: + * Used to fill a buffer with random bytes + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN dwLen - Number of bytes of random data requested + * IN OUT pbBuffer - Pointer to the buffer where the random + * bytes are to be placed + * + * Returns: + */ + +BOOL WINAPI +CPGenRandom( + IN HCRYPTPROV hProv, + IN DWORD cbLen, + OUT LPBYTE pbBuffer) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptGenRandom(hProv, cbLen, pbBuffer); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + if (g_state.p11->C_GenerateRandom(context->p11_, pbBuffer, cbLen) != CKR_OK) + ThrowMsg(NTE_FAIL, "C_GenerateRandom failed"); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPGetUserKey + - + * Purpose: + * Gets a handle to a permanent user key + * + * + * Parameters: + * IN hProv - Handle to the user identifcation + * IN dwKeySpec - Specification of the key to retrieve + * OUT phUserKey - Pointer to key handle of retrieved key + * + * Returns: + */ + +BOOL WINAPI +CPGetUserKey( + IN HCRYPTPROV hProv, + IN DWORD dwKeySpec, + OUT HCRYPTKEY *phUserKey) +{ + BOOL rv = TRUE; + BEGIN_API_CALL; + + LOG("dwKeySpec: 0x%X\n", dwKeySpec); + +#ifdef CSP_PASSTHROUGH + rv = CryptGetUserKey(hProv, dwKeySpec, phUserKey); +#else + try + { + Session::Ptr context = g_state.checkValidSession(hProv); + if (phUserKey == 0) + Throw(NTE_BAD_KEY); + + ALG_ID newAlgId = dwKeySpec; + + if (newAlgId == AT_KEYEXCHANGE) + newAlgId = CALG_RSA_KEYX; + else if (newAlgId == AT_SIGNATURE) + newAlgId = CALG_RSA_SIGN; + + CK_OBJECT_HANDLE hPubKey, hPrivKey; + + // Find the objects we want + if (!FindObject(context, &hPrivKey, CKO_PRIVATE_KEY)) + ThrowMsg(NTE_NO_KEY, "ERROR: Could not find the private key"); + if (!FindObject(context, &hPubKey, CKO_PUBLIC_KEY)) + { + hPubKey = -1; + LOG("WARNING: Could not find the public key (will attempt to get it from cert when needed)\n"); + } + + Key* keyPair = new Key(false); + if (!keyPair) + Throw(NTE_NO_MEMORY); + + keyPair->algId_ = newAlgId; + keyPair->hPrivateKey_ = hPrivKey; + keyPair->hPublicKey_ = hPubKey; + + *phUserKey = (HCRYPTKEY)keyPair; + g_state.addKey(keyPair); + + LOG("GetUserKey returns %x\n", *phUserKey); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPDuplicateHash + - + * Purpose: + * Duplicates the state of a hash and returns a handle to it. + * This is an optional entry. Typically it only occurs in + * SChannel related CSPs. + * + * Parameters: + * IN hUID - Handle to a CSP + * IN hHash - Handle to a hash + * IN pdwReserved - Reserved + * IN dwFlags - Flags + * IN phHash - Handle to the new hash + * + * Returns: + */ + +BOOL WINAPI +CPDuplicateHash( + IN HCRYPTPROV hProv, + IN HCRYPTHASH hHash, + IN LPDWORD pdwReserved, + IN DWORD dwFlags, + OUT HCRYPTHASH *phHash) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptDuplicateHash(hHash, pdwReserved, dwFlags, phHash); +#else + try + { + g_state.checkValidSession(hProv); + rv = CryptDuplicateHash(hHash, pdwReserved, dwFlags, phHash); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} + + +/* + - CPDuplicateKey + - + * Purpose: + * Duplicates the state of a key and returns a handle to it. + * This is an optional entry. Typically it only occurs in + * SChannel related CSPs. + * + * Parameters: + * IN hUID - Handle to a CSP + * IN hKey - Handle to a key + * IN pdwReserved - Reserved + * IN dwFlags - Flags + * IN phKey - Handle to the new key + * + * Returns: + */ + +BOOL WINAPI +CPDuplicateKey( + IN HCRYPTPROV hProv, + IN HCRYPTKEY hKey, + IN LPDWORD pdwReserved, + IN DWORD dwFlags, + OUT HCRYPTKEY *phKey) +{ + BOOL rv = FALSE; + BEGIN_API_CALL; + +#ifdef CSP_PASSTHROUGH + rv = CryptDuplicateKey(hKey, pdwReserved, dwFlags, phKey); +#else + try + { + g_state.checkValidSession(hProv); + Key::Ptr key = g_state.checkValidKey(hKey); + + if (key->sessionKey_) + rv = CryptDuplicateKey(key->hFakeSessionKey_, pdwReserved, dwFlags, phKey); + else + ThrowMsg(ERROR_CALL_NOT_IMPLEMENTED, "ERROR: Non-session key"); + } + catch(Error& e) + { + e.log(); + if (e.code_ != 0) + SetLastError(e.code_); + rv = FALSE; + } +#endif // CSP_PASSTHROUGH + + END_API_CALL; + return rv; +} diff --git a/src/windows/csp/csp.h b/src/windows/csp/csp.h new file mode 100644 index 0000000..ba02f5c --- /dev/null +++ b/src/windows/csp/csp.h @@ -0,0 +1,146 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : csp.h +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#ifndef __INCLUDE_CSP_H__ +#define __INCLUDE_CSP_H__ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#undef UNICODE + +#ifndef CSP_PASSTHROUGH +#define PROVIDER_NAME "Identity Alliance CSP" +#else +#define PROVIDER_NAME "Identity Alliance CSP - Passthrough" +#endif + +#define PROVIDER_TYPE PROV_RSA_FULL +#define PROVIDER_MAJOR_VERSION 1 +#define PROVIDER_MINOR_VERSION 0 + +#define PP_REGISTER_CERTIFICATE 1000 + +// Logging macros +#define LOG flogf +#define BEGIN_API_CALL LOG("+%s() - called\n", __FUNCTION__) +#define END_API_CALL LOG(" -%s() - finished: %s (0x%X)\n", __FUNCTION__, rv ? "TRUE" : "FALSE", GetLastError()); + +#include <windows.h> +#include <wincrypt.h> +#include <string> +#include <set> +#include "cspdk.h" +#include "cryptoki_win32.h" +#include "BinStr.h" +#include "Key.h" +#include "Session.h" +#include "State.h" + +extern "C" HINSTANCE g_hModule; + +namespace MCSP { + +/////////////////////////////////////////////////////////////////////////////// +// The global state +/////////////////////////////////////////////////////////////////////////////// +extern State g_state; + +/////////////////////////////////////////////////////////////////////////////// +// Function prototypes (in alphabetical order) +/////////////////////////////////////////////////////////////////////////////// +CK_ULONG ASN1Len(const CK_BYTE* buf, bool withHeader = true); +void DisplayError(const Session* context, const std::string& str); +void DisplayWin32Error(const Session* context); +bool DisplayPINDialog(BinStr* pin); +bool FindDefaultCert(Session* context, CK_OBJECT_HANDLE* phCert, BinStr* container); +bool FindLastContainer(Session* context, CK_OBJECT_HANDLE* phObj, BinStr* container); +bool FindObject(Session* context, CK_OBJECT_HANDLE* phObj, CK_OBJECT_CLASS objClass); +void flogf(const char* msg, ...); +bool GenUUID(BinStr* uuid); +bool GetExtKeyUsageFromCert(std::vector<std::string>* ext, const BinStr& cert); +bool GetModulusFromCert(Session* context, BinStr* modulus, BinStr* exponent, const BinStr& cert); +void HexIfBin(BinStr* str); +bool InitP11(); +void Reverse(BinStr* buf); +void Reverse(LPBYTE buf, size_t len); +std::string StringifyAquireFlags(DWORD param); +std::string StringifyBin(const BinStr& data, bool hexMode = true); +std::string StringifyBin(const LPBYTE data, size_t len, bool hexMode = true); +std::string StringifyCALG(ALG_ID id); +std::string StringifyProvParam(DWORD param); +std::string GetCurrentExecutable(); +std::string GetCurrentDLL(); + +// GetProvParam helpers +void GetProvParam_PP_ENUMALGS(Session* context, DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen); + +void GetProvParam_PP_ENUMALGS_EX(Session* context, DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen); + +void GetProvParam_PP_ENUMCONTAINERS(Session* context, DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen); + +void PutDataIntoBuffer(LPBYTE dest, LPDWORD destLen, const LPBYTE source, + DWORD sourceLen); + +} // namespace MCSP + +#include "Error.h" + +// END STANDARD CODE ////////////////////////////////////////////////////////// +// END STANDARD CODE ////////////////////////////////////////////////////////// +// END STANDARD CODE ////////////////////////////////////////////////////////// +// END STANDARD CODE ////////////////////////////////////////////////////////// +// END STANDARD CODE ////////////////////////////////////////////////////////// + +// Microsoft helper functions +namespace CryptoHelper { + +BOOL CreatePrivateExponentOneKey(HCRYPTPROV hProv, + DWORD dwKeySpec, + HCRYPTKEY *hPrivateKey); + +BOOL ExportPlainSessionBlob(HCRYPTKEY hPublicKey, + HCRYPTKEY hSessionKey, + LPBYTE *pbKeyMaterial, + DWORD *dwKeyMaterial); + +BOOL ImportPlainSessionBlob(HCRYPTPROV hProv, + HCRYPTKEY hPrivateKey, + ALG_ID dwAlgId, + LPBYTE pbKeyMaterial, + DWORD dwKeyMaterial, + HCRYPTKEY *hSessionKey); +} // namespace CryptoHelper + +#endif // __INCLUDE_CSP_H__ diff --git a/src/windows/csp/csp.rc b/src/windows/csp/csp.rc new file mode 100644 index 0000000..6a35c82 --- /dev/null +++ b/src/windows/csp/csp.rc @@ -0,0 +1,163 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +CRYPT_SIG_RESOURCE_NUMBER RCDATA +BEGIN + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PIN_DIALOG DIALOGEX 0, 0, 137, 42 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Please enter your PIN" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,49,23,40,14 + PUSHBUTTON "Cancel",IDCANCEL,93,23,39,14 + EDITTEXT IDC_PIN_EDIT,49,5,83,13,ES_PASSWORD | ES_AUTOHSCROLL + CONTROL 104,IDC_STATIC,"Static",SS_BITMAP,5,5,37,34 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,1,0,10 + PRODUCTVERSION 1,1,0,10 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Identity Alliance" + VALUE "FileDescription", "Identity Alliance Cryptographic Service Provider" + VALUE "FileVersion", "1, 1, 0, 10" + VALUE "InternalName", "IDACSP" + VALUE "LegalCopyright", "Copyright © 2003-2005 Identity Alliance" + VALUE "ProductVersion", "1, 1, 0, 10" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PIN_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 132 + TOPMARGIN, 4 + BOTTOMMARGIN, 39 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_PIN_LOGO BITMAP "IALogo2.bmp" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/windows/csp/cspx.cpp b/src/windows/csp/cspx.cpp new file mode 100644 index 0000000..200f43f --- /dev/null +++ b/src/windows/csp/cspx.cpp @@ -0,0 +1,1342 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : cspx.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include "csp.h" +#include <stdarg.h> +#include <time.h> +#include <sstream> + +using namespace std; + +namespace MCSP { + +/////////////////////////////////////////////////////////////////////////////// +// This cleans up messages that will be logged. Linefeeds are converted to +// CR/LF and a timestamp is added. +// +// Parameters: +// msg0 - Message to clean +// +// Returns: +// string result +/////////////////////////////////////////////////////////////////////////////// +string clean_flogf(const char* msg) +{ + ostringstream out; + time_t t; + + time(&t); + struct tm* time_s = localtime(&t); + + char timestr[32]; + sprintf(timestr, "%.2d/%.2d %.2d:%.2d:%.2d ", + time_s->tm_mon+1, + time_s->tm_mday, + time_s->tm_hour, + time_s->tm_min, + time_s->tm_sec); + + out << timestr; + + char last = 0; + for (size_t i = 0; msg[i] != 0x00; i++) + { + if (last == '\n') + out << " "; + + if (msg[i] == '\n' && last != '\r') + out << '\r'; + + out << msg[i]; + last = msg[i]; + } + + if (last != '\n') + out << '\r' << '\n'; + + return out.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Logs stuff +// +// Parameters: +// msg - Message to log +// ... - Variable parameters (like printf) +// +// Returns: +// none +/////////////////////////////////////////////////////////////////////////////// +void flogf(const char* msg0, ...) +{ + if (!g_state.logging()) + return; + + // Preserve error state + DWORD lastErr = GetLastError(); + + FILE* fp = fopen("C:\\CSPDEBUG.log", "ab"); + + if (!fp) + { + fp = stderr; + fprintf(fp, "ERROR: no log file"); + } + + string msg1 = clean_flogf(msg0); + const char* msg = msg1.c_str(); + + va_list args; + va_start(args, msg0); + vfprintf(fp, msg, args); + va_end(args); + + if (fp == stderr) + fflush(fp); + else + fclose(fp); + + SetLastError(lastErr); +} + +/////////////////////////////////////////////////////////////////////////////// +// Converts a BinStr binary string to hex or printable characters +// +// Parameters: +// data - Binary string to convert +// hexMode - (optional) If hexMode is on then the return string will be hex +// characters. Otherwise it returns a string of printable +// characters (unprintable characters are converted to '.'). +// +// Returns: +// string of hex data or printable characters +/////////////////////////////////////////////////////////////////////////////// +string StringifyBin(const BinStr& data, bool hexMode) +{ + return StringifyBin((LPBYTE)&data[0], data.size(), hexMode); +} + +/////////////////////////////////////////////////////////////////////////////// +// Converts a BYTE binary string to hex or printable characters +// +// Parameters: +// data - Binary string to convert +// len - Length of string +// hexMode - (optional) If hexMode is on then the return string will be hex +// characters. Otherwise it returns a string of printable +// characters (unprintable characters are converted to '.'). +// +// Returns: +// string of hex data or printable characters +/////////////////////////////////////////////////////////////////////////////// +string StringifyBin(const LPBYTE data, size_t len, bool hexMode) +{ + ostringstream out; + + if (hexMode) + { + // ostringstream can do hex, but the .width flag doesn't + // work in Microsoft's implementation (!) + char hex[32]; + for (size_t i = 0; i < len; i++) + { + sprintf(hex, "%.2X", data[i]); + out << hex; + } + } + else + { + for (size_t i = 0; i < len; i++) + { + if (isgraph(data[i]) || data[i] == ' ') + out << data[i]; + else + out << '.'; + } + } + + return out.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Convert a CryptProvParam to text +// +// Parameters: +// param - Parameter value +// +// Returns: +// string +/////////////////////////////////////////////////////////////////////////////// +string StringifyProvParam(DWORD param) +{ + switch(param) + { + case PP_CONTAINER: + return "PP_CONTAINER"; + break; + case PP_ENUMALGS: + return "PP_ENUMALGS"; + break; + case PP_ENUMALGS_EX: + return "PP_ENUMALGS_EX"; + break; + case PP_ENUMCONTAINERS: + return "PP_ENUMCONTAINERS"; + break; + case PP_IMPTYPE: + return "PP_IMPTYPE"; + break; + case PP_NAME: + return "PP_NAME"; + break; + case PP_VERSION: + return "PP_VERSION"; + break; + case PP_SIG_KEYSIZE_INC: + return "PP_SIG_KEYSIZE_INC"; + break; + case PP_KEYX_KEYSIZE_INC: + return "PP_KEYX_KEYSIZE_INC"; + break; + case PP_KEYSET_SEC_DESCR: + return "PP_KEYSET_SEC_DESCR"; + break; + case PP_UNIQUE_CONTAINER: + return "PP_UNIQUE_CONTAINER"; + break; + case PP_PROVTYPE: + return "PP_PROVTYPE"; + break; + default: + return "PP_UNKNOWN"; + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Converts AcquireContext flags to text +// +// Parameters: +// param - Parameter value +// +// Returns: +// string +/////////////////////////////////////////////////////////////////////////////// +string StringifyAquireFlags(DWORD param) +{ + string rv; + + if (param & CRYPT_VERIFYCONTEXT) + rv += "CRYPT_VERIFYCONTEXT | "; + + if (param & CRYPT_NEWKEYSET) + rv += "CRYPT_NEWKEYSET | "; + + if (param & CRYPT_MACHINE_KEYSET) + rv += "CRYPT_MACHINE_KEYSET | "; + + if (param & CRYPT_DELETEKEYSET) + rv += "CRYPT_DELETEKEYSET | "; + + if (param & CRYPT_SILENT) + rv += "CRYPT_SILENT | "; + + return rv; +} + +/////////////////////////////////////////////////////////////////////////////// +// Converts CALG_XXXX algorithm to text +// +// Parameters: +// id - Algorithm ID +// +// Returns: +// string +/////////////////////////////////////////////////////////////////////////////// +string StringifyCALG(ALG_ID id) +{ + switch(id) + { + case CALG_MD2: + return "CALG_MD2"; + case CALG_MD4: + return "CALG_MD4"; + case CALG_MD5: + return "CALG_MD5"; + case CALG_SHA1: + return "CALG_SHA1"; + case CALG_MAC: + return "CALG_MAC"; + case CALG_RSA_SIGN: + return "CALG_RSA_SIGN"; + case CALG_DSS_SIGN: + return "CALG_DSS_SIGN"; + case CALG_NO_SIGN: + return "CALG_NO_SIGN"; + case CALG_RSA_KEYX: + return "CALG_RSA_KEYX"; + case CALG_DES: + return "CALG_DES"; + case CALG_3DES_112: + return "CALG_3DES_112"; + case CALG_3DES: + return "CALG_3DES"; + case CALG_DESX: + return "CALG_DESX"; + case CALG_RC2: + return "CALG_RC2"; + case CALG_RC4: + return "CALG_RC4"; + case CALG_SEAL: + return "CALG_SEAL"; + case CALG_DH_SF: + return "CALG_DH_SF"; + case CALG_DH_EPHEM: + return "CALG_DH_EPHEM"; + case CALG_AGREEDKEY_ANY: + return "CALG_AGREEDKEY_ANY"; + case CALG_KEA_KEYX: + return "CALG_KEA_KEYX"; + case CALG_HUGHES_MD5: + return "CALG_HUGHES_MD5"; + case CALG_SKIPJACK: + return "CALG_SKIPJACK"; + case CALG_TEK: + return "CALG_TEK"; + case CALG_CYLINK_MEK: + return "CALG_CYLINK_MEK"; + case CALG_SSL3_SHAMD5: + return "CALG_SSL3_SHAMD5"; + case CALG_SSL3_MASTER: + return "CALG_SSL3_MASTER"; + case CALG_SCHANNEL_MASTER_HASH: + return "CALG_SCHANNEL_MASTER_HASH"; + case CALG_SCHANNEL_MAC_KEY: + return "CALG_SCHANNEL_MAC_KEY"; + case CALG_SCHANNEL_ENC_KEY: + return "CALG_SCHANNEL_ENC_KEY"; + case CALG_PCT1_MASTER: + return "CALG_PCT1_MASTER"; + case CALG_SSL2_MASTER: + return "CALG_SSL2_MASTER"; + case CALG_TLS1_MASTER: + return "CALG_TLS1_MASTER"; + case CALG_RC5: + return "CALG_RC5"; + case CALG_HMAC: + return "CALG_HMAC"; + case CALG_TLS1PRF: + return "CALG_TLS1PRF"; + case CALG_HASH_REPLACE_OWF: + return "CALG_HASH_REPLACE_OWF"; + case CALG_AES_128: + return "CALG_AES_128"; + case CALG_AES_192: + return "CALG_AES_192"; + case CALG_AES_256: + return "CALG_AES_256"; + case CALG_AES: + return "CALG_AES"; + case AT_KEYEXCHANGE: + return "AT_KEYEXCHANGE"; + case AT_SIGNATURE: + return "AT_SIGNATURE"; + default: + { + char buf[256]; + sprintf(buf, "UNKNOWN (0x%X)", id); + return buf; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Used with GetProvParam and PP_ENUMALGS +// +// Parameters: +// context - CSP context +// dwFlags - Flags from GetProvParam call +// pbData - Same as GetProvParam call +// pcbDataLen - Same as GetProvParam call +// +// Returns: +// none +/////////////////////////////////////////////////////////////////////////////// +void GetProvParam_PP_ENUMALGS(Session* context, DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen) +{ + static int algCursor = 0; + PROV_ENUMALGS output; + + static struct + { + char* name; + ALG_ID id; + DWORD bitLen; + } + algs[] = { { "MD5", CALG_MD5, 128 }, + { "SHA1", CALG_SHA1, 160 }, + { "DES", CALG_DES, 56 }, + { "3DES", CALG_3DES, 168 }, + { "RC2", CALG_RC2, 128 }, + { NULL } }; + + LOG("GetProvParam_PP_ENUMALGS called\n"); + if (dwFlags & CRYPT_FIRST) + algCursor = 0; + + if (algs[algCursor].name == NULL) + Throw(ERROR_NO_MORE_ITEMS); + else + { + output.aiAlgid = algs[algCursor].id; + output.dwBitLen = algs[algCursor].bitLen; + output.dwNameLen = (DWORD)strlen(algs[algCursor].name) + 1; + strcpy(output.szName, algs[algCursor].name); + + PutDataIntoBuffer(pbData, pcbDataLen, reinterpret_cast<LPBYTE>(&output), sizeof(output)); + } + + LOG("aiAlgid:0x%X dwBitLen:%u dwNameLen:%u szName:\"%s\"\n", + output.aiAlgid, output.dwBitLen, output.dwNameLen, output.szName); + + algCursor++; +} + +void GetProvParam_PP_ENUMALGS_EX(Session* context, DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen) +{ + static int algCursor = 0; + PROV_ENUMALGS_EX output; + + static struct + { + char* name; + ALG_ID id; + DWORD defLen; + DWORD minLen; + DWORD maxLen; + } // def min max + algs[] = { { "MD5", CALG_MD5, 128, 128, 128 }, + { "SHA1", CALG_SHA1, 160, 160, 160 }, + { "RSA_SIGN", CALG_RSA_SIGN, 1024, 512, 1024 }, + { "RSA_KEYX", CALG_RSA_KEYX, 1024, 512, 1024 }, + { "DES", CALG_DES, 56, 56, 56 }, + { "3DES", CALG_3DES, 168, 168, 168 }, + { "RC2", CALG_RC2, 128, 40, 128 }, + { NULL } }; + + LOG("GetProvParam_PP_ENUMALGS_EX called\n"); + + if (dwFlags & CRYPT_FIRST) + algCursor = 0; + + if (algs[algCursor].name == NULL) + Throw(ERROR_NO_MORE_ITEMS); + else + { + output.aiAlgid = algs[algCursor].id; + output.dwDefaultLen = algs[algCursor].defLen; + output.dwMinLen = algs[algCursor].minLen; + output.dwMaxLen = algs[algCursor].maxLen; + output.dwProtocols = 1; + output.dwNameLen = (DWORD)strlen(algs[algCursor].name) + 1; + strcpy(output.szName, algs[algCursor].name); + output.dwLongNameLen = (DWORD)strlen(algs[algCursor].name) + 1; + strcpy(output.szLongName, algs[algCursor].name); + + PutDataIntoBuffer(pbData, pcbDataLen, reinterpret_cast<LPBYTE>(&output), sizeof(output)); + } + + LOG("aiAlgid:0x%X dwDefaultLen:%u dwMinLen:%u dwMaxLen:%u dwProtocols:%u dwNameLen:%u szName:\"%s\"\n", + output.aiAlgid, + output.dwDefaultLen, + output.dwMinLen, + output.dwMaxLen, + output.dwProtocols, + output.dwNameLen, + output.szName); + + algCursor++; +} + +/////////////////////////////////////////////////////////////////////////////// +// Used with GetProvParam and PP_ENUMCONTAINERS +// +// Parameters: +// context - CSP context +// dwFlags - Flags from GetProvParam call +// pbData - Same as GetProvParam call +// pcbDataLen - Same as GetProvParam call +// +// Returns: +// none +/////////////////////////////////////////////////////////////////////////////// +void GetProvParam_PP_ENUMCONTAINERS(Session* context, DWORD dwFlags, + OUT LPBYTE pbData, + IN OUT LPDWORD pcbDataLen) +{ + LOG("GetProvParam_PP_ENUMCONTAINERS called\n"); + + if (dwFlags & CRYPT_FIRST) + { + LOG("ENUMCONTAINERS resetting container enumeration\n"); + context->containers_.clear(); + context->containerItr_ = context->containers_.begin(); + + // Init search (all objects) + if (g_state.p11->C_FindObjectsInit(context->p11_, 0, 0) != CKR_OK) + ThrowMsg(ERROR_NO_MORE_ITEMS, "C_FindObjectsInit failed"); + + CK_ULONG count = 1; + CK_OBJECT_HANDLE hObj; + + while(true) + { + if (CKR_OK != g_state.p11->C_FindObjects(context->p11_, &hObj, 1, &count) || count == 0) + { + // No more objects (or any other error) + g_state.p11->C_FindObjectsFinal(context->p11_); + break; + } + else + { + CK_ATTRIBUTE pTemplate = { CKA_ID, 0, 0 }; + + // Get the length + if (g_state.p11->C_GetAttributeValue(context->p11_, hObj, &pTemplate, 1) != CKR_OK) + continue; + + // Get the data + BinStr id; + id.resize(pTemplate.ulValueLen); + pTemplate.pValue = &id[0]; + if (g_state.p11->C_GetAttributeValue(context->p11_, hObj, &pTemplate, 1) != CKR_OK) + continue; + + id.BinToHex(); + id.push_back(0); + + context->containers_.insert(id); + } + } + + // Set it again in case of poor STL implementaion + context->containerItr_ = context->containers_.begin(); + } + + if (context->containerItr_ == context->containers_.end()) + Throw(ERROR_NO_MORE_ITEMS); + + PutDataIntoBuffer(pbData, pcbDataLen, &(*context->containerItr_)[0], + context->containerItr_->size()); + + if (pbData) + context->containerItr_++; +} + +/////////////////////////////////////////////////////////////////////////////// +// Checks input and output settings and returns data and/or length +// +// Parameters: +// dest - Destination buffer +// destLen - Destination buffer size +// source - Source buffer +// sourceLen - Source buffer size +// +// Returns: +// none - Throws exception on bad data +/////////////////////////////////////////////////////////////////////////////// +void PutDataIntoBuffer(LPBYTE dest, LPDWORD destLen, const LPBYTE source, + DWORD sourceLen) +{ + if (destLen == NULL) + Throw(ERROR_MORE_DATA); + else if (dest == NULL) + *destLen = sourceLen; + else if (*destLen < sourceLen) + Throw(ERROR_MORE_DATA); + else + { + memcpy(dest, source, sourceLen); + *destLen = sourceLen; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Reverses a BinStr +// +// Parameters: +// buf - String to reverse +// +// Returns: +// none +/////////////////////////////////////////////////////////////////////////////// +void Reverse(BinStr* buf) +{ + Reverse(&(*buf)[0], buf->size()); +} + +/////////////////////////////////////////////////////////////////////////////// +// Reverses a BYTE string +// +// Parameters: +// buf - String to reverse +// len - Length of string +// +// Returns: +// none +/////////////////////////////////////////////////////////////////////////////// +void Reverse(LPBYTE buf, size_t len) +{ + size_t pos, maxPos = len / 2 - 1; + + for (pos = 0; pos <= maxPos; pos++) + { + char temp; + + temp = buf[pos]; + buf[pos] = buf[len - 1 - pos]; + buf[len - 1 - pos] = temp; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// If there are any logon certs this returns the last one. +// If there are no logon certs then this just returns the last cert on the +// card. +// +// Parameters: +// context - CSP context +// phCert - CK_OBJECT_HANDLE of found cert +// container - Container name that cert exists in +// +// Returns: +// FALSE on failure +/////////////////////////////////////////////////////////////////////////////// +bool FindDefaultCert(Session* context, CK_OBJECT_HANDLE* phCert, BinStr* container) +{ + bool rv = true; + *phCert = 0; + + CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; + CK_ATTRIBUTE attrib = { CKA_CLASS, &objClass, sizeof(objClass) }; + + // start object search for all certificates + if (g_state.p11->C_FindObjectsInit(context->p11_, &attrib, 1) != CKR_OK) + { + LOG("C_FindObjectsInit failed\n"); + return false; + } + + try + { + bool haveLogonCert = false; + + // Set up the structure so we can get the cert's CKA_ID and CKA_VALUE + CK_ATTRIBUTE attrib[] = { + { CKA_ID, 0, 0 }, + { CKA_VALUE, 0, 0 } + }; + + // Loop through all certs + CK_ULONG ulNumFound = 1; + while (ulNumFound > 0) + { + CK_OBJECT_HANDLE hCert; + if (g_state.p11->C_FindObjects(context->p11_, &hCert, 1, &ulNumFound) != CKR_OK) + ThrowMsg(0, "C_FindObjects failed\n"); + + if (ulNumFound == 0) + break; + + // First we want the CKA_ID and CKA_VALUE lengths + attrib[0].pValue = 0; + attrib[1].pValue = 0; + if (g_state.p11->C_GetAttributeValue(context->p11_, hCert, attrib, sizeof(attrib)/sizeof(CK_ATTRIBUTE)) != CKR_OK) + continue; + + BinStr ckaid(attrib[0].ulValueLen); + attrib[0].pValue = &ckaid[0]; + BinStr cert(attrib[1].ulValueLen); + attrib[1].pValue = &cert[0]; + + // Get the CKA_ID and CKA_VALUE + if (g_state.p11->C_GetAttributeValue(context->p11_, hCert, attrib, sizeof(attrib)/sizeof(CK_ATTRIBUTE)) != CKR_OK) + continue; + + vector<string> ext; + GetExtKeyUsageFromCert(&ext, cert); + + DWORD i; + for (i = 0; i < ext.size(); i++) + { + // Logon or enrollment agent + if (ext[i] == "1.3.6.1.4.1.311.20.2.2" || ext[i] == "1.3.6.1.4.1.311.20.2.1") + { + haveLogonCert = true; + container->swap(ckaid); + *phCert = hCert; + break; + } + } + + if (i >= ext.size() && !haveLogonCert) + { + container->swap(ckaid); + *phCert = hCert; + } + } + } + catch (Error&) + { + *phCert = 0; + } + + g_state.p11->C_FindObjectsFinal(context->p11_); + + if (*phCert) + return true; + else + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Finds last container name on card +// +// Parameters: +// context - CSP context +// phCert - CK_OBJECT_HANDLE of last obj +// container - Container name of last container +// +// Returns: +// FALSE on failure +/////////////////////////////////////////////////////////////////////////////// +bool FindLastContainer(Session* context, CK_OBJECT_HANDLE* phObj, BinStr* container) +{ + bool rv = true; + *phObj = 0; + + // start object search for all objects + if (g_state.p11->C_FindObjectsInit(context->p11_, 0, 0) != CKR_OK) + { + LOG("C_FindObjectsInit failed\n"); + return false; + } + + try + { + CK_ATTRIBUTE attrib = { CKA_ID, 0, 0 }; + + CK_ULONG ulNumFound = 1; + while (ulNumFound > 0) + { + CK_OBJECT_HANDLE hObj; + if (g_state.p11->C_FindObjects(context->p11_, &hObj, 1, &ulNumFound) != CKR_OK) + ThrowMsg(0, "C_FindObjects failed\n"); + + if (ulNumFound == 0) + break; + + attrib.pValue = 0; + if (g_state.p11->C_GetAttributeValue(context->p11_, hObj, &attrib, 1) != CKR_OK) + continue; + + BinStr ckaid(attrib.ulValueLen); + attrib.pValue = &ckaid[0]; + + if (g_state.p11->C_GetAttributeValue(context->p11_, hObj, &attrib, 1) != CKR_OK) + continue; + + container->swap(ckaid); + *phObj = hObj; + } + } + catch (Error&) + { + *phObj = 0; + } + + g_state.p11->C_FindObjectsFinal(context->p11_); + + if (*phObj) + return true; + else + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Finds a single object (first matching CKA_CLASS) in the current container +// +// Parameters: +// context - CSP context +// phCert - CK_OBJECT_HANDLE of found object +// objClass - CKA_CLASS of object to find +// +// Returns: +// FALSE on failure +/////////////////////////////////////////////////////////////////////////////// +bool FindObject(Session* context, CK_OBJECT_HANDLE* phObj, CK_OBJECT_CLASS objClass) +{ + bool rv; + + CK_ATTRIBUTE search[] = { + { CKA_ID, &context->CKAID_[0], context->CKAID_.size() }, + { CKA_CLASS, &objClass, sizeof(objClass) } + }; + + LOG("FindObject() CLA_CLASS:0x%X CKA_ID:%s \"%s\"\n", objClass, + StringifyBin(context->CKAID_).c_str(), StringifyBin(context->CKAID_, false).c_str()); + + // start object search + if (g_state.p11->C_FindObjectsInit(context->p11_, search, sizeof(search)/sizeof(CK_ATTRIBUTE)) != CKR_OK) + { + LOG("C_FindObjectsInit failed\n"); + rv = false; + } + else + { + // do the search + CK_ULONG ulNumFound = 0; + CK_OBJECT_HANDLE hObj; + if (g_state.p11->C_FindObjects(context->p11_, &hObj, 1, &ulNumFound) != CKR_OK) + { + LOG("C_FindObjects failed\n"); + rv = false; + } + else if (ulNumFound < 1) + rv = false; + else + { + if (phObj) + *phObj = hObj; + + rv = true; + } + + g_state.p11->C_FindObjectsFinal(context->p11_); + } + + LOG("FindObject returned: %s\n", rv ? "TRUE" : "FALSE"); + return rv; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns length of a ASN.1 SEQUENCE-OF. Note that this function is extremely +// dangerous. If non-ASN.1 encoded data is passed in then bad things could +// happen. +// +// Parameters: +// buf - BYTE buffer +// withHeader - (default: true) Returns length with ASN.1 header length +// included +// +// Returns: +// length +/////////////////////////////////////////////////////////////////////////////// +CK_ULONG ASN1Len(const CK_BYTE* buf, bool withHeader) +{ + // Make a very simplistic check for valid data since this + // function is inherently dangerous + if (buf[0] != 0x30) + return 0; + + CK_ULONG used_length = 1; // Skip the tag + CK_ULONG data_length = buf[used_length++];; + + if (data_length & 0x80) + { + CK_ULONG len_count = data_length & 0x7f; + data_length = 0; + while (len_count-- > 0) + data_length = (data_length << 8) | buf[used_length++]; + } + + if (withHeader) + return data_length + used_length; + else + return data_length; +} + +/////////////////////////////////////////////////////////////////////////////// +// Returns the modulus and exponent in big-endian format +// +// Parameters: +// context - CSP context +// modulus - Output of modulus +// exponent - Output of exponent +// cert - Certificate to extract from (raw binary) +// +// Returns: +// FALSE on failure +/////////////////////////////////////////////////////////////////////////////// +bool GetModulusFromCert(Session* context, BinStr* modulus, BinStr* exponent, const BinStr& cert) +{ + bool rv = true; + + CRYPT_SEQUENCE_OF_ANY* modseq = 0; + CRYPT_INTEGER_BLOB* mod = 0; + PCCERT_CONTEXT certContext = 0; + + try + { + certContext = + CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &cert[0], cert.size()); + + if (certContext == 0) + ThrowMsg(0, "CertCreateCertificateContext failed"); + + HCRYPTKEY hKey; + if (!CryptImportPublicKeyInfo(context->cryptProv_, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &certContext->pCertInfo->SubjectPublicKeyInfo, &hKey)) + Throw(0); + + DWORD dwDataLen; + if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, 0, &dwDataLen)) + Throw(0); + BinStr blob(dwDataLen); + if (!CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, &blob[0], &dwDataLen)) + Throw(0); + + BLOBHEADER* header = (BLOBHEADER*)&blob[0]; + RSAPUBKEY* rsakey = (RSAPUBKEY*)&blob[sizeof(BLOBHEADER)]; + + modulus->resize(rsakey->bitlen/8); + exponent->resize(sizeof(rsakey->pubexp)); + + memcpy(&(*modulus)[0], &blob[sizeof(BLOBHEADER)+sizeof(RSAPUBKEY)], rsakey->bitlen/8); + memcpy(&(*exponent)[0], &rsakey->pubexp, sizeof(rsakey->pubexp)); + + while (exponent->back() == 0x00) + exponent->pop_back(); + + Reverse(modulus); + Reverse(exponent); + } + catch (Error&) + { + rv = false; + } + + if (certContext) + CertFreeCertificateContext(certContext); + + if (modseq) + LocalFree(modseq); + + if (mod) + LocalFree(mod); + + return rv; +} + +/////////////////////////////////////////////////////////////////////////////// +// Fills an array with the extended key usage OID's +// +// Parameters: +// ext - Array of returned strings +// cert - Certificate data (raw binary) +// +// Returns: +// FALSE on failure +/////////////////////////////////////////////////////////////////////////////// +bool GetExtKeyUsageFromCert(vector<string>* ext, const BinStr& cert) +{ + bool rv = true; + + CRYPT_SEQUENCE_OF_ANY* extusage = 0; + PCCERT_CONTEXT certContext = 0; + + try + { + certContext = + CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &cert[0], cert.size()); + + if (certContext == 0) + ThrowMsg(0, "CertCreateCertificateContext failed"); + + CERT_ENHKEY_USAGE* usage; + DWORD usageSize; + if (!CertGetEnhancedKeyUsage(certContext, 0, 0, &usageSize)) + Throw(0); + + usage = (CERT_ENHKEY_USAGE*)new char[usageSize]; + if (!CertGetEnhancedKeyUsage(certContext, 0, usage, &usageSize)) + Throw(0); + + ext->resize(usage->cUsageIdentifier); + + for (DWORD i = 0; i < usage->cUsageIdentifier; i++) + (*ext)[i] = usage->rgpszUsageIdentifier[i]; + } + catch (Error&) + { + rv = false; + } + + if (certContext) + CertFreeCertificateContext(certContext); + + if (extusage) + LocalFree(extusage); + + return rv; +} + +string GetCurrentExecutable() +{ + TCHAR szModulePath[MAX_PATH]; + + if (GetModuleFileName(0, szModulePath, sizeof(szModulePath) / sizeof(TCHAR)) == 0) + return ""; + else + return string(szModulePath); +} + +string GetCurrentDLL() +{ + TCHAR szModulePath[MAX_PATH]; + + if (GetModuleFileName(g_hModule, szModulePath, sizeof(szModulePath) / sizeof(TCHAR)) == 0) + return ""; + else + return string(szModulePath); +} + +} // namespace MCSP + +// Microsoft helpers for handling session keys +namespace CryptoHelper { + +BOOL CreatePrivateExponentOneKey(HCRYPTPROV hProv, + DWORD dwKeySpec, + HCRYPTKEY *hPrivateKey) +{ + BOOL fReturn = FALSE; + BOOL fResult; + DWORD n; + LPBYTE keyblob = NULL; + DWORD dwkeyblob; + DWORD dwBitLen; + BYTE *ptr; + + __try + { + *hPrivateKey = 0; + + if ((dwKeySpec != AT_KEYEXCHANGE) && (dwKeySpec != AT_SIGNATURE)) __leave; + + // Generate the private key + fResult = CryptGenKey(hProv, dwKeySpec, CRYPT_EXPORTABLE, hPrivateKey); + if (!fResult) __leave; + + // Export the private key, we'll convert it to a private + // exponent of one key + fResult = CryptExportKey(*hPrivateKey, 0, PRIVATEKEYBLOB, 0, NULL, &dwkeyblob); + if (!fResult) __leave; + + keyblob = (LPBYTE)LocalAlloc(LPTR, dwkeyblob); + if (!keyblob) __leave; + + fResult = CryptExportKey(*hPrivateKey, 0, PRIVATEKEYBLOB, 0, keyblob, &dwkeyblob); + if (!fResult) __leave; + + + CryptDestroyKey(*hPrivateKey); + *hPrivateKey = 0; + + // Get the bit length of the key + memcpy(&dwBitLen, &keyblob[12], 4); + + // Modify the Exponent in Key BLOB format + // Key BLOB format is documented in SDK + + // Convert pubexp in rsapubkey to 1 + ptr = &keyblob[16]; + for (n = 0; n < 4; n++) + { + if (n == 0) ptr[n] = 1; + else ptr[n] = 0; + } + + // Skip pubexp + ptr += 4; + // Skip modulus, prime1, prime2 + ptr += (dwBitLen/8); + ptr += (dwBitLen/16); + ptr += (dwBitLen/16); + + // Convert exponent1 to 1 + for (n = 0; n < (dwBitLen/16); n++) + { + if (n == 0) ptr[n] = 1; + else ptr[n] = 0; + } + + // Skip exponent1 + ptr += (dwBitLen/16); + + // Convert exponent2 to 1 + for (n = 0; n < (dwBitLen/16); n++) + { + if (n == 0) ptr[n] = 1; + else ptr[n] = 0; + } + + // Skip exponent2, coefficient + ptr += (dwBitLen/16); + ptr += (dwBitLen/16); + + // Convert privateExponent to 1 + for (n = 0; n < (dwBitLen/8); n++) + { + if (n == 0) ptr[n] = 1; + else ptr[n] = 0; + } + + // Import the exponent-of-one private key. + if (!CryptImportKey(hProv, keyblob, dwkeyblob, 0, 0, hPrivateKey)) + { + __leave; + } + + fReturn = TRUE; + } + __finally + { + if (keyblob) LocalFree(keyblob); + + if (!fReturn) + { + if (*hPrivateKey) CryptDestroyKey(*hPrivateKey); + } + } + + return fReturn; +} + +BOOL ExportPlainSessionBlob(HCRYPTKEY hPublicKey, + HCRYPTKEY hSessionKey, + LPBYTE *pbKeyMaterial , + DWORD *dwKeyMaterial ) +{ + BOOL fReturn = FALSE; + BOOL fResult; + DWORD dwSize, n; + LPBYTE pbSessionBlob = NULL; + DWORD dwSessionBlob; + LPBYTE pbPtr; + + __try + { + *pbKeyMaterial = NULL; + *dwKeyMaterial = 0; + + fResult = CryptExportKey(hSessionKey, hPublicKey, SIMPLEBLOB, + 0, NULL, &dwSessionBlob ); + if (!fResult) __leave; + + pbSessionBlob = (LPBYTE)LocalAlloc(LPTR, dwSessionBlob ); + if (!pbSessionBlob) __leave; + + fResult = CryptExportKey(hSessionKey, hPublicKey, SIMPLEBLOB, + 0, pbSessionBlob , &dwSessionBlob ); + if (!fResult) __leave; + + // Get session key size in bits + dwSize = sizeof(DWORD); + fResult = CryptGetKeyParam(hSessionKey, KP_KEYLEN, (LPBYTE)dwKeyMaterial, &dwSize, 0); + if (!fResult) __leave; + + // Get the number of bytes and allocate buffer + *dwKeyMaterial /= 8; + *pbKeyMaterial = (LPBYTE)LocalAlloc(LPTR, *dwKeyMaterial); + if (!*pbKeyMaterial) __leave; + + // Skip the header + pbPtr = pbSessionBlob; + pbPtr += sizeof(BLOBHEADER); + pbPtr += sizeof(ALG_ID); + + // We are at the beginning of the key + // but we need to start at the end since + // it's reversed + pbPtr += (*dwKeyMaterial - 1); + + // Copy the raw key into our return buffer + for (n = 0; n < *dwKeyMaterial; n++) + { + (*pbKeyMaterial)[n] = *pbPtr; + pbPtr--; + } + + fReturn = TRUE; + } + __finally + { + if (pbSessionBlob) LocalFree(pbSessionBlob); + + if ((!fReturn) && (*pbKeyMaterial )) + { + LocalFree(*pbKeyMaterial ); + *pbKeyMaterial = NULL; + *dwKeyMaterial = 0; + } + } + + return fReturn; +} + + +BOOL ImportPlainSessionBlob(HCRYPTPROV hProv, + HCRYPTKEY hPrivateKey, + ALG_ID dwAlgId, + LPBYTE pbKeyMaterial , + DWORD dwKeyMaterial , + HCRYPTKEY *hSessionKey) +{ + BOOL fResult; + BOOL fReturn = FALSE; + BOOL fFound = FALSE; + LPBYTE pbSessionBlob = NULL; + DWORD dwSessionBlob, dwSize, n; + DWORD dwPublicKeySize; + DWORD dwProvSessionKeySize; + ALG_ID dwPrivKeyAlg; + LPBYTE pbPtr; + DWORD dwFlags = CRYPT_FIRST; + PROV_ENUMALGS_EX ProvEnum; + HCRYPTKEY hTempKey = 0; + + __try + { + // Double check to see if this provider supports this algorithm + // and key size + do + { + dwSize = sizeof(ProvEnum); + fResult = CryptGetProvParam(hProv, PP_ENUMALGS_EX, (LPBYTE)&ProvEnum, + &dwSize, dwFlags); + if (!fResult) break; + + dwFlags = 0; + + if (ProvEnum.aiAlgid == dwAlgId) fFound = TRUE; + + } while (!fFound); + + if (!fFound) __leave; + + // We have to get the key size(including padding) + // from an HCRYPTKEY handle. PP_ENUMALGS_EX contains + // the key size without the padding so we can't use it. + fResult = CryptGenKey(hProv, dwAlgId, 0, &hTempKey); + if (!fResult) __leave; + + dwSize = sizeof(DWORD); + fResult = CryptGetKeyParam(hTempKey, KP_KEYLEN, (LPBYTE)&dwProvSessionKeySize, + &dwSize, 0); + if (!fResult) __leave; + CryptDestroyKey(hTempKey); + hTempKey = 0; + + // Our key is too big, leave + if ((dwKeyMaterial * 8) > dwProvSessionKeySize) __leave; + + // Get private key's algorithm + dwSize = sizeof(ALG_ID); + fResult = CryptGetKeyParam(hPrivateKey, KP_ALGID, (LPBYTE)&dwPrivKeyAlg, &dwSize, 0); + if (!fResult) __leave; + + // Get private key's length in bits + dwSize = sizeof(DWORD); + fResult = CryptGetKeyParam(hPrivateKey, KP_KEYLEN, (LPBYTE)&dwPublicKeySize, &dwSize, 0); + if (!fResult) __leave; + + // calculate Simple blob's length + dwSessionBlob = (dwPublicKeySize/8) + sizeof(ALG_ID) + sizeof(BLOBHEADER); + + // allocate simple blob buffer + pbSessionBlob = (LPBYTE)LocalAlloc(LPTR, dwSessionBlob); + if (!pbSessionBlob) __leave; + + pbPtr = pbSessionBlob; + + // SIMPLEBLOB Format is documented in SDK + // Copy header to buffer + ((BLOBHEADER *)pbPtr)->bType = SIMPLEBLOB; + ((BLOBHEADER *)pbPtr)->bVersion = 2; + ((BLOBHEADER *)pbPtr)->reserved = 0; + ((BLOBHEADER *)pbPtr)->aiKeyAlg = dwAlgId; + pbPtr += sizeof(BLOBHEADER); + + // Copy private key algorithm to buffer + *((DWORD *)pbPtr) = dwPrivKeyAlg; + pbPtr += sizeof(ALG_ID); + + // Place the key material in reverse order + for (n = 0; n < dwKeyMaterial; n++) + { + pbPtr[n] = pbKeyMaterial[dwKeyMaterial-n-1]; + } + + // 3 is for the first reserved byte after the key material + the 2 reserved bytes at the end. + dwSize = dwSessionBlob - (sizeof(ALG_ID) + sizeof(BLOBHEADER) + dwKeyMaterial + 3); + pbPtr += (dwKeyMaterial+1); + + // Generate random data for the rest of the buffer + // (except that last two bytes) + fResult = CryptGenRandom(hProv, dwSize, pbPtr); + if (!fResult) __leave; + + for (n = 0; n < dwSize; n++) + { + if (pbPtr[n] == 0) pbPtr[n] = 1; + } + + pbSessionBlob[dwSessionBlob - 2] = 2; + + fResult = CryptImportKey(hProv, pbSessionBlob , dwSessionBlob, + hPrivateKey, CRYPT_EXPORTABLE, hSessionKey); + if (!fResult) __leave; + + fReturn = TRUE; + } + __finally + { + if (hTempKey) CryptDestroyKey(hTempKey); + if (pbSessionBlob) LocalFree(pbSessionBlob); + } + + return fReturn; +} + +} // namespace CryptoHelper diff --git a/src/windows/csp/gui.cpp b/src/windows/csp/gui.cpp new file mode 100644 index 0000000..2926728 --- /dev/null +++ b/src/windows/csp/gui.cpp @@ -0,0 +1,220 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : gui.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include "resource.h" +#include "csp.h" +#include <tchar.h> + +using namespace std; + +namespace MCSP { + +static BOOL DoInitDialog(HWND hDlg, LPARAM lParam) +{ + RECT rect; + int width, height; + int screen_x, screen_y; + int x, y; + + GetWindowRect(hDlg, &rect); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + screen_x = GetSystemMetrics(SM_CXSCREEN); + screen_y = GetSystemMetrics(SM_CYSCREEN); + + x = screen_x/2 - width/2; + y = screen_y/2 - height/2; + + SetWindowPos(hDlg, HWND_TOPMOST, x, y, width, height, SWP_NOSIZE); + SetFocus(GetDlgItem(hDlg, IDC_PIN_EDIT)); + + if (lParam == 0) + { + SetLastError(ERROR_INVALID_BLOCK); + EndDialog(hDlg, 0); + return TRUE; + } + + // FIXME: Why does lParam need to be type-cast to LONG? + // The parameter is suppose to be LONG_PTR (LPARAM) + SetWindowLongPtr(hDlg, GWLP_USERDATA, static_cast<LONG>(lParam)); + EnableWindow(GetDlgItem(hDlg, IDOK), FALSE); + + return FALSE; +} + +static BOOL DoPINChanged(HWND hDlg) +{ + HWND pinCtrl; + BOOL enable = TRUE; + + pinCtrl = GetDlgItem(hDlg, IDC_PIN_EDIT); + int len = GetWindowTextLength(pinCtrl); + + if (len == 0) + enable = FALSE; + + EnableWindow(GetDlgItem(hDlg, IDOK), enable); + + return TRUE; +} + +static BOOL DoPIN(HWND hDlg, WPARAM wParam) +{ + switch(HIWORD(wParam)) + { + case EN_UPDATE: + return DoPINChanged(hDlg); + break; + default: + break; + } + + return TRUE; +} + +static void OnOK(HWND hDlg, LPARAM lParam) +{ + BinStr* s = reinterpret_cast<BinStr*>((LPARAM)GetWindowLongPtr(hDlg, GWLP_USERDATA)); + if (!s) + return; + + HWND pinCtrl = GetDlgItem(hDlg, IDC_PIN_EDIT); + int len = GetWindowTextLength(pinCtrl); + + s->resize(len + 1); + GetWindowText(pinCtrl, reinterpret_cast<LPSTR>(&(*s)[0]), static_cast<int>(s->size())); + + // Chop off null cause we don't need it + s->resize(s->size() - 1); + + EndDialog(hDlg, IDOK); +} + +static BOOL DoCommand(HWND hDlg, WPARAM wParam, LPARAM lParam) +{ + switch(LOWORD(wParam)) + { + case IDCANCEL: + EndDialog(hDlg, IDCANCEL); + break; + case IDOK: + OnOK(hDlg, lParam); + break; + case IDC_PIN_EDIT: + return DoPIN(hDlg, wParam); + break; + default: + break; + } + + return TRUE; +} + +static +INT_PTR CALLBACK PINDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_COMMAND: + return DoCommand(hDlg, wParam, lParam); + break; + case WM_INITDIALOG: + return DoInitDialog(hDlg, lParam); + break; + default: + break; + } + + return FALSE; +} + +// asks the user for a pin +bool DisplayPINDialog(BinStr* pin) +{ + INT_PTR result; + + result = DialogBoxParam(g_hModule, MAKEINTRESOURCE(IDD_PIN_DIALOG), NULL, + PINDialogProc, reinterpret_cast<LPARAM>(pin)); + + switch(result) + { + case 0: + return false; + break; + case IDCANCEL: + return false; + break; + case IDOK: + if (pin->empty()) + return false; + else + return true; + break; + default: + break; + } + + return false; +} + +// for debugging +void DisplayError(const Session* context, const string& str) +{ + if (!context->silent_) + MessageBox(NULL, str.c_str(), PROVIDER_NAME" Error", MB_OK | MB_ICONERROR | MB_TASKMODAL); + + LOG("ERROR: \"%s\"\n", str.c_str()); +} + +// for debugging +void DisplayWin32Error(const Session* context) +{ + LPVOID lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + // Display the string + if (!context->silent_) + MessageBox(NULL, (LPCSTR)lpMsgBuf, PROVIDER_NAME" Win32 Error", MB_OK | MB_ICONERROR | MB_TASKMODAL); + + LOG("WIN32 error: \"%s\"\n", lpMsgBuf); + + // Free the buffer. + LocalFree( lpMsgBuf ); +} + +} // namespace MCSP diff --git a/src/windows/csp/resource.h b/src/windows/csp/resource.h new file mode 100644 index 0000000..6e2d736 --- /dev/null +++ b/src/windows/csp/resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by csp.rc +// +#define IDD_PIN_DIALOG 101 +#define IDB_PIN_LOGO 104 +#define CRYPT_SIG_RESOURCE_NUMBER 0x29A +#define IDC_PIN_EDIT 1001 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1002 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/windows/csp/uuid.cpp b/src/windows/csp/uuid.cpp new file mode 100644 index 0000000..b969d06 --- /dev/null +++ b/src/windows/csp/uuid.cpp @@ -0,0 +1,53 @@ +/** BEGIN COPYRIGHT BLOCK +* This Program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License as published by the Free Software +* Foundation; version 2 of the License. +* +* This Program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +* Place, Suite 330, Boston, MA 02111-1307 USA. +* +* Copyright (C) 2003-2004 Identity Alliance + +* All rights reserved. +* END COPYRIGHT BLOCK **/ + +/***************************************************************** +/ +/ File : uuid.cpp +/ Date : December 3, 2002 +/ Purpose: Crypto API CSP->PKCS#11 Module +/ License: Copyright (C) 2003-2004 Identity Alliance +/ +******************************************************************/ + +#include <windows.h> +#include <rpcdce.h> + +#include "BinStr.h" + +namespace MCSP { + +bool GenUUID(BinStr* uuid) +{ + uuid->clear(); + + unsigned char* strId; + UUID id; + UuidCreate(&id); + if (UuidToString(&id, &strId) == RPC_S_OK) + { + uuid->resize(strlen((char*)strId)); + memcpy(&(*uuid)[0], strId, strlen((char*)strId)); + RpcStringFree(&strId); + return true; + } + else + return false; +} + +} // namespace MCSP |