#include "global.hxx" // crypto defs #include #include "randlib.h" #include "pfxhelp.h" #include "pfxcmn.h" #include "pfxcrypt.h" #include "sha.h" #include "shacomm.h" #include "rc2.h" #include "modes.h" #include "des.h" #include "tripldes.h" // constants used in PKCS5-like key derivation #define DERIVE_ENCRYPT_DECRYPT 0x1 #define DERIVE_INITIAL_VECTOR 0x2 #define DERIVE_INTEGRITY_KEY 0x3 #define HMAC_K_PADSIZE 64 BOOL FMyPrimitiveSHA( PBYTE pbData, DWORD cbData, BYTE rgbHash[A_SHA_DIGEST_LEN]) { BOOL fRet = FALSE; A_SHA_CTX sSHAHash; A_SHAInit(&sSHAHash); A_SHAUpdate(&sSHAHash, (BYTE *) pbData, cbData); A_SHAFinal(&sSHAHash, rgbHash); fRet = TRUE; //Ret: return fRet; } BOOL FMyPrimitiveHMACParam( PBYTE pbKeyMaterial, DWORD cbKeyMaterial, PBYTE pbData, DWORD cbData, BYTE rgbHMAC[A_SHA_DIGEST_LEN]) { BOOL fRet = FALSE; BYTE rgbKipad[HMAC_K_PADSIZE]; BYTE rgbKopad[HMAC_K_PADSIZE]; // truncate if (cbKeyMaterial > HMAC_K_PADSIZE) cbKeyMaterial = HMAC_K_PADSIZE; ZeroMemory(rgbKipad, HMAC_K_PADSIZE); CopyMemory(rgbKipad, pbKeyMaterial, cbKeyMaterial); ZeroMemory(rgbKopad, HMAC_K_PADSIZE); CopyMemory(rgbKopad, pbKeyMaterial, cbKeyMaterial); BYTE rgbHMACTmp[HMAC_K_PADSIZE+A_SHA_DIGEST_LEN]; // assert we're a multiple assert( (HMAC_K_PADSIZE % sizeof(DWORD)) == 0); // Kipad, Kopad are padded sMacKey. Now XOR across... for(DWORD dwBlock=0; dwBlock sizeof(rgbPKCS5Key)) { // P_hash (secret, seed) = HMAC_hash (secret, A(0) + seed), // HMAC_hash (secret, A(1) + seed), // HMAC_hash (secret, A(2) + seed), // HMAC_hash (secret, A(3) + seed) ... // where // A(0) = seed // A(i) = HMAC_hash(secret, A(i-1)) // seed = PKCS5 salt for PKCS5 PBE param // secret = normal PKCS5 hashed key if (!P_Hash ( rgbPKCS5Key, sizeof(rgbPKCS5Key), pbPKCS5Salt, cbPKCS5Salt, pbDerivedMaterial, // output cbDerivedMaterial, // # of output bytes requested TRUE) ) // NSCP compat mode? goto Ret; } else { // we already have enough bits to satisfy the request CopyMemory(pbDerivedMaterial, rgbPKCS5Key, cbDerivedMaterial); } fRet = TRUE; Ret: if (pbVirtualPW) SSFree(pbVirtualPW); return fRet; } static BYTE AddWithCarry( BYTE byte1, BYTE byte2, BYTE *carry // IN and OUT ) { BYTE tempCarry = *carry; if (((DWORD)byte1 + (DWORD)byte2 + (DWORD)tempCarry) >= 256) { *carry = 1; } else { *carry = 0; } return (byte1 + byte2 + tempCarry); } // 512 bits = ? bytes #define SHA_INTERNAL_BLOCKLEN (512/8) #define SHA_V_LENGTH (512/8) //+ -------------------------------------------------------------- // In PKCS12 v1.0 Draft, this is the way they describe to // derive a key from a password. BOOL PKCS12DeriveKey( LPCWSTR szPassword, BYTE bID, int iIterations, PBYTE pbSalt, DWORD cbSalt, PBYTE pbDerivedMaterial, DWORD cbDerivedMaterial) { #if DBG if (iIterations>1) OutputDebugString("Perf hit: iterating key derivation! (pfxcrypt:PKCS12DeriveKey())\n"); #endif BOOL fRet = FALSE; BYTE rgSaltPwd[2*SHA_INTERNAL_BLOCKLEN]; DWORD cbSaltPwd; BYTE rgDiversifier[SHA_INTERNAL_BLOCKLEN]; BYTE B[SHA_V_LENGTH]; DWORD i; DWORD cbPassword = WSZ_BYTECOUNT(szPassword); BYTE bCarry; DWORD vBlocks; A_SHA_CTX sSHAHash; // construct D FillMemory(rgDiversifier, sizeof(rgDiversifier), bID); // concat salt to create string of length 64*(cb/64) bytes // copy salt (multiple) times, don't copy the last time for (i=0; i<(SHA_INTERNAL_BLOCKLEN-cbSalt); i+=cbSalt) { CopyMemory(&rgSaltPwd[i], pbSalt, cbSalt); } // do final copy (assert we have less than cbSalt bytes left to copy) assert(cbSalt >= (SHA_INTERNAL_BLOCKLEN - (i%SHA_INTERNAL_BLOCKLEN)) ); CopyMemory(&rgSaltPwd[i], pbSalt, (SHA_INTERNAL_BLOCKLEN-(i%SHA_INTERNAL_BLOCKLEN))); // if the password is not NULL, concat pwd to create string of length 64*(cbPwd/64) bytes // copy pwd (multiple) times, don't copy the last time if (szPassword) { // truncate if necessary if (cbPassword > SHA_INTERNAL_BLOCKLEN) cbPassword = SHA_INTERNAL_BLOCKLEN; for (i=SHA_INTERNAL_BLOCKLEN; i<( (2*SHA_INTERNAL_BLOCKLEN)-cbPassword); i+=cbPassword) { // use CopyPassword because bytes need to be swapped CopyPassword(&rgSaltPwd[i], szPassword, cbPassword); } // do final copy (assert we have less than cbSalt bytes left to copy) assert(cbPassword >= (SHA_INTERNAL_BLOCKLEN - (i%SHA_INTERNAL_BLOCKLEN)) ); CopyPassword(&rgSaltPwd[i], szPassword, (SHA_INTERNAL_BLOCKLEN-(i%SHA_INTERNAL_BLOCKLEN))); cbSaltPwd = sizeof(rgSaltPwd); } else { cbSaltPwd = sizeof(rgSaltPwd) / 2; } // concat S|P // done, available in rgSaltPwd // set c = cbDerivedMaterial/A_SHA_DIGEST_LEN //assert(0 == cbDerivedMaterial%A_SHA_DIGEST_LEN); // compute working size >= output size DWORD cBlocks = (DWORD)((cbDerivedMaterial/A_SHA_DIGEST_LEN) +1); DWORD cbTmpBuf = cBlocks * A_SHA_DIGEST_LEN; PBYTE pbTmpBuf = (PBYTE)LocalAlloc(LPTR, cbTmpBuf); if (pbTmpBuf == NULL) goto Ret; // now do only full blocks for (i=0; i< cBlocks; i++) { int iIter; int iCount; A_SHAInit(&sSHAHash); for (iIter=0; iIter= 0; iCount--) { rgSaltPwd[iCount+vBlocks] = AddWithCarry(rgSaltPwd[iCount+vBlocks], B[iCount], &bCarry); } } } // copy from (larger) working buffer to output buffer CopyMemory(pbDerivedMaterial, pbTmpBuf, cbDerivedMaterial); fRet = TRUE; Ret: if (pbTmpBuf) LocalFree(pbTmpBuf); return fRet; } //+ -------------------------------------------------------------- // in NSCP's initial implementation of PFX020, this // is the algorithm they used to decrypt data. This uses the // key derivation code above. // We include it so we can interoperate. BOOL NSCPPasswordDecryptData( int iEncrType, LPCWSTR szPassword, PBYTE pbPrivacySalt, // privacy salt DWORD cbPrivacySalt, int iPKCS5Iterations, // pkcs5 data PBYTE pbPKCS5Salt, DWORD cbPKCS5Salt, PBYTE* ppbData, // in/out DWORD* pcbData) { BOOL fRet = FALSE; BYTE rgbDerivedKeyMatl[40]; // 320 bits is enough for 128 bit key, 64 bit IV DWORD cbNeeded; if (iEncrType == RC2_40) cbNeeded = (40/8)+RC2_BLOCKLEN; // key + IV else cbNeeded = 0; // make next muliple of SHA dig len if (cbNeeded % A_SHA_DIGEST_LEN) { cbNeeded += (A_SHA_DIGEST_LEN - (cbNeeded % A_SHA_DIGEST_LEN)); } assert(0 == (cbNeeded % A_SHA_DIGEST_LEN)); assert(cbNeeded <= sizeof(rgbDerivedKeyMatl)); if (!NSCPDeriveKey( szPassword, pbPrivacySalt, cbPrivacySalt, iPKCS5Iterations, pbPKCS5Salt, cbPKCS5Salt, rgbDerivedKeyMatl, cbNeeded) ) goto Ret; // NOW decrypt data if (iEncrType == RC2_40) { DWORD dwDataPos; DWORD cbToBeDec = *pcbData; WORD rc2Table[RC2_TABLESIZE]; BYTE rc2Fdbk [RC2_BLOCKLEN]; assert( (40/8) <= sizeof(rgbDerivedKeyMatl)); assert( 0 == cbToBeDec % RC2_BLOCKLEN ); // must be even multiple // key setup RC2Key(rc2Table, rgbDerivedKeyMatl, (40/8)); // take first 40 bits of keying material CopyMemory(rc2Fdbk, &rgbDerivedKeyMatl[cbNeeded - sizeof(rc2Fdbk)], sizeof(rc2Fdbk)); // fdbk is last chunk // decryption for (dwDataPos=0; cbToBeDec > 0; dwDataPos+=RC2_BLOCKLEN, cbToBeDec -= RC2_BLOCKLEN) { BYTE rgbDec[RC2_BLOCKLEN]; CBC( RC2, RC2_BLOCKLEN, rgbDec, &(*ppbData)[dwDataPos], rc2Table, DECRYPT, rc2Fdbk); CopyMemory(&(*ppbData)[dwDataPos], rgbDec, RC2_BLOCKLEN); } } else goto Ret; fRet = TRUE; Ret: return fRet; } //+ -------------------------------------------------------------- // in the PKCS12 v1.0 Draft, this is how they describe how to // encrypt data. BOOL PFXPasswordEncryptData( int iEncrType, LPCWSTR szPassword, int iPKCS5Iterations, // pkcs5 data PBYTE pbPKCS5Salt, DWORD cbPKCS5Salt, PBYTE* ppbData, DWORD* pcbData) { BOOL fRet = FALSE; BOOL fIsBlockCipher = FALSE; DWORD cbToBeEnc; BYTE rgbDerivedKey[A_SHA_DIGEST_LEN*2]; // 320 bits is enough for 256 bit key BYTE rgbDerivedIV[A_SHA_DIGEST_LEN*2]; // 320 bits is enough for 256 bit IV DWORD cbKeyNeeded, cbIVNeeded, cbBlockLen; if (iEncrType == RC2_40) { cbKeyNeeded = (40/8); // key cbIVNeeded = RC2_BLOCKLEN; // IV cbBlockLen = RC2_BLOCKLEN; fIsBlockCipher = TRUE; } else if (iEncrType == TripleDES) { cbKeyNeeded = (64/8) * 3; cbIVNeeded = DES_BLOCKLEN; cbBlockLen = DES_BLOCKLEN; fIsBlockCipher = TRUE; } else { cbKeyNeeded = 0; cbIVNeeded = 0; cbBlockLen = 0; } // make next muliple of SHA dig len if (cbKeyNeeded % A_SHA_DIGEST_LEN) cbKeyNeeded += (A_SHA_DIGEST_LEN - (cbKeyNeeded % A_SHA_DIGEST_LEN)); if (cbIVNeeded % A_SHA_DIGEST_LEN) cbIVNeeded += (A_SHA_DIGEST_LEN - (cbIVNeeded % A_SHA_DIGEST_LEN)); assert(0 == (cbKeyNeeded % A_SHA_DIGEST_LEN)); assert(0 == (cbIVNeeded % A_SHA_DIGEST_LEN)); assert(cbKeyNeeded <= sizeof(rgbDerivedKey)); assert(cbIVNeeded <= sizeof(rgbDerivedIV)); if (!PKCS12DeriveKey( szPassword, DERIVE_ENCRYPT_DECRYPT, iPKCS5Iterations, pbPKCS5Salt, cbPKCS5Salt, rgbDerivedKey, cbKeyNeeded) ) goto Ret; if (!PKCS12DeriveKey( szPassword, DERIVE_INITIAL_VECTOR, iPKCS5Iterations, pbPKCS5Salt, cbPKCS5Salt, rgbDerivedIV, cbIVNeeded) ) goto Ret; if (fIsBlockCipher) { // extend buffer to multiple of blocklen cbToBeEnc = *pcbData; cbToBeEnc += cbBlockLen - (cbToBeEnc%cbBlockLen); // {1..BLOCKLEN} *ppbData = (PBYTE)SSReAlloc(*ppbData, cbToBeEnc); if (NULL == *ppbData) goto Ret; // pad remaining bytes with length FillMemory(&((*ppbData)[*pcbData]), cbToBeEnc-(*pcbData), (BYTE)(cbToBeEnc-(*pcbData))); *pcbData = cbToBeEnc; assert( cbBlockLen <= sizeof(rgbDerivedKey)); assert( 0 == cbToBeEnc % cbBlockLen ); // must be even multiple } // NOW encrypt data if (iEncrType == RC2_40) { DWORD dwDataPos; WORD rc2Table[RC2_TABLESIZE]; BYTE rc2Fdbk [RC2_BLOCKLEN]; // already done: extend buffer, add PKCS byte padding // key setup RC2Key(rc2Table, rgbDerivedKey, (40/8)); // take first 40 bits of keying material CopyMemory(rc2Fdbk, rgbDerivedIV, sizeof(rc2Fdbk)); // decryption for (dwDataPos=0; cbToBeEnc > 0; dwDataPos+=RC2_BLOCKLEN, cbToBeEnc -= RC2_BLOCKLEN) { BYTE rgbEnc[RC2_BLOCKLEN]; CBC( RC2, RC2_BLOCKLEN, rgbEnc, &(*ppbData)[dwDataPos], rc2Table, ENCRYPT, rc2Fdbk); CopyMemory(&(*ppbData)[dwDataPos], rgbEnc, sizeof(rgbEnc)); } } else if (iEncrType == TripleDES) { DWORD dwDataPos; DES3TABLE des3Table; BYTE des3Fdbk [DES_BLOCKLEN]; // already done: extend buffer, add PKCS byte padding // key setup tripledes3key(&des3Table, rgbDerivedKey); CopyMemory(des3Fdbk, rgbDerivedIV, sizeof(des3Fdbk)); // fdbk is last chunk for (dwDataPos=0; cbToBeEnc > 0; dwDataPos+=DES_BLOCKLEN, cbToBeEnc -= DES_BLOCKLEN) { BYTE rgbEnc[DES_BLOCKLEN]; CBC( tripledes, DES_BLOCKLEN, rgbEnc, &(*ppbData)[dwDataPos], (void *) &des3Table, ENCRYPT, des3Fdbk); CopyMemory(&(*ppbData)[dwDataPos], rgbEnc, DES_BLOCKLEN); } } else goto Ret; fRet = TRUE; Ret: return fRet; } //+ -------------------------------------------------------------- // in the PKCS12 v1.0 Draft, this is how they describe how to // decrypt data. BOOL PFXPasswordDecryptData( int iEncrType, LPCWSTR szPassword, int iPKCS5Iterations, // pkcs5 data PBYTE pbPKCS5Salt, DWORD cbPKCS5Salt, PBYTE* ppbData, DWORD* pcbData) { BOOL fRet = FALSE; BOOL fIsBlockCipher = FALSE; BYTE rgbDerivedKey[A_SHA_DIGEST_LEN*2]; // 320 bits is enough for 256 bit key BYTE rgbDerivedIV[A_SHA_DIGEST_LEN*2]; // 320 bits is enough for 256 bit IV DWORD cbKeyNeeded, cbIVNeeded, cbBlockLen; if (iEncrType == RC2_40) { cbKeyNeeded = (40/8); // key cbIVNeeded = RC2_BLOCKLEN; // IV cbBlockLen = RC2_BLOCKLEN; fIsBlockCipher = TRUE; } else if (iEncrType == TripleDES) { cbKeyNeeded = (64/8) * 3; cbIVNeeded = DES_BLOCKLEN; cbBlockLen = DES_BLOCKLEN; fIsBlockCipher = TRUE; } else { cbKeyNeeded = 0; cbIVNeeded = 0; cbBlockLen = 0; } // make next muliple of SHA dig len if (cbKeyNeeded % A_SHA_DIGEST_LEN) cbKeyNeeded += (A_SHA_DIGEST_LEN - (cbKeyNeeded % A_SHA_DIGEST_LEN)); if (cbIVNeeded % A_SHA_DIGEST_LEN) cbIVNeeded += (A_SHA_DIGEST_LEN - (cbIVNeeded % A_SHA_DIGEST_LEN)); assert(0 == (cbKeyNeeded % A_SHA_DIGEST_LEN)); assert(0 == (cbIVNeeded % A_SHA_DIGEST_LEN)); assert(cbKeyNeeded <= sizeof(rgbDerivedKey)); assert(cbIVNeeded <= sizeof(rgbDerivedIV)); if (!PKCS12DeriveKey( szPassword, DERIVE_ENCRYPT_DECRYPT, iPKCS5Iterations, pbPKCS5Salt, cbPKCS5Salt, rgbDerivedKey, cbKeyNeeded) ) goto Ret; if (!PKCS12DeriveKey( szPassword, DERIVE_INITIAL_VECTOR, iPKCS5Iterations, pbPKCS5Salt, cbPKCS5Salt, rgbDerivedIV, cbIVNeeded) ) goto Ret; // NOW decrypt data if (iEncrType == RC2_40) { BYTE rgbDec[RC2_BLOCKLEN]; DWORD dwDataPos; DWORD cbToBeDec = *pcbData; WORD rc2Table[RC2_TABLESIZE]; BYTE rc2Fdbk [RC2_BLOCKLEN]; assert( (40/8) <= sizeof(rgbDerivedKey)); assert( 0 == cbToBeDec % RC2_BLOCKLEN ); // must be even multiple // key setup RC2Key(rc2Table, rgbDerivedKey, (40/8)); // take first 40 bits of keying material CopyMemory(rc2Fdbk, rgbDerivedIV, sizeof(rc2Fdbk)); // decryption for (dwDataPos=0; cbToBeDec > 0; dwDataPos+=RC2_BLOCKLEN, cbToBeDec -= RC2_BLOCKLEN) { CBC( RC2, RC2_BLOCKLEN, rgbDec, &(*ppbData)[dwDataPos], rc2Table, DECRYPT, rc2Fdbk); CopyMemory(&(*ppbData)[dwDataPos], rgbDec, sizeof(rgbDec)); } } else if (iEncrType == TripleDES) { DWORD dwDataPos; DWORD cbToBeDec = *pcbData; DES3TABLE des3Table; BYTE des3Fdbk [DES_BLOCKLEN]; // key setup tripledes3key(&des3Table, rgbDerivedKey); CopyMemory(des3Fdbk, rgbDerivedIV, sizeof(des3Fdbk)); // fdbk is last chunk for (dwDataPos=0; cbToBeDec > 0; dwDataPos += DES_BLOCKLEN, cbToBeDec -= DES_BLOCKLEN) { BYTE rgbDec[DES_BLOCKLEN]; CBC( tripledes, DES_BLOCKLEN, rgbDec, &(*ppbData)[dwDataPos], (void *) &des3Table, DECRYPT, des3Fdbk); CopyMemory(&(*ppbData)[dwDataPos], rgbDec, DES_BLOCKLEN); } } else goto Ret; // Remove padding if (fIsBlockCipher) { // last byte of decr is pad byte BYTE iPadBytes; iPadBytes = (*ppbData)[*pcbData-1]; if (iPadBytes > cbBlockLen) goto Ret; *ppbData = (PBYTE)SSReAlloc( (*ppbData), *pcbData - iPadBytes); if (NULL == *ppbData) goto Ret; *pcbData -= iPadBytes; } fRet = TRUE; Ret: return fRet; } //+ -------------------------------------------------------------- // in the PKCS12 v1.0 Draft, this is how they describe how to // generate a checksum that will prove data integrid. BOOL FGenerateMAC( LPCWSTR szPassword, PBYTE pbPKCS5Salt, DWORD cbPKCS5Salt, DWORD iterationCount, PBYTE pbData, // pb data DWORD cbData, // cb data BYTE rgbMAC[]) // output { // UNDONE UNDONE: Use RSABase BOOL fRet = FALSE; BYTE rgbDerivedKey[A_SHA_DIGEST_LEN]; // 160 bits is enough for a MAC key DWORD cbKeyNeeded = A_SHA_DIGEST_LEN; assert(0 == (cbKeyNeeded % A_SHA_DIGEST_LEN)); assert(cbKeyNeeded <= sizeof(rgbDerivedKey)); if (!PKCS12DeriveKey( szPassword, DERIVE_INTEGRITY_KEY, iterationCount, // no other way to determine iterations: HARDCODE pbPKCS5Salt, cbPKCS5Salt, rgbDerivedKey, cbKeyNeeded) ) goto Ret; if (!FMyPrimitiveHMACParam( rgbDerivedKey, cbKeyNeeded, pbData, cbData, rgbMAC)) goto Ret; fRet = TRUE; Ret: return fRet; } ///////////////////////////////////////////////////////////////// // begin tls1key.cpp /*----------------------------------------------------------------------------- * Copyright (C) Microsoft Corporation, 1995 - 1999 * All rights reserved. *----------------------------------------------------------------------------*/ // the original PKCS5 algorithm for generating a key from a password BOOL PKCS5_GenKey ( int iIterations, PBYTE pbPW, DWORD cbPW, PBYTE pbSalt, DWORD cbSalt, BYTE rgbPKCS5Key[A_SHA_DIGEST_LEN] ) { BOOL fRet = FALSE; int i; DWORD cbTmp = cbSalt + cbPW; PBYTE pbTmp = (PBYTE) SSAlloc(cbTmp); if (pbTmp == NULL) goto Ret; // pbTmp is ( PW | Salt ) CopyMemory(pbTmp, pbPW, cbPW); CopyMemory(&pbTmp[cbPW], pbSalt, cbSalt); for (i=0; i sizeof(rgbTestVectorOutput)); P_Hash( rgbKey, sizeof(rgbKey), rgbSalt, sizeof(rgbSalt), rgbKeyOut, sizeof(rgbTestVectorOutput), TRUE); if (0 != memcmp(rgbKeyOut, rgbTestVectorOutput, sizeof(rgbTestVectorOutput)) ) { OutputDebugString("ERROR: NSCP phash vector test invalid!!!\n"); return FALSE; } return TRUE; } #endif // DBG