/* 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 // crypto defs #include #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; i0; 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 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 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