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