diff --git a/dlls/bcrypt/bcrypt.spec b/dlls/bcrypt/bcrypt.spec index 891381f2a86..052a0996d43 100644 --- a/dlls/bcrypt/bcrypt.spec +++ b/dlls/bcrypt/bcrypt.spec @@ -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 diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index e511495eed9..1b84ace8abe 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -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) diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index ca7c22d5c40..5c1a9dc8340 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -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); } diff --git a/dlls/ncrypt/ncrypt.spec b/dlls/ncrypt/ncrypt.spec index c6e7f7fb6fc..85fa5c0ea56 100644 --- a/dlls/ncrypt/ncrypt.spec +++ b/dlls/ncrypt/ncrypt.spec @@ -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 diff --git a/include/bcrypt.h b/include/bcrypt.h index 919da6586bc..ba78c1d9d0b 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -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);