Windows Embedded Compact 2013的BCryptDeriveKeyPBKDF2替代

时间:2018-09-03 17:24:52

标签: cryptography windows-embedded-compact cng

我必须使用CNG (Cryptography API: Next Generation)函数为Windows Embedded Compact 2013编译现有的C代码。此代码使用BCryptDeriveKeyPBKDF2,而Windows Embedded Compact 2013中不提供此代码。

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

我发现一些使用CryptoAPI函数here的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);
}

2 个答案:

答案 0 :(得分:0)

您可以使用CNG原语(例如BCryptCreateHash)来实现算法。最重要的是在original function中使用标志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 []的含义。
根据{{​​3}}(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的四个八位字节编码(对于第一次迭代),最高有效八位字节在前。

答案 1 :(得分:0)

我拿了this code,将已弃用的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;
}