BCryptDeriveKeyPBKDF2 replacement for Windows Embedded Compa



我必须使用Windows Embedded Compact 2013的CNG(Cryptography API:Next Generation(函数编译现有的C代码。此代码使用的是 BCryptDeriveKeyPBKDF2,这在 Windows Embedded Compact 2013 下不可用。

这意味着我需要替换下面的函数来实现 RFC 2898 第 5.2 节中定义的 PBKDF2 密钥派生算法,但不使用 BCryptDeriveKeyPBKDF2。

我在这里找到了一些使用 CryptoAPI 函数的 C 代码,但如果可能的话,我不想使用第二个已弃用的 API。

BOOL pbkdf2(
PUCHAR pbPassword,  ULONG cbPassword,
PUCHAR pbSalt, ULONG cbSalt,
ULONGLONG cIterations,
PUCHAR pbDerivedKey, ULONG cbDerivedKey)
{
NTSTATUS status;
BCRYPT_ALG_HANDLE hAlgorithm;
status = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (BCRYPT_SUCCESS(status))
{
status = BCryptDeriveKeyPBKDF2(hAlgorithm, pbPassword, cbPassword, pbSalt, cbSalt, cIterations, pbDerivedKey, cbDerivedKey, 0);
BCryptCloseAlgorithmProvider(hAlgorithm, 0);
}
return BCRYPT_SUCCESS(status);
}

可以使用 CNG 原语(如 BCryptCreateHash(来实现算法。最重要的是在BCryptOpenAlgorithmProvider中使用标志BCRYPT_ALG_HANDLE_HMAC_FLAG:

void pbkdf2()
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
std::vector<BYTE> pass = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
std::vector<BYTE> salt = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
std::vector<BYTE> derived_key(32);
std::vector<BYTE> dig(32);
byte t[] = { 0x00, 0x00, 0x00, 0x01 };
DWORD itcount = 10000;
SECURITY_STATUS status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM,
nullptr, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptCreateHash(hAlg, &hHash, nullptr, 0, pass.data(), pass.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptHashData(hHash, salt.data(), salt.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptHashData(hHash, t, 4, 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptFinishHash(hHash, dig.data(), dig.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
derived_key = dig;
BCryptDestroyHash(hHash);
for (DWORD i = 1; i < itcount; ++i)
{
status = BCryptCreateHash(hAlg, &hHash, nullptr, 0, pass.data(), pass.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptHashData(hHash, dig.data(), dig.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
status = BCryptFinishHash(hHash, dig.data(), dig.size(), 0);
if (status != ERROR_SUCCESS) {
goto Exit;
}
BCryptDestroyHash(hHash);
for (DWORD j = 0; j < dig.size(); ++j) {
derived_key[j] ^= dig[j];
}
}

Exit:
if (hHash) {
BCryptDestroyHash(hHash);
}
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
}
return;
}

编辑:澄清t[]的含义。
根据 RFC (5.2(:

对于派生密钥的每个块,应用定义的函数 F 下面到密码 P、盐 S、迭代计数 c,以及 用于计算块的块索引:

T_1 = F (P, S, c, 1) ,
T_2 = F (P, S, c, 2) ,
...
T_l = F (P, S, c, l) ,
where the function F is defined as the exclusive-or sum of the
first c iterates of the underlying pseudorandom function PRF
applied to the password P and the concatenation of the salt S
and the block index i:  F (P, S, c, i) = U_1 xor U_2 xor ... xor U_c
where
U_1 = PRF (P, S || INT (i)) ,
U_2 = PRF (P, U_1) ,
...
U_c = PRF (P, U_{c-1}) .
Here, INT (i) is a four-octet encoding of the integer i, most
significant octet first.

所以,在我的代码中 t[] - 是整数 1 的四八位字节编码(第一次迭代(,最重要的八位字节在前。

我采用了这段代码,将已弃用的 wincrypt 调用转换为新的 CNG API,对其进行重构并删除了我不需要的东西。

虽然我不明白代码在做什么,但它似乎产生的结果与我问题中使用 BCryptDeriveKeyPBKDF2 的函数相同。

#define NOCRYPT
#include <windows.h>
#include <bcrypt.h>
#include <math.h>
#include <assert.h>
#define DIGEST_SIZE 20
#define BLOCK_SIZE 64
typedef struct
{
BCRYPT_ALG_HANDLE hAlgorithm;
BCRYPT_HASH_HANDLE hInnerHash;
BCRYPT_HASH_HANDLE hOuterHash;
} PRF_CTX;
static void hmacFree(PRF_CTX* pContext)
{
if (pContext->hOuterHash) BCryptDestroyHash(pContext->hOuterHash);
if (pContext->hInnerHash) BCryptDestroyHash(pContext->hInnerHash);
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;
assert(cbPassword <= BLOCK_SIZE);
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];
if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&pContext->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, 0)) ||
!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hInnerHash, NULL, 0, NULL, 0, 0)) ||
!BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hOuterHash, NULL, 0, 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)
{
BOOL success = FALSE;
BCRYPT_HASH_HANDLE hHash = NULL;
if (BCRYPT_SUCCESS(BCryptDuplicateHash(hHashTemplate, &hHash, NULL, 0, 0)))
{
success =
BCRYPT_SUCCESS(BCryptHashData(hHash, pbData, cbData, 0)) &&
BCRYPT_SUCCESS(BCryptFinishHash(hHash, pbOutput, cbOutput, 0));
BCryptDestroyHash(hHash);
}
return success;
}
static BOOL hmacCalculate(PRF_CTX* pContext, PUCHAR pbData, DWORD cbData, PUCHAR pbDigest)
{
return
hmacCalculateInternal(pContext->hInnerHash, pbData, cbData, pbDigest, DIGEST_SIZE) &&
hmacCalculateInternal(pContext->hOuterHash, pbDigest, DIGEST_SIZE, pbDigest, DIGEST_SIZE);
}
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 };
assert(pbPassword != NULL && cbPassword != 0 && pbSalt != NULL && cbSalt != 0);
assert(cIterations > 0 && pbDerivedKey > 0 && cbDerivedKey > 0);
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;
}

相关内容

最新更新