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:
parent
158ce169e4
commit
f0a18f6f19
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue