From d232882c571a14f4da8a134071a2125805ebd41f Mon Sep 17 00:00:00 2001
From: Derek Lesho <dlesho@codeweavers.com>
Date: Tue, 7 Jan 2020 14:22:49 -0600
Subject: [PATCH] bcrypt: Implement BCRYPT_KDF_HASH.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47699
Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
 dlls/bcrypt/bcrypt_main.c  | 108 ++++++++++++++++++++++++++++++++++++-
 dlls/bcrypt/tests/bcrypt.c |   2 +-
 2 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 65c28ca63e2..6e7b52e93b0 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -1891,7 +1891,113 @@ NTSTATUS WINAPI BCryptDeriveKey(BCRYPT_SECRET_HANDLE handle, LPCWSTR kdf, BCrypt
     if (!secret || secret->hdr.magic != MAGIC_SECRET) return STATUS_INVALID_HANDLE;
     if (!kdf) return STATUS_INVALID_PARAMETER;
 
-    if (!(strcmpW( kdf, BCRYPT_KDF_RAW_SECRET )))
+    if (flags) FIXME("flags ignored: %08x\n", flags);
+
+    if (!(strcmpW( kdf, BCRYPT_KDF_HASH )))
+    {
+        unsigned int i;
+        BCryptBuffer *hash_algorithm = NULL;
+        BCryptBuffer *secret_prepend = NULL;
+        BCryptBuffer *secret_append = NULL;
+        enum alg_id hash_alg_id;
+        ULONG hash_length;
+        struct hash_impl hash;
+        NTSTATUS status;
+
+        if (parameter)
+        {
+            for (i = 0; i < parameter->cBuffers; i++)
+            {
+                BCryptBuffer *cur_buffer = &parameter->pBuffers[i];
+                switch(cur_buffer->BufferType)
+                {
+                case KDF_HASH_ALGORITHM:
+                    if (hash_algorithm)
+                        FIXME("Duplicate KDF_HASH_ALGORITHM, untested\n");
+                    hash_algorithm = cur_buffer;
+                    break;
+                case KDF_SECRET_PREPEND:
+                    if (secret_prepend)
+                        FIXME("Multiple prefixes unsupported\n");
+                    secret_prepend = cur_buffer;
+                    break;
+                case KDF_SECRET_APPEND:
+                    if (secret_append)
+                        FIXME("Multiple suffixes unsupported\n");
+                    secret_append = cur_buffer;
+                    break;
+                default:
+                    FIXME("Unsupported BCRYPT_KDF_HASH parameter type %x\n", cur_buffer->BufferType);
+                    break;
+                }
+            }
+        }
+
+        if (!(hash_algorithm))
+            hash_alg_id = ALG_ID_SHA1;
+        else
+        {
+            for (i = 0; i < ARRAY_SIZE( builtin_algorithms ); i++)
+            {
+                if (!strcmpW( hash_algorithm->pvBuffer, builtin_algorithms[i].name))
+                {
+                    hash_alg_id = i;
+                    break;
+                }
+            }
+            if (i == ARRAY_SIZE(builtin_algorithms))
+            {
+                WARN("Algorithm %s not found\n", debugstr_w(hash_algorithm->pvBuffer));
+                return STATUS_NOT_SUPPORTED;
+            }
+            if (builtin_algorithms[hash_alg_id].class != BCRYPT_HASH_INTERFACE)
+            {
+                return STATUS_NOT_SUPPORTED;
+            }
+        }
+
+        hash_length = builtin_algorithms[hash_alg_id].hash_length;
+
+        if (!derived)
+        {
+            *result = hash_length;
+            return STATUS_SUCCESS;
+        }
+
+        if ((status = hash_init(&hash, hash_alg_id)))
+        {
+            return status;
+        }
+
+        if (secret_prepend)
+        {
+            hash_update(&hash, hash_alg_id, secret_prepend->pvBuffer, secret_prepend->cbBuffer);
+        }
+
+        hash_update(&hash, hash_alg_id, secret->data, secret->len);
+
+        if (secret_append)
+        {
+            hash_update(&hash, hash_alg_id, secret_append->pvBuffer, secret_append->cbBuffer);
+        }
+
+        if (derived_size >= hash_length)
+        {
+            hash_finish(&hash, hash_alg_id, derived, derived_size);
+            *result = hash_length;
+        }
+        else
+        {
+            UCHAR *output = heap_alloc(hash_length);
+            hash_finish(&hash, hash_alg_id, output, hash_length);
+            memcpy(derived, output, derived_size);
+            heap_free(output);
+            *result = derived_size;
+        }
+
+        return STATUS_SUCCESS;
+    }
+    else if (!(strcmpW( kdf, BCRYPT_KDF_RAW_SECRET )))
     {
         ULONG n;
         ULONG secret_length = secret->len;
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c
index a351aacf1f5..5333b879817 100644
--- a/dlls/bcrypt/tests/bcrypt.c
+++ b/dlls/bcrypt/tests/bcrypt.c
@@ -2085,7 +2085,7 @@ static void test_ECDH(void)
     raw_secret_end:
 
     status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0);
-    todo_wine ok (status == STATUS_SUCCESS, "got %08x\n", status);
+    ok (status == STATUS_SUCCESS, "got %08x\n", status);
 
     if (status != STATUS_SUCCESS)
     {
-- 
2.27.0

