bcrypt: Implement BCryptDeriveKeyPBKDF2.

Based on a patch by Jack Grigg.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=42704
Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2019-02-13 10:21:18 +01:00 committed by Alexandre Julliard
parent 158ce169e4
commit f0a18f6f19
5 changed files with 180 additions and 6 deletions

View File

@ -8,6 +8,7 @@
@ stdcall BCryptDecrypt(ptr ptr long ptr ptr long ptr long ptr long)
@ stub BCryptDeleteContext
@ stub BCryptDeriveKey
@ stdcall BCryptDeriveKeyPBKDF2(ptr ptr long ptr long int64 ptr long long)
@ stdcall BCryptDestroyHash(ptr)
@ stdcall BCryptDestroyKey(ptr)
@ stub BCryptDestroySecret

View File

@ -1461,6 +1461,97 @@ NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHA
}
}
static NTSTATUS pbkdf2( BCRYPT_ALG_HANDLE algorithm, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len,
ULONGLONG iterations, ULONG i, UCHAR *dst, ULONG hash_len )
{
BCRYPT_HASH_HANDLE handle = NULL;
NTSTATUS status = STATUS_INVALID_PARAMETER;
UCHAR bytes[4], *buf;
ULONG j, k;
if (!(buf = heap_alloc( hash_len ))) return STATUS_NO_MEMORY;
for (j = 0; j < iterations; j++)
{
status = BCryptCreateHash( algorithm, &handle, NULL, 0, pwd, pwd_len, 0 );
if (status != STATUS_SUCCESS)
goto done;
if (j == 0)
{
/* use salt || INT(i) */
status = BCryptHashData( handle, salt, salt_len, 0 );
if (status != STATUS_SUCCESS)
goto done;
bytes[0] = (i >> 24) & 0xff;
bytes[1] = (i >> 16) & 0xff;
bytes[2] = (i >> 8) & 0xff;
bytes[3] = i & 0xff;
status = BCryptHashData( handle, bytes, 4, 0 );
}
else status = BCryptHashData( handle, buf, hash_len, 0 ); /* use U_j */
if (status != STATUS_SUCCESS)
goto done;
status = BCryptFinishHash( handle, buf, hash_len, 0 );
if (status != STATUS_SUCCESS)
goto done;
if (j == 0) memcpy( dst, buf, hash_len );
else for (k = 0; k < hash_len; k++) dst[k] ^= buf[k];
BCryptDestroyHash( handle );
handle = NULL;
}
done:
BCryptDestroyHash( handle );
heap_free( buf );
return status;
}
NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULONG pwd_len, UCHAR *salt, ULONG salt_len,
ULONGLONG iterations, UCHAR *dk, ULONG dk_len, ULONG flags )
{
struct algorithm *alg = handle;
ULONG hash_len, block_count, bytes_left, i;
UCHAR *partial;
NTSTATUS status;
TRACE( "%p, %p, %u, %p, %u, %s, %p, %u, %08x\n", handle, pwd, pwd_len, salt, salt_len,
wine_dbgstr_longlong(iterations), dk, dk_len, flags );
if (!alg || alg->hdr.magic != MAGIC_ALG) return STATUS_INVALID_HANDLE;
hash_len = alg_props[alg->id].hash_length;
if (dk_len <= 0 || dk_len > ((((ULONGLONG)1) << 32) - 1) * hash_len) return STATUS_INVALID_PARAMETER;
block_count = 1 + ((dk_len - 1) / hash_len); /* ceil(dk_len / hash_len) */
bytes_left = dk_len - (block_count - 1) * hash_len;
/* full blocks */
for (i = 1; i < block_count; i++)
{
status = pbkdf2( handle, pwd, pwd_len, salt, salt_len, iterations, i, dk + ((i - 1) * hash_len), hash_len );
if (status != STATUS_SUCCESS)
return status;
}
/* final partial block */
if (!(partial = heap_alloc( hash_len ))) return STATUS_NO_MEMORY;
status = pbkdf2( handle, pwd, pwd_len, salt, salt_len, iterations, block_count, partial, hash_len );
if (status != STATUS_SUCCESS)
{
heap_free( partial );
return status;
}
memcpy( dk + ((block_count - 1) * hash_len), partial, bytes_left );
heap_free( partial );
return STATUS_SUCCESS;
}
BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
{
switch (reason)

View File

@ -31,6 +31,8 @@ static NTSTATUS (WINAPI *pBCryptCreateHash)(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDL
ULONG, ULONG);
static NTSTATUS (WINAPI *pBCryptDecrypt)(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG,
ULONG *, ULONG);
static NTSTATUS (WINAPI *pBCryptDeriveKeyPBKDF2)(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, PUCHAR, ULONG, ULONGLONG,
PUCHAR, ULONG, ULONG);
static NTSTATUS (WINAPI *pBCryptDestroyHash)(BCRYPT_HASH_HANDLE);
static NTSTATUS (WINAPI *pBCryptDestroyKey)(BCRYPT_KEY_HANDLE);
static NTSTATUS (WINAPI *pBCryptDuplicateHash)(BCRYPT_HASH_HANDLE, BCRYPT_HASH_HANDLE *, UCHAR *, ULONG, ULONG);
@ -373,6 +375,12 @@ static void test_BcryptHash(void)
char str[65];
NTSTATUS ret;
if (!pBCryptHash) /* < Win10 */
{
win_skip("BCryptHash is not available\n");
return;
}
alg = NULL;
ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
@ -405,6 +413,81 @@ static void test_BcryptHash(void)
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
}
/* test vectors from RFC 6070 */
static UCHAR password[] = "password";
static UCHAR salt[] = "salt";
static UCHAR long_password[] = "passwordPASSWORDpassword";
static UCHAR long_salt[] = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
static UCHAR password_NUL[] = "pass\0word";
static UCHAR salt_NUL[] = "sa\0lt";
static UCHAR dk1[] = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
static UCHAR dk2[] = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
static UCHAR dk3[] = "4b007901b765489abead49d926f721d065a429c1";
static UCHAR dk4[] = "364dd6bc200ec7d197f1b85f4a61769010717124";
static UCHAR dk5[] = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
static UCHAR dk6[] = "56fa6aa75548099dcc37d7f03425e0c3";
static const struct
{
ULONG pwd_len;
ULONG salt_len;
ULONGLONG iterations;
ULONG dk_len;
UCHAR *pwd;
UCHAR *salt;
const UCHAR *dk;
} rfc6070[] =
{
{ 8, 4, 1, 20, password, salt, dk1 },
{ 8, 4, 2, 20, password, salt, dk2 },
{ 8, 4, 4096, 20, password, salt, dk3 },
{ 8, 4, 1000000, 20, password, salt, dk4 },
{ 24, 36, 4096, 25, long_password, long_salt, dk5 },
{ 9, 5, 4096, 16, password_NUL, salt_NUL, dk6 }
};
static void test_BcryptDeriveKeyPBKDF2(void)
{
BCRYPT_ALG_HANDLE alg;
UCHAR buf[25];
char str[51];
NTSTATUS ret;
ULONG i;
if (!pBCryptDeriveKeyPBKDF2) /* < Win7 */
{
win_skip("BCryptDeriveKeyPBKDF2 is not available\n");
return;
}
alg = NULL;
ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER,
BCRYPT_ALG_HANDLE_HMAC_FLAG);
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
ok(alg != NULL, "alg not set\n");
test_hash_length(alg, 20);
test_alg_name(alg, "SHA1");
ret = pBCryptDeriveKeyPBKDF2(alg, rfc6070[0].pwd, rfc6070[0].pwd_len, rfc6070[0].salt, rfc6070[0].salt_len,
0, buf, rfc6070[0].dk_len, 0);
ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret);
for (i = 0; i < ARRAY_SIZE(rfc6070); i++)
{
memset(buf, 0, sizeof(buf));
ret = pBCryptDeriveKeyPBKDF2(alg, rfc6070[i].pwd, rfc6070[i].pwd_len, rfc6070[i].salt, rfc6070[i].salt_len,
rfc6070[i].iterations, buf, rfc6070[i].dk_len, 0);
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
format_hash(buf, rfc6070[i].dk_len, str);
ok(!memcmp(str, rfc6070[i].dk, rfc6070[i].dk_len), "got %s\n", str);
}
ret = pBCryptCloseAlgorithmProvider(alg, 0);
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
}
static void test_rng(void)
{
BCRYPT_ALG_HANDLE alg;
@ -1762,6 +1845,7 @@ START_TEST(bcrypt)
pBCryptCloseAlgorithmProvider = (void *)GetProcAddress(module, "BCryptCloseAlgorithmProvider");
pBCryptCreateHash = (void *)GetProcAddress(module, "BCryptCreateHash");
pBCryptDecrypt = (void *)GetProcAddress(module, "BCryptDecrypt");
pBCryptDeriveKeyPBKDF2 = (void *)GetProcAddress(module, "BCryptDeriveKeyPBKDF2");
pBCryptDestroyHash = (void *)GetProcAddress(module, "BCryptDestroyHash");
pBCryptDestroyKey = (void *)GetProcAddress(module, "BCryptDestroyKey");
pBCryptDuplicateHash = (void *)GetProcAddress(module, "BCryptDuplicateHash");
@ -1786,6 +1870,8 @@ START_TEST(bcrypt)
test_BCryptGenRandom();
test_BCryptGetFipsAlgorithmMode();
test_hashes();
test_BcryptHash();
test_BcryptDeriveKeyPBKDF2();
test_rng();
test_aes();
test_BCryptGenerateSymmetricKey();
@ -1796,10 +1882,5 @@ START_TEST(bcrypt)
test_RSA();
test_ECDH();
if (pBCryptHash) /* >= Win 10 */
test_BcryptHash();
else
win_skip("BCryptHash is not available\n");
FreeLibrary(module);
}

View File

@ -9,7 +9,7 @@
@ stub BCryptDeleteContext
@ stub BCryptDeriveKey
@ stub BCryptDeriveKeyCapi
@ stub BCryptDeriveKeyPBKDF2
@ stdcall BCryptDeriveKeyPBKDF2(ptr ptr long ptr long int64 ptr long long) bcrypt.BCryptDeriveKeyPBKDF2
@ stdcall BCryptDestroyHash(ptr) bcrypt.BCryptDestroyHash
@ stdcall BCryptDestroyKey(ptr) bcrypt.BCryptDestroyKey
@ stub BCryptDestroySecret

View File

@ -225,6 +225,7 @@ typedef PVOID BCRYPT_HASH_HANDLE;
NTSTATUS WINAPI BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE, ULONG);
NTSTATUS WINAPI BCryptCreateHash(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG);
NTSTATUS WINAPI BCryptDecrypt(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG *, ULONG);
NTSTATUS WINAPI BCryptDeriveKeyPBKDF2(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, PUCHAR, ULONG, ULONGLONG, PUCHAR, ULONG, ULONG);
NTSTATUS WINAPI BCryptDestroyHash(BCRYPT_HASH_HANDLE);
NTSTATUS WINAPI BCryptDestroyKey(BCRYPT_KEY_HANDLE);
NTSTATUS WINAPI BCryptDuplicateHash(BCRYPT_HASH_HANDLE, BCRYPT_HASH_HANDLE *, UCHAR *, ULONG, ULONG);