Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1178 lines
32 KiB

/////////////////////////////////////////////////////////////////////////////
// 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;
}