1179 lines
32 KiB
C
1179 lines
32 KiB
C
/////////////////////////////////////////////////////////////////////////////
|
|
// FILE : nt_crypt.c //
|
|
// DESCRIPTION : Crypto CP interfaces: //
|
|
// CPEncrypt //
|
|
// CPDecrypt //
|
|
// AUTHOR : //
|
|
// HISTORY : //
|
|
// Jan 25 1995 larrys Changed from Nametag //
|
|
// Jan 30 1995 larrys Cleanup code //
|
|
// Feb 23 1995 larrys Changed NTag_SetLastError to SetLastError //
|
|
// Apr 10 1995 larrys Added freeing of RC4 key data on final flag //
|
|
// May 8 1995 larrys Changes for MAC hashing //
|
|
// May 9 1995 larrys Added check for double encryption //
|
|
// May 10 1995 larrys added private api calls //
|
|
// Jul 13 1995 larrys Changed MAC stuff //
|
|
// Aug 16 1995 larrys Removed exchange key stuff //
|
|
// Oct 05 1995 larrys Fixed bugs 50 & 51 //
|
|
// Nov 8 1995 larrys Fixed SUR bug 10769 //
|
|
// May 15 1996 larrys Changed NTE_NO_MEMORY to ERROR_NOT_ENOUGH... //
|
|
// May 3 2000 dbarlow Fix return codes //
|
|
// //
|
|
// Copyright (C) 1993 Microsoft Corporation All Rights Reserved //
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "precomp.h"
|
|
#include "nt_rsa.h"
|
|
#include "mac.h"
|
|
#include "tripldes.h"
|
|
#include "swnt_pk.h"
|
|
#include "protstor.h"
|
|
#include "ntagum.h"
|
|
#include "aes.h"
|
|
|
|
#define DE_BLOCKLEN 8 // size of double encrypt block
|
|
|
|
BYTE dbDEncrypt[DE_BLOCKLEN]; // First 8 bytes of last encrypt
|
|
BOOL fDEncrypt = FALSE; // Flag for Double encrypt
|
|
BYTE dbDDecrypt[DE_BLOCKLEN]; // First 8 bytes of last Decrypt
|
|
DWORD fDDecrypt = FALSE; // Flag for Double Decrypt
|
|
|
|
extern CSP_STRINGS g_Strings;
|
|
|
|
extern BOOL
|
|
FIsLegalKey(
|
|
PNTAGUserList pTmpUser,
|
|
PNTAGKeyList pKey,
|
|
BOOL fRC2BigKeyOK);
|
|
|
|
extern DWORD
|
|
InflateKey(
|
|
IN PNTAGKeyList pTmpKey);
|
|
|
|
|
|
/* BlockEncrypt -
|
|
|
|
Run a block cipher over a block of size *pdwDataLen.
|
|
|
|
*/
|
|
|
|
DWORD
|
|
BlockEncrypt(
|
|
void EncFun(BYTE *In, BYTE *Out, void *key, int op),
|
|
PNTAGKeyList pKey,
|
|
int BlockLen,
|
|
BOOL Final,
|
|
BYTE *pbData,
|
|
DWORD *pdwDataLen,
|
|
DWORD dwBufLen)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
DWORD cbPartial, dwPadVal, dwDataLen;
|
|
BYTE *pbBuf;
|
|
|
|
dwDataLen = *pdwDataLen;
|
|
|
|
// Check to see if we are encrypting something already
|
|
|
|
if (pKey->InProgress == FALSE)
|
|
{
|
|
pKey->InProgress = TRUE;
|
|
if (pKey->Mode == CRYPT_MODE_CBC || pKey->Mode == CRYPT_MODE_CFB)
|
|
memcpy(pKey->FeedBack, pKey->IV, BlockLen);
|
|
}
|
|
|
|
// check length of the buffer and calculate the pad
|
|
// (if multiple of blocklen, do a full block of pad)
|
|
|
|
cbPartial = (dwDataLen % BlockLen);
|
|
if (Final)
|
|
{
|
|
dwPadVal = BlockLen - cbPartial;
|
|
if (pbData == NULL || dwBufLen < dwDataLen + dwPadVal)
|
|
{
|
|
// set what we need
|
|
*pdwDataLen = dwDataLen + dwPadVal;
|
|
dwReturn = (NULL == pbData) ? ERROR_SUCCESS : ERROR_MORE_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
else
|
|
{
|
|
// Clear encryption flag
|
|
pKey->InProgress = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pbData == NULL)
|
|
{
|
|
*pdwDataLen = dwDataLen;
|
|
dwReturn = ERROR_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Non-Final make multiple of the blocklen
|
|
if (cbPartial)
|
|
{
|
|
// set what we need
|
|
*pdwDataLen = dwDataLen + cbPartial;
|
|
ASSERT((*pdwDataLen % BlockLen) == 0);
|
|
dwReturn = (DWORD)NTE_BAD_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
dwPadVal = 0;
|
|
}
|
|
|
|
// allocate memory for a temporary buffer
|
|
if ((pbBuf = (BYTE *)_nt_malloc(BlockLen)) == NULL)
|
|
{
|
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
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.
|
|
|
|
memset(pbData + dwDataLen, (int)dwPadVal, (size_t)dwPadVal);
|
|
}
|
|
|
|
dwDataLen += dwPadVal;
|
|
*pdwDataLen = dwDataLen;
|
|
|
|
ASSERT((dwDataLen % BlockLen) == 0);
|
|
|
|
// pump the full blocks of data through
|
|
while (dwDataLen)
|
|
{
|
|
ASSERT(dwDataLen >= (DWORD)BlockLen);
|
|
|
|
// put the plaintext into a temporary
|
|
// buffer, then encrypt the data
|
|
// back into the caller's buffer
|
|
|
|
memcpy(pbBuf, pbData, BlockLen);
|
|
|
|
switch (pKey->Mode)
|
|
{
|
|
case CRYPT_MODE_CBC:
|
|
CBC(EncFun, BlockLen, pbData, pbBuf, pKey->pData,
|
|
ENCRYPT, pKey->FeedBack);
|
|
break;
|
|
|
|
case CRYPT_MODE_ECB:
|
|
EncFun(pbData, pbBuf, pKey->pData, ENCRYPT);
|
|
break;
|
|
|
|
case CRYPT_MODE_CFB:
|
|
CFB(EncFun, BlockLen, pbData, pbBuf, pKey->pData,
|
|
ENCRYPT, pKey->FeedBack);
|
|
break;
|
|
|
|
default:
|
|
_nt_free(pbBuf, BlockLen);
|
|
dwReturn = (DWORD) NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
pbData += BlockLen;
|
|
dwDataLen -= BlockLen;
|
|
}
|
|
|
|
_nt_free(pbBuf, BlockLen);
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
DWORD
|
|
BlockDecrypt(
|
|
void DecFun(BYTE *In, BYTE *Out, void *key, int op),
|
|
PNTAGKeyList pKey,
|
|
int BlockLen,
|
|
BOOL Final,
|
|
BYTE *pbData,
|
|
DWORD *pdwDataLen)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
BYTE *pbBuf;
|
|
DWORD dwDataLen, BytePos, dwPadVal, i;
|
|
|
|
dwDataLen = *pdwDataLen;
|
|
|
|
// Check to see if we are decrypting something already
|
|
|
|
if (pKey->InProgress == FALSE)
|
|
{
|
|
pKey->InProgress = TRUE;
|
|
if (pKey->Mode == CRYPT_MODE_CBC ||
|
|
pKey->Mode == CRYPT_MODE_CFB)
|
|
{
|
|
memcpy(pKey->FeedBack, pKey->IV, BlockLen);
|
|
}
|
|
}
|
|
|
|
// The data length must be a multiple of the algorithm
|
|
// pad size.
|
|
if (dwDataLen % BlockLen)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// allocate memory for a temporary buffer
|
|
if ((pbBuf = (BYTE *)_nt_malloc(BlockLen)) == NULL)
|
|
{
|
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// pump the data through the decryption, including padding
|
|
// NOTE: the total length is a multiple of BlockLen
|
|
|
|
for (BytePos = 0; (BytePos + BlockLen) <= dwDataLen; BytePos += BlockLen)
|
|
{
|
|
// put the encrypted text into a temp buffer
|
|
memcpy (pbBuf, pbData + BytePos, BlockLen);
|
|
|
|
switch (pKey->Mode)
|
|
{
|
|
case CRYPT_MODE_CBC:
|
|
CBC(DecFun, BlockLen, pbData + BytePos, pbBuf, pKey->pData,
|
|
DECRYPT, pKey->FeedBack);
|
|
break;
|
|
|
|
case CRYPT_MODE_ECB:
|
|
DecFun(pbData + BytePos, pbBuf, pKey->pData, DECRYPT);
|
|
break;
|
|
|
|
case CRYPT_MODE_CFB:
|
|
CFB(DecFun, BlockLen, pbData + BytePos, pbBuf, pKey->pData,
|
|
DECRYPT, pKey->FeedBack);
|
|
break;
|
|
|
|
default:
|
|
_nt_free(pbBuf, BlockLen);
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
_nt_free(pbBuf, BlockLen);
|
|
|
|
// if this is the final block of data then
|
|
// verify the padding and remove the pad size
|
|
// from the data length. NOTE: The padding is
|
|
// filled with a value equal to the length
|
|
// of the padding and we are guaranteed >= 1
|
|
// byte of pad.
|
|
// ## 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 (Final)
|
|
{
|
|
pKey->InProgress = FALSE;
|
|
|
|
dwPadVal = (DWORD)*(pbData + dwDataLen - 1);
|
|
if (dwPadVal == 0 || dwPadVal > (DWORD) BlockLen)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Make sure all the (rest of the) pad bytes are correct.
|
|
for (i=1; i<dwPadVal; i++)
|
|
{
|
|
if (pbData[dwDataLen - (i + 1)] != dwPadVal)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// Only need to update the length on final
|
|
*pdwDataLen -= dwPadVal;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- SymEncrypt
|
|
-
|
|
* Purpose:
|
|
* Encrypt data with symmetric algorithms. This function is used
|
|
* by the LocalEncrypt function as well as the WrapSymKey (nt_key.c)
|
|
* function.
|
|
*
|
|
* Parameters:
|
|
* IN pKey - Handle to the key
|
|
* IN fFinal - Boolean indicating if this is the final
|
|
* block of plaintext
|
|
* IN OUT pbData - Data to be encrypted
|
|
* IN OUT pcbData - Pointer to the length of the data to be
|
|
* encrypted
|
|
* IN cbBuf - Size of Data buffer
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
DWORD
|
|
SymEncrypt(
|
|
IN PNTAGKeyList pKey,
|
|
IN BOOL fFinal,
|
|
IN OUT BYTE *pbData,
|
|
IN OUT DWORD *pcbData,
|
|
IN DWORD cbBuf)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
DWORD dwSts;
|
|
|
|
// determine which algorithm is to be used
|
|
switch (pKey->Algid)
|
|
{
|
|
#ifdef CSP_USE_RC2
|
|
case CALG_RC2:
|
|
dwSts = BlockEncrypt(RC2, pKey, RC2_BLOCKLEN, fFinal, pbData,
|
|
pcbData, cbBuf);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_DES
|
|
case CALG_DES:
|
|
dwSts = BlockEncrypt(des, pKey, DES_BLOCKLEN, fFinal, pbData,
|
|
pcbData, cbBuf);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_3DES
|
|
case CALG_3DES_112:
|
|
case CALG_3DES:
|
|
dwSts = BlockEncrypt(tripledes, pKey, DES_BLOCKLEN, fFinal, pbData,
|
|
pcbData, cbBuf);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_RC4
|
|
|
|
case CALG_RC4:
|
|
if (pbData == NULL)
|
|
{
|
|
dwReturn = ERROR_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
if (*pcbData > cbBuf)
|
|
{
|
|
dwReturn = ERROR_MORE_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
rc4((struct RC4_KEYSTRUCT *)pKey->pData, *pcbData, pbData);
|
|
|
|
if (fFinal)
|
|
{
|
|
if (pKey->pData)
|
|
_nt_free (pKey->pData, pKey->cbDataLen);
|
|
pKey->pData = 0;
|
|
pKey->cbDataLen = 0;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_AES
|
|
case CALG_AES_128:
|
|
case CALG_AES_192:
|
|
case CALG_AES_256:
|
|
dwSts = BlockEncrypt(aes, pKey, pKey->dwBlockLen, fFinal, pbData,
|
|
pcbData, cbBuf);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- LocalEncrypt
|
|
-
|
|
* Purpose:
|
|
* Encrypt data
|
|
*
|
|
*
|
|
* Parameters:
|
|
* IN hUID - Handle to the CSP user
|
|
* IN hKey - Handle to the key
|
|
* IN hHash - Optional handle to a hash
|
|
* IN Final - Boolean indicating if this is the final
|
|
* block of plaintext
|
|
* IN OUT pbData - Data to be encrypted
|
|
* IN OUT pdwDataLen - Pointer to the length of the data to be
|
|
* encrypted
|
|
* IN dwBufLen - Size of Data buffer
|
|
* IN fIsExternal - Flag to tell if the call is for internal
|
|
* CSP use or external caller
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
DWORD
|
|
LocalEncrypt(
|
|
IN HCRYPTPROV hUID,
|
|
IN HCRYPTKEY hKey,
|
|
IN HCRYPTHASH hHash,
|
|
IN BOOL Final,
|
|
IN DWORD dwFlags,
|
|
IN OUT BYTE *pbData,
|
|
IN OUT DWORD *pdwDataLen,
|
|
IN DWORD dwBufSize,
|
|
IN BOOL fIsExternal)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
DWORD dwDataLen;
|
|
PNTAGUserList pTmpUser;
|
|
PNTAGKeyList pTmpKey;
|
|
PNTAGKeyList pTmpKey2;
|
|
PNTAGHashList pTmpHash;
|
|
DWORD dwLen;
|
|
MACstate *pMAC;
|
|
BSAFE_PUB_KEY *pBsafePubKey;
|
|
BYTE *pbOutput = NULL;
|
|
DWORD dwSts;
|
|
|
|
if (0 != (dwFlags & ~CRYPT_OAEP))
|
|
// && (0x9C580000 != dwFlags))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_FLAGS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwDataLen = *pdwDataLen;
|
|
|
|
if ((Final == FALSE) && (dwDataLen == 0))
|
|
{
|
|
// If no data to encrypt and this isn't the last block,
|
|
// then we're done. (if Final, we need to pad)
|
|
dwReturn = ERROR_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
|
|
if (NULL == pTmpUser)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_UID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
|
|
//
|
|
// Check if encryption allowed
|
|
//
|
|
|
|
if (fIsExternal &&
|
|
(PROV_RSA_SCHANNEL != pTmpUser->dwProvType) &&
|
|
((pTmpUser->Rights & CRYPT_DISABLE_CRYPT) == CRYPT_DISABLE_CRYPT))
|
|
{
|
|
dwReturn = (DWORD)NTE_PERM;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwSts = NTLValidate(hKey, hUID, KEY_HANDLE, &pTmpKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwSts= NTLValidate(hKey, hUID, EXCHPUBKEY_HANDLE, &pTmpKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
// NTLValidate doesn't know what error to set
|
|
// so it set NTE_FAIL -- fix it up.
|
|
dwReturn = (NTE_FAIL == dwSts) ? (DWORD)NTE_BAD_KEY : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((pTmpKey->Algid != CALG_RSA_KEYX) &&
|
|
(!FIsLegalKey(pTmpUser, pTmpKey, FALSE)))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((Final == FALSE) && (pTmpKey->Algid != CALG_RC4))
|
|
{
|
|
if (dwDataLen < pTmpKey->dwBlockLen)
|
|
{
|
|
*pdwDataLen = pTmpKey->dwBlockLen;
|
|
dwReturn = (DWORD)NTE_BAD_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
|
|
&& (fDEncrypt && pbData != NULL && *pdwDataLen != 0))
|
|
{
|
|
if (memcmp(dbDEncrypt, pbData, DE_BLOCKLEN) == 0)
|
|
{
|
|
dwReturn = (DWORD)NTE_DOUBLE_ENCRYPT;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// Check if we should do an auto-inflate
|
|
if ((pTmpKey->pData == NULL) && (pTmpKey->Algid != CALG_RSA_KEYX))
|
|
{
|
|
dwSts = InflateKey(pTmpKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((hHash != 0) && (NULL != pbData))
|
|
{
|
|
dwSts = NTLValidate(hHash, hUID, HASH_HANDLE, &pTmpHash);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = (NTE_FAIL == dwSts) ? (DWORD)NTE_BAD_HASH : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (pTmpHash->Algid == CALG_MAC)
|
|
{
|
|
// Check if we should do an auto-inflate
|
|
pMAC = pTmpHash->pHashData;
|
|
dwSts = NTLValidate(pTmpHash->hKey, hUID, KEY_HANDLE, &pTmpKey2);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
// NTLValidate doesn't know what error to set
|
|
// so it set NTE_FAIL -- fix it up.
|
|
dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
if (pTmpKey2->pData == NULL)
|
|
{
|
|
dwSts = InflateKey(pTmpKey2);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!CPHashData(hUID, hHash, pbData, *pdwDataLen, 0))
|
|
{
|
|
dwReturn = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// determine which algorithm is to be used
|
|
switch (pTmpKey->Algid)
|
|
{
|
|
case CALG_RSA_KEYX:
|
|
pBsafePubKey = (BSAFE_PUB_KEY *) pTmpKey->pKeyValue;
|
|
if (pBsafePubKey == NULL)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// compute length of resulting data
|
|
dwLen = (pBsafePubKey->bitlen + 7) / 8;
|
|
if (!CheckDataLenForRSAEncrypt(dwLen, *pdwDataLen, dwFlags))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_LEN;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (pbData == NULL || dwBufSize < dwLen)
|
|
{
|
|
*pdwDataLen = dwLen; // set what we need
|
|
dwReturn = (pbData == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pbOutput = (BYTE*)_nt_malloc(dwLen);
|
|
if (NULL == pbOutput)
|
|
{
|
|
dwReturn =ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// perform the RSA encryption
|
|
dwSts = RSAEncrypt(pTmpUser, pBsafePubKey, pbData, *pdwDataLen,
|
|
pTmpKey->pbParams, pTmpKey->cbParams, dwFlags,
|
|
pbOutput);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
*pdwDataLen = dwLen;
|
|
memcpy(pbData, pbOutput, *pdwDataLen);
|
|
|
|
break;
|
|
|
|
default:
|
|
dwSts = SymEncrypt(pTmpKey, Final, pbData, pdwDataLen, dwBufSize);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
|
|
&& (pbData != NULL && *pdwDataLen >= DE_BLOCKLEN))
|
|
{
|
|
memcpy(dbDEncrypt, pbData, DE_BLOCKLEN);
|
|
fDEncrypt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fDEncrypt = FALSE;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (pbOutput)
|
|
_nt_free(pbOutput, dwLen);
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- CPEncrypt
|
|
-
|
|
* Purpose:
|
|
* Encrypt data
|
|
*
|
|
*
|
|
* Parameters:
|
|
* IN hUID - Handle to the CSP user
|
|
* IN hKey - Handle to the key
|
|
* IN hHash - Optional handle to a hash
|
|
* IN Final - Boolean indicating if this is the final
|
|
* block of plaintext
|
|
* IN dwFlags - Flags values
|
|
* IN OUT pbData - Data to be encrypted
|
|
* IN OUT pdwDataLen - Pointer to the length of the data to be
|
|
* encrypted
|
|
* IN dwBufLen - Size of Data buffer
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
BOOL WINAPI
|
|
CPEncrypt(
|
|
IN HCRYPTPROV hUID,
|
|
IN HCRYPTKEY hKey,
|
|
IN HCRYPTHASH hHash,
|
|
IN BOOL Final,
|
|
IN DWORD dwFlags,
|
|
IN OUT BYTE *pbData,
|
|
IN OUT DWORD *pdwDataLen,
|
|
IN DWORD dwBufSize)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
DWORD dwSts;
|
|
DWORD fRet;
|
|
|
|
EntryPoint
|
|
dwSts = LocalEncrypt(hUID, hKey, hHash, Final, dwFlags,
|
|
pbData, pdwDataLen, dwBufSize, TRUE);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
fRet = (ERROR_SUCCESS == dwReturn);
|
|
if (!fRet)
|
|
SetLastError(dwReturn);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/*
|
|
- SymDecrypt
|
|
-
|
|
* Purpose:
|
|
* Decrypt data with symmetric algorithms. This function is used
|
|
* by the LocalDecrypt function as well as the UnWrapSymKey (nt_key.c)
|
|
* function.
|
|
*
|
|
* Parameters:
|
|
* IN pKey - Handle to the key
|
|
* IN pHash - Handle to a hash if needed
|
|
* IN fFinal - Boolean indicating if this is the final
|
|
* block of plaintext
|
|
* IN OUT pbData - Data to be decrypted
|
|
* IN OUT pcbData - Pointer to the length of the data to be
|
|
* decrypted
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
DWORD
|
|
SymDecrypt(
|
|
IN PNTAGKeyList pKey,
|
|
IN PNTAGHashList pHash,
|
|
IN BOOL fFinal,
|
|
IN OUT BYTE *pbData,
|
|
IN OUT DWORD *pcbData)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
MACstate *pMAC;
|
|
DWORD dwSts;
|
|
|
|
// determine which algorithm is to be used
|
|
switch (pKey->Algid)
|
|
{
|
|
#ifdef CSP_USE_RC2
|
|
|
|
// the decryption is to be done with the RC2 algorithm
|
|
case CALG_RC2:
|
|
dwSts = BlockDecrypt(RC2, pKey, RC2_BLOCKLEN, fFinal, pbData,
|
|
pcbData);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((fFinal) && (NULL != pHash) && (pHash->Algid == CALG_MAC) &&
|
|
(pKey->Mode == CRYPT_MODE_CBC))
|
|
{
|
|
pMAC = (MACstate *)pHash->pHashData;
|
|
memcpy(pMAC->Feedback, pKey->FeedBack, RC2_BLOCKLEN);
|
|
pHash->dwHashState |= DATA_IN_HASH;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_DES
|
|
|
|
// the decryption is to be done with DES
|
|
case CALG_DES:
|
|
dwSts = BlockDecrypt(des, pKey, DES_BLOCKLEN, fFinal, pbData,
|
|
pcbData);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((fFinal) && (NULL != pHash) && (pHash->Algid == CALG_MAC) &&
|
|
(pKey->Mode == CRYPT_MODE_CBC))
|
|
{
|
|
pMAC = (MACstate *)pHash->pHashData;
|
|
memcpy(pMAC->Feedback, pKey->FeedBack, DES_BLOCKLEN);
|
|
pHash->dwHashState |= DATA_IN_HASH;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_3DES
|
|
|
|
// the decryption is to be done with the triple DES
|
|
case CALG_3DES_112:
|
|
case CALG_3DES:
|
|
dwSts = BlockDecrypt(tripledes, pKey, DES_BLOCKLEN, fFinal, pbData,
|
|
pcbData);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((fFinal) && (NULL != pHash) && (pHash->Algid == CALG_MAC) &&
|
|
(pKey->Mode == CRYPT_MODE_CBC))
|
|
{
|
|
pMAC = (MACstate *)pHash->pHashData;
|
|
memcpy(pMAC->Feedback, pKey->FeedBack, DES_BLOCKLEN);
|
|
pHash->dwHashState |= DATA_IN_HASH;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_RC4
|
|
case CALG_RC4:
|
|
rc4((struct RC4_KEYSTRUCT *)pKey->pData, *pcbData, pbData);
|
|
if (fFinal)
|
|
{
|
|
_nt_free (pKey->pData, pKey->cbDataLen);
|
|
pKey->pData = 0;
|
|
pKey->cbDataLen = 0;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_AES
|
|
case CALG_AES_128:
|
|
case CALG_AES_192:
|
|
case CALG_AES_256:
|
|
dwSts = BlockDecrypt(aes, pKey, pKey->dwBlockLen, fFinal, pbData,
|
|
pcbData);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((fFinal) && (NULL != pHash) && (pHash->Algid == CALG_MAC) &&
|
|
(pKey->Mode == CRYPT_MODE_CBC))
|
|
{
|
|
pMAC = (MACstate *)pHash->pHashData;
|
|
memcpy(pMAC->Feedback, pKey->FeedBack, pKey->dwBlockLen);
|
|
pHash->dwHashState |= DATA_IN_HASH;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- LocalDecrypt
|
|
-
|
|
* Purpose:
|
|
* Decrypt data
|
|
*
|
|
*
|
|
* Parameters:
|
|
* IN hUID - Handle to the CSP user
|
|
* IN hKey - Handle to the key
|
|
* IN hHash - Optional handle to a hash
|
|
* IN Final - Boolean indicating if this is the final
|
|
* block of ciphertext
|
|
* IN dwFlags - Flags values
|
|
* IN OUT pbData - Data to be decrypted
|
|
* IN OUT pdwDataLen - Pointer to the length of the data to be
|
|
* decrypted
|
|
* IN fIsExternal - Flag to tell if the call is for internal
|
|
* CSP use or external caller
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
DWORD
|
|
LocalDecrypt(
|
|
IN HCRYPTPROV hUID,
|
|
IN HCRYPTKEY hKey,
|
|
IN HCRYPTHASH hHash,
|
|
IN BOOL Final,
|
|
IN DWORD dwFlags,
|
|
IN OUT BYTE *pbData,
|
|
IN OUT DWORD *pdwDataLen,
|
|
IN BOOL fIsExternal)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
PNTAGUserList pTmpUser;
|
|
PNTAGKeyList pTmpKey;
|
|
PNTAGKeyList pTmpKey2;
|
|
MACstate *pMAC;
|
|
PNTAGHashList pTmpHash = NULL;
|
|
BSAFE_PRV_KEY *pBsafePrvKey = NULL;
|
|
BYTE *pbNewData = NULL;
|
|
DWORD cbNewData;
|
|
DWORD dwSts;
|
|
|
|
if (0 != (dwFlags & ~CRYPT_OAEP))
|
|
// && (0x9C580000 != dwFlags))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_FLAGS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// We're done if decrypting 0 bytes.
|
|
if (*pdwDataLen == 0)
|
|
{
|
|
dwReturn = (Final == TRUE) ? (DWORD)NTE_BAD_LEN : ERROR_SUCCESS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
|
|
if (NULL == pTmpUser)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_UID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Check if decryption allowed
|
|
//
|
|
|
|
if (fIsExternal &&
|
|
(PROV_RSA_SCHANNEL != pTmpUser->dwProvType) &&
|
|
((pTmpUser->Rights & CRYPT_DISABLE_CRYPT) == CRYPT_DISABLE_CRYPT))
|
|
{
|
|
dwReturn = (DWORD)NTE_PERM;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Check the key against the user.
|
|
dwSts = NTLValidate(hKey, hUID, KEY_HANDLE, &pTmpKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwSts = NTLValidate(hKey, hUID, EXCHPUBKEY_HANDLE, &pTmpKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
// NTLValidate doesn't know what error to set
|
|
// so it set NTE_FAIL -- fix it up.
|
|
dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
|
|
&& fDDecrypt)
|
|
{
|
|
if (memcmp(dbDDecrypt, pbData, DE_BLOCKLEN) == 0)
|
|
{
|
|
dwReturn = (DWORD)NTE_DOUBLE_ENCRYPT;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((pTmpKey->Algid != CALG_RSA_KEYX) &&
|
|
(!FIsLegalKey(pTmpUser, pTmpKey, TRUE)))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Check if we should do an auto-inflate
|
|
if ((pTmpKey->pData == NULL) && (pTmpKey->Algid != CALG_RSA_KEYX))
|
|
{
|
|
dwSts = InflateKey(pTmpKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// determine which algorithm is to be used
|
|
switch (pTmpKey->Algid)
|
|
{
|
|
case CALG_RSA_KEYX:
|
|
// check if the public key matches the private key
|
|
if (pTmpUser->ContInfo.pbExchPub == NULL)
|
|
{
|
|
dwReturn = (DWORD)NTE_NO_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((pTmpUser->ContInfo.ContLens.cbExchPub != pTmpKey->cbKeyLen) ||
|
|
memcmp(pTmpUser->ContInfo.pbExchPub, pTmpKey->pKeyValue,
|
|
pTmpUser->ContInfo.ContLens.cbExchPub))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// if using protected store then load the key now
|
|
dwSts = UnprotectPrivKey(pTmpUser, g_Strings.pwszImportSimple,
|
|
FALSE, FALSE);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts; // NTE_BAD_KEYSET
|
|
goto ErrorExit;
|
|
}
|
|
pBsafePrvKey = (BSAFE_PRV_KEY *)pTmpUser->pExchPrivKey;
|
|
|
|
if (NULL == pBsafePrvKey)
|
|
{
|
|
dwReturn = (DWORD)NTE_NO_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// perform the RSA decryption
|
|
dwSts= RSADecrypt(pTmpUser, pBsafePrvKey, pbData, *pdwDataLen,
|
|
pTmpKey->pbParams, pTmpKey->cbParams,
|
|
dwFlags, &pbNewData, &cbNewData);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
*pdwDataLen = cbNewData;
|
|
memcpy(pbData, pbNewData, *pdwDataLen);
|
|
break;
|
|
|
|
default:
|
|
dwSts = SymDecrypt(pTmpKey, NULL, Final, pbData, pdwDataLen);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if (hHash != 0)
|
|
{
|
|
dwSts = NTLValidate(hHash, hUID, HASH_HANDLE, &pTmpHash);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = (NTE_FAIL == dwSts) ? (DWORD)NTE_BAD_HASH : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (pTmpHash->Algid == CALG_MAC)
|
|
{
|
|
// Check if we should do an auto-inflate
|
|
pMAC = pTmpHash->pHashData;
|
|
dwSts = NTLValidate(pTmpHash->hKey, hUID, KEY_HANDLE, &pTmpKey2);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
// NTLValidate doesn't know what error to set
|
|
// so it set NTE_FAIL -- fix it up.
|
|
dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
if (pTmpKey2->pData == NULL)
|
|
{
|
|
dwSts = InflateKey(pTmpKey2);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!CPHashData(hUID, hHash, pbData, *pdwDataLen, 0))
|
|
{
|
|
dwReturn = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
if ((POLICY_MS_DEF == pTmpUser->dwCspTypeId)
|
|
&& (*pdwDataLen >= DE_BLOCKLEN))
|
|
{
|
|
memcpy(dbDDecrypt, pbData, DE_BLOCKLEN);
|
|
fDDecrypt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fDDecrypt = FALSE;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (pbNewData)
|
|
_nt_free(pbNewData, cbNewData);
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- CPDecrypt
|
|
-
|
|
* Purpose:
|
|
* Decrypt data
|
|
*
|
|
*
|
|
* Parameters:
|
|
* IN hUID - Handle to the CSP user
|
|
* IN hKey - Handle to the key
|
|
* IN hHash - Optional handle to a hash
|
|
* IN Final - Boolean indicating if this is the final
|
|
* block of ciphertext
|
|
* IN dwFlags - Flags values
|
|
* IN OUT pbData - Data to be decrypted
|
|
* IN OUT pdwDataLen - Pointer to the length of the data to be
|
|
* decrypted
|
|
*
|
|
* Returns:
|
|
*/
|
|
|
|
BOOL WINAPI
|
|
CPDecrypt(
|
|
IN HCRYPTPROV hUID,
|
|
IN HCRYPTKEY hKey,
|
|
IN HCRYPTHASH hHash,
|
|
IN BOOL Final,
|
|
IN DWORD dwFlags,
|
|
IN OUT BYTE *pbData,
|
|
IN OUT DWORD *pdwDataLen)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
BOOL fRet;
|
|
DWORD dwSts;
|
|
|
|
EntryPoint
|
|
dwSts = LocalDecrypt(hUID, hKey, hHash, Final,
|
|
dwFlags, pbData, pdwDataLen, TRUE);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
fRet = (ERROR_SUCCESS == dwReturn);
|
|
if (!fRet)
|
|
SetLastError(dwReturn);
|
|
return fRet;
|
|
}
|
|
|