forked from Mirrors/wine-wine
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>stable
parent
158ce169e4
commit
f0a18f6f19
|
@ -8,6 +8,7 @@
|
||||||
@ stdcall BCryptDecrypt(ptr ptr long ptr ptr long ptr long ptr long)
|
@ stdcall BCryptDecrypt(ptr ptr long ptr ptr long ptr long ptr long)
|
||||||
@ stub BCryptDeleteContext
|
@ stub BCryptDeleteContext
|
||||||
@ stub BCryptDeriveKey
|
@ stub BCryptDeriveKey
|
||||||
|
@ stdcall BCryptDeriveKeyPBKDF2(ptr ptr long ptr long int64 ptr long long)
|
||||||
@ stdcall BCryptDestroyHash(ptr)
|
@ stdcall BCryptDestroyHash(ptr)
|
||||||
@ stdcall BCryptDestroyKey(ptr)
|
@ stdcall BCryptDestroyKey(ptr)
|
||||||
@ stub BCryptDestroySecret
|
@ 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 )
|
BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
|
||||||
{
|
{
|
||||||
switch (reason)
|
switch (reason)
|
||||||
|
|
|
@ -31,6 +31,8 @@ static NTSTATUS (WINAPI *pBCryptCreateHash)(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDL
|
||||||
ULONG, ULONG);
|
ULONG, ULONG);
|
||||||
static NTSTATUS (WINAPI *pBCryptDecrypt)(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG,
|
static NTSTATUS (WINAPI *pBCryptDecrypt)(BCRYPT_KEY_HANDLE, PUCHAR, ULONG, VOID *, PUCHAR, ULONG, PUCHAR, ULONG,
|
||||||
ULONG *, 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 *pBCryptDestroyHash)(BCRYPT_HASH_HANDLE);
|
||||||
static NTSTATUS (WINAPI *pBCryptDestroyKey)(BCRYPT_KEY_HANDLE);
|
static NTSTATUS (WINAPI *pBCryptDestroyKey)(BCRYPT_KEY_HANDLE);
|
||||||
static NTSTATUS (WINAPI *pBCryptDuplicateHash)(BCRYPT_HASH_HANDLE, BCRYPT_HASH_HANDLE *, UCHAR *, ULONG, ULONG);
|
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];
|
char str[65];
|
||||||
NTSTATUS ret;
|
NTSTATUS ret;
|
||||||
|
|
||||||
|
if (!pBCryptHash) /* < Win10 */
|
||||||
|
{
|
||||||
|
win_skip("BCryptHash is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
alg = NULL;
|
alg = NULL;
|
||||||
ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
|
ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_MD5_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
|
||||||
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
|
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);
|
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)
|
static void test_rng(void)
|
||||||
{
|
{
|
||||||
BCRYPT_ALG_HANDLE alg;
|
BCRYPT_ALG_HANDLE alg;
|
||||||
|
@ -1762,6 +1845,7 @@ START_TEST(bcrypt)
|
||||||
pBCryptCloseAlgorithmProvider = (void *)GetProcAddress(module, "BCryptCloseAlgorithmProvider");
|
pBCryptCloseAlgorithmProvider = (void *)GetProcAddress(module, "BCryptCloseAlgorithmProvider");
|
||||||
pBCryptCreateHash = (void *)GetProcAddress(module, "BCryptCreateHash");
|
pBCryptCreateHash = (void *)GetProcAddress(module, "BCryptCreateHash");
|
||||||
pBCryptDecrypt = (void *)GetProcAddress(module, "BCryptDecrypt");
|
pBCryptDecrypt = (void *)GetProcAddress(module, "BCryptDecrypt");
|
||||||
|
pBCryptDeriveKeyPBKDF2 = (void *)GetProcAddress(module, "BCryptDeriveKeyPBKDF2");
|
||||||
pBCryptDestroyHash = (void *)GetProcAddress(module, "BCryptDestroyHash");
|
pBCryptDestroyHash = (void *)GetProcAddress(module, "BCryptDestroyHash");
|
||||||
pBCryptDestroyKey = (void *)GetProcAddress(module, "BCryptDestroyKey");
|
pBCryptDestroyKey = (void *)GetProcAddress(module, "BCryptDestroyKey");
|
||||||
pBCryptDuplicateHash = (void *)GetProcAddress(module, "BCryptDuplicateHash");
|
pBCryptDuplicateHash = (void *)GetProcAddress(module, "BCryptDuplicateHash");
|
||||||
|
@ -1786,6 +1870,8 @@ START_TEST(bcrypt)
|
||||||
test_BCryptGenRandom();
|
test_BCryptGenRandom();
|
||||||
test_BCryptGetFipsAlgorithmMode();
|
test_BCryptGetFipsAlgorithmMode();
|
||||||
test_hashes();
|
test_hashes();
|
||||||
|
test_BcryptHash();
|
||||||
|
test_BcryptDeriveKeyPBKDF2();
|
||||||
test_rng();
|
test_rng();
|
||||||
test_aes();
|
test_aes();
|
||||||
test_BCryptGenerateSymmetricKey();
|
test_BCryptGenerateSymmetricKey();
|
||||||
|
@ -1796,10 +1882,5 @@ START_TEST(bcrypt)
|
||||||
test_RSA();
|
test_RSA();
|
||||||
test_ECDH();
|
test_ECDH();
|
||||||
|
|
||||||
if (pBCryptHash) /* >= Win 10 */
|
|
||||||
test_BcryptHash();
|
|
||||||
else
|
|
||||||
win_skip("BCryptHash is not available\n");
|
|
||||||
|
|
||||||
FreeLibrary(module);
|
FreeLibrary(module);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
@ stub BCryptDeleteContext
|
@ stub BCryptDeleteContext
|
||||||
@ stub BCryptDeriveKey
|
@ stub BCryptDeriveKey
|
||||||
@ stub BCryptDeriveKeyCapi
|
@ stub BCryptDeriveKeyCapi
|
||||||
@ stub BCryptDeriveKeyPBKDF2
|
@ stdcall BCryptDeriveKeyPBKDF2(ptr ptr long ptr long int64 ptr long long) bcrypt.BCryptDeriveKeyPBKDF2
|
||||||
@ stdcall BCryptDestroyHash(ptr) bcrypt.BCryptDestroyHash
|
@ stdcall BCryptDestroyHash(ptr) bcrypt.BCryptDestroyHash
|
||||||
@ stdcall BCryptDestroyKey(ptr) bcrypt.BCryptDestroyKey
|
@ stdcall BCryptDestroyKey(ptr) bcrypt.BCryptDestroyKey
|
||||||
@ stub BCryptDestroySecret
|
@ stub BCryptDestroySecret
|
||||||
|
|
|
@ -225,6 +225,7 @@ typedef PVOID BCRYPT_HASH_HANDLE;
|
||||||
NTSTATUS WINAPI BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE, ULONG);
|
NTSTATUS WINAPI BCryptCloseAlgorithmProvider(BCRYPT_ALG_HANDLE, ULONG);
|
||||||
NTSTATUS WINAPI BCryptCreateHash(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, 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 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 BCryptDestroyHash(BCRYPT_HASH_HANDLE);
|
||||||
NTSTATUS WINAPI BCryptDestroyKey(BCRYPT_KEY_HANDLE);
|
NTSTATUS WINAPI BCryptDestroyKey(BCRYPT_KEY_HANDLE);
|
||||||
NTSTATUS WINAPI BCryptDuplicateHash(BCRYPT_HASH_HANDLE, BCRYPT_HASH_HANDLE *, UCHAR *, ULONG, ULONG);
|
NTSTATUS WINAPI BCryptDuplicateHash(BCRYPT_HASH_HANDLE, BCRYPT_HASH_HANDLE *, UCHAR *, ULONG, ULONG);
|
||||||
|
|
Loading…
Reference in New Issue