Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

510 lines
14 KiB

/*
File: primitiv.cpp
Title: Cryptographic Primitives for Protected Storage
Author: Matt Thomlinson
Date: 11/22/96
With the help of the rsa32 library, this module manufactures
actually _usable_ primitives.
Since CryptoAPI base provider may call us, we can't use
CryptXXX primitives (circular dependencies may result).
Functions in this module:
FMyMakeDESKey
Given key material, does necessary DES key setup. Has the
side effect of stashing the keying material in the DESKey
structure.
FMyPrimitiveSHA
Given pbData/cbData, runs SHA1 Init-Update-Final and returns
the hash buffer.
FMyPrimitiveDESEncrypt
Given a ppbBlock/pcbBlock/DESKey structure, DES CBC (with an
IV of 0)encrypts the block with the DESKey passed in. DESKey
must be passed in with key setup already done. Note that this
call might realloc ppbBlock since the encrypted length must be
a multiple of the blocksize.
FMyPrimitiveDESDecrypt
Given a pbBlock/pcbBlock/DESKey structure, decrypts the
block with the given (prepared) DESKey. Note that pbBlock
will not be realloced, since the output buffer is always
smaller than or the same size as the encrypted buffer.
FMyPrimitiveDeriveKey
Derives a DES key from multiple input buffers in
the following manner:
FMyMakeDESKey( SHA1(Salt | OtherData) )
FMyOldPrimitiveHMAC (non-interoperable, buggy version of HMAC)
FMyPrimitiveHMAC
Derives a quality HMAC (Keyed message-authentication code) for
passed-in data and prepared DESKey. The HMAC is computed in
the following (standard HMAC) manner:
KoPad = KiPad = DESKey key setup buffer
XOR(KoPad, 0x5c5c5c5c)
XOR(KiPad, 0x36363636)
HMAC = SHA1(KoPad | SHA1(KiPad | Data))
*/
#include <windows.h>
// crypto defs
#include <wincrypt.h>
#include "sha.h"
#include "des.h"
#include "modes.h"
#include "randlib.h"
// others
#include "primitiv.h"
#include "debug.h"
#define OLD_MAC_K_PADSIZE 16
#define HMAC_K_PADSIZE 64
BOOL FMyMakeDESKey(
PDESKEY pDESKey,
BYTE* pbKeyMaterial)
{
CopyMemory(pDESKey->rgbKey, pbKeyMaterial, DES_BLOCKLEN);
// assumes pbKeyMaterial is at least DES_BLOCKLEN bytes
deskey(&pDESKey->sKeyTable, pbKeyMaterial); // takes material, runs DES table setup
return TRUE;
}
BOOL FMyPrimitiveSHA(
PBYTE pbData,
DWORD cbData,
BYTE rgbHash[A_SHA_DIGEST_LEN])
{
A_SHA_CTX sSHAHash;
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, (BYTE *) pbData, cbData);
A_SHAFinal(&sSHAHash, rgbHash);
return TRUE;
}
BOOL FMyPrimitiveDESDecrypt(
PBYTE pbBlock, // in out
DWORD *pcbBlock, // in out
DESKEY sDESKey) // in
{
BOOL fRet = FALSE;
DWORD dwDataLen = *pcbBlock;
DWORD i;
if (dwDataLen % DES_BLOCKLEN)
{
SetLastError((DWORD) NTE_BAD_DATA);
return FALSE;
}
BYTE rgbBuf[DES_BLOCKLEN];
BYTE rgbFeedBack[DES_BLOCKLEN];
ZeroMemory(rgbFeedBack, DES_BLOCKLEN);
// pump the data through the decryption, including padding
// NOTE: the total length is a multiple of BlockLen
for (DWORD BytePos = 0; (BytePos + DES_BLOCKLEN) <= dwDataLen; BytePos += DES_BLOCKLEN)
{
// put the encrypted text into a temp buffer
CopyMemory(rgbBuf, pbBlock + BytePos, DES_BLOCKLEN);
CBC(des, DES_BLOCKLEN, pbBlock + BytePos, rgbBuf, &sDESKey.sKeyTable,
DECRYPT, rgbFeedBack);
}
// ## NOTE: if the pad is wrong, the user's buffer is hosed, because
// ## we've decrypted into the user's buffer -- can we re-encrypt it?
//
// if dwPadVal is wrong, we've failed to decrypt correctly. Bad key?
//
DWORD dwPadVal = (DWORD) *(pbBlock + dwDataLen - 1);
if (dwPadVal == 0 || dwPadVal > DES_BLOCKLEN)
{
SetLastError((DWORD) NTE_BAD_DATA);
goto Ret;
}
// Make sure all the (rest of the) pad bytes are correct.
for (i=1; i<dwPadVal; i++)
{
if ((pbBlock)[dwDataLen - (i + 1)] != dwPadVal)
{
SetLastError((DWORD) NTE_BAD_DATA);
goto Ret;
}
}
// update the length
*pcbBlock -= dwPadVal;
fRet = TRUE;
Ret:
return fRet;
}
BOOL FMyPrimitiveDESEncrypt(
PBYTE* ppbBlock, // in out
DWORD *pcbBlock, // in out
DESKEY sDESKey) // in
{
BOOL fRet = FALSE;
DWORD dwDataLen = *pcbBlock;
PBYTE pTemp;
DWORD cbPartial = (*pcbBlock % DES_BLOCKLEN);
DWORD dwPadVal = DES_BLOCKLEN - cbPartial;
if (dwPadVal != 0)
{
*pcbBlock += dwPadVal;
pTemp = (PBYTE)SSReAlloc(*ppbBlock, *pcbBlock);
if (NULL == pTemp) {
return FALSE;
}
*ppbBlock = pTemp;
}
// now we're a multiple of DES_BLOCKLEN
SS_ASSERT((*pcbBlock % DES_BLOCKLEN) == 0);
if (dwPadVal)
{
// Fill the pad with a value equal to the
// length of the padding, so decrypt will
// know the length of the original data
// and as a simple integrity check.
FillMemory(*ppbBlock + dwDataLen, (int)dwPadVal, (size_t)dwPadVal);
}
// allocate memory for a temporary buffer
BYTE rgbBuf[DES_BLOCKLEN];
BYTE rgbFeedBack[DES_BLOCKLEN];
ZeroMemory(rgbFeedBack, DES_BLOCKLEN);
PBYTE pbData = *ppbBlock;
// pump the full blocks of data through
for (dwDataLen = *pcbBlock; dwDataLen>0; dwDataLen-=DES_BLOCKLEN, pbData+=DES_BLOCKLEN)
{
SS_ASSERT(dwDataLen >= DES_BLOCKLEN);
// put the plaintext into a temporary
// buffer, then encrypt the data
// back into the caller's buffer
CopyMemory(rgbBuf, pbData, DES_BLOCKLEN);
CBC(des, DES_BLOCKLEN, pbData, rgbBuf, &sDESKey.sKeyTable,
ENCRYPT, rgbFeedBack);
}
fRet = TRUE;
//Ret:
return fRet;
}
BOOL FMyPrimitiveDeriveKey(
PBYTE pbSalt,
DWORD cbSalt,
PBYTE pbOtherData,
DWORD cbOtherData,
DESKEY* pDesKey)
{
BOOL fRet = FALSE;
A_SHA_CTX sSHAHash;
BYTE HashVal[A_SHA_DIGEST_LEN];
/*
PBYTE pbToBeHashed = (PBYTE)SSAlloc(cbSalt+cbOtherData);
if(pbToBeHashed == NULL) return FALSE;
CopyMemory(pbToBeHashed, pbSalt, cbSalt);
CopyMemory((PBYTE)((DWORD)pbToBeHashed+cbSalt), pbOtherData, cbOtherData);
// hash data
BYTE rgbHash[A_SHA_DIGEST_LEN];
if (!FMyPrimitiveSHA(pbToBeHashed, cbSalt+cbOtherData, rgbHash))
goto Ret;
*/
// NEW: put hashing code inline: saves alloc, copy, free
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, (BYTE *) pbSalt, cbSalt);
A_SHAUpdate(&sSHAHash, (BYTE *) pbOtherData, cbOtherData);
A_SHAFinal(&sSHAHash, HashVal);
// now all data hashed, derive a session key
SS_ASSERT(sizeof(HashVal) >= DES_BLOCKLEN);
if (!FMyMakeDESKey(pDesKey, HashVal))
goto Ret;
ZeroMemory( HashVal, sizeof(HashVal) );
fRet = TRUE;
Ret:
return fRet;
}
BOOL FMyOldPrimitiveHMAC(
DESKEY sMacKey,
PBYTE pbData,
DWORD cbData,
BYTE rgbHMAC[A_SHA_DIGEST_LEN])
{
BOOL fRet = FALSE;
BYTE rgbKipad[OLD_MAC_K_PADSIZE];
BYTE rgbKopad[OLD_MAC_K_PADSIZE];
ZeroMemory(rgbKipad, OLD_MAC_K_PADSIZE);
CopyMemory(rgbKipad, &sMacKey.rgbKey, DES_BLOCKLEN);
CopyMemory(rgbKopad, rgbKipad, sizeof(rgbKipad));
BYTE rgbHMACTmp[OLD_MAC_K_PADSIZE+A_SHA_DIGEST_LEN];
// assert we're a multiple
SS_ASSERT( (OLD_MAC_K_PADSIZE % sizeof(DWORD)) == 0);
// Kipad, Kopad are padded sMacKey. Now XOR across...
for(DWORD dwBlock=0; dwBlock<OLD_MAC_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;
BYTE HashVal[A_SHA_DIGEST_LEN];
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, pbData, cbData);
A_SHAUpdate(&sSHAHash, rgbKipad, OLD_MAC_K_PADSIZE);
// Finish off the hash
A_SHAFinal(&sSHAHash, HashVal);
// prepend Kopad to H1, hash to get HMAC
CopyMemory(rgbHMACTmp, rgbKopad, OLD_MAC_K_PADSIZE);
CopyMemory(rgbHMACTmp+OLD_MAC_K_PADSIZE, HashVal, A_SHA_DIGEST_LEN);
}
if (!FMyPrimitiveSHA(
rgbHMACTmp,
sizeof(rgbHMACTmp),
rgbHMAC))
goto Ret;
fRet = TRUE;
Ret:
return fRet;
}
BOOL FMyPrimitiveHMAC(
DESKEY sMacKey,
PBYTE pbData,
DWORD cbData,
BYTE rgbHMAC[A_SHA_DIGEST_LEN])
{
return FMyPrimitiveHMACParam(
sMacKey.rgbKey,
DES_BLOCKLEN,
pbData,
cbData,
rgbHMAC);
}
BOOL FMyPrimitiveHMACParam(
PBYTE pbKeyMaterial,
DWORD cbKeyMaterial,
PBYTE pbData,
DWORD cbData,
BYTE rgbHMAC[A_SHA_DIGEST_LEN] // output buffer
)
{
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
SS_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;
BYTE HashVal[A_SHA_DIGEST_LEN];
A_SHAInit(&sSHAHash);
A_SHAUpdate(&sSHAHash, rgbKipad, HMAC_K_PADSIZE);
A_SHAUpdate(&sSHAHash, pbData, cbData);
// Finish off the hash
A_SHAFinal(&sSHAHash, HashVal);
// prepend Kopad to H1, hash to get HMAC
CopyMemory(rgbHMACTmp, rgbKopad, HMAC_K_PADSIZE);
CopyMemory(rgbHMACTmp+HMAC_K_PADSIZE, HashVal, A_SHA_DIGEST_LEN);
}
// final hash: output value into passed-in buffer
if (!FMyPrimitiveSHA(
rgbHMACTmp,
sizeof(rgbHMACTmp),
rgbHMAC))
goto Ret;
fRet = TRUE;
Ret:
return fRet;
}
/////////////////////////////////////////////////////////////////////////
// PKCS5DervivePBKDF2_SHA
//
// Performs a PKCS #5 Iterative key derivation (type 2)
// using HMAC_SHA as the primitive hashing function
/////////////////////////////////////////////////////////////////////////
BOOL PKCS5DervivePBKDF2(
PBYTE pbKeyMaterial,
DWORD cbKeyMaterial,
PBYTE pbSalt,
DWORD cbSalt,
DWORD KeyGenAlg,
DWORD cIterationCount,
DWORD iBlockIndex,
BYTE rgbPKCS5Key[A_SHA_DIGEST_LEN] // output buffer
)
{
DWORD i,j;
A_SHA_CTX sSHAHash;
BYTE rgbPKCS5Temp[A_SHA_DIGEST_LEN];
BYTE rgbTempData[PBKDF2_MAX_SALT_SIZE + 4];
if((cIterationCount <1) ||
(NULL == pbKeyMaterial) ||
(NULL == pbSalt) ||
(0 == cbKeyMaterial) ||
(0 == cbSalt) ||
(cbSalt > PBKDF2_MAX_SALT_SIZE) ||
(KeyGenAlg != CALG_HMAC))
{
return FALSE;
}
//
// Add in the block index
//
CopyMemory(rgbTempData, pbSalt, cbSalt);
rgbTempData[cbSalt] = 0;
rgbTempData[cbSalt+1] = 0;
rgbTempData[cbSalt+2] = 0;
rgbTempData[cbSalt+3] = (BYTE)(iBlockIndex & 0xff);
//
// Perfom the initial iteration, which is
// HMAC_SHA1(KeyMaterial, Salt || cBlockIndex)
//
if(!FMyPrimitiveHMACParam(pbKeyMaterial,
cbKeyMaterial,
rgbTempData,
cbSalt+4,
rgbPKCS5Key))
return FALSE;
//
// Perform additional iterations
// HMAC_SHA1(KeyMaterial, last)
//
for (i=1; i<cIterationCount; i++)
{
if(!FMyPrimitiveHMACParam(pbKeyMaterial,
cbKeyMaterial,
rgbPKCS5Key,
A_SHA_DIGEST_LEN,
rgbPKCS5Temp))
return FALSE;
// xor back into the primary key.
for(j=0; j < (A_SHA_DIGEST_LEN / 4); j++)
{
((DWORD *)rgbPKCS5Key)[j] ^= ((DWORD *)rgbPKCS5Temp)[j];
}
}
return TRUE;
}