1068 lines
28 KiB
C++
1068 lines
28 KiB
C++
#include "global.hxx"
|
|
|
|
// crypto defs
|
|
#include <wincrypt.h>
|
|
#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<HMAC_K_PADSIZE/sizeof(DWORD); dwBlock++)
|
|
{
|
|
((DWORD*)rgbKipad)[dwBlock] ^= 0x36363636;
|
|
((DWORD*)rgbKopad)[dwBlock] ^= 0x5C5C5C5C;
|
|
}
|
|
|
|
|
|
// prepend Kipad to data, Hash to get H1
|
|
{
|
|
// do this inline, don't call MyPrimitiveSHA since it would require data copy
|
|
A_SHA_CTX sSHAHash;
|
|
|
|
A_SHAInit(&sSHAHash);
|
|
A_SHAUpdate(&sSHAHash, rgbKipad, HMAC_K_PADSIZE);
|
|
A_SHAUpdate(&sSHAHash, pbData, cbData);
|
|
|
|
// Finish off the hash
|
|
A_SHAFinal(&sSHAHash, sSHAHash.HashVal);
|
|
|
|
// prepend Kopad to H1, hash to get HMAC
|
|
CopyMemory(rgbHMACTmp, rgbKopad, HMAC_K_PADSIZE);
|
|
CopyMemory(rgbHMACTmp+HMAC_K_PADSIZE, sSHAHash.HashVal, A_SHA_DIGEST_LEN);
|
|
}
|
|
|
|
if (!FMyPrimitiveSHA(
|
|
rgbHMACTmp,
|
|
sizeof(rgbHMACTmp),
|
|
rgbHMAC))
|
|
goto Ret;
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
|
|
return fRet;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
CopyPassword(
|
|
BYTE *pbLocation,
|
|
LPCWSTR szPassword,
|
|
DWORD dwMaxBytes
|
|
)
|
|
{
|
|
DWORD i = 0;
|
|
DWORD cbWideChars = WSZ_BYTECOUNT(szPassword);
|
|
BYTE *pbWideChars = (BYTE *) szPassword;
|
|
|
|
while ((i<cbWideChars) && (i<dwMaxBytes))
|
|
{
|
|
pbLocation[i] = pbWideChars[i+1];
|
|
pbLocation[i+1] = pbWideChars[i];
|
|
i+=2;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+ --------------------------------------------------------------
|
|
// in NSCP's initial implementation of PFX020, this
|
|
// is the algorithm they used to derive a key from a password.
|
|
// We include it so we can interoperate.
|
|
BOOL NSCPDeriveKey(
|
|
LPCWSTR szPassword,
|
|
PBYTE pbPrivacySalt,
|
|
DWORD cbPrivacySalt,
|
|
int iPKCS5Iterations,
|
|
PBYTE pbPKCS5Salt,
|
|
DWORD cbPKCS5Salt,
|
|
PBYTE pbDerivedMaterial,
|
|
DWORD cbDerivedMaterial)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
BYTE rgbPKCS5Key[A_SHA_DIGEST_LEN];
|
|
|
|
DWORD cbVirtualPW = cbPrivacySalt + WSZ_BYTECOUNT(szPassword);
|
|
PBYTE pbVirtualPW = (PBYTE)SSAlloc(cbVirtualPW);
|
|
if (pbVirtualPW == NULL)
|
|
goto Ret;
|
|
|
|
// Virtual PW = (salt | szPW)
|
|
CopyMemory(pbVirtualPW, pbPrivacySalt, cbPrivacySalt);
|
|
CopyPassword(&pbVirtualPW[cbPrivacySalt], szPassword, WSZ_BYTECOUNT(szPassword));
|
|
|
|
// use PKCS#5 to generate initial bit stream (seed)
|
|
if (!PKCS5_GenKey(
|
|
iPKCS5Iterations,
|
|
pbVirtualPW, cbVirtualPW,
|
|
pbPKCS5Salt, cbPKCS5Salt,
|
|
rgbPKCS5Key))
|
|
goto Ret;
|
|
|
|
if (cbDerivedMaterial > 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<iIterations; iIter++)
|
|
{
|
|
// Tmp = Hash(D | I);
|
|
if (iIter==0)
|
|
{
|
|
A_SHAUpdate(&sSHAHash, rgDiversifier, sizeof(rgDiversifier));
|
|
A_SHAUpdate(&sSHAHash, rgSaltPwd, cbSaltPwd);
|
|
}
|
|
else
|
|
{
|
|
// rehash last output
|
|
A_SHAUpdate(&sSHAHash, &pbTmpBuf[i*A_SHA_DIGEST_LEN], A_SHA_DIGEST_LEN);
|
|
}
|
|
|
|
// spit iteration output to final buffer
|
|
A_SHAFinal(&sSHAHash, &pbTmpBuf[i*A_SHA_DIGEST_LEN]);
|
|
}
|
|
|
|
// concat A[x] | A[x] | ... and truncate to get 64 bytes
|
|
iCount = 0;
|
|
while (iCount+A_SHA_DIGEST_LEN <= sizeof(B)) {
|
|
CopyMemory(&B[iCount], &pbTmpBuf[i*A_SHA_DIGEST_LEN], A_SHA_DIGEST_LEN);
|
|
iCount += A_SHA_DIGEST_LEN;
|
|
}
|
|
CopyMemory(&B[iCount], &pbTmpBuf[i*A_SHA_DIGEST_LEN], sizeof(B) % A_SHA_DIGEST_LEN);
|
|
|
|
|
|
// modify I by setting Ij += (B + 1) (mod 2^512)
|
|
for (vBlocks = 0; vBlocks < cbSaltPwd; vBlocks += SHA_V_LENGTH) {
|
|
bCarry = 1;
|
|
for (iCount = SHA_V_LENGTH-1; iCount >= 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<iIterations; i++)
|
|
{
|
|
if (i == 0) {
|
|
if (!FMyPrimitiveSHA(
|
|
pbTmp, // in
|
|
cbTmp, // in
|
|
rgbPKCS5Key))
|
|
goto Ret;
|
|
|
|
}
|
|
else {
|
|
if (!FMyPrimitiveSHA(
|
|
rgbPKCS5Key, // in
|
|
A_SHA_DIGEST_LEN, // in
|
|
rgbPKCS5Key))
|
|
goto Ret;
|
|
}
|
|
}
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
SSFree(pbTmp);
|
|
return fRet;
|
|
}
|
|
|
|
//+ ---------------------------------------------------------------------
|
|
// the P_Hash algorithm from TLS that was used in NSCP's PFX020 version
|
|
// to derive a key from a password. It is included here for completeness.
|
|
|
|
// NSCP made some implementation errors when they coded this up; to interop,
|
|
// use the fNSCPInteropMode parameter. The real P_Hash algorithm is used
|
|
// when fNSCPInteropMode is FALSE.
|
|
BOOL P_Hash
|
|
(
|
|
PBYTE pbSecret,
|
|
DWORD cbSecret,
|
|
|
|
PBYTE pbSeed,
|
|
DWORD cbSeed,
|
|
|
|
PBYTE pbKeyOut, //Buffer to copy the result...
|
|
DWORD cbKeyOut, //# of bytes of key length they want as output.
|
|
|
|
BOOL fNSCPInteropMode
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
BYTE rgbDigest[A_SHA_DIGEST_LEN];
|
|
DWORD iKey;
|
|
|
|
PBYTE pbAofiDigest = (PBYTE)SSAlloc(cbSeed + A_SHA_DIGEST_LEN);
|
|
if (pbAofiDigest == NULL)
|
|
goto Ret;
|
|
|
|
ZeroMemory(pbAofiDigest, cbSeed+A_SHA_DIGEST_LEN);
|
|
|
|
// First, we define a data expansion function, P_hash(secret, data)
|
|
// which uses a single hash function to expand a secret and seed into
|
|
// an arbitrary quantity of output:
|
|
|
|
// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
|
|
// HMAC_hash(secret, A(2) + seed) +
|
|
// HMAC_hash(secret, A(3) + seed) + ...
|
|
|
|
// Where + indicates concatenation.
|
|
|
|
// A() is defined as:
|
|
// A(0) = seed
|
|
// A(i) = HMAC_hash(secret, A(i-1))
|
|
|
|
|
|
if (fNSCPInteropMode)
|
|
{
|
|
// NSCP interop mode: 7/7/97
|
|
// nscp leaves (A_SHA_DIGEST_LEN-cbSeed) bytes zeroed between
|
|
// the seed and the appended seed. For interop, do derivation this way
|
|
|
|
// Also, they use A(0) to derive key bytes, whereas TLS spec
|
|
// specifies to wait for A(1).
|
|
CopyMemory(pbAofiDigest, pbSeed, cbSeed);
|
|
}
|
|
else
|
|
{
|
|
// build A(1)
|
|
if (!FMyPrimitiveHMACParam(pbSecret, cbSecret, pbSeed, cbSeed, pbAofiDigest))
|
|
goto Ret;
|
|
}
|
|
|
|
|
|
// create Aofi: ( A(i) | seed )
|
|
CopyMemory(&pbAofiDigest[A_SHA_DIGEST_LEN], pbSeed, cbSeed);
|
|
|
|
for (iKey=0; cbKeyOut; iKey++)
|
|
{
|
|
// build Digest = HMAC(key | A(i) | seed);
|
|
if (!FMyPrimitiveHMACParam(pbSecret, cbSecret, pbAofiDigest, cbSeed + A_SHA_DIGEST_LEN, rgbDigest))
|
|
goto Ret;
|
|
|
|
// append to pbKeyOut
|
|
CopyMemory(pbKeyOut, rgbDigest, A_SHA_DIGEST_LEN);
|
|
pbKeyOut += A_SHA_DIGEST_LEN;
|
|
|
|
if(cbKeyOut < A_SHA_DIGEST_LEN)
|
|
break;
|
|
cbKeyOut -= A_SHA_DIGEST_LEN;
|
|
|
|
// build A(i) = HMAC(key, A(i-1))
|
|
if (!FMyPrimitiveHMACParam(pbSecret, cbSecret, pbAofiDigest, A_SHA_DIGEST_LEN, pbAofiDigest))
|
|
goto Ret;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
|
|
Ret:
|
|
if (pbAofiDigest)
|
|
SSFree(pbAofiDigest);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
|
|
// test vector for real P_Hash
|
|
BOOL FTestPHASH_and_HMAC()
|
|
{
|
|
BYTE rgbKey[] = {0x33, 0x62, 0xf9, 0x42, 0x43};
|
|
CHAR szPwd[] = "My Password";
|
|
|
|
BYTE rgbKeyOut[7*A_SHA_DIGEST_LEN];
|
|
static BYTE rgbTestVectorOutput[] = {
|
|
0x24, 0xF2, 0x98, 0x75, 0xE1, 0x90, 0x6D, 0x49,
|
|
0x96, 0x5B, 0x87, 0xB8, 0xBC, 0xD3, 0x11, 0x6C,
|
|
0x13, 0xDC, 0xBD, 0xC2, 0x7E, 0x56, 0xD0, 0x3C,
|
|
0xAC, 0xCD, 0x86, 0x58, 0x31, 0x67, 0x7B, 0x23,
|
|
0x19, 0x6E, 0x36, 0x65, 0xBF, 0x9F, 0x3D, 0x03,
|
|
0x5A, 0x9C, 0x6E, 0xD7, 0xEB, 0x3E, 0x5A, 0xE6,
|
|
0x05, 0x86, 0x84, 0x5A, 0xC3, 0x97, 0xFC, 0x17,
|
|
0xF5, 0xF0, 0xF5, 0x16, 0x67, 0xAD, 0x7C, 0xED,
|
|
0x65, 0xDC, 0x0B, 0x99, 0x58, 0x5D, 0xCA, 0x66,
|
|
0x28, 0xAD, 0xA5, 0x39, 0x54, 0x44, 0x36, 0x13,
|
|
0x91, 0xCE, 0xE9, 0x73, 0x23, 0x43, 0x2E, 0xEC,
|
|
0xA2, 0xC3, 0xE7, 0xFA, 0x74, 0xA7, 0xB6, 0x75,
|
|
0x77, 0xF5, 0xF5, 0x16, 0xC2, 0xEE, 0xED, 0x7A,
|
|
0x21, 0x86, 0x1D, 0x84, 0x6F, 0xC6, 0x03, 0xF3,
|
|
0xCC, 0x77, 0x02, 0xFA, 0x76, 0x46, 0x64, 0x57,
|
|
0xBB, 0x56, 0x3A, 0xF7, 0x7E, 0xB4, 0xD6, 0x52,
|
|
0x72, 0x8C, 0x34, 0xF1, 0xA4, 0x1E, 0xA7, 0xA6,
|
|
0xCD, 0xBD, 0x3C, 0x16, 0x4D, 0x79, 0x20, 0x50 };
|
|
|
|
P_Hash(
|
|
rgbKey, sizeof(rgbKey),
|
|
(PBYTE)szPwd, strlen(szPwd),
|
|
rgbKeyOut, sizeof(rgbKeyOut), FALSE);
|
|
|
|
if (0 != memcmp(rgbKeyOut, rgbTestVectorOutput, sizeof(rgbKeyOut)) )
|
|
{
|
|
OutputDebugString("ERROR: phash vector test invalid!!!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// test vector for NSCP P_Hash
|
|
BOOL F_NSCP_TestPHASH_and_HMAC()
|
|
{
|
|
BYTE rgbKey[] = { 0xc9, 0xc1, 0x69, 0x6e, 0x30, 0xa8, 0x91, 0x0d,
|
|
0x12, 0x19, 0x48, 0xef, 0x23, 0xac, 0x5b, 0x1f,
|
|
0x2e, 0xc4, 0x0e, 0xc2 };
|
|
|
|
BYTE rgbSalt[] = { 0x1a, 0xb5, 0xf1, 0x1a, 0x5b, 0x6a, 0x6a, 0x5e };
|
|
|
|
BYTE rgbKeyOut[7*A_SHA_DIGEST_LEN];
|
|
static BYTE rgbTestVectorOutput[] = {
|
|
0x52, 0x7c, 0xbf, 0x90, 0xb1, 0xa1, 0xd0, 0xbf,
|
|
0x21, 0x56, 0x34, 0xf2, 0x1f, 0x5c, 0x98, 0xcf,
|
|
0x55, 0x95, 0xb1, 0x35, 0x65, 0xe3, 0x31, 0x44,
|
|
0x78, 0xc5, 0x41, 0xa9, 0x2a, 0x14, 0x80, 0x19,
|
|
0x56, 0x86, 0xa4, 0x71, 0x07, 0x24, 0x2d, 0x64 };
|
|
|
|
assert(sizeof(rgbKeyOut) > 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
|