windows-nt/Source/XPSP1/NT/ds/security/cryptoapi/pki/pfxlib/pfxcrypt.cpp
2020-09-26 16:20:57 +08:00

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