none
WEC2013 - BCryptDeriveKeyPBKDF2 not available RRS feed

  • Question

  • Hello,

    i'd like to use BCryptDeriveKeyPBKDF2, but this function is not available under WEC2013.

    I don't want to use OpenSSL or Poco just for this one function.

    Did anyone already wrote a replacement function based on bcrypt and is willing to share it?


    • Edited by haide_biz Friday, August 31, 2018 4:01 PM fixed type in title
    Friday, August 31, 2018 3:57 PM

Answers

  • I took this code, converted the deprecated wincrypt calls to the new CNG API, refactored it and removed the stuff which i don't need.

    Although i don't understand what the code is doing, it seems that it is producing the same result than the function in my question which is using BCryptDeriveKeyPBKDF2.

    #include <math.h>
    
    #define DIGEST_SIZE 20
    #define BLOCK_SIZE 64
    
    typedef struct
    {
    	BCRYPT_ALG_HANDLE hAlgorithm;
    	BCRYPT_HASH_HANDLE hInnerHash;
    	BCRYPT_HASH_HANDLE hOuterHash;
    	ULONG cbHashObject;
    	PUCHAR pbInnerHash;
    	PUCHAR pbOuterHash;
    } PRF_CTX;
    
    static void hmacFree(PRF_CTX* pContext)
    {
    	if (pContext->hOuterHash) BCryptDestroyHash(pContext->hOuterHash);
    	if (pContext->hInnerHash) BCryptDestroyHash(pContext->hInnerHash);
    	free(pContext->pbOuterHash);
    	free(pContext->pbInnerHash);
    	if (pContext->hAlgorithm) BCryptCloseAlgorithmProvider(pContext->hAlgorithm, 0);
    }
    
    static BOOL hmacPrecomputeDigest(BCRYPT_HASH_HANDLE hHash, PUCHAR pbPassword, DWORD cbPassword, BYTE mask)
    {
    	BYTE buffer[BLOCK_SIZE];
    	DWORD i;
    
    	if (cbPassword > BLOCK_SIZE)
    	{
    		return FALSE;
    	}
    
    	memset(buffer, mask, sizeof(buffer));
    
    	for (i = 0; i < cbPassword; ++i)
    	{
    		buffer[i] = (char)(pbPassword[i] ^ mask);
    	}
    
    	return BCRYPT_SUCCESS(BCryptHashData(hHash, buffer, sizeof(buffer), 0));
    }
    
    static BOOL hmacInit(PRF_CTX* pContext, PUCHAR pbPassword, DWORD cbPassword)
    {
    	BCRYPT_HASH_HANDLE hHash = NULL;
    	BOOL bStatus = FALSE;
    	BYTE key[DIGEST_SIZE];
    	ULONG cbResult;
    
    	if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&pContext->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, 0)) ||
    		!BCRYPT_SUCCESS(BCryptGetProperty(pContext->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&pContext->cbHashObject, sizeof(pContext->cbHashObject), &cbResult, 0)) ||
    		((pContext->pbInnerHash = malloc(pContext->cbHashObject)) == NULL) ||
    		((pContext->pbOuterHash = malloc(pContext->cbHashObject)) == NULL) ||
    		!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hInnerHash, pContext->pbInnerHash, pContext->cbHashObject, NULL, 0, 0)) ||
    		!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hOuterHash, pContext->pbOuterHash, pContext->cbHashObject, NULL, 0, 0)))
    	{
    		goto hmacInit_end;
    	}
    
    	if (cbPassword > BLOCK_SIZE)
    	{
    		ULONG cbResult;
    		if (!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &hHash, NULL, 0, NULL, 0, 0)) ||
    			!BCRYPT_SUCCESS(BCryptHashData(hHash, pbPassword, cbPassword, 0)) ||
    			!BCRYPT_SUCCESS(BCryptGetProperty(hHash, BCRYPT_HASH_LENGTH, (PUCHAR)&cbPassword, sizeof(cbPassword), &cbResult, 0)) ||
    			!BCRYPT_SUCCESS(BCryptFinishHash(hHash, key, cbPassword, 0)))
    		{
    			goto hmacInit_end;
    		}
    
    		pbPassword = key;
    	}
    
    	bStatus =
    		hmacPrecomputeDigest(pContext->hInnerHash, pbPassword, cbPassword, 0x36) &&
    		hmacPrecomputeDigest(pContext->hOuterHash, pbPassword, cbPassword, 0x5C);
    
    hmacInit_end:
    
    	if (hHash) BCryptDestroyHash(hHash);
    	if (bStatus == FALSE) hmacFree(pContext);
    
    	return bStatus;
    }
    
    static BOOL hmacCalculateInternal(BCRYPT_HASH_HANDLE hHashTemplate, PUCHAR pbData, DWORD cbData, PUCHAR pbOutput, DWORD cbOutput, DWORD cbHashObject)
    {
    	BOOL success = FALSE;
    	BCRYPT_HASH_HANDLE hHash = NULL;
    	PUCHAR pbHashObject = malloc(cbHashObject);
    
    	if (BCRYPT_SUCCESS(BCryptDuplicateHash(hHashTemplate, &hHash, pbHashObject, cbHashObject, 0)))
    	{
    		success =
    			BCRYPT_SUCCESS(BCryptHashData(hHash, pbData, cbData, 0)) &&
    			BCRYPT_SUCCESS(BCryptFinishHash(hHash, pbOutput, cbOutput, 0));
    
    		BCryptDestroyHash(hHash);
    	}
    
    	free(pbHashObject);
    
    	return success;
    }
    
    static BOOL hmacCalculate(PRF_CTX* pContext, PUCHAR pbData, DWORD cbData, PUCHAR pbDigest)
    {
    	DWORD cbResult;
    	DWORD cbHashObject;
    
    	return
    		BCRYPT_SUCCESS(BCryptGetProperty(pContext->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&cbHashObject, sizeof(cbHashObject), &cbResult, 0)) &&
    		hmacCalculateInternal(pContext->hInnerHash, pbData, cbData, pbDigest, DIGEST_SIZE, cbHashObject) &&
    		hmacCalculateInternal(pContext->hOuterHash, pbDigest, DIGEST_SIZE, pbDigest, DIGEST_SIZE, cbHashObject);
    }
    
    static void xor(LPBYTE ptr1, LPBYTE ptr2, DWORD dwLen)
    {
    	while (dwLen--)
    		*ptr1++ ^= *ptr2++;
    }
    
    BOOL pbkdf2(
    	PUCHAR pbPassword,
    	ULONG cbPassword,
    	PUCHAR pbSalt,
    	ULONG cbSalt,
    	DWORD cIterations,
    	PUCHAR pbDerivedKey,
    	ULONG cbDerivedKey)
    {
    	BOOL bStatus = FALSE;
    	DWORD l, r, dwULen, i, j;
    	BYTE Ti[DIGEST_SIZE];
    	BYTE V[DIGEST_SIZE];
    	LPBYTE U = malloc(max((cbSalt + 4), DIGEST_SIZE));
    	PRF_CTX prfCtx = { 0 };
    
    	if (pbPassword == NULL || cbPassword == 0 || pbSalt == NULL || cbSalt == 0 || cIterations == 0 || pbDerivedKey == NULL || cbDerivedKey == 0)
    	{
    		return FALSE;
    	}
    
    	if (!hmacInit(&prfCtx, pbPassword, cbPassword))
    	{
    		goto PBKDF2_end;
    	}
    
    	l = (DWORD)ceil((double)cbDerivedKey / (double)DIGEST_SIZE);
    	r = cbDerivedKey - (l - 1) * DIGEST_SIZE;
    
    	for (i = 1; i <= l; i++)
    	{
    		ZeroMemory(Ti, DIGEST_SIZE);
    		for (j = 0; j < cIterations; j++)
    		{
    			if (j == 0)
    			{
    				// construct first input for PRF
    				memcpy(U, pbSalt, cbSalt);
    				U[cbSalt] = (BYTE)((i & 0xFF000000) >> 24);
    				U[cbSalt + 1] = (BYTE)((i & 0x00FF0000) >> 16);
    				U[cbSalt + 2] = (BYTE)((i & 0x0000FF00) >> 8);
    				U[cbSalt + 3] = (BYTE)((i & 0x000000FF));
    				dwULen = cbSalt + 4;
    			}
    			else
    			{
    				memcpy(U, V, DIGEST_SIZE);
    				dwULen = DIGEST_SIZE;
    			}
    
    			if (!hmacCalculate(&prfCtx, U, dwULen, V))
    			{
    				goto PBKDF2_end;
    			}
    
    			xor (Ti, V, DIGEST_SIZE);
    		}
    
    		if (i != l)
    		{
    			memcpy(&pbDerivedKey[(i - 1) * DIGEST_SIZE], Ti, DIGEST_SIZE);
    		}
    		else
    		{
    			// Take only the first r bytes
    			memcpy(&pbDerivedKey[(i - 1) * DIGEST_SIZE], Ti, r);
    		}
    	}
    
    	bStatus = TRUE;
    
    PBKDF2_end:
    
    	hmacFree(&prfCtx);
    	free(U);
    	return bStatus;
    }
    


    • Marked as answer by haide_biz Thursday, September 6, 2018 12:08 PM
    • Edited by haide_biz Friday, September 7, 2018 11:04 AM small changes in c code
    Thursday, September 6, 2018 12:08 PM