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