[PATCH 4/4] bcrypt: Implement BCryptDeriveKeyPBKDF2.

Hans Leidekker hans at codeweavers.com
Wed Feb 13 03:21:18 CST 2019


Based on a patch by Jack Grigg.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=42704
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/bcrypt/bcrypt.spec    |  1 +
 dlls/bcrypt/bcrypt_main.c  | 91 ++++++++++++++++++++++++++++++++++++++
 dlls/bcrypt/tests/bcrypt.c | 91 +++++++++++++++++++++++++++++++++++---
 dlls/ncrypt/ncrypt.spec    |  2 +-
 include/bcrypt.h           |  1 +
 5 files changed, 180 insertions(+), 6 deletions(-)

diff --git a/dlls/bcrypt/bcrypt.spec b/dlls/bcrypt/bcrypt.spec
index 891381f2a8..052a0996d4 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 e511495eed..1b84ace8ab 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 ca7c22d5c4..5c1a9dc834 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 c6e7f7fb6f..85fa5c0ea5 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 919da6586b..ba78c1d9d0 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);
-- 
2.20.1




More information about the wine-devel mailing list