PKIFPlatformCryptUtils.cpp

Go to the documentation of this file.
00001 
00010 #include "PKIFCryptUtils.h"
00011 #include "PKIFNSSDatabase.h"
00012 #include "PKIFNSSPasswordStorage.h"
00013 
00014 #ifdef _WIN32
00015 #include "PKIFCAPIRaw.h"
00016 #include "PKIFCAPI2.h"
00017 #include "PKIFBCryptGuard.h"
00018 #include "PKIFCNGCAPI.h"
00019 #include "PKIFCNGCAPIRaw.h"
00020 #else
00021 #include "PKIFCryptoPPRaw.h"
00022 #endif //_WIN32
00023 
00024 #include "PKIFKeyMaterial.h"
00025 #include "Buffer.h"
00026 #include "IPKIFCryptoRaw.h"
00027 #include "ToolkitUtils.h"
00028 #include "components.h"
00029 #include "PKIFAlgorithm.h"
00030 #include "PKIFCryptoConstants.h"
00031 #include "PKIFCryptoErrors.h"
00032 #include "PKIFCryptoException.h"
00033 #include "PKIFMemoryUtils.h"
00034 #include "OID.h"
00035 
00036 #include <cstring>
00037 #include <sstream>
00038 #include <boost/scoped_array.hpp>
00039 #include <boost/cstdint.hpp>
00040 using namespace std;
00041 using namespace boost;
00042 #ifdef _WIN32
00043 static CPKIFCAPIRaw sRaw;
00044 #else
00045 static CPKIFCryptoPPRaw sRaw;
00046 #endif //_WIN32
00047 
00048 static bool sInited = false;
00049 
00050 static const unsigned char rfc3394iv[] = { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 };
00051 static const int rfc3394ivLen = 8;
00052 static void increment_and_xor(unsigned char *A, unsigned char *T);
00053 static void xor_and_decrement(unsigned char *A, unsigned char *T);
00054 static void set_t(unsigned char *pt, unsigned long t);
00055 static CPKIFAlgorithm * get_symmetric_alg_for_wrap_alg(const CPKIFKeyMaterialPtr & km);
00056 
00066 IPKIFCryptoRaw * GetPlatformCryptoRaw(void)
00067 {
00068     if(!sInited) 
00069     {
00070         sRaw.Initialize();
00071         sInited = true;
00072     }
00073     return &sRaw;
00074 }
00075 
00085 IPKIFCryptoMisc * GetPlatformCryptoMisc(void)
00086 {
00087     if(!sInited) 
00088     {
00089         sRaw.Initialize();
00090         sInited = true;
00091     }
00092     return &sRaw;
00093 }
00094 
00103 IPKIFColleaguePtr MakeDefaultKeyIDColleague(void)
00104 {
00105 #ifdef _WIN32
00106     CPKIFBCryptGuard cngGuard;
00107     if(!cngGuard.IsCNGAvailable())
00108     {
00109         CPKIFCAPI2Ptr tmp(new CPKIFCAPI2);
00110         return dynamic_pointer_cast<IPKIFColleague, CPKIFCAPI2>(tmp);
00111     } else {
00112         CPKIFCNGCAPIPtr tmp(new CPKIFCNGCAPI);
00113         return dynamic_pointer_cast<IPKIFColleague, CPKIFCNGCAPI>(tmp);
00114     }
00115 #else
00116     IPKIFColleaguePtr empty;
00117     return empty;
00118 #endif //_WIN32
00119 }
00120 
00129 IPKIFColleaguePtr MakeDefaultRawColleague(void)
00130 {
00131 #ifdef _WIN32
00132     CPKIFBCryptGuard cngGuard;
00133     if(!cngGuard.IsCNGAvailable())
00134     {
00135         CPKIFCAPIRawPtr tmp(new CPKIFCAPIRaw);
00136         return dynamic_pointer_cast<IPKIFColleague, CPKIFCAPIRaw>(tmp);
00137     } else {
00138         CPKIFCNGCAPIRawPtr tmp(new CPKIFCNGCAPIRaw);
00139         return dynamic_pointer_cast<IPKIFColleague, CPKIFCNGCAPIRaw>(tmp);
00140     }
00141 #else 
00142     CPKIFCryptoPPRawPtr tmp(new CPKIFCryptoPPRaw);
00143     return dynamic_pointer_cast<IPKIFColleague, CPKIFCryptoPPRaw>(tmp);
00144 #endif //_WIN32
00145 }
00146 
00154 void ShutdownCrypto(void)
00155 {
00156     CPKIFNSSDatabase * db = 0;
00157     try {
00158         db = CPKIFNSSDatabase::GetInstance();
00159     }catch(...){
00160         // if that throws but somehow created a database first, make sure it gets shutdown next
00161     }
00162     try {
00163         if(db) {
00164             CPKIFNSSPasswordStorage::SetUserCallback(0);
00165             db->Shutdown();
00166         }
00167     }catch(...) {
00168         // if this fails it didn't need to happen anyway, so just ignore it
00169     }
00170 }
00179 std::string GetCurrentNSSDBDir(void)
00180 {
00181     CPKIFNSSDatabase * db = CPKIFNSSDatabase::GetInstance();
00182     std::string rv = db->GetDBDir();
00183 
00184     return rv;
00185 }
00193 bool IsNSSDBInitialized(void)
00194 {
00195     return CPKIFNSSDatabase::IsInitialized();
00196 }
00197 
00213 CPKIFBufferPtr WrapSymmKey(
00215                            const CPKIFKeyMaterialPtr & kek,
00217                            const CPKIFKeyMaterialPtr & key,
00219                            const IPKIFCryptoRawOperations * crypto)
00220 {
00221     CPKIFBufferPtr rv;
00222     // XXX *** need to cast off const-ness because SupportsAlgorithm() is not
00223     // but should be const. That requires touching too many files for today.
00224     // we should fix it then remove this cast.
00225     IPKIFCryptoRawOperations * cc = const_cast<IPKIFCryptoRawOperations *>(crypto);
00226     if(!kek) throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"WrapSymmKey called without a KEK");
00227     if(!key) throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"WrapSymmKey called without a key");
00228     CPKIFAlgorithm * kwAlg = CPKIFAlgorithm::GetAlg(kek->GetSymmetricKeyAlgorithm());
00229     if(!kwAlg) {
00230         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,CRYPTO_ALG_NOT_SUPPORTED,"WrapSymmKey called with invalid KEK");
00231     } else {
00232         CPKIFOIDPtr kwOid = kwAlg->OID();
00233         if( *kwOid != *g_aes128Wrap &&
00234             *kwOid != *g_aes192Wrap &&
00235             *kwOid != *g_aes256Wrap )
00236         {
00237             ostringstream oss;
00238             oss << kwOid->ToString() << " is not a supported key wrap algorithm." << endl;
00239             throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,CRYPTO_ALG_NOT_SUPPORTED,oss.str().c_str());
00240         }
00241     }
00242     if(0 != key->GetSymmetricKeyLength() % kwAlg->BlockSize()) {
00243         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"WrapSymmKey called with a key that can't be wrapped using this algorithm");
00244     }
00245     if(!cc) {
00246         cc = GetPlatformCryptoRaw();        
00247     }
00248     CPKIFAlgorithm * baseAlg = get_symmetric_alg_for_wrap_alg(kek);
00249     if(!baseAlg) {  
00250         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"WrapSymmKey called with an unimplemented wrap algorithm");
00251     }
00252 
00253     CPKIFKeyMaterialPtr baseKM = CPKIFKeyMaterial::CreateWithSymmetricKey(kek);
00254     baseKM->SetSymmetricKeyAlgorithm(baseAlg->SymkeyAlg());
00255     baseKM->SetMode(baseAlg->SymkeyMode());
00256 
00257     // need to consider merging RawOperations and AlgorithmSupport!
00258     if(!cc || !cc->SupportsAlgorithm(*baseKM))
00259     {
00260         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,CRYPTO_ALG_NOT_SUPPORTED,
00261             "No colleague was specified and the default can't be used.");
00262     }
00263 
00264     int ivLen = 0;
00265     baseKM->GetIV(NULL,&ivLen);
00266 
00267     if(!ivLen) {
00268         baseKM->SetIV(rfc3394iv,rfc3394ivLen);
00269         ivLen = rfc3394ivLen;
00270     }
00271     
00272     scoped_array<unsigned char> iv(new unsigned char[ivLen]);
00273     baseKM->GetIV(iv.get(),&ivLen);
00274     if(ivLen != rfc3394ivLen) {
00275         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,
00276             "An IV was supplied for AES Key wrap but the length was incorrect.");
00277     }
00278 
00279 
00280     rv = CPKIFBufferPtr(new CPKIFBuffer());
00281     int bs = kwAlg->BlockSize();
00282     int plainLen = key->GetSymmetricKeyLength();
00283     unsigned char * ctBuf =
00284         rv->AllocateBuffer(plainLen+bs);
00285     const unsigned char * plainBuf = key->GetSymmetricKey();
00286     unsigned int n = plainLen/bs;
00287     boost::scoped_array<boost::uint64_t> Rp(new boost::uint64_t[n+1]);
00288     boost::uint64_t * R = Rp.get();
00289     boost::uint64_t wbuf[2];
00290     boost::uint64_t * A = &wbuf[0];
00291     boost::uint64_t t;
00292     memset(&t,0x00,sizeof(boost::uint64_t));
00293     int baseBS = baseAlg->BlockSize();
00294     int baseOut = baseBS;
00295     // Straight out of the RFC
00296     // this started as a naiive implementation of the steps from 3394, but I actually find
00297     // the NSS version easier to read, so this bears more resemblance to that than to
00298     // the pseudocode in the RFC.
00299     memcpy(A,iv.get(),rfc3394ivLen);
00300     // R is now sensitive. zero before leaving
00301     memcpy(&R[1],plainBuf,plainLen);
00302     try {   
00303         for(unsigned int j = 0; j <= 5; ++j)
00304         {
00305             for(unsigned int i = 1; i <= n; ++i)
00306             {
00307 /*              B = AES(K, A | R[i])
00308                 A = MSB(64, B) ^ t where t = (n*j)+i
00309                 R[i] = LSB(64, B)
00310 */
00311                 wbuf[1] = R[i]; //wbuf = A|R[i]
00312                 cc->Encrypt(*baseKM,(unsigned char *)wbuf,baseBS,
00313                     (unsigned char *)wbuf,&baseOut,false);
00314                 increment_and_xor((unsigned char *)A,(unsigned char *)&t);
00315                 R[i] = wbuf[1];
00316             }
00317         }
00318         R[0] = *A;
00319         memcpy(ctBuf,R,plainLen+bs);
00320         // R is no longer sensitive... no need to worry about zeroing upon success
00321     }catch(...){
00322         // encrypt can throw, and I don't have a secure array pointer lying around
00323         // for an array of uint64_t
00324         PKIFZero(R,n+1*sizeof(boost::uint64_t));
00325         throw;
00326     }
00327 
00328     return rv;
00329 
00330 }
00331 
00350 CPKIFKeyMaterialPtr UnwrapSymmKey(
00352                            const CPKIFKeyMaterialPtr & kek,
00354                            const CPKIFBufferPtr & key,
00356                            const IPKIFCryptoRawOperations * crypto)
00357 {
00358     CPKIFKeyMaterialPtr rv;
00359     // XXX *** need to cast off const-ness because SupportsAlgorithm() is not
00360     // but should be const. That requires touching too many files for today.
00361     // we should fix it then remove this cast.
00362     IPKIFCryptoRawOperations * cc = const_cast<IPKIFCryptoRawOperations *>(crypto);
00363     if(!kek) throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"UnwrapSymmKey called without a KEK");
00364     if(!key) throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"UnwrapSymmKey called without a key");
00365     CPKIFAlgorithm * kwAlg = CPKIFAlgorithm::GetAlg(kek->GetSymmetricKeyAlgorithm());
00366     if(!kwAlg) {
00367         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,CRYPTO_ALG_NOT_SUPPORTED,"UnwrapSymmKey called with invalid KEK");
00368     } else {
00369         CPKIFOIDPtr kwOid = kwAlg->OID();
00370         if( *kwOid != *g_aes128Wrap &&
00371             *kwOid != *g_aes192Wrap &&
00372             *kwOid != *g_aes256Wrap )
00373         {
00374             ostringstream oss;
00375             oss << kwOid->ToString() << " is not a supported key wrap algorithm." << endl;
00376             throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,CRYPTO_ALG_NOT_SUPPORTED,oss.str().c_str());
00377         }
00378     }
00379     if(!cc) {
00380         cc = GetPlatformCryptoRaw();        
00381     }
00382     CPKIFAlgorithm * baseAlg = get_symmetric_alg_for_wrap_alg(kek);
00383     if(!baseAlg) {  
00384         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"UnwrapSymmKey called with an unimplemented wrap algorithm");
00385     }
00386     
00387     int bs = kwAlg->BlockSize();
00388     int cipherLen = key->GetLength();
00389     if( cipherLen < bs*3 && 0 != (cipherLen % bs)) {
00390         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,"UnwrapSymmKey called with invalid ciphertext");
00391     }
00392 
00393     CPKIFKeyMaterialPtr baseKM = CPKIFKeyMaterial::CreateWithSymmetricKey(kek);
00394     baseKM->SetSymmetricKeyAlgorithm(baseAlg->SymkeyAlg());
00395     baseKM->SetMode(baseAlg->SymkeyMode());
00396 
00397     // need to consider merging RawOperations and AlgorithmSupport!
00398     if(!cc /*|| !cc->SupportsAlgorithm(*baseKM)*/)
00399     {
00400         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,CRYPTO_ALG_NOT_SUPPORTED,
00401             "No colleague was specified and the default can't be used.");
00402     }
00403 
00404     int ivLen = 0;
00405     baseKM->GetIV(NULL,&ivLen);
00406 
00407     if(!ivLen) {
00408         baseKM->SetIV(rfc3394iv,rfc3394ivLen);
00409         ivLen = rfc3394ivLen;
00410     }
00411     
00412     scoped_array<unsigned char> iv(new unsigned char[ivLen]);
00413     baseKM->GetIV(iv.get(),&ivLen);
00414     if(ivLen != rfc3394ivLen) {
00415         throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,
00416             "An IV was supplied for AES Key wrap but the length was incorrect.");
00417     }
00418 
00419 
00420     rv = CPKIFKeyMaterialPtr(new CPKIFKeyMaterial());
00421 
00422     const unsigned char * ctBuf = key->GetBuffer();
00423     unsigned int n = cipherLen/bs;
00424     // R needs to be treated as potentially sensitive once Decrypt() is called for the first time.
00425     boost::scoped_array<boost::uint64_t> Rp(new boost::uint64_t[n]);
00426     boost::uint64_t * R = Rp.get();
00427     boost::uint64_t wbuf[2];
00428     boost::uint64_t t;
00429     memset(&t,0x00,sizeof(boost::uint64_t));
00430     int baseBS = baseAlg->BlockSize();
00431     int baseOut = baseBS;
00432     int outLen = cipherLen-bs;
00433     int nOut = outLen/bs;
00434 
00435     // this started as a naiive implementation of the steps from 3394, but I actually find
00436     // the NSS version easier to read, so this bears more resemblance to that than to
00437     // the pseudocode in the RFC.
00438     memcpy(&R[0],ctBuf,cipherLen);
00439     boost::uint64_t * A = &wbuf[0];
00440     *A = R[0];
00441     set_t((unsigned char *)&t,(unsigned long)6*nOut);
00442     try {   
00443         for(unsigned int j = 0; j <= 5; ++j)
00444         {
00445             for(unsigned int i = nOut; i != 0; --i)
00446             {
00447                 xor_and_decrement((unsigned char *)A,(unsigned char *)&t);
00448                 wbuf[1] = R[i]; //wbuf = A|R[i]
00449                 cc->Decrypt(*baseKM,(unsigned char *)wbuf,baseBS,
00450                     (unsigned char *)wbuf,&baseOut,false);
00451                 R[i] = wbuf[1];
00452             }
00453         }
00454         if(0 != memcmp(baseKM->GetIV(),A,bs)) {
00455             throw CPKIFCryptoException(TOOLKIT_CRYPTO_MISC,COMMON_INVALID_INPUT,
00456                 "Bad ciphertext.");
00457         }
00458         rv->SetSymmetricKey((unsigned char  *)(&R[1]),outLen);
00459         PKIFZero(wbuf,2*sizeof(boost::uint64_t));
00460         PKIFZero(R,cipherLen);
00461 
00462     }catch(...){
00463         // encrypt can throw, and I don't have a secure array pointer lying around
00464         // for an array of uint64_t
00465         PKIFZero(wbuf,2*sizeof(boost::uint64_t));
00466         PKIFZero(R,cipherLen);
00467         throw;
00468     }
00469 
00470     return rv;
00471 
00472 }
00473 
00474 // return the symmetric cipher that will be used internally for wrapping/unwrapping
00475 // given a key material pointer.
00476 static CPKIFAlgorithm * get_symmetric_alg_for_wrap_alg(const CPKIFKeyMaterialPtr & km)
00477 {
00478     CPKIFAlgorithm * baseAlg = 0;
00479     switch(km->GetSymmetricKeyAlgorithm())
00480     {
00481     case PKIFCRYPTO::AES128Wrap:
00482         baseAlg = CPKIFAlgorithm::GetAlg(PKIFCRYPTO::AES128);
00483         break;
00484     case PKIFCRYPTO::AES192Wrap:
00485         baseAlg = CPKIFAlgorithm::GetAlg(PKIFCRYPTO::AES192);
00486         break;
00487     case PKIFCRYPTO::AES256Wrap:
00488         baseAlg = CPKIFAlgorithm::GetAlg(PKIFCRYPTO::AES256);
00489         break;
00490     default:
00491         break;
00492     }
00493     return baseAlg;
00494 }
00495 
00496 // increment_and_xor, xor_and_decrement, and set_t are all taken verbatim from
00497 // the NSS freebl library. They're from security/nss/lib/freebl/aeskeywrap.c
00498 // which is a MPL/GPL/LGPL licensed.
00499 
00500 /* The AES Key Wrap algorithm has 64-bit values that are ALWAYS big-endian
00501 ** (Most significant byte first) in memory.  The only ALU operations done
00502 ** on them are increment, decrement, and XOR.  So, on little-endian CPUs,
00503 ** and on CPUs that lack 64-bit registers, these big-endian 64-bit operations
00504 ** are simulated in the following code.  This is thought to be faster and
00505 ** simpler than trying to convert the data to little-endian and back.
00506 */
00507 
00508 /* A and T point to two 64-bit values stored most signficant byte first
00509 ** (big endian).  This function increments the 64-bit value T, and then
00510 ** XORs it with A, changing A.
00511 */ 
00512 static void
00513 increment_and_xor(unsigned char *A, unsigned char *T)
00514 {
00515     if (!++T[7])
00516         if (!++T[6])
00517         if (!++T[5])
00518         if (!++T[4])
00519             if (!++T[3])
00520             if (!++T[2])
00521                 if (!++T[1])
00522                  ++T[0];
00523 
00524     A[0] ^= T[0];
00525     A[1] ^= T[1];
00526     A[2] ^= T[2];
00527     A[3] ^= T[3];
00528     A[4] ^= T[4];
00529     A[5] ^= T[5];
00530     A[6] ^= T[6];
00531     A[7] ^= T[7];
00532 }
00533 
00534 /* A and T point to two 64-bit values stored most signficant byte first
00535 ** (big endian).  This function XORs T with A, giving a new A, then 
00536 ** decrements the 64-bit value T.
00537 */ 
00538 static void
00539 xor_and_decrement(unsigned char *A, unsigned char *T)
00540 {
00541     A[0] ^= T[0];
00542     A[1] ^= T[1];
00543     A[2] ^= T[2];
00544     A[3] ^= T[3];
00545     A[4] ^= T[4];
00546     A[5] ^= T[5];
00547     A[6] ^= T[6];
00548     A[7] ^= T[7];
00549 
00550     if (!T[7]--)
00551         if (!T[6]--)
00552         if (!T[5]--)
00553         if (!T[4]--)
00554             if (!T[3]--)
00555             if (!T[2]--)
00556                 if (!T[1]--)
00557                  T[0]--;
00558 
00559 }
00560 
00561 /* Given an unsigned long t (in host byte order), store this value as a
00562 ** 64-bit big-endian value (MSB first) in *pt.
00563 */
00564 static void
00565 set_t(unsigned char *pt, unsigned long t)
00566 {
00567     pt[7] = (unsigned char)t; t >>= 8;
00568     pt[6] = (unsigned char)t; t >>= 8;
00569     pt[5] = (unsigned char)t; t >>= 8;
00570     pt[4] = (unsigned char)t; t >>= 8;
00571     pt[3] = (unsigned char)t; t >>= 8;
00572     pt[2] = (unsigned char)t; t >>= 8;
00573     pt[1] = (unsigned char)t; t >>= 8;
00574     pt[0] = (unsigned char)t;
00575 }

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