///////////////////////////////////////////////////////////////////////////// // 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_ENOUGHT... // // // // Copyright (C) 1993 Microsoft Corporation All Rights Reserved // ///////////////////////////////////////////////////////////////////////////// #include #include "precomp.h" #include "nt_rsa.h" #include "mac.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 /* BlockEncrypt - Run a block cipher over a block of size *pdwDataLen. */ BOOL BlockEncrypt(void EncFun(BYTE *In, BYTE *Out, void *key, int op), PNTAGKeyList pKey, int BlockLen, BOOL Final, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen) { 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) { // Clear encryption flag pKey->InProgress = FALSE; dwPadVal = BlockLen - cbPartial; if (pbData == NULL || dwBufLen < dwDataLen + dwPadVal) { // set what we need *pdwDataLen = dwDataLen + dwPadVal; if (pbData == NULL) { return NTF_SUCCEED; } SetLastError(ERROR_MORE_DATA); return NTF_FAILED; } } else { if (pbData == NULL) { *pdwDataLen = dwDataLen; return NTF_SUCCEED; } // Non-Final make multiple of the blocklen if (cbPartial) { // set what we need *pdwDataLen = dwDataLen + cbPartial; ASSERT((*pdwDataLen % BlockLen) == 0); SetLastError((DWORD) NTE_BAD_LEN); return NTF_FAILED; } dwPadVal = 0; } // allocate memory for a temporary buffer if ((pbBuf = (BYTE *)_nt_malloc(BlockLen)) == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NTF_FAILED; } 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 >= 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); SetLastError((DWORD) NTE_BAD_ALGID); return NTF_FAILED; break; } pbData += BlockLen; dwDataLen -= BlockLen; } _nt_free(pbBuf, BlockLen); return NTF_SUCCEED; } BOOL BlockDecrypt(void DecFun(BYTE *In, BYTE *Out, void *key, int op), PNTAGKeyList pKey, int BlockLen, BOOL Final, BYTE *pbData, DWORD *pdwDataLen) { 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) { SetLastError((DWORD) NTE_BAD_DATA); return NTF_FAILED; } // allocate memory for a temporary buffer if ((pbBuf = (BYTE *)_nt_malloc(BlockLen)) == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return NTF_FAILED; } // 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); SetLastError((DWORD) NTE_BAD_ALGID); return NTF_FAILED; break; } } _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) { SetLastError((DWORD) NTE_BAD_DATA); return NTF_FAILED; } // Make sure all the (rest of the) pad bytes are correct. for (i=1; iRights & CRYPT_DISABLE_CRYPT) == CRYPT_DISABLE_CRYPT) { SetLastError((DWORD) NTE_PERM); return NTF_FAILED; } if (CryptConfirmEncryption(pTmpUser->hPrivuid, hKey, Final) == CPPAPI_FAILED) { return NTF_FAILED; } if ((pTmpKey = (PNTAGKeyList) NTLValidate(hKey, hUID, KEY_HANDLE)) == NULL) { // NTLValidate doesn't know what error to set // so it set NTE_FAIL -- fix it up. if (GetLastError() == NTE_FAIL) { SetLastError((DWORD) NTE_BAD_KEY); } return NTF_FAILED; } if ((Final == FALSE) && (pTmpKey->Algid != CALG_RC4)) { if (dwDataLen < CRYPT_BLKLEN) { *pdwDataLen = CRYPT_BLKLEN; SetLastError((DWORD) NTE_BAD_DATA); return NTF_FAILED; } } if (fDEncrypt && pbData != NULL && *pdwDataLen != 0) { if (memcmp(dbDEncrypt, pbData, DE_BLOCKLEN) == 0) { SetLastError((DWORD) NTE_DOUBLE_ENCRYPT); return NTF_FAILED; } } // Check if we should do an auto-inflate if (pTmpKey->pData == NULL) { if (NTAG_FAILED(CPInflateKey(hUID, hKey, dwFlags))) { return NTF_FAILED; } } if (hHash != 0) { if ((pTmpHash = (PNTAGHashList)NTLValidate(hHash, hUID, HASH_HANDLE)) == NULL) { SetLastError((DWORD) NTE_BAD_HASH); return NTF_FAILED; } if (pTmpHash->Algid == CALG_MAC) { // Check if we should do an auto-inflate pMAC = pTmpHash->pHashData; if ((pTmpKey2 = (PNTAGKeyList) NTLValidate(pMAC->hKey, hUID, KEY_HANDLE)) == NULL) { // NTLValidate doesn't know what error to set // so it set NTE_FAIL -- fix it up. if (GetLastError() == NTE_FAIL) { SetLastError((DWORD) NTE_BAD_KEY); } return NTF_FAILED; } if (pTmpKey2->pData == NULL) { if (NTAG_FAILED(CPInflateKey(hUID, pMAC->hKey, dwFlags))) { return NTF_FAILED; } } } if (!CPHashData(hUID, hHash, pbData, *pdwDataLen, 0)) { return NTF_FAILED; } } // determine which algorithm is to be used switch (pTmpKey->Algid) { #ifdef CSP_USE_RC2 case CALG_RC2: if (BlockEncrypt(RC2, pTmpKey, RC2_BLOCKLEN, Final, pbData, pdwDataLen, dwBufSize) == NTF_FAILED) { return NTF_FAILED; } break; #endif #ifdef CSP_USE_DES case CALG_DES: if (BlockEncrypt(des, pTmpKey, DES_BLOCKLEN, Final, pbData, pdwDataLen, dwBufSize) == NTF_FAILED) { return NTF_FAILED; } break; #endif #ifdef CSP_USE_RC4 case CALG_RC4: rc4((struct RC4_KEYSTRUCT *)pTmpKey->pData, dwDataLen, pbData); if (Final) { _nt_free (pTmpKey->pData, pTmpKey->cbDataLen); pTmpKey->pData = 0; pTmpKey->cbDataLen = 0; } break; #endif default: SetLastError((DWORD) NTE_BAD_ALGID); return NTF_FAILED; break; } if (pbData != NULL && *pdwDataLen >= DE_BLOCKLEN) { memcpy(dbDEncrypt, pbData, DE_BLOCKLEN); fDEncrypt = TRUE; } else { fDEncrypt = FALSE; } return NTF_SUCCEED; } /* - 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 CPDecrypt(IN HCRYPTPROV hUID, IN HCRYPTKEY hKey, IN HCRYPTHASH hHash, IN BOOL Final, IN DWORD dwFlags, IN OUT BYTE *pbData, IN OUT DWORD *pdwDataLen) { PNTAGUserList pTmpUser; PNTAGKeyList pTmpKey; PNTAGHashList pTmpHash; MACstate *pMAC; if (dwFlags != 0) { SetLastError((DWORD) NTE_BAD_FLAGS); return NTF_FAILED; } // We're done if decrypting 0 bytes. if (*pdwDataLen == 0) { if (Final == TRUE) { SetLastError((DWORD) NTE_BAD_LEN); return NTF_FAILED; } return NTF_SUCCEED; } if ((pTmpUser = (PNTAGUserList) NTLCheckList (hUID, USER_HANDLE)) == NULL) { SetLastError((DWORD) NTE_BAD_UID); return NTF_FAILED; } // // Check if decryption allowed // if ((pTmpUser->Rights & CRYPT_DISABLE_CRYPT) == CRYPT_DISABLE_CRYPT) { SetLastError((DWORD) NTE_PERM); return NTF_FAILED; } if (CryptConfirmDecryption(pTmpUser->hPrivuid, hKey, Final) == CPPAPI_FAILED) { return NTF_FAILED; } // Check the key against the user. if ((pTmpKey = (PNTAGKeyList) NTLValidate(hKey, hUID, KEY_HANDLE)) == NULL) { // NTLValidate doesn't know what error to set // so it set NTE_FAIL -- fix it up. if (GetLastError() == NTE_FAIL) SetLastError((DWORD) NTE_BAD_KEY); return NTF_FAILED; } if (fDDecrypt) { if (memcmp(dbDDecrypt, pbData, DE_BLOCKLEN) == 0) { SetLastError((DWORD) NTE_DOUBLE_ENCRYPT); return NTF_FAILED; } } // Check if we should do an auto-inflate if (pTmpKey->pData == NULL) { if (NTAG_FAILED(CPInflateKey(hUID, hKey, dwFlags))) { return NTF_FAILED; } } // Check if we need to hash before encryption if (hHash != 0) { if ((pTmpHash = (PNTAGHashList)NTLValidate(hHash, hUID, HASH_HANDLE)) == NULL) { SetLastError((DWORD) NTE_BAD_HASH); return NTF_FAILED; } } // determine which algorithm is to be used switch (pTmpKey->Algid) { #ifdef CSP_USE_RC2 // the decryption is to be done with the RC2 algorithm case CALG_RC2: if (BlockDecrypt(RC2, pTmpKey, RC2_BLOCKLEN, Final, pbData, pdwDataLen) == NTF_FAILED) { return NTF_FAILED; } if ((Final) && (hHash != 0) && (pTmpHash->Algid == CALG_MAC) && (pTmpKey->Mode == CRYPT_MODE_CBC)) { pMAC = (MACstate *)pTmpHash->pHashData; memcpy(pMAC->Feedback, pTmpKey->FeedBack, RC2_BLOCKLEN); } break; #endif #ifdef CSP_USE_DES // the decryption is to be done with the RC2 algorithm case CALG_DES: if (BlockDecrypt(des, pTmpKey, DES_BLOCKLEN, Final, pbData, pdwDataLen) == NTF_FAILED) return NTF_FAILED; if ((Final) && (hHash != 0) && (pTmpHash->Algid == CALG_MAC) && (pTmpKey->Mode == CRYPT_MODE_CBC)) { pMAC = (MACstate *)pTmpHash->pHashData; memcpy(pMAC->Feedback, pTmpKey->FeedBack, RC2_BLOCKLEN); } break; #endif #ifdef CSP_USE_RC4 case CALG_RC4: rc4((struct RC4_KEYSTRUCT *)pTmpKey->pData, *pdwDataLen, pbData); if (Final) { _nt_free (pTmpKey->pData, pTmpKey->cbDataLen); pTmpKey->pData = 0; pTmpKey->cbDataLen = 0; } break; #endif default: SetLastError((DWORD) NTE_BAD_ALGID); return NTF_FAILED; break; } if (hHash != 0) { if (pTmpHash->Algid != CALG_MAC) { if (!CPHashData(hUID, hHash, pbData, *pdwDataLen, 0)) { return NTF_FAILED; } } } if (*pdwDataLen >= DE_BLOCKLEN) { memcpy(dbDDecrypt, pbData, DE_BLOCKLEN); fDDecrypt = TRUE; } else { fDDecrypt = FALSE; } return NTF_SUCCEED; }