windows-nt/Source/XPSP1/NT/ds/win32/ntcrypto/rsaaes/swnt_pk.c
2020-09-26 16:20:57 +08:00

1340 lines
38 KiB
C

/////////////////////////////////////////////////////////////////////////////
// FILE : swnt_pk.c //
// DESCRIPTION : //
// Software nametag public key management functions. These functions //
// isolate the peculiarities of public key management without a token //
// //
// AUTHOR : //
// HISTORY : //
// Jan 25 1995 larrys Changed from Nametag //
// Mar 01 1995 terences Fixed key pair handle creation //
// Mar 08 1995 larrys Fixed warning //
// Mar 23 1995 larrys Added variable key length //
// Apr 17 1995 larrys Added 1024 key gen //
// Apr 19 1995 larrys Changed CRYPT_EXCH_PUB to AT_KEYEXCHANGE //
// Aug 16 1995 larrys Removed exchange key stuff //
// Sep 12 1995 larrys Removed 2 DWORDS from exported keys //
// Sep 28 1995 larrys Changed format of PKCS //
// Oct 04 1995 larrys Fixed problem with PKCS format //
// Oct 27 1995 rajeshk RandSeed Stuff added hUID to PKCS2Encrypt //
// Nov 3 1995 larrys Merge for NT checkin //
// Dec 11 1995 larrys Added check for error return from RSA routine //
// May 15 1996 larrys Changed NTE_NO_MEMORY to ERROR_NOT_ENOUGHT... //
// Oct 14 1996 jeffspel Changed GenRandoms to NewGenRandoms //
// May 8 2000 dbarlow Reworked status return codes //
// //
// Copyright (C) 1993 - 2000, Microsoft Corporation //
// All Rights Reserved //
/////////////////////////////////////////////////////////////////////////////
#include "precomp.h"
#include "randlib.h"
#include "ntagum.h"
#include "swnt_pk.h"
#include "protstor.h"
#include "sha.h"
#define GetNextAlignedValue(c, alignment) ((c + alignment) & ~(alignment - 1))
extern CSP_STRINGS g_Strings;
extern void
FIPS186GenRandomWithException(
IN HANDLE *phRNGDriver,
IN BYTE **ppbContextSeed,
IN DWORD *pcbContextSeed,
IN OUT BYTE *pb,
IN DWORD cb);
// do the modular exponentiation calculation M^PubKey mod N
DWORD
RSAPublicEncrypt(
IN PEXPO_OFFLOAD_STRUCT pOffloadInfo,
IN BSAFE_PUB_KEY *pBSPubKey,
IN BYTE *pbInput,
IN BYTE *pbOutput)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BOOL fOffloadSuccess = FALSE;
DWORD cbMod;
//
// Two checks (needed for FIPS) before offloading to the offload
// module.
//
// First check is if there is an offload module, this
// will only be the case if the pOffloadInfo is not NULL.
//
// Second check is if this public key is OK, by checking
// the magic value in the key struct.
//
if (NULL != pOffloadInfo)
{
if (RSA1 != pBSPubKey->magic)
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
cbMod = (pBSPubKey->bitlen + 7) / 8;
fOffloadSuccess = ModularExpOffload(pOffloadInfo,
pbInput,
(BYTE*)&(pBSPubKey->pubexp),
sizeof(pBSPubKey->pubexp),
(BYTE*)pBSPubKey +
sizeof(BSAFE_PUB_KEY),
cbMod, pbOutput, NULL, 0);
}
if (!fOffloadSuccess)
{
if (!BSafeEncPublic(pBSPubKey, pbInput, pbOutput))
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY; // ?Really?
goto ErrorExit;
}
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
return dwReturn;
}
// do the modular exponentiation calculation M^PrivKey Exponent mod N
DWORD
RSAPrivateDecrypt(
IN PEXPO_OFFLOAD_STRUCT pOffloadInfo,
IN BSAFE_PRV_KEY *pBSPrivKey,
IN BYTE *pbInput,
IN BYTE *pbOutput)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BOOL fOffloadSuccess = FALSE;
DWORD cbMod;
DWORD cbHalfKeylen;
OFFLOAD_PRIVATE_KEY OffloadPrivateKey;
//
// Two checks (needed for FIPS) before offloading to the offload
// module.
//
// First check is if there is an offload module, this
// will only be the case if the pOffloadInfo is not NULL.
//
// Second check is if this private key is OK, by checking
// the magic value in the key struct.
//
if (NULL != pOffloadInfo)
{
if (RSA2 != pBSPrivKey->magic)
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
cbMod = (pBSPrivKey->bitlen + 7) / 8;
cbHalfKeylen = (pBSPrivKey->keylen + 1) / 2;
OffloadPrivateKey.dwVersion = CUR_OFFLOAD_VERSION;
OffloadPrivateKey.pbPrime1 =
(BYTE*)pBSPrivKey + sizeof(BSAFE_PRV_KEY) + cbHalfKeylen * 2;
OffloadPrivateKey.cbPrime1 = cbHalfKeylen;
OffloadPrivateKey.pbPrime2 =
(BYTE*)pBSPrivKey + sizeof(BSAFE_PRV_KEY) + cbHalfKeylen * 3;
OffloadPrivateKey.cbPrime2 = cbHalfKeylen;
fOffloadSuccess = ModularExpOffload(pOffloadInfo,
pbInput,
(BYTE*)pBSPrivKey + sizeof(BSAFE_PUB_KEY)
+ cbHalfKeylen * 7,
cbMod,
(BYTE*)pBSPrivKey + sizeof(BSAFE_PUB_KEY),
cbMod, pbOutput, (PVOID) &OffloadPrivateKey, 0);
}
if (!fOffloadSuccess)
{
if (!BSafeDecPrivate(pBSPrivKey, pbInput, pbOutput))
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY; // ?Really?
goto ErrorExit;
}
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
return dwReturn;
}
BOOL
CheckDataLenForRSAEncrypt(
IN DWORD cbMod, // length of the modulus
IN DWORD cbData, // length of the data
IN DWORD dwFlags) // flags
{
BOOL fRet = FALSE;
if (dwFlags & CRYPT_OAEP)
{
// if the OAEP flag is set then check for that length
if (cbMod < (cbData + A_SHA_DIGEST_LEN * 2 + 1))
goto ErrorExit;
}
else
{
// Check for PKCS 1 type 2 padding
// one byte for the top zero byte, one byte for the type,
// and one byte for the low zero byte,
// plus a minimum padding string is 8 bytes
if (cbMod < (cbData + 11))
goto ErrorExit;
}
fRet = TRUE;
ErrorExit:
return fRet;
}
/************************************************************************/
/* MaskGeneration generates a mask for OAEP based on the SHA1 hash */
/* function. */
/* NULL for the ppbMask parameter indicates the buffer is to be alloced.*/
/************************************************************************/
/*static*/ DWORD
MaskGeneration(
IN BYTE *pbSeed,
IN DWORD cbSeed,
IN DWORD cbMask,
OUT BYTE **ppbMask,
IN BOOL fAlloc)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
DWORD dwCount;
BYTE rgbCount[sizeof(DWORD)];
BYTE *pbCount;
A_SHA_CTX SHA1Ctxt;
DWORD cb = cbMask;
BYTE *pb;
DWORD i;
DWORD j;
// NULL for *ppbMask indicates the buffer is to be alloced
if (fAlloc)
{
*ppbMask = (BYTE*)_nt_malloc(cbMask);
if (NULL == *ppbMask)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
}
pb = *ppbMask;
dwCount = (cbMask + (A_SHA_DIGEST_LEN - 1)) / A_SHA_DIGEST_LEN;
for (i = 0; i < dwCount; i++)
{
// clear the hash context
memset(&SHA1Ctxt, 0, sizeof(SHA1Ctxt));
// hash the seed and the count
A_SHAInit(&SHA1Ctxt);
A_SHAUpdate(&SHA1Ctxt, pbSeed, cbSeed);
// Reverse the count bytes
pbCount = (BYTE*)&i;
for (j = 0; j < sizeof(DWORD); j++)
rgbCount[j] = pbCount[sizeof(DWORD) - j - 1];
A_SHAUpdate(&SHA1Ctxt, rgbCount, sizeof(DWORD));
A_SHAFinal(&SHA1Ctxt, SHA1Ctxt.HashVal);
// copy the bytes from this hash into the mask buffer
if (cb >= A_SHA_DIGEST_LEN)
memcpy(pb, SHA1Ctxt.HashVal, A_SHA_DIGEST_LEN);
else
{
memcpy(pb, SHA1Ctxt.HashVal, cb);
break;
}
cb -= A_SHA_DIGEST_LEN;
pb += A_SHA_DIGEST_LEN;
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
return dwReturn;
}
/************************************************************************/
/* ApplyPadding applies OAEP (Bellare-Rogoway) padding to a RSA key */
/* blob. The function does the seed generation, MGF and masking. */
/************************************************************************/
/*static*/ DWORD
ApplyPadding(
IN PNTAGUserList pTmpUser,
IN OUT BYTE* pb, // buffer
IN DWORD cb) // length of the data to mask not including seed
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BYTE rgbSeed[A_SHA_DIGEST_LEN];
BYTE *pbMask = NULL;
BYTE rgbSeedMask[A_SHA_DIGEST_LEN];
BYTE *pbSeedMask;
DWORD i;
DWORD dwSts;
// generate the random seed
dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
&pTmpUser->ContInfo.pbRandom,
&pTmpUser->ContInfo.ContLens.cbRandom,
rgbSeed, A_SHA_DIGEST_LEN);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
// generate the data mask from the seed
dwSts = MaskGeneration(rgbSeed, sizeof(rgbSeed), cb, &pbMask, TRUE);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
// XOR the data mask with the data
for (i = 0; i < cb; i++)
pb[i + A_SHA_DIGEST_LEN + 1] = (BYTE)(pb[i + A_SHA_DIGEST_LEN + 1] ^ pbMask[i]);
// generate the seed mask from the masked data
pbSeedMask = rgbSeedMask;
dwSts = MaskGeneration(pb + A_SHA_DIGEST_LEN + 1, cb,
A_SHA_DIGEST_LEN, &pbSeedMask, FALSE);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
// XOR the seed mask with the seed and put that into the
// pb buffer
for (i = 0; i < A_SHA_DIGEST_LEN; i++)
pb[i + 1] = (BYTE)(rgbSeed[i] ^ rgbSeedMask[i]);
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (pbMask)
_nt_free(pbMask, cb);
return dwReturn;
}
/************************************************************************/
/* RemovePadding checks OAEP (Bellare-Rogoway) padding on a RSA decrypt */
/* blob. */
/************************************************************************/
/*static*/ DWORD
RemovePadding(
IN OUT BYTE* pb,
IN DWORD cb)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BYTE rgbSeedMask[A_SHA_DIGEST_LEN];
BYTE *pbSeedMask;
BYTE *pbMask = NULL;
DWORD i;
DWORD dwSts;
memset(rgbSeedMask, 0, A_SHA_DIGEST_LEN);
// check the most significant byte is 0x00
if (0x00 != pb[0])
{
dwReturn = (DWORD)NTE_BAD_DATA;
goto ErrorExit;
}
// generate the seed mask from the masked data
pbSeedMask = rgbSeedMask;
dwSts = MaskGeneration(pb + A_SHA_DIGEST_LEN + 1, cb - (A_SHA_DIGEST_LEN + 1),
A_SHA_DIGEST_LEN, &pbSeedMask, FALSE);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
// XOR the seed mask with the seed and put that into the
// pb buffer
for (i = 0; i < A_SHA_DIGEST_LEN; i++)
pb[i + 1] = (BYTE)(pb[i + 1] ^ rgbSeedMask[i]);
// generate the data mask from the seed
dwSts = MaskGeneration(pb + 1, A_SHA_DIGEST_LEN,
cb - (A_SHA_DIGEST_LEN + 1), &pbMask, TRUE);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
// XOR the data mask with the data
for (i = 0; i < cb - (A_SHA_DIGEST_LEN + 1); i++)
{
pb[i + A_SHA_DIGEST_LEN + 1] =
(BYTE)(pb[i + A_SHA_DIGEST_LEN + 1] ^ pbMask[i]);
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (pbMask)
_nt_free(pbMask, cb - (A_SHA_DIGEST_LEN + 1));
return dwReturn;
}
/************************************************************************/
/* OAEPEncrypt performs a RSA encryption using OAEP (Bellare-Rogoway) */
/* as the padding scheme. The current implementation uses SHA1 as the */
/* hash function. */
/************************************************************************/
/*static*/ DWORD
OAEPEncrypt(
IN PNTAGUserList pTmpUser,
IN BSAFE_PUB_KEY *pBSPubKey,
IN BYTE *pbPlaintext,
IN DWORD cbPlaintext,
IN BYTE *pbParams,
IN DWORD cbParams,
OUT BYTE *pbOut)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BYTE *pbInput = NULL;
BYTE *pbOutput = NULL;
BYTE *pbReverse = NULL;
A_SHA_CTX SHA1Ctxt;
DWORD i;
DWORD cb;
DWORD dwSts;
memset(&SHA1Ctxt, 0, sizeof(SHA1Ctxt));
// start off by hashing the Encoding parameters (pbParams)
A_SHAInit(&SHA1Ctxt);
if (0 != cbParams)
A_SHAUpdate(&SHA1Ctxt, pbParams, cbParams);
A_SHAFinal(&SHA1Ctxt, SHA1Ctxt.HashVal);
// alloc space for an internal buffer
pbInput = (BYTE *)_nt_malloc(pBSPubKey->keylen * 2
+ ((pBSPubKey->bitlen + 7) / 8));
if (NULL == pbInput)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
pbOutput = pbInput + pBSPubKey->keylen;
pbReverse = pbInput + pBSPubKey->keylen * 2;
// add the pHash
memcpy(pbReverse + A_SHA_DIGEST_LEN + 1, SHA1Ctxt.HashVal,
A_SHA_DIGEST_LEN);
// figure the length of PS,
// put the 0x01 byte in, skipping past the PS,
// note that the PS is zero bytes so it is just there
cb = ((pBSPubKey->bitlen + 7) / 8) - (1 + cbPlaintext);
pbReverse[cb] = 0x01;
cb++;
// copy in the message bytes
memcpy(pbReverse + cb, pbPlaintext, cbPlaintext);
// do the seed generation, MGF and masking
cb = ((pBSPubKey->bitlen + 7) / 8) - (A_SHA_DIGEST_LEN + 1);
dwSts = ApplyPadding(pTmpUser, pbReverse, cb);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
// byte reverse the whole thing before RSA encrypting
for (i = 0; i < (pBSPubKey->bitlen + 7) / 8; i++)
pbInput[i] = pbReverse[((pBSPubKey->bitlen + 7) / 8) - i - 1];
// RSA encrypt this
dwSts = RSAPublicEncrypt(pTmpUser->pOffloadInfo, pBSPubKey,
pbInput, pbOutput);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
memcpy(pbOut, pbOutput, (pBSPubKey->bitlen + 7) / 8);
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (pbInput)
{
memset(pbInput, 0, pBSPubKey->keylen * 2 + (pBSPubKey->bitlen + 7) / 8);
_nt_free(pbInput, pBSPubKey->keylen * 2 + (pBSPubKey->bitlen + 7) / 8);
}
return dwReturn;
}
/************************************************************************/
/* OAEPDecrypt performs a RSA decryption checking that OAEP */
/* (Bellare-Rogoway) is the padding scheme. The current implementation */
/* uses SHA1 as the hash function. */
/************************************************************************/
/*static*/ DWORD
OAEPDecrypt(
IN PNTAGUserList pTmpUser,
IN BSAFE_PRV_KEY *pBSPrivKey,
IN CONST BYTE *pbBlob,
IN DWORD cbBlob,
IN BYTE *pbParams,
IN DWORD cbParams,
OUT BYTE **ppbPlaintext,
OUT DWORD *pcbPlaintext)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BYTE* pbOutput = NULL;
BYTE* pbInput = NULL;
BYTE* pbReverse = NULL;
A_SHA_CTX SHA1Ctxt;
DWORD cb;
DWORD i;
DWORD dwSts;
DWORD dwAlignedBufLen = 0;
memset(&SHA1Ctxt, 0, sizeof(SHA1Ctxt));
cb = (pBSPrivKey->bitlen + 7) / 8;
if (cbBlob > cb)
{
dwReturn = (DWORD)NTE_BAD_DATA;
goto ErrorExit;
}
dwAlignedBufLen = GetNextAlignedValue(pBSPrivKey->keylen + 2, sizeof(DWORD));
pbOutput = (BYTE *)_nt_malloc(dwAlignedBufLen * 2 + cb);
if (NULL == pbOutput)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
pbInput = pbOutput + dwAlignedBufLen;
pbReverse = pbOutput + dwAlignedBufLen * 2;
// perform the RSA decryption
memcpy(pbInput, pbBlob, cb);
dwSts = RSAPrivateDecrypt(pTmpUser->pOffloadInfo, pBSPrivKey,
pbInput, pbOutput);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
for (i = 0; i < cb; i++)
pbReverse[i] = pbOutput[cb - i - 1];
// remove OAEP (Bellare-Rogoway) padding
dwSts = RemovePadding(pbReverse, cb);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
// hash the Encoding parameters (pbParams)
A_SHAInit(&SHA1Ctxt);
if (0 != cbParams)
A_SHAUpdate(&SHA1Ctxt, pbParams, cbParams);
A_SHAFinal(&SHA1Ctxt, SHA1Ctxt.HashVal);
// check the hash of the encoding parameters against the message
if (0 != memcmp(SHA1Ctxt.HashVal, pbReverse + A_SHA_DIGEST_LEN + 1,
A_SHA_DIGEST_LEN))
{
dwReturn = (DWORD)NTE_BAD_DATA;
goto ErrorExit;
}
// check the zero bytes and check the 0x01 byte
for (i = A_SHA_DIGEST_LEN * 2 + 1; i < cb; i++)
{
if (0x01 == pbReverse[i])
{
i++;
break;
}
else if (0x00 != pbReverse[i])
{
dwReturn = (DWORD)NTE_BAD_DATA;
goto ErrorExit;
}
}
*pcbPlaintext = cb - i;
*ppbPlaintext = (BYTE*)_nt_malloc(*pcbPlaintext);
if (NULL == *ppbPlaintext)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
memcpy(*ppbPlaintext, pbReverse + i, *pcbPlaintext);
dwReturn = ERROR_SUCCESS;
ErrorExit:
// scrub the output buffer
if (pbOutput)
{
memset(pbOutput, 0, dwAlignedBufLen * 2 + cb);
_nt_free(pbOutput, dwAlignedBufLen * 2 + cb);
}
return dwReturn;
}
/*static*/ DWORD
PKCS2Encrypt(
PNTAGUserList pTmpUser,
DWORD dwFlags,
BSAFE_PUB_KEY *pKey,
BYTE *InBuf,
DWORD InBufLen,
BYTE *OutBuf)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BYTE *pScratch = NULL;
BYTE *pScratch2 = NULL;
BYTE *pLocal;
DWORD temp;
DWORD z;
DWORD dwSts;
pScratch = (BYTE *)_nt_malloc(pKey->keylen);
if (NULL == pScratch)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
pScratch2 = (BYTE *)_nt_malloc(pKey->keylen);
if (NULL == pScratch2)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
memset(pScratch, 0, pKey->keylen);
pScratch[pKey->datalen - 1] = PKCS_BLOCKTYPE_2;
dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
&pTmpUser->ContInfo.pbRandom,
&pTmpUser->ContInfo.ContLens.cbRandom,
pScratch+InBufLen+1,
(pKey->datalen)-InBufLen-2);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
pLocal = pScratch + InBufLen + 1;
// Need to insure that none of the padding bytes are zero.
temp = pKey->datalen - InBufLen - 2;
while (temp)
{
if (*pLocal == 0)
{
dwSts = FIPS186GenRandom(&pTmpUser->hRNGDriver,
&pTmpUser->ContInfo.pbRandom,
&pTmpUser->ContInfo.ContLens.cbRandom,
pLocal, 1);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
}
else
{
pLocal++;
temp--;
}
}
#ifdef CSP_USE_SSL3
// if SSL2_FALLBACK has been specified then put threes in the 8
// least significant bytes of the random padding
if (CRYPT_SSL2_FALLBACK & dwFlags)
memset(pScratch + InBufLen + 1, 0x03, 8);
#endif
// Reverse the session key bytes
for (z = 0; z < InBufLen; ++z)
pScratch[z] = InBuf[InBufLen - z - 1];
dwSts = RSAPublicEncrypt(pTmpUser->pOffloadInfo,
pKey, pScratch, pScratch2);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
memcpy(OutBuf, pScratch2, (pKey->bitlen + 7) / 8);
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (pScratch)
_nt_free(pScratch, pKey->keylen);
if (pScratch2)
_nt_free(pScratch2, pKey->keylen);
return dwReturn;
}
/*static*/ DWORD
PKCS2Decrypt(
IN PNTAGUserList pTmpUser,
IN BSAFE_PRV_KEY *pKey,
IN DWORD dwFlags,
IN CONST BYTE *InBuf,
OUT BYTE **ppbOutBuf,
OUT DWORD *pcbOutBuf)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
DWORD i;
BYTE *pScratch = NULL;
BYTE *pScratch2 = NULL;
DWORD z;
DWORD dwSts;
pScratch = (BYTE *)_nt_malloc(pKey->keylen * 2);
if (NULL == pScratch)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
pScratch2 = pScratch + pKey->keylen;
memcpy(pScratch2, InBuf, (pKey->bitlen + 7) / 8);
dwSts = RSAPrivateDecrypt(pTmpUser->pOffloadInfo, pKey,
pScratch2, pScratch);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts; // NTE_FAIL
goto ErrorExit;
}
if ((pScratch[pKey->datalen - 1] != PKCS_BLOCKTYPE_2) ||
(pScratch[pKey->datalen] != 0))
{
dwReturn = (DWORD) NTE_BAD_DATA;
goto ErrorExit;
}
i = pKey->datalen - 2;
while ((i > 0) && (pScratch[i]))
i--;
*ppbOutBuf = (BYTE *)_nt_malloc(i);
if (NULL == *ppbOutBuf)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
*pcbOutBuf = i;
#ifdef CSP_USE_SSL3
// if SSL2_FALLBACK has been specified then check if threes
// are in the 8 least significant bytes of the random padding
if (CRYPT_SSL2_FALLBACK & dwFlags)
{
BOOL fFallbackError = TRUE;
for (z = i + 1; z < i + 9; z++)
{
if (0x03 != pScratch[z])
{
fFallbackError = FALSE;
break;
}
}
if (fFallbackError)
{
dwReturn = (DWORD)NTE_BAD_VER;
goto ErrorExit;
}
}
#endif
// Reverse the session key bytes
for (z = 0; z < i; ++z)
(*ppbOutBuf)[z] = pScratch[i - z - 1];
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (pScratch)
_nt_free(pScratch, pKey->keylen);
return dwReturn;
}
/************************************************************************/
/* RSAEncrypt performs a RSA encryption. */
/************************************************************************/
DWORD
RSAEncrypt(
IN PNTAGUserList pTmpUser,
IN BSAFE_PUB_KEY *pBSPubKey,
IN BYTE *pbPlaintext,
IN DWORD cbPlaintext,
IN BYTE *pbParams,
IN DWORD cbParams,
IN DWORD dwFlags,
OUT BYTE *pbOut)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
DWORD dwSts;
// check the length of the data
if (!CheckDataLenForRSAEncrypt((pBSPubKey->bitlen + 7) / 8,
cbPlaintext, dwFlags))
{
dwReturn = (DWORD)NTE_BAD_LEN;
goto ErrorExit;
}
// use OAEP if the flag is set
if (dwFlags & CRYPT_OAEP)
{
// use OAEP if the flag is set
dwSts = OAEPEncrypt(pTmpUser, pBSPubKey, pbPlaintext,
cbPlaintext, pbParams, cbParams, pbOut);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
}
else
{
// use PKCS #1 Type 2
dwSts = PKCS2Encrypt(pTmpUser, dwFlags, pBSPubKey,
pbPlaintext, cbPlaintext, pbOut);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
return dwReturn;
}
/************************************************************************/
/* RSADecrypt performs a RSA decryption. */
/************************************************************************/
DWORD
RSADecrypt(
IN PNTAGUserList pTmpUser,
IN BSAFE_PRV_KEY *pBSPrivKey,
IN CONST BYTE *pbBlob,
IN DWORD cbBlob,
IN BYTE *pbParams,
IN DWORD cbParams,
IN DWORD dwFlags,
OUT BYTE **ppbPlaintext,
OUT DWORD *pcbPlaintext)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
DWORD dwSts;
// use OAEP if the flag is set
if (dwFlags & CRYPT_OAEP)
{
// use OAEP if the flag is set
dwSts = OAEPDecrypt(pTmpUser, pBSPrivKey, pbBlob, cbBlob, pbParams,
cbParams, ppbPlaintext, pcbPlaintext);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
}
else
{
// use PKCS #1 Type 2
dwSts = PKCS2Decrypt(pTmpUser, pBSPrivKey, dwFlags, pbBlob,
ppbPlaintext, pcbPlaintext);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
return dwReturn;
}
//
// Function : EncryptAndDecryptWithRSAKey
//
// Description : This function creates a buffer and then encrypts that with
// the passed in private key and decrypts with the passed in
// public key. The function is used for FIPS 140-1 compliance
// to make sure that newly generated/imported keys work and
// in the self test during DLL initialization.
//
DWORD
EncryptAndDecryptWithRSAKey(
IN BYTE *pbRSAPub,
IN BYTE *pbRSAPriv,
IN BOOL fSigKey,
IN BOOL fEncryptCheck)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BSAFE_PRV_KEY *pBSafePriv = (BSAFE_PRV_KEY*)pbRSAPriv;
BYTE *pb = NULL;
DWORD cb;
DWORD cbKey;
DWORD i;
// alloc space for the plaintext and ciphertext
cb = pBSafePriv->keylen;
cbKey = pBSafePriv->bitlen / 8;
pb = _nt_malloc(cb * 3);
if (NULL == pb)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
// reverse the hash so it is in little endian
for (i = 0; i < 16; i++)
pb[i] = (BYTE)(i + 1);
memset(pb + 17, 0xFF, cbKey - 18);
if (fSigKey)
{
// encrypt with the private key
if (!BSafeDecPrivate(pBSafePriv, pb, pb + cb))
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
}
else
{
// encrypt with the public key
if (!BSafeEncPublic((BSAFE_PUB_KEY*)pbRSAPub, pb, pb + cb))
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
}
// we can't do this check when importing private keys since many
// applications use private keys with exponent of 1 to import
// plaintext symmetric keys
if (fEncryptCheck)
{
if (0 == (memcmp(pb, pb + cb, cb)))
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
}
if (fSigKey)
{
// decrypt with the public key
if (!BSafeEncPublic((BSAFE_PUB_KEY*)pbRSAPub, pb + cb, pb + (cb * 2)))
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
}
else
{
// encrypt with the private key
if (!BSafeDecPrivate(pBSafePriv, pb + cb, pb + (cb * 2)))
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
}
// compare to the plaintext and the decrypted text
if (memcmp(pb, pb + cb * 2, cbKey))
{
dwReturn = (DWORD)NTE_BAD_KEY;
goto ErrorExit;
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (pb)
_nt_free(pb, cbB * 3);
return dwReturn;
}
DWORD
ReGenKey(
HCRYPTPROV hUser,
DWORD dwFlags,
DWORD dwWhichKey,
HCRYPTKEY *phKey,
DWORD bits)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
BYTE **ThisPubKey, **ThisPrivKey;
DWORD *pThisPubLen, *pThisPrivLen;
BYTE *pNewPubKey = NULL;
BYTE *pNewPrivKey = NULL;
DWORD PrivateKeySize, PublicKeySize;
DWORD localbits;
PNTAGUserList pOurUser;
BOOL fSigKey;
LPWSTR szPrompt;
BOOL *pfExportable;
BOOL fAlloc = FALSE;
BOOL fInCritSec = FALSE;
BSAFE_OTHER_INFO OtherInfo;
BSAFE_OTHER_INFO *pOtherInfo = NULL;
DWORD dwSts;
PNTAGKeyList pTmpKey = NULL;
memset(&OtherInfo, 0, sizeof(OtherInfo));
// ## MTS: No user structure locking
pOurUser = (PNTAGUserList) NTLCheckList(hUser, USER_HANDLE);
if (NULL == pOurUser)
{
dwReturn = (DWORD)NTE_BAD_UID;
goto ErrorExit;
}
// wrap with a try since there is a critical sections in here
__try
{
EnterCriticalSection(&pOurUser->CritSec);
fInCritSec = TRUE;
localbits = bits;
if (!BSafeComputeKeySizes(&PublicKeySize, &PrivateKeySize,
&localbits))
{
dwReturn = (DWORD)NTE_FAIL;
goto ErrorExit;
}
pNewPubKey = (BYTE *)_nt_malloc(PublicKeySize);
if (NULL == pNewPubKey)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
fAlloc = TRUE;
// allocate space for the new key exchange public key
pNewPrivKey = (BYTE *)_nt_malloc(PrivateKeySize);
if (NULL == pNewPrivKey)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
// generate the key exchange key pair
if (INVALID_HANDLE_VALUE != pOurUser->hRNGDriver)
{
OtherInfo.pRNGInfo = &pOurUser->hRNGDriver;
OtherInfo.pFuncRNG = FIPS186GenRandomWithException;
pOtherInfo = &OtherInfo;
}
// ?Note? -- Shouldn't this be in a try/except?
if (!BSafeMakeKeyPairEx2(pOtherInfo,
(BSAFE_PUB_KEY *) pNewPubKey,
(BSAFE_PRV_KEY *) pNewPrivKey,
bits,
0x10001))
{
dwReturn = (DWORD)NTE_FAIL;
goto ErrorExit;
}
// test the RSA key to make sure it works
dwSts = EncryptAndDecryptWithRSAKey(pNewPubKey, pNewPrivKey,
TRUE, TRUE);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
// test the RSA key to make sure it works
dwSts = EncryptAndDecryptWithRSAKey(pNewPubKey, pNewPrivKey,
FALSE, TRUE);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
if (dwWhichKey == NTPK_USE_SIG)
{
ThisPubKey = &pOurUser->ContInfo.pbSigPub;
ThisPrivKey = &pOurUser->pSigPrivKey;
pThisPubLen = &pOurUser->ContInfo.ContLens.cbSigPub;
pThisPrivLen = &pOurUser->SigPrivLen;
pfExportable = &pOurUser->ContInfo.fSigExportable;
fSigKey = TRUE;
szPrompt = g_Strings.pwszCreateRSASig;
}
else
{
ThisPubKey = &pOurUser->ContInfo.pbExchPub;
ThisPrivKey = &pOurUser->pExchPrivKey;
pThisPubLen = &pOurUser->ContInfo.ContLens.cbExchPub;
pThisPrivLen = &pOurUser->ExchPrivLen;
pfExportable = &pOurUser->ContInfo.fExchExportable;
fSigKey = FALSE;
szPrompt = g_Strings.pwszCreateRSAExch;
}
if (*ThisPubKey)
{
ASSERT(*pThisPubLen);
ASSERT(*pThisPrivLen);
ASSERT(*ThisPrivKey);
_nt_free (*ThisPubKey, *pThisPubLen);
_nt_free (*ThisPrivKey, *pThisPrivLen);
}
#ifdef NTAGDEBUG
else
{
ASSERT(*pThisPrivLen == 0);
ASSERT(*pThisPubLen == 0);
ASSERT(*ThisPrivKey == 0);
ASSERT(*ThisPubKey == 0);
}
#endif
fAlloc = FALSE;
*pThisPrivLen = PrivateKeySize;
*pThisPubLen = PublicKeySize;
*ThisPrivKey = pNewPrivKey;
*ThisPubKey = pNewPubKey;
if (dwFlags & CRYPT_EXPORTABLE)
*pfExportable = TRUE;
else
*pfExportable = FALSE;
// if the context being used is a Verify Context then the key is not
// persisted to storage
if (!(pOurUser->Rights & CRYPT_VERIFYCONTEXT))
{
// write the new keys to the user storage file
dwSts = ProtectPrivKey(pOurUser, szPrompt, dwFlags, fSigKey);
if (ERROR_SUCCESS != dwSts)
{
dwReturn = dwSts;
goto ErrorExit;
}
}
if (dwWhichKey == NTPK_USE_SIG)
{
if (!CPGetUserKey(hUser, AT_SIGNATURE, phKey))
{
dwReturn = GetLastError();
goto ErrorExit;
}
dwSts = NTLValidate(*phKey, hUser, SIGPUBKEY_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;
}
}
else
{
if (!CPGetUserKey(hUser, AT_KEYEXCHANGE, phKey))
{
dwReturn = GetLastError();
goto ErrorExit;
}
dwSts = NTLValidate(*phKey, hUser, 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;
}
}
//
// Fix-up archivability.
//
if (0 != (CRYPT_ARCHIVABLE & dwFlags))
{
pTmpKey->Rights |= CRYPT_ARCHIVABLE;
pTmpKey->Permissions |= CRYPT_ARCHIVE;
}
}
__except ( EXCEPTION_EXECUTE_HANDLER )
{
dwReturn = ERROR_INVALID_PARAMETER;
goto ErrorExit;
}
dwReturn = ERROR_SUCCESS;
ErrorExit:
if (fInCritSec)
LeaveCriticalSection(&pOurUser->CritSec);
if (fAlloc)
{
if (pNewPrivKey)
_nt_free(pNewPrivKey, PrivateKeySize);
if (pNewPubKey)
_nt_free(pNewPubKey, PublicKeySize);
}
return dwReturn;
}
//
// Routine : DerivePublicFromPrivate
//
// Description : Derive the public RSA key from the private RSA key. This is
// done and the resulting public key is placed in the appropriate
// place in the context pointer (pTmpUser).
//
DWORD
DerivePublicFromPrivate(
IN PNTAGUserList pUser,
IN BOOL fSigKey)
{
DWORD dwReturn = ERROR_INTERNAL_ERROR;
DWORD *pcbPubKey;
BYTE **ppbPubKey = NULL;
BSAFE_PUB_KEY *pBSafePubKey;
BSAFE_PRV_KEY *pBSafePrivKey;
DWORD cb;
// variable assignments depending on if its sig or exch
if (fSigKey)
{
pcbPubKey = &pUser->ContInfo.ContLens.cbSigPub;
ppbPubKey = &pUser->ContInfo.pbSigPub;
pBSafePrivKey = (BSAFE_PRV_KEY*)pUser->pSigPrivKey;
}
else
{
pcbPubKey = &pUser->ContInfo.ContLens.cbExchPub;
ppbPubKey = &pUser->ContInfo.pbExchPub;
pBSafePrivKey = (BSAFE_PRV_KEY*)pUser->pExchPrivKey;
}
// figure out how much space is needed for the public key
cb = ((((pBSafePrivKey->bitlen >> 1) + 63) / 32) * 8); // 8 = 2 * DIGIT_BYTES (rsa_fast.h)
cb += sizeof(BSAFE_PUB_KEY);
// check if space has been alloced for the public key and if
// so is it large enough
if (cb > *pcbPubKey)
{
_nt_free(*ppbPubKey, *pcbPubKey);
*pcbPubKey = cb;
*ppbPubKey = _nt_malloc(*pcbPubKey);
if (NULL == *ppbPubKey)
{
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
}
// copy over the public key components
pBSafePubKey = (BSAFE_PUB_KEY*)*ppbPubKey;
pBSafePubKey->magic = RSA1;
pBSafePubKey->keylen = pBSafePrivKey->keylen;
pBSafePubKey->bitlen = pBSafePrivKey->bitlen;
pBSafePubKey->datalen = pBSafePrivKey->datalen;
pBSafePubKey->pubexp = pBSafePrivKey->pubexp;
memcpy(*ppbPubKey + sizeof(BSAFE_PUB_KEY),
(BYTE*)pBSafePrivKey + sizeof(BSAFE_PRV_KEY),
cb - sizeof(BSAFE_PUB_KEY));
dwReturn = ERROR_SUCCESS;
ErrorExit:
return dwReturn;
}