PKIFCNGCAPIRaw.cpp

Go to the documentation of this file.
00001 
00010 #include "PKIFCNGCAPIRaw.h"
00011 #include "PKIFCAPICryptContext2.h"
00012 #include "PKIFCAPIHashContext.h"
00013 #include "CAPIUtils.h"
00014 
00015 #include "PKIFCryptoException.h"
00016 #include "PKIFKeyMaterial.h"
00017 #include "PKIFCryptoErrors.h"
00018 #include "PKIFCAPIErrors.h"
00019 #include "CAPIRawCryptContext.h"
00020 #include "Buffer.h"
00021 
00022 #include "AlgorithmIdentifier.h"
00023 #include "Certificate.h"
00024 #include "ToolkitUtils.h"
00025 #include "components.h"
00026 #include "PKIFException.h"
00027 
00028 #include "SubjectPublicKeyInfo.h"
00029 
00030 #include "ASN1Helper.h"
00031 #include "PKIX1Algorithms88.h"
00032 
00033 #include <iostream>
00034 #include <sstream>
00035 
00036 #include "PKIFCNGUtils.h"
00037 #include "PKIFBCryptPublicKey.h"
00038 // for DSA format conversion from crypto++
00039 #include "dsa.h"
00040 
00041 #include "boost/numeric/conversion/cast.hpp"
00042 
00043 using boost::numeric_cast;
00044 using boost::bad_numeric_cast;
00045 
00046 using namespace std;
00047 
00048 #ifndef STATUS_NOT_SUPPORTED
00049 #define STATUS_NOT_SUPPORTED            ((NTSTATUS)0xC00000BBL)
00050 #endif
00051 #ifndef STATUS_BUFFER_TOO_SMALL
00052 #define STATUS_BUFFER_TOO_SMALL         ((NTSTATUS)0xC0000023L)
00053 #endif
00054 
00055 //CAC_API char g_defCACCAPITrustStore[] = "Root";
00056 
00057 #define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)
00058 #define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)
00059 
00061 struct CPKIFCNGCAPIRawImpl
00062 {
00063 
00064     CPKIFCNGCAPIRaw* m_parent;
00072     CPKIFCNGCAPIRawImpl ()
00073     {
00074         m_parent = NULL;
00075     }
00083     CPKIFCNGCAPIRawImpl (CPKIFCNGCAPIRaw  *p) 
00084     {
00085         m_parent = p;
00086     }
00088     HCRYPTPROV m_hProv;
00090     HCRYPTKEY m_hPubPrivKey;
00091 
00092     template<bool _CryptDirection>
00093     void AsymCrypt(const CPKIFKeyMaterial& key, unsigned char* pData, int nDataLen, unsigned char* pResult, int* pnResultLen, bool pad = true);
00094 
00095     template <bool _CryptDirection>
00096     void CryptFunc(IPKIFRawCryptContext* cryptContext, unsigned char* pData, int nDataLen, unsigned char* pResult, int* pnResultLen, bool final);
00097 
00098 };
00100 
00109 CPKIFCNGCAPIRaw::CPKIFCNGCAPIRaw(void)
00110     :m_impl (new CPKIFCNGCAPIRawImpl)
00111 {
00112     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::CPKIFCNGCAPIRaw(void)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00113 
00114     m_impl->m_parent = this;
00115     m_impl->m_hProv = NULL;
00116     m_impl->m_hPubPrivKey = NULL;
00117 }
00125 CPKIFCNGCAPIRaw::~CPKIFCNGCAPIRaw(void)
00126 {
00127     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::~CPKIFCNGCAPIRaw(void)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00128 
00129     if(NULL != m_impl->m_hPubPrivKey)
00130     {
00131         BOOL succ = CryptDestroyKey(m_impl->m_hPubPrivKey);
00132         m_impl->m_hPubPrivKey = NULL;
00133     }
00134     if(NULL != m_impl->m_hProv)
00135     {
00136         BOOL succ = CryptReleaseContext(m_impl->m_hProv, 0);
00137         m_impl->m_hProv = NULL;
00138     }
00139     delete m_impl;
00140     m_impl = NULL;
00141 }
00149 void CPKIFCNGCAPIRaw::Initialize()
00150 {
00151     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::Initialize()", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00152 }
00153 
00163 LPCWSTR GetCNGSymAlgorithm(
00165     const CPKIFKeyMaterial& key)
00166 {
00167     switch(key.GetSymmetricKeyAlgorithm())
00168     {
00169     case PKIFCRYPTO::DES:
00170         return BCRYPT_DES_ALGORITHM;
00171     case PKIFCRYPTO::TDES:
00172         return BCRYPT_3DES_ALGORITHM;
00173     case PKIFCRYPTO::AES:
00174     case PKIFCRYPTO::AES128:
00175     case PKIFCRYPTO::AES192:
00176     case PKIFCRYPTO::AES256:
00177         return BCRYPT_AES_ALGORITHM;
00178     default:
00179         RAISE_CRYPTO_EXCEPTION("", TOOLKIT_CRYPTO, CRYPTO_ALG_NOT_SUPPORTED, NULL)
00180     }
00181 }
00182 
00198 bool CPKIFCNGCAPIRaw::SupportsAlgorithm(
00200     const CPKIFKeyMaterial& key)
00201 {
00202     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::SupportsAlgorithm(const CPKIFKeyMaterial& key)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00203 
00204     if(key.ContainsSymmetricKeyMaterial())
00205     {   
00206         try
00207         {
00208             GetCNGSymAlgorithm(key);
00209             return true;
00210         }
00211         catch(CPKIFCryptoException& e)
00212         {
00213             if(CRYPTO_ALG_NOT_SUPPORTED == e.GetErrorCode())
00214             {
00215                 
00216                 //The GetSymAlgorithm function throws upon failure but
00217                 //this function should return false.
00218                 return false;
00219             } else {
00220                 throw e; //should never happen
00221             }
00222         }
00223     }
00224     else if(key.ContainsCertificate())
00225     {
00226         try
00227         {
00228             CPKIFCertificatePtr pkifCert(new CPKIFCertificate());
00229             pkifCert->Decode(key.GetCertificate(),key.GetCertificateLength());
00230             CPKIFSubjectPublicKeyInfoPtr spki = pkifCert->GetSubjectPublicKeyInfo();
00231             // this really shouldn't happen. it'll throw elsewhere, though
00232             if(!spki) return false;
00233             switch(GetAlgClass(spki->alg()))
00234             {
00235             case RSA_CLASS:
00236             case DSA_CLASS:
00237             case ECDSA_CLASS:
00238                 return true;
00239                 break;
00240             default:
00241                 return false;
00242                 break;
00243             }
00244         }
00245         catch(CPKIFException&)
00246         {
00247             // if there's an exception here, it'll be a decoding error that
00248             // will throw elsewhere. just return false here.
00249             return false;
00250         }
00251     } else  {
00252         RAISE_CRYPTO_EXCEPTION("key parameter contents not recognized.", thisComponent, COMMON_INVALID_INPUT, this);
00253     }
00254 }
00265 void CPKIFCNGCAPIRaw::Sign(
00267     const CPKIFKeyMaterial& key,
00269     unsigned char* pHashData,
00271     int nHashDataLen,
00273     unsigned char* pSignature,
00275     int* nSignatureLen,
00277     PKIFCRYPTO::HASH_ALG hashAlg
00278     )
00279 {
00280     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::Sign(const CPKIFKeyMaterial& key,...)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00281     // only the stored key material colleague signs.
00282     RAISE_CRYPTO_EXCEPTION("Signing operations are not implemented for raw key material.", thisComponent, COMMON_NOT_IMPLEMENTED, this);
00283 }
00284 
00306 template <bool _CryptDirection>
00307 void CPKIFCNGCAPIRawImpl::AsymCrypt(
00309     const CPKIFKeyMaterial& key,
00311     unsigned char* pData, 
00313     int nDataLen, 
00315     unsigned char* pResult, 
00318     int* pnResultLen, 
00322     bool pad)
00323 {
00324     LOG_STRING_DEBUG("CPKIFCNGCAPIRawImpl::AsymCrypt(const CPKIFKeyMaterial& key, ...)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00325     
00326     CPKIFBCryptPublicKey capiKey;
00327     BCRYPT_KEY_HANDLE hKey = capiKey.Initialize(key);
00328     DWORD paddingFlags = 0;
00329     if(RSA_CLASS == GetAlgClass(capiKey.GetAlgId()))
00330     {
00331         paddingFlags = BCRYPT_PAD_PKCS1;
00332     }// else throw? none of the other algs really support integrated encryption anyway
00333 
00334     int maxOut = *pnResultLen;
00335     *pnResultLen = nDataLen;
00336     int err = 0;
00337     memcpy(pResult, pData, nDataLen);
00338     
00339     pkif_byte_array iv(0);
00340     int ivLen;
00341     key.GetIV(NULL,&ivLen);
00342     // Currently supported  encryption schemes don't support IVs for asymmetric keys,
00343     // so this should really be a no-op
00344     if(ivLen) {
00345         iv.reset(new unsigned char[ivLen]);
00346         key.GetIV(iv,&ivLen);
00347     }
00348 
00349     NTSTATUS ntrv = 0L;
00350     if(_CryptDirection)
00351     {
00352         DWORD cbCipherText = 0;
00353         if(!NT_SUCCESS(ntrv = BCryptEncrypt(hKey,pData,nDataLen,NULL,iv,ivLen,
00354                                         NULL,0,&cbCipherText,paddingFlags)))
00355         {
00356             std::ostringstream os;
00357             os << "BCryptEncrypt failed: (" << GetLastError() << ")" << "NT status code " << ntrv;
00358             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
00359         }
00360 
00361         pkif_byte_array pbCipherText(new unsigned char[cbCipherText]);
00362         DWORD cbCTOut = 0;
00363         if(!NT_SUCCESS(ntrv = BCryptEncrypt(hKey,pData,nDataLen,NULL,iv,ivLen,
00364                                         pbCipherText,cbCipherText,&cbCTOut,paddingFlags)))
00365         {
00366             std::ostringstream os;
00367             os << "BCryptEncrypt failed: (" << GetLastError() << ")" << "NT status code " << ntrv;
00368             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
00369         }
00370 
00371         memcpy(pResult, pbCipherText.get(), cbCTOut);
00372         *pnResultLen = cbCTOut;
00373     }
00374     else
00375     {
00376         DWORD cbPlainText = 0;
00377         if(!NT_SUCCESS(ntrv = BCryptDecrypt(hKey,pData,nDataLen,NULL,iv,ivLen,
00378                                         NULL,0,&cbPlainText,paddingFlags)))
00379         {
00380             std::ostringstream os;
00381             os << "BCryptDecrypt failed: (" << GetLastError() << ")" << "NT status code " << ntrv;
00382             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
00383         }
00384 
00385         pkif_byte_array pbPlainText(new unsigned char[cbPlainText]);
00386         DWORD cbPTOut = 0;
00387         if(!NT_SUCCESS(ntrv = BCryptEncrypt(hKey,pData,nDataLen,NULL,iv,ivLen,
00388                                         pbPlainText,cbPlainText,&cbPTOut,paddingFlags)))
00389         {
00390             std::ostringstream os;
00391             os << "BCryptDecrypt failed: (" << GetLastError() << ")" << "NT status code " << ntrv;
00392             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
00393         }
00394 
00395         memcpy(pResult, pbPlainText.get(), cbPTOut);
00396         *pnResultLen = cbPTOut;
00397     }
00398 
00399 
00400 }
00408 void CPKIFCNGCAPIRaw::Decrypt(
00410     const CPKIFKeyMaterial& key, 
00412     unsigned char* pData, 
00414     int nDataLen, 
00416     unsigned char* pResult,
00419     int* pnResultLen, 
00422     bool pad)
00423 {
00424     if(key.ContainsSymmetricKeyMaterial()) {
00425         // Encrypt() can throw
00426         std::auto_ptr<CPKIFCAPIRawCryptContext> ctx(
00427             static_cast<CPKIFCAPIRawCryptContext *>(this->CryptInit(key,pad))
00428             );
00429         Decrypt(ctx.get(),pData,nDataLen,pResult,pnResultLen,true);
00430     } else {
00431         m_impl->AsymCrypt<false>(key, pData, nDataLen, pResult, pnResultLen, pad);
00432     }
00433 }
00441 void CPKIFCNGCAPIRaw::Encrypt(
00443     const CPKIFKeyMaterial& key,
00445     unsigned char* pData,
00447     int nDataLen,
00449     unsigned char* pResult,
00452     int* pnResultLen,
00455     bool pad)
00456 {
00457     if(key.ContainsSymmetricKeyMaterial()) {
00458         // Encrypt() can throw
00459         std::auto_ptr<CPKIFCAPIRawCryptContext> ctx(
00460             static_cast<CPKIFCAPIRawCryptContext *>(this->CryptInit(key,pad))
00461             );
00462         Encrypt(ctx.get(),pData,nDataLen,pResult,pnResultLen,true);
00463     } else {
00464         m_impl->AsymCrypt<true>(key, pData, nDataLen, pResult, pnResultLen, pad);
00465     }
00466 }
00467 
00480 bool _CNGVerify(
00482     const CPKIFKeyMaterial& key,
00485     unsigned char* pHashData,
00487     int nHashDataLen,
00489     unsigned char* pSignature,
00491     int nSignatureLen,
00493     PKIFCRYPTO::HASH_ALG ha
00494     )
00495 {
00496     LOG_STRING_DEBUG("_CNGVerify(const CPKIFKeyMaterial& key, unsigned char* pHashData, int nHashDataLen, unsigned char* pSignature, int nSignatureLen)", TOOLKIT_CRYPTO_CAPIRAW, 0, NULL);
00497 
00498     DWORD hashLen = 0;
00499     SECURITY_STATUS cngStatus = ERROR_SUCCESS;
00500     NTSTATUS ntrv = 0L;
00501     BCRYPT_PKCS1_PADDING_INFO   PKCS1PaddingInfo= {0};
00502 
00503 
00504     CPKIFBCryptPublicKey bcKeyWrapper;
00505     BCRYPT_KEY_HANDLE bcKey = bcKeyWrapper.Initialize(key);
00506 
00507 
00508     switch(ha)
00509     {
00510     case PKIFCRYPTO::SHA1:
00511         PKCS1PaddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
00512         break;
00513     case PKIFCRYPTO::SHA256:
00514         PKCS1PaddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;
00515         break;
00516     case PKIFCRYPTO::SHA384:
00517         PKCS1PaddingInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM;
00518         break;
00519     case PKIFCRYPTO::SHA512:
00520         PKCS1PaddingInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM;
00521         break;
00522     default:
00523         std::ostringstream os;
00524         os << "Unsupported hash algorithm encountered: " << ha;
00525         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, CRYPTO_ALG_NOT_SUPPORTED, NULL);
00526     }
00527 
00528     BCRYPT_PKCS1_PADDING_INFO * pi = 0;
00529     DWORD pflags = 0;
00530     unsigned char * pSigToVerify = pSignature;
00531     DWORD nFinalSigLen = nSignatureLen;
00532 
00533     // if this is an RSA signature, need to set those properly.
00534     // if it's not an RSA signature, CNG gets angry if you do :-/
00535     DWORD algPropSize = 0;
00536     if(!NT_SUCCESS(ntrv = BCryptGetProperty(bcKey,BCRYPT_ALGORITHM_NAME,NULL,0,&algPropSize,0)))
00537     {
00538         std::ostringstream os;
00539         os << "Unable to obtain signature algorithm group for key material :(" 
00540             << GetLastError() << ")" << "(NTSTATUS code " << ntrv << ")" << endl;
00541         RAISE_CRYPTO_EXCEPTION(os.str().c_str(),TOOLKIT_CRYPTO, CRYPTO_ALG_NOT_SUPPORTED, NULL);
00542     }
00543     pkif_byte_array algName(new unsigned char[algPropSize]);
00544     memset(algName,0x00,algPropSize);
00545     if(!NT_SUCCESS(ntrv = BCryptGetProperty(bcKey,BCRYPT_ALGORITHM_NAME,algName,algPropSize,&algPropSize,0)))
00546     {
00547         std::ostringstream os;
00548         os << "Unable to obtain signature algorithm group for key material :(" 
00549             << GetLastError() << ")" << "(NTSTATUS code " << ntrv << ")" << endl;
00550         RAISE_CRYPTO_EXCEPTION(os.str().c_str(),TOOLKIT_CRYPTO, CRYPTO_ALG_NOT_SUPPORTED, NULL);
00551     }
00552     pkif_byte_array converted(0);
00553     if(0 == wcscmp(BCRYPT_RSA_ALGORITHM,(wchar_t *)algName.get()))
00554     {
00555         pi = &PKCS1PaddingInfo;
00556         pflags = BCRYPT_PAD_PKCS1;
00557     }
00558     else // ECDSA or DSA... should probably do a better check than "!RSA" here
00559     {
00560         DWORD dssSigLen,dssSigLenLen;
00561         if(FAILED(cngStatus = BCryptGetProperty(bcKey,
00562                                                 BCRYPT_SIGNATURE_LENGTH,
00563                                                 (unsigned char *)&dssSigLen,
00564                                                 sizeof(DWORD),
00565                                                 &dssSigLenLen,
00566                                                 0)))
00567         {
00568             RAISE_CRYPTO_EXCEPTION("Unable to obtain signature length for key", TOOLKIT_CRYPTO, CRYPTO_ALG_NOT_SUPPORTED, NULL);
00569         }
00570         size_t targetLen = dssSigLen;
00571         converted.reset(new unsigned char[targetLen]);
00572         try {
00573             targetLen = CryptoPP::DSAConvertSignatureFormat(converted,targetLen,CryptoPP::DSA_P1363,pSignature,nSignatureLen,CryptoPP::DSA_DER);
00574         }catch(CryptoPP::BERDecodeErr &){
00575             // if this throws, assume the format's OK and try the verification anyway
00576             converted.clear();
00577         }
00578         if(converted.get() != 0) {
00579             pSigToVerify = converted.get();
00580             nFinalSigLen = (DWORD)targetLen;
00581         }
00582 
00583     }
00584     
00585 
00586     if(!NT_SUCCESS(ntrv = BCryptVerifySignature(bcKey,pi,pHashData,nHashDataLen,
00587                         pSigToVerify,nFinalSigLen,pflags)))
00588     {
00589         return false;
00590     }
00591 
00592 
00593     return true;
00594 }
00603 bool CPKIFCNGCAPIRaw::VerifyCertificate(
00606     const CPKIFCertificate& issCert,
00609     const CPKIFCertificate& subCert)
00610 {
00611     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::VerifyCertificate(const CPKIFCertificate& issCert, const CPKIFCertificate& subCert)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00612 
00613     CPKIFBufferPtr issBuf = issCert.Encoded();
00614     CPKIFBufferPtr subBuf = subCert.Encoded();
00615 
00616     PCCERT_CONTEXT issCtx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
00617         issBuf->GetBuffer(), issBuf->GetLength());
00618 //  PCCERT_CONTEXT subCtx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
00619 //      subBuf->GetBuffer(), subBuf->GetLength());
00620 
00621     int err = 0;
00622     BOOL b = CryptVerifyCertificateSignature(NULL,X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
00623         subBuf->GetBuffer(), subBuf->GetLength(), &issCtx->pCertInfo->SubjectPublicKeyInfo);
00624     if(!b)
00625     {
00626         err = GetLastError();
00627     }
00628 
00629     CertFreeCertificateContext(issCtx);
00630 //  CertFreeCertificateContext(subCtx);
00631 
00632     return TRUE == b;
00633 }
00634 
00635 //support RSA w/ SHA1 (DSS and MD5)
00645 bool CPKIFCNGCAPIRaw::Verify(
00647     const CPKIFKeyMaterial& key,
00649     unsigned char* pHashData, 
00651     int nHashDataLen,
00653     unsigned char* pSignature,
00655     int nSignatureLen,
00657     PKIFCRYPTO::HASH_ALG hashAlg
00658     )
00659 {
00660     //no state 
00661     return _CNGVerify(key, pHashData, nHashDataLen, pSignature, nSignatureLen,hashAlg);
00662 }
00675 void CPKIFCNGCAPIRaw::GenRandom(
00677     unsigned char* buf, 
00679     int len)
00680 {
00681     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::GenRandom(unsigned char* buf, int len)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00682 
00683     BCRYPT_ALG_HANDLE_PKIF       hRandomAlg(NULL);
00684     NTSTATUS ntrv = 0L;
00685 
00686     if(!NT_SUCCESS(ntrv = BCryptOpenAlgorithmProvider(&hRandomAlg,BCRYPT_RNG_ALGORITHM,
00687                                                 NULL,0)))
00688     {
00689         std::ostringstream os;
00690         os << "BCryptOpenAlgorithmProvider failed: (" << GetLastError() <<") NT Status: " << ntrv;
00691         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), thisComponent, PKIFCAPI_ACQUIRE_CONTEXT_FAILED, NULL);
00692     }
00693 
00694     if (!NT_SUCCESS(ntrv = BCryptGenRandom(hRandomAlg,buf,len,0)))
00695     {
00696         std::ostringstream os;
00697         os << "BCryptGenRandom failed: (" << GetLastError() <<") NT Status: " << ntrv;
00698         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), thisComponent, PKIFCAPI_GEN_RANDOM_FAILED, NULL);
00699 
00700     }
00701 }
00716 IPKIFHashContext* CPKIFCNGCAPIRaw::HashInit(
00719     PKIFCRYPTO::HASH_ALG alg)
00720 {
00721     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::HashInit(HASH_ALG alg)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00722 
00723     //support SHA1 (MD5)
00724     wchar_t* hashAlg;
00725     switch(alg)
00726     {
00727     case PKIFCRYPTO::SHA1:
00728         hashAlg = BCRYPT_SHA1_ALGORITHM;
00729         break;
00730     case PKIFCRYPTO::SHA256:
00731         hashAlg = BCRYPT_SHA256_ALGORITHM;
00732         break;
00733     case PKIFCRYPTO::SHA384:
00734         hashAlg = BCRYPT_SHA384_ALGORITHM;
00735         break;
00736     case PKIFCRYPTO::SHA512:
00737         hashAlg = BCRYPT_SHA512_ALGORITHM;
00738         break;
00739     default:
00740         std::ostringstream os;
00741         os << "Unsupported hash algorithm encountered: " << alg;
00742         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), thisComponent, CRYPTO_ALG_NOT_SUPPORTED, NULL);
00743     }
00744 
00745     //create a hash context object and populate it with context and hash objects
00746     //throw bad_alloc; nothing to clean up in this function
00747     CPKIFCAPIHashContext* hashCtx = new CPKIFCAPIHashContext();
00748     NTSTATUS                status          = STATUS_UNSUCCESSFUL;
00749     DWORD                   cbData          = 0,
00750                             cbHash          = 0,
00751                             cbHashObject    = 0;
00752     PBYTE                   pbHash          = NULL;
00753 
00754     //open an algorithm handle
00755     if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
00756                                             &hashCtx->m_hHashAlg,
00757                                             GetCNGHashAlg((PKIFCRYPTO::HASH_ALG)alg),
00758                                             NULL,0)))
00759     {
00760 
00761         std::ostringstream os;
00762         os << "CryptCreateHash failed: " << GetLastError();;
00763         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
00764     }
00765 
00766     //calculate the size of the buffer to hold the hash object
00767     if(!NT_SUCCESS(status = BCryptGetProperty(
00768                                         hashCtx->m_hHashAlg, 
00769                                         BCRYPT_OBJECT_LENGTH, 
00770                                         (PBYTE)&cbHashObject, 
00771                                         sizeof(DWORD), 
00772                                         &cbData, 
00773                                         0)))
00774     {
00775         std::ostringstream os;
00776         os << "BCryptGetProperty failed: " << GetLastError();;
00777         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
00778     }
00779 
00780     //allocate the hash object on the heap
00781     hashCtx->m_pbHashObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHashObject);
00782     if(NULL == hashCtx->m_pbHashObject)
00783     {
00784         std::ostringstream os;
00785         os << "Memory allocation failed: " << GetLastError();;
00786         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
00787     }
00788 
00789     //calculate the length of the hash
00790     if(!NT_SUCCESS(status = BCryptGetProperty(
00791                                         hashCtx->m_hHashAlg, 
00792                                         BCRYPT_HASH_LENGTH, 
00793                                         (PBYTE)&cbHash, 
00794                                         sizeof(DWORD), 
00795                                         &cbData, 
00796                                         0)))
00797     {
00798         std::ostringstream os;
00799         os << "BCryptGetProperty failed: " << GetLastError();;
00800         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
00801     }
00802 
00803     //create a hash
00804     if(!NT_SUCCESS(status = BCryptCreateHash(
00805                                         hashCtx->m_hHashAlg, 
00806                                         &hashCtx->m_cngHashHandle, 
00807                                         hashCtx->m_pbHashObject, 
00808                                         cbHashObject, 
00809                                         NULL, 
00810                                         0, 
00811                                         0)))
00812     {
00813         std::ostringstream os;
00814         os << "BCryptCreateHash failed: " << GetLastError();;
00815         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
00816     }
00817 
00818     return hashCtx;
00819 }
00834 void CPKIFCNGCAPIRaw::HashUpdate(
00836     IPKIFHashContext* hash, 
00838     unsigned char* pData, 
00840     int nDataLen)
00841 {
00842     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::HashUpdate(IPKIFHashContext* hash, unsigned char* pData, int nDataLen)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00843 
00844     NTSTATUS                status          = STATUS_UNSUCCESSFUL;
00845 
00846     //sanity check the inputs: 
00847     //  hash must not be NULL, must be of a type this class supports and must not be empty
00848     //  pData MAY be NULL (if not it is assumed to be at least nDataLen bytes)
00849     //  nDataLen must not be less than zero
00850     if(NULL == hash/* || NULL == pData || 0 >= nDataLen*/ || 0 > nDataLen)
00851         throw CPKIFCryptoException(thisComponent, COMMON_INVALID_INPUT, "NULL input passed to HashUpdate.");
00852 
00853     //cast the hash context to the type this class handles.  if the cast fails
00854     //then the context is not the correct type.
00855     CPKIFCAPIHashContext* hashCtx = dynamic_cast<CPKIFCAPIHashContext*>(hash);
00856     if(NULL == hashCtx)
00857         throw CPKIFCryptoException(thisComponent, PKIFCAPI_INCORRECT_HASH_CONTEXT, "Incorrect hash context passed to HashUpdate.");
00858 
00859     if(NULL == hashCtx->m_cngHashHandle)
00860         throw CPKIFCryptoException(thisComponent, PKIFCAPI_EMPTY_HASH_CONTEXT, "Empty hash context passed to HashUpdate.");
00861 
00862     //hash some data
00863     if(!NT_SUCCESS(status = BCryptHashData(
00864                                         hashCtx->m_cngHashHandle,
00865                                         (PBYTE)pData,
00866                                         nDataLen,
00867                                         0)))
00868     {
00869         std::ostringstream os;
00870         os << "BCryptHashData failed: " << GetLastError();;
00871         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
00872     }
00873 }
00889 void CPKIFCNGCAPIRaw::HashFinal(
00891     IPKIFHashContext* hash,
00893     unsigned char* pResult, 
00896     int* pnResultLen)
00897 {
00898     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::HashFinal(IPKIFHashContext* hash, unsigned char* pResult, int* pnResultLen)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00899 
00900     NTSTATUS                status          = STATUS_UNSUCCESSFUL;
00901 
00902     //sanity check the inputs: 
00903     //  hash must not be NULL, must be of a type this class supports and must not be empty
00904     //  result must not be NULL (it is assumed to be at least *pnResultLen bytes)
00905     //  pnResultLen must not be NULL and *pnResultLen must not be less than or equal to zero
00906     if(NULL == hash || NULL == pResult || NULL == pnResultLen || 0 >= *pnResultLen)
00907         throw CPKIFCryptoException(thisComponent, COMMON_INVALID_INPUT, "NULL input passed to HashFinal.");
00908 
00909     CPKIFCAPIHashContext* hashCtx = dynamic_cast<CPKIFCAPIHashContext*>(hash);
00910     if(NULL == hashCtx)
00911         throw CPKIFCryptoException(thisComponent, PKIFCAPI_INCORRECT_HASH_CONTEXT, "Incorrect hash context passed to HashFinal.");
00912 
00913     if(NULL == hashCtx->m_cngHashHandle)
00914         throw CPKIFCryptoException(thisComponent, PKIFCAPI_EMPTY_HASH_CONTEXT, "Empty hash context passed to HashUpdate.");
00915 
00916     DWORD digestLen = 0, maxOutLen = 0;
00917     try {
00918         maxOutLen = boost::numeric_cast<DWORD,int>(*pnResultLen);
00919     } catch(...) {
00920         throw CPKIFCryptoException(thisComponent, COMMON_INVALID_INPUT, "Invalid output buffer length passed to HashFinal.");
00921     }
00922     unsigned long digestLenLen = 0;
00923     if(!NT_SUCCESS(status = BCryptGetProperty(
00924                                         hashCtx->m_cngHashHandle,
00925                                         BCRYPT_HASH_LENGTH,
00926                                         (PBYTE)&digestLen,
00927                                         sizeof(DWORD),
00928                                         &digestLenLen,
00929                                         0)))
00930     {
00931         // if the context can't tell us that property, things are unlikely to work.
00932         // try anyway with the length provided by the caller.
00933         digestLen = maxOutLen;
00934     }
00935 
00936     if(digestLen > maxOutLen)
00937         throw CPKIFCryptoException(thisComponent, COMMON_INVALID_INPUT, "Invalid output buffer passed to HashFinal.");
00938     //terminate the hash and retrieve the hash value
00939     //close the hash
00940     if(!NT_SUCCESS(status = BCryptFinishHash(
00941                                         hashCtx->m_cngHashHandle, 
00942                                         pResult, 
00943                                         digestLen, 
00944                                         0)))
00945     {
00946         std::ostringstream os;
00947         os << "BCryptFinishHash failed: " << GetLastError();;
00948         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_MISC_HASH_CALL_FAILED, NULL);
00949     }
00950     try {
00951         *pnResultLen = boost::numeric_cast<int,DWORD>(digestLen);
00952     } catch(...) {
00953         RAISE_CRYPTO_EXCEPTION("Internal error: Unable to cast digest length from DWORD to int.", TOOLKIT_CRYPTO, PKIFCAPI_MISC_HASH_CALL_FAILED, NULL);
00954     }
00955 }
00978 IPKIFRawCryptContext* CPKIFCNGCAPIRaw::CryptInit(
00981     const CPKIFKeyMaterial& key,
00983     bool pad)
00984 {
00985     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::CryptInit(const CPKIFKeyMaterial& key)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
00986 
00987     // the iterative crypt functions are a bad idea with RSA or DSA keys...
00988     if(!key.ContainsSymmetricKeyMaterial())
00989     {
00990         throw CPKIFCryptoException(TOOLKIT_CRYPTO, COMMON_INVALID_INPUT, "CryptInit() only makes sense for symmetric keys.");   
00991     }
00992 
00993     //throw bad_alloc; nothing to clean up in this function
00994     std::auto_ptr<CPKIFCAPIRawCryptContext> cryptContext(new CPKIFCAPIRawCryptContext());
00995 
00996     NTSTATUS status = STATUS_UNSUCCESSFUL;
00997 
00998     cryptContext->m_bNeedsPad = pad;
00999     
01000     LPCWSTR tmpAlgID = GetCNGSymAlgorithm(key);
01001     BCRYPT_ALG_HANDLE_PKIF hAlg(0);
01002     //open an algorithm handle
01003     if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAlg,tmpAlgID,NULL,0)))
01004     {
01005         std::ostringstream os;
01006         os << "BCryptOpenAlgorithmProvider failed: " << GetLastError();;
01007         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_OPEN_ALG_PROVIDER_FAILED, NULL);
01008     }
01009 
01010     //calculate the size of the buffer to hold the KeyObject
01011     DWORD cbData = 0;
01012     DWORD cbKeyObject = 0;
01013     if(!NT_SUCCESS(status = BCryptGetProperty(hAlg,BCRYPT_OBJECT_LENGTH,(PBYTE)&cbKeyObject, 
01014         sizeof(DWORD),&cbData,0)))
01015     {
01016         std::ostringstream os;
01017         os << "BCryptGetProperty failed: " << GetLastError();;
01018         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_GET_PROPERTY_FAILED, NULL);
01019     }
01020 
01021     pkif_byte_array pbKeyObject(new unsigned char[cbKeyObject]);
01022 
01023     LPCWSTR tmpMode = 0;
01024     switch(key.GetMode())
01025     {
01026     case PKIFCRYPTO::ECB:
01027         tmpMode = BCRYPT_CHAIN_MODE_ECB;
01028         break;
01029     case PKIFCRYPTO::CBC:
01030         tmpMode = BCRYPT_CHAIN_MODE_CBC;
01031         if(NULL == key.GetIV())
01032             throw CPKIFCryptoException(thisComponent, CRYPTO_MISSING_IV);
01033         break;
01034     default:
01035         throw CPKIFCryptoException(thisComponent, CRYPTO_MODE_NOT_SUPPORTED);
01036     }
01037 
01038     //calculate the block length
01039     if(!NT_SUCCESS(status = BCryptGetProperty(hAlg,BCRYPT_BLOCK_LENGTH,
01040         (PBYTE)&cryptContext->m_blockLen,sizeof(DWORD),&cbData,0)))
01041     {
01042         std::ostringstream os;
01043         os << "BCryptGetProperty: " << GetLastError();;
01044         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_GET_PROPERTY_FAILED, NULL);
01045 
01046     }
01047 
01048     if(NULL != key.GetIV())
01049     {
01050         int ivLen = 0;
01051         key.GetIV(NULL, &ivLen);
01052         DWORD dwIVLen = 0;
01053         try {
01054             dwIVLen = boost::numeric_cast<DWORD,int>(ivLen);
01055         }catch(...){
01056             RAISE_CRYPTO_EXCEPTION("Invalid IV length.", TOOLKIT_CRYPTO, PKIFCAPING_GET_PROPERTY_FAILED, NULL);
01057         }
01058         // is an IV ever different from the block size legitimately for a block cipher?
01059         // (for any mode we support?)
01060         if (cryptContext->m_blockLen > dwIVLen)
01061         {
01062             std::ostringstream os;
01063             os << "Block length is not equal to the supplied IV length: " << GetLastError();;
01064             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_GET_PROPERTY_FAILED, NULL);
01065 
01066         }
01067 
01068         CPKIFBufferPtr tmp(new CPKIFBuffer(key.GetIV(), ivLen));
01069         cryptContext->m_iv = tmp;
01070     }
01071 
01072     if(pad)
01073         cryptContext->m_padbuf = new unsigned char[cryptContext->m_blockLen];
01074 
01075     // these are all short constants, never passed in by user
01076     ULONG modeLen = (ULONG) (wcslen(tmpMode)+1*sizeof(wchar_t));
01077     if(!NT_SUCCESS(status = BCryptSetProperty(hAlg,BCRYPT_CHAINING_MODE,(PBYTE)tmpMode, 
01078         modeLen,0)))
01079     {
01080         std::ostringstream os;
01081         os << "Failed to set mode: " << GetLastError();
01082         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_SET_PROPERTY_FAILED, NULL);
01083     }
01084 
01085     PKIFCRYPTO::SYMKEY_ALG symAlg = key.GetSymmetricKeyAlgorithm();
01086     int keyLen = 0;
01087     key.GetSymmetricKey(NULL, &keyLen, &symAlg);
01088     // import raw key into a key object
01089     if(!NT_SUCCESS(status = BCryptGenerateSymmetricKey(hAlg,&cryptContext->m_hKey,
01090         pbKeyObject,cbKeyObject,(PBYTE)key.GetSymmetricKey(), 
01091         keyLen,0)))
01092     {
01093         std::ostringstream os;
01094         os << "BCryptGenerateSymmetricKey failed: " << GetLastError();;
01095         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_GENERATE_SYM_KEY_FAILED, NULL);
01096     }
01097     // let the context take care of these now; they need to live longer than its
01098     // m_hKey member
01099     cryptContext->m_hBCAlg = hAlg.release();
01100     cryptContext->m_hKeyBuffer = pbKeyObject.release();
01101 
01102     // let the caller manage the raw pointer from here.
01103     return cryptContext.release();
01104 }
01113 void CPKIFCNGCAPIRaw::Decrypt(
01116     IPKIFRawCryptContext* cryptContext, 
01118     unsigned char* pData, 
01120     int nDataLen, 
01122     unsigned char* pResult, 
01125     int* pnResultLen, 
01128     bool final)
01129 {
01130     m_impl->CryptFunc<false>(cryptContext, pData, nDataLen, pResult, pnResultLen, final);
01131 }
01140 void CPKIFCNGCAPIRaw::Encrypt(
01143     IPKIFRawCryptContext* cryptContext, 
01145     unsigned char* pData, 
01147     int nDataLen, 
01149     unsigned char* pResult, 
01152     int* pnResultLen, 
01155     bool final)
01156 {
01157     m_impl->CryptFunc<true>(cryptContext, pData, nDataLen, pResult, pnResultLen, final);
01158 }
01159 
01181 template <bool _CryptDirection>
01182 void CPKIFCNGCAPIRawImpl::CryptFunc(
01185     IPKIFRawCryptContext* cryptContext,
01187     unsigned char* pData,
01189     int nDataLen,
01191     unsigned char* pResult,
01193     int* pnResultLen,
01196     bool final)
01197 {
01198     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::CryptFunc(IPKIFRawCryptContext* cryptContext, unsigned char* pData, int nDataLen, unsigned char* pResult, int* pnResultLen, bool final)", TOOLKIT_CRYPTO_CAPIRAW, 0, this);
01199 
01200     CPKIFCAPIRawCryptContext* icc = dynamic_cast<CPKIFCAPIRawCryptContext*>(cryptContext);
01201     const unsigned char* iv = NULL;
01202     ULONG ivLen = 0;
01203 
01204     if(icc->m_iv != (CPKIFBuffer*)NULL)
01205     {
01206         iv = icc->m_iv->GetBuffer();
01207         ivLen = icc->m_iv->GetLength();
01208         // XXX *** Maybe assert IV == blocksize? That should really be handled elsewhere
01209     }
01210     NTSTATUS status = STATUS_UNSUCCESSFUL;
01211     // if no padding was requested, or if it's an asymmetric op, just pass it right through
01212     if(icc->m_bFromCert || !icc->m_bNeedsPad)
01213     {
01214         if(_CryptDirection)
01215         {
01216             DWORD cbCipherText = 0;
01217             if(!NT_SUCCESS(status = BCryptEncrypt(icc->m_hKey,pData,nDataLen,
01218                                             NULL,(PBYTE)iv,ivLen,NULL,0,&cbCipherText,0)))
01219             {
01220                 std::ostringstream os;
01221                 os << "BCryptEncrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01222                     << ")" << endl;
01223                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01224             }
01225             pkif_byte_array pbCipherText(new unsigned char[cbCipherText]);
01226             DWORD cbData = 0;
01227             if(!NT_SUCCESS(status = BCryptEncrypt(icc->m_hKey,pData,nDataLen,
01228                                                 NULL,(PBYTE)iv,ivLen, 
01229                                                 pbCipherText,cbCipherText,&cbData,0)))
01230             {
01231                 std::ostringstream os;
01232                 os << "BCryptEncrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01233                     << ")" << endl;
01234                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01235             }
01236 
01237             memcpy(pResult, pbCipherText.get(), cbData);
01238             *pnResultLen = cbData;
01239         }
01240         else
01241         {
01242             DWORD cbPlainText = 0;
01243             //get the output buffer size
01244             if(!NT_SUCCESS(status = BCryptDecrypt( icc->m_hKey,pData,nDataLen,NULL,
01245                                             (PBYTE)iv,ivLen,NULL,0,&cbPlainText,0)))
01246             {
01247                 std::ostringstream os;
01248                 os << "BCryptDecrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01249                     << ")" << endl;
01250                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01251             }
01252             pkif_byte_array pbPlainText(new unsigned char[cbPlainText]);
01253             DWORD cbData = 0;
01254             if(!NT_SUCCESS(status = BCryptDecrypt(icc->m_hKey, pData, nDataLen,
01255                                             NULL,(PBYTE)iv,ivLen,
01256                                             pbPlainText,cbPlainText,&cbData, 
01257                                             0)))
01258             {
01259                 std::ostringstream os;
01260                 os << "BCryptDecrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01261                     << ")" << endl;
01262                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01263             }
01264 
01265             memcpy(pResult, pbPlainText.get(), cbData);
01266             try {
01267                 *pnResultLen = cbData;
01268             }catch(...){
01269                 RAISE_CRYPTO_EXCEPTION("BCryptDecrypt gave invalid length", TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01270             }
01271         }
01272         return;
01273     }
01274     // otherwise, we've been asked to handle the padding here.
01275     int maxOut = *pnResultLen;
01276     int inLen = nDataLen;
01277     unsigned char * start = pData;
01278     unsigned char * outBuf = pResult;
01279 
01280     DWORD updateLen = 0;
01281     DWORD padUpdateLen = 0;
01282     *pnResultLen = 0;
01283     unsigned int finalLen = 0;
01284 
01285     *pnResultLen = 0;
01286     int err = 0;
01287 
01288     DWORD dwMaxOut = 0;
01289     try {
01290         dwMaxOut = boost::numeric_cast<DWORD,int>(maxOut);
01291     } catch(...) {
01292         RAISE_CRYPTO_EXCEPTION("Invalid output length", TOOLKIT_CRYPTO, COMMON_INVALID_INPUT, NULL);
01293     }
01294     if(_CryptDirection)
01295     {   
01296         DWORD resLen = *pnResultLen;
01297         if(final)
01298         {
01299             // XXX *** assert maxout >= padlen + inlen + blocksize
01300             if(icc->m_padlen) {
01301                 memcpy(outBuf,icc->m_padbuf,icc->m_padlen);
01302             }
01303             if(start && inLen) memcpy(outBuf + icc->m_padlen,start,inLen);
01304             updateLen = inLen + icc->m_padlen;
01305             DWORD cbCipherText = 0;
01306             
01307             // plaintext gets copied to outBuf because this code is derived from
01308             // our CAPI1 colleague, where CryptEncrypt used pbData as in/out.
01309             // refactoring would make this easier to read.
01310             if(!NT_SUCCESS(status = BCryptEncrypt(icc->m_hKey,outBuf,updateLen,
01311                                             NULL,(PBYTE)iv,ivLen,NULL,0,&cbCipherText,BCRYPT_BLOCK_PADDING)))
01312             {
01313                 std::ostringstream os;
01314                 os << "BCryptEncrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01315                     << ")" << endl;
01316                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01317             }
01318             if(cbCipherText > dwMaxOut) {
01319                 RAISE_CRYPTO_EXCEPTION("Output buffer is too small for ciphertext.", TOOLKIT_CRYPTO, COMMON_INVALID_INPUT, NULL);
01320             }
01321             pkif_byte_array pbCipherText(new unsigned char[cbCipherText]);
01322             DWORD cbData = 0;
01323             if(!NT_SUCCESS(status = BCryptEncrypt(icc->m_hKey,outBuf,updateLen,
01324                                                 NULL,(PBYTE)iv,ivLen, 
01325                                                 pbCipherText,cbCipherText,&cbData,BCRYPT_BLOCK_PADDING)))
01326             {
01327                 std::ostringstream os;
01328                 os << "BCryptEncrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01329                     << ")" << endl;
01330                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01331             }
01332             memcpy(outBuf,pbCipherText.get(),cbData);
01333             try {
01334                 *pnResultLen = boost::numeric_cast<int,DWORD>(cbData);
01335             }catch(...){
01336                 RAISE_CRYPTO_EXCEPTION("BCryptEncrypt returned invalid length", TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01337             }
01338             return;
01339         }
01340 
01341         // if it's not final, we need to have input
01342         if(!start || !inLen) return;
01343         int leftInBlock = icc->m_blockLen - icc->m_padlen;
01344 
01345 
01346         // if there's not enough to exceed the pad buffer, fill the pad buffer and bail
01347         if(leftInBlock >= inLen) {
01348             memcpy(icc->m_padbuf,start,inLen);
01349             icc->m_padlen += inLen;
01350             *pnResultLen = 0;
01351             return;
01352         }
01353 
01354         // if we're here, we got more than a cipherblock
01355         // 1. Empty the pad buffer
01356         memcpy(outBuf,icc->m_padbuf,icc->m_padlen);
01357         updateLen = icc->m_padlen;
01358         icc->m_padlen = 0;
01359         
01360         // 2. calculate how many bytes go into the pad buffer
01361         int trailing = (inLen + updateLen) % icc->m_blockLen ? 
01362             (inLen + updateLen) % icc->m_blockLen : icc->m_blockLen;
01363 
01364         // 3. empty the input buffer
01365         int copied = inLen - trailing;
01366         memcpy(outBuf+updateLen,start,copied);
01367         start += copied;
01368         updateLen += copied;
01369 
01370         // 4. encrypt
01371         DWORD cbCipherText = 0;
01372         if(!NT_SUCCESS(status = BCryptEncrypt(icc->m_hKey,outBuf,updateLen,
01373                                             NULL,(PBYTE)iv,ivLen,NULL,0,&cbCipherText,0)))
01374         {
01375             std::ostringstream os;
01376             os << "BCryptEncrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01377                 << ")" << endl;
01378             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01379         }
01380         if(cbCipherText > dwMaxOut) {
01381             RAISE_CRYPTO_EXCEPTION("Output buffer is too small for ciphertext.", TOOLKIT_CRYPTO, COMMON_INVALID_INPUT, NULL);
01382         }
01383         pkif_byte_array pbCipherText(new unsigned char[cbCipherText]);
01384         DWORD cbData = 0;
01385         if(!NT_SUCCESS(status = BCryptEncrypt(icc->m_hKey,outBuf,updateLen,
01386                                             NULL,(PBYTE)iv,ivLen, 
01387                                             pbCipherText,cbCipherText,&cbData,0)))
01388         {
01389             std::ostringstream os;
01390             os << "BCryptEncrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01391                 << ")" << endl;
01392             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01393         }
01394         memcpy(outBuf,pbCipherText.get(),cbData);
01395                 
01396         // 5. copy off the trailing bytes for next time
01397         memcpy(icc->m_padbuf,start,trailing);
01398         icc->m_padlen += trailing;
01399 
01400         try {
01401             *pnResultLen = boost::numeric_cast<int,DWORD>(cbData);
01402         }catch(...){
01403             RAISE_CRYPTO_EXCEPTION("BCryptEncrypt returned invalid length", TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01404         }
01405     }
01406     else
01407     {
01408         if(final) {
01409             if(icc->m_padlen) {
01410                 memcpy(outBuf,icc->m_padbuf,icc->m_padlen);
01411             }
01412             if(start && inLen) memcpy(outBuf + icc->m_padlen,start,inLen);
01413             updateLen = inLen + icc->m_padlen;
01414             icc->m_padlen = 0;
01415             DWORD cbPlainText = 0;
01416             //get the output buffer size
01417             if(!NT_SUCCESS(status = BCryptDecrypt( icc->m_hKey,outBuf,updateLen,NULL,
01418                                             (PBYTE)iv,ivLen,NULL,0,&cbPlainText,BCRYPT_BLOCK_PADDING)))
01419             {
01420                 std::ostringstream os;
01421                 os << "BCryptDecrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01422                     << ")" << endl;
01423                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01424             }
01425             pkif_byte_array pbPlainText(new unsigned char[cbPlainText]);
01426             DWORD cbData = 0;
01427             if(!NT_SUCCESS(status = BCryptDecrypt(icc->m_hKey, outBuf, updateLen,
01428                                             NULL,(PBYTE)iv,ivLen,
01429                                             pbPlainText,cbPlainText,&cbData, 
01430                                             BCRYPT_BLOCK_PADDING)))
01431             {
01432                 std::ostringstream os;
01433                 os << "BCryptDecrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01434                     << ")" << endl;
01435                 RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01436             }
01437             // check this here since CAPI can't
01438             if(dwMaxOut < cbData) {
01439                 RAISE_CRYPTO_EXCEPTION("Decrypt() called with insufficient buffer available", m_parent->thisComponent, COMMON_INVALID_INPUT, NULL);
01440             }
01441 
01442             memcpy(pResult, pbPlainText.get(), cbData);
01443             try {
01444                 *pnResultLen = boost::numeric_cast<int,DWORD>(cbData);
01445             }catch(...){
01446                 RAISE_CRYPTO_EXCEPTION("BCryptDecrypt returned invalid length", TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01447             }
01448             return;
01449         }
01450 
01451         // if it's not final, we need to have input
01452         if(!start || !inLen) return;
01453         int leftInBlock = icc->m_blockLen - icc->m_padlen;
01454         // if there's not enough to exceed the pad buffer, fill the pad buffer and bail
01455         if(leftInBlock >= inLen) {
01456             memcpy(icc->m_padbuf,start,inLen);
01457             icc->m_padlen += inLen;
01458             *pnResultLen = 0;
01459             return;
01460         }
01461 
01462         // if we're here, we got more than a cipherblock
01463         // 1. Empty the pad buffer
01464         memcpy(outBuf,icc->m_padbuf,icc->m_padlen);
01465         updateLen = icc->m_padlen;
01466         icc->m_padlen = 0;
01467         
01468         // 2. calculate how many bytes go into the pad buffer
01469         int trailing = (inLen + updateLen) % icc->m_blockLen ? 
01470             (inLen + updateLen) % icc->m_blockLen : icc->m_blockLen;
01471 
01472         // 3. empty the input buffer
01473         int copied = inLen - trailing;
01474         memcpy(outBuf+updateLen,start,copied);
01475         start += copied;
01476         updateLen += copied;
01477 
01478         // 4. decrypt
01479         DWORD cbPlainText = 0;
01480         //get the output buffer size
01481         if(!NT_SUCCESS(status = BCryptDecrypt( icc->m_hKey,outBuf,updateLen,NULL,
01482                                         (PBYTE)iv,ivLen,NULL,0,&cbPlainText,0)))
01483         {
01484             std::ostringstream os;
01485             os << "BCryptDecrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01486                 << ")" << endl;
01487             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01488         }
01489         // check this here since CAPI can't
01490         if(dwMaxOut < cbPlainText) {
01491             RAISE_CRYPTO_EXCEPTION("Decrypt() called with insufficient buffer available", m_parent->thisComponent, COMMON_INVALID_INPUT, NULL);
01492         }
01493         pkif_byte_array pbPlainText(new unsigned char[cbPlainText]);
01494         DWORD cbData = 0;
01495         if(!NT_SUCCESS(status = BCryptDecrypt(icc->m_hKey, outBuf, updateLen,
01496                                         NULL,(PBYTE)iv,ivLen,
01497                                         pbPlainText,cbPlainText,&cbData, 
01498                                         0)))
01499         {
01500             std::ostringstream os;
01501             os << "BCryptDecrypt failed :(" << GetLastError() << ")" << "(NTSTATUS code " << status
01502                 << ")" << endl;
01503             RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPING_DECRYPT_FAILED, NULL);
01504         }
01505 
01506         memcpy(pResult, pbPlainText.get(), cbData);
01507         
01508         // 5. copy off the trailing bytes for next time
01509         memcpy(icc->m_padbuf,start,trailing);
01510         icc->m_padlen += trailing;
01511         try {
01512             *pnResultLen = boost::numeric_cast<int,DWORD>(cbData);
01513         }catch(...){
01514             RAISE_CRYPTO_EXCEPTION("BCryptDecrypt returned invalid length", TOOLKIT_CRYPTO, PKIFCAPING_ENCRYPT_FAILED, NULL);
01515         }
01516     }
01517 }
01518 
01519 
01532 IPKIFRawCryptContext* CPKIFCNGCAPIRaw::HMACInit(const CPKIFKeyMaterial &key, PKIFCRYPTO::HASH_ALG ha)
01533 {
01534     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::HMACInit(const CPKIFKeyMaterial& key, HASH_ALG ha)", thisComponent, 0, this);
01535     return 0;
01536 
01537     wchar_t* hashAlg;
01538     switch(ha)
01539     {
01540     case PKIFCRYPTO::SHA1:
01541         hashAlg = BCRYPT_SHA1_ALGORITHM;
01542         break;
01543     case PKIFCRYPTO::SHA256:
01544         hashAlg = BCRYPT_SHA256_ALGORITHM;
01545         break;
01546     case PKIFCRYPTO::SHA384:
01547         hashAlg = BCRYPT_SHA384_ALGORITHM;
01548         break;
01549     case PKIFCRYPTO::SHA512:
01550         hashAlg = BCRYPT_SHA512_ALGORITHM;
01551         break;
01552     default:
01553         std::ostringstream os;
01554         os << "Unsupported hash algorithm encountered: " << hashAlg;
01555         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), thisComponent, CRYPTO_ALG_NOT_SUPPORTED, NULL);
01556     }
01557 
01558     //create a hash context object and populate it with context and hash objects
01559     //throw bad_alloc; nothing to clean up in this function
01560     CPKIFCAPIRawCryptContext* cryptCtx = new CPKIFCAPIRawCryptContext();
01561     NTSTATUS                status          = STATUS_UNSUCCESSFUL;
01562     BCRYPT_ALG_HANDLE       hHashAlg            = NULL; 
01563     DWORD                   cbData          = 0,
01564                             cbHash          = 0,
01565                             cbHashObject    = 0;
01566     PBYTE                   pbHashObject    = NULL;
01567     PBYTE                   pbHash          = NULL;
01568 
01569     //open an algorithm handle
01570     if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
01571                                             &hHashAlg,
01572                                             hashAlg,
01573                                             NULL,
01574                                             BCRYPT_ALG_HANDLE_HMAC_FLAG)))
01575     {
01576 
01577         std::ostringstream os;
01578         os << "CryptCreateHash failed: " << GetLastError();;
01579         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01580     }
01581 
01582     //calculate the size of the buffer to hold the hash object
01583     if(!NT_SUCCESS(status = BCryptGetProperty(
01584                                         hHashAlg, 
01585                                         BCRYPT_OBJECT_LENGTH, 
01586                                         (PBYTE)&cbHashObject, 
01587                                         sizeof(DWORD), 
01588                                         &cbData, 
01589                                         0)))
01590     {
01591         std::ostringstream os;
01592         os << "BCryptGetProperty failed: " << GetLastError();;
01593         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01594     }
01595 
01596     //allocate the hash object on the heap
01597     pbHashObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHashObject);
01598     if(NULL == pbHashObject)
01599     {
01600         std::ostringstream os;
01601         os << "Memory allocation failed: " << GetLastError();;
01602         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01603     }
01604 
01605     //calculate the length of the hash
01606     if(!NT_SUCCESS(status = BCryptGetProperty(
01607                                         hHashAlg, 
01608                                         BCRYPT_HASH_LENGTH, 
01609                                         (PBYTE)&cbHash, 
01610                                         sizeof(DWORD), 
01611                                         &cbData, 
01612                                         0)))
01613     {
01614         std::ostringstream os;
01615         os << "BCryptGetProperty failed: " << GetLastError();;
01616         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01617     }
01618 
01619     //create a hash
01620     if(!NT_SUCCESS(status = BCryptCreateHash(
01621                                         hHashAlg, 
01622                                         &cryptCtx->m_cngHashHandle, 
01623                                         pbHashObject, 
01624                                         cbHashObject, 
01625                                         (PBYTE)const_cast<unsigned char *>(key.GetSymmetricKey()), 
01626                                         key.GetSymmetricKeyLength(), 
01627                                         0)))
01628     {
01629         std::ostringstream os;
01630         os << "BCryptCreateHash failed: " << GetLastError();;
01631         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01632     }
01633 
01634     return cryptCtx;
01635 }
01636 
01648 void CPKIFCNGCAPIRaw::HMACUpdate(IPKIFRawCryptContext* ctx, unsigned char* pData, int nDataLen)
01649 {
01650     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::HMACUpdate(IPKIFRawCryptContext* ctx, unsigned char* pData, int nDataLen)", thisComponent, 0, this);
01651 
01652         NTSTATUS                status          = STATUS_UNSUCCESSFUL;
01653 
01654     //sanity check the inputs: 
01655     //  hash must not be NULL, must be of a type this class supports and must not be empty
01656     //  pData MAY be NULL (if not it is assumed to be at least nDataLen bytes)
01657     //  nDataLen must not be less than zero
01658     if(NULL == ctx/* || NULL == pData || 0 >= nDataLen*/ || 0 > nDataLen)
01659         throw CPKIFCryptoException(thisComponent, COMMON_INVALID_INPUT, "NULL input passed to HMACUpdate.");
01660 
01661     //cast the hash context to the type this class handles.  if the cast fails
01662     //then the context is not the correct type.
01663     CPKIFCAPIRawCryptContext* cryptCtx = dynamic_cast<CPKIFCAPIRawCryptContext*>(ctx);
01664     if(NULL == cryptCtx)
01665         throw CPKIFCryptoException(thisComponent, PKIFCAPI_INCORRECT_HASH_CONTEXT, "Incorrect hash context passed to HMACUpdate.");
01666 
01667     if(NULL == cryptCtx->m_cngHashHandle)
01668         throw CPKIFCryptoException(thisComponent, PKIFCAPI_EMPTY_HASH_CONTEXT, "Empty hash context passed to HMACUpdate.");
01669 
01670     //hash some data
01671     if(!NT_SUCCESS(status = BCryptHashData(
01672                                         cryptCtx->m_cngHashHandle,
01673                                         (PBYTE)pData,
01674                                         nDataLen,
01675                                         0)))
01676     {
01677         std::ostringstream os;
01678         os << "BCryptHashData failed: " << GetLastError();;
01679         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01680     }
01681 }
01682 
01694 void CPKIFCNGCAPIRaw::HMACFinal(IPKIFRawCryptContext* ctx, unsigned char* pResult, int* pnResultLen)
01695 {
01696     LOG_STRING_DEBUG("CPKIFCNGCAPIRaw::HMACFinal(IPKIFRawCryptContext* ctx, unsigned char* pResult, int* pnResultLen)", thisComponent, 0, this);
01697 
01698     NTSTATUS                status          = STATUS_UNSUCCESSFUL;
01699 
01700     //sanity check the inputs: 
01701     //  hash must not be NULL, must be of a type this class supports and must not be empty
01702     //  result must not be NULL (it is assumed to be at least *pnResultLen bytes)
01703     //  pnResultLen must not be NULL and *pnResultLen must not be less than or equal to zero
01704     if(NULL == ctx || NULL == pResult || NULL == pnResultLen || 0 >= *pnResultLen)
01705         throw CPKIFCryptoException(thisComponent, COMMON_INVALID_INPUT, "NULL input passed to HMACFinal.");
01706 
01707     CPKIFCAPIRawCryptContext* cryptCtx = dynamic_cast<CPKIFCAPIRawCryptContext*>(ctx);
01708     if(NULL == cryptCtx)
01709         throw CPKIFCryptoException(thisComponent, PKIFCAPI_INCORRECT_HASH_CONTEXT, "Incorrect hash context passed to HMACFinal.");
01710 
01711     if(NULL == cryptCtx->m_cngHashHandle)
01712         throw CPKIFCryptoException(thisComponent, PKIFCAPI_EMPTY_HASH_CONTEXT, "Empty hash context passed to HMACFinal.");
01713 
01714     //terminate the hash and retrieve the hash value
01715     //close the hash
01716     if(!NT_SUCCESS(status = BCryptFinishHash(
01717                                         cryptCtx->m_cngHashHandle, 
01718                                         pResult, 
01719                                         *((DWORD*)pnResultLen), 
01720                                         0)))
01721     {
01722         std::ostringstream os;
01723         os << "BCryptFinishHash failed: " << GetLastError();;
01724         RAISE_CRYPTO_EXCEPTION(os.str().c_str(), TOOLKIT_CRYPTO, PKIFCAPI_CREATE_HASH_FAILED, NULL);
01725     }
01726 }

Generated on Mon Nov 15 11:15:54 2010 for PublicKeyInfrastructureFramework(PKIF) by  doxygen 1.5.6