[PATCH v4 1/2] bcrypt: Implement BCryptSecretAgreement with libgcrypt.

Derek Lesho dlesho at codeweavers.com
Fri Jan 24 09:30:57 CST 2020


Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
v4:
  - Add support for older gcrypt versions (for Proton).
  - Skip tests if BCryptSecretAgreement fails.
---
 configure.ac                  |  14 ++
 dlls/bcrypt/Makefile.in       |   1 +
 dlls/bcrypt/bcrypt_internal.h |  13 ++
 dlls/bcrypt/bcrypt_main.c     |  75 +++++++-
 dlls/bcrypt/gcrypt.c          | 337 ++++++++++++++++++++++++++++++++++
 dlls/bcrypt/gnutls.c          |   9 +
 dlls/bcrypt/tests/bcrypt.c    |   4 +-
 7 files changed, 441 insertions(+), 12 deletions(-)
 create mode 100644 dlls/bcrypt/gcrypt.c

diff --git a/configure.ac b/configure.ac
index 681d315eed..0fc02a533a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@ AC_ARG_WITH(faudio,    AS_HELP_STRING([--without-faudio],[do not use FAudio (XAu
 AC_ARG_WITH(float-abi, AS_HELP_STRING([--with-float-abi=abi],[specify the ABI (soft|softfp|hard) for ARM platforms]))
 AC_ARG_WITH(fontconfig,AS_HELP_STRING([--without-fontconfig],[do not use fontconfig]))
 AC_ARG_WITH(freetype,  AS_HELP_STRING([--without-freetype],[do not use the FreeType library]))
+AC_ARG_WITH(gcrypt,    AS_HELP_STRING([--without-gcrypt],[do not use libgcrypt]))
 AC_ARG_WITH(gettext,   AS_HELP_STRING([--without-gettext],[do not use gettext]))
 AC_ARG_WITH(gettextpo, AS_HELP_STRING([--with-gettextpo],[use the GetTextPO library to rebuild po files]),
             [if test "x$withval" = "xno"; then ac_cv_header_gettext_po_h=no; fi])
@@ -1987,6 +1988,19 @@ WINE_NOTICE_WITH(vkd3d,[test "x$ac_cv_lib_soname_vkd3d" = "x"],
                  [vkd3d ${notice_platform}development files not found (or too old), Direct3D 12 won't be supported.])
 test "x$ac_cv_lib_soname_vkd3d" != "x" || enable_d3d12=${enable_d3d12:-no}
 
+dnl **** Check for gcrypt ****
+if test "x$with_gcrypt" != "xno"
+then
+    WINE_PACKAGE_FLAGS(GCRYPT,[libgcrypt],,,,
+        [AC_CHECK_HEADERS([gcrypt.h])
+        if test "$ac_cv_header_gcrypt_h" = "yes"
+        then
+            WINE_CHECK_SONAME(gcrypt,gcry_sexp_build,,,[$GCRYPT_LIBS])
+        fi])
+fi
+WINE_NOTICE_WITH(gcrypt,[test "x$ac_cv_lib_soname_gcrypt" = "x"],
+                 [libgcrypt ${notice_platform}development files not found, GCRYPT won't be supported.])
+
 dnl **** Check for gcc specific options ****
 
 AC_SUBST(EXTRACFLAGS,"")
diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in
index dd6d4a7664..ea3486a400 100644
--- a/dlls/bcrypt/Makefile.in
+++ b/dlls/bcrypt/Makefile.in
@@ -5,6 +5,7 @@ EXTRAINCL = $(GNUTLS_CFLAGS)
 
 C_SRCS = \
 	bcrypt_main.c \
+	gcrypt.c \
 	gnutls.c \
 	macos.c \
 	md2.c \
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h
index d026dab729..09e675a655 100644
--- a/dlls/bcrypt/bcrypt_internal.h
+++ b/dlls/bcrypt/bcrypt_internal.h
@@ -25,6 +25,9 @@
 #include <gnutls/gnutls.h>
 #include <gnutls/crypto.h>
 #include <gnutls/abstract.h>
+#ifdef SONAME_LIBGCRYPT
+#include <gcrypt.h>
+#endif
 #elif HAVE_COMMONCRYPTO_COMMONCRYPTOR_H
 #include <AvailabilityMacros.h>
 #include <CommonCrypto/CommonCryptor.h>
@@ -157,6 +160,12 @@ struct algorithm
     BOOL          hmac;
 };
 
+struct secret
+{
+    UCHAR *data;
+    ULONG len;
+};
+
 #if defined(HAVE_GNUTLS_CIPHER_INIT)
 struct key_symmetric
 {
@@ -251,6 +260,7 @@ NTSTATUS key_destroy( struct key * ) DECLSPEC_HIDDEN;
 BOOL key_is_symmetric( struct key * ) DECLSPEC_HIDDEN;
 NTSTATUS key_export_ecc( struct key *, UCHAR *, ULONG, ULONG * ) DECLSPEC_HIDDEN;
 NTSTATUS key_import_ecc( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS compute_secret_ecc (struct key *pubkey_in, struct key *privkey_in, struct secret *secret) DECLSPEC_HIDDEN;
 
 BOOL is_zero_vector( const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
 BOOL is_equal_vector( const UCHAR *, ULONG, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
@@ -258,4 +268,7 @@ BOOL is_equal_vector( const UCHAR *, ULONG, const UCHAR *, ULONG ) DECLSPEC_HIDD
 BOOL gnutls_initialize(void) DECLSPEC_HIDDEN;
 void gnutls_uninitialize(void) DECLSPEC_HIDDEN;
 
+BOOL gcrypt_initialize(void) DECLSPEC_HIDDEN;
+void gcrypt_uninitialize(void) DECLSPEC_HIDDEN;
+
 #endif /* __BCRYPT_INTERNAL_H */
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 2ac36d3db0..ed64cd1965 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -1331,6 +1331,12 @@ NTSTATUS key_import_ecc( struct key *key, UCHAR *input, ULONG len )
     ERR( "support for keys not available at build time\n" );
     return STATUS_NOT_IMPLEMENTED;
 }
+
+NTSTATUS compute_secret_ecc (struct key *pubkey_in, struct key *privkey_in, struct secret *secret)
+{
+    ERR( "support for secrets not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
 #endif
 
 NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE algorithm, BCRYPT_KEY_HANDLE *handle,
@@ -1696,27 +1702,70 @@ NTSTATUS WINAPI BCryptDeriveKeyPBKDF2( BCRYPT_ALG_HANDLE handle, UCHAR *pwd, ULO
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptSecretAgreement(BCRYPT_KEY_HANDLE handle, BCRYPT_KEY_HANDLE key, BCRYPT_SECRET_HANDLE *secret, ULONG flags)
+NTSTATUS WINAPI BCryptSecretAgreement(BCRYPT_KEY_HANDLE hPrivKey, BCRYPT_KEY_HANDLE hPubKey, BCRYPT_SECRET_HANDLE *secret_out, ULONG flags)
 {
-    FIXME( "%p, %p, %p, %08x\n", handle, key, secret, flags );
+    struct key *privkey = hPrivKey;
+    struct key *pubkey = hPubKey;
+    struct secret *secret;
+    NTSTATUS status;
 
-    if(secret)
-        *secret = (BCRYPT_SECRET_HANDLE *)0xDEADFEED;
+    TRACE( "%p, %p, %p, %08x\n", hPrivKey, hPubKey, secret_out, flags );
 
-    return STATUS_SUCCESS;
+    secret = heap_alloc( sizeof(*secret) );
+
+    if ((status = compute_secret_ecc(privkey, pubkey, secret)))
+    {
+        heap_free(secret);
+    }
+    else
+    {
+        *secret_out = secret;
+    }
+
+    return status;
 }
 
-NTSTATUS WINAPI BCryptDestroySecret(BCRYPT_SECRET_HANDLE secret)
+NTSTATUS WINAPI BCryptDestroySecret(BCRYPT_SECRET_HANDLE hSecret)
 {
-    FIXME( "%p\n", secret );
+    struct secret *secret = hSecret;
+
+    TRACE( "%p\n", hSecret );
+
+    heap_free(secret->data);
+    heap_free(secret);
+
     return STATUS_SUCCESS;
 }
 
-NTSTATUS WINAPI BCryptDeriveKey(BCRYPT_SECRET_HANDLE secret, LPCWSTR kdf, BCryptBufferDesc *parameter,
+NTSTATUS WINAPI BCryptDeriveKey(BCRYPT_SECRET_HANDLE hSecret, LPCWSTR deriv_func, BCryptBufferDesc *parameter,
         PUCHAR derived, ULONG derived_size, ULONG *result, ULONG flags)
 {
-    FIXME( "%p, %s, %p, %p, %d, %p, %08x\n", secret, debugstr_w(kdf), parameter, derived, derived_size, result, flags );
-    return STATUS_INTERNAL_ERROR;
+    struct secret *secret = hSecret;
+
+    TRACE( "%p, %s, %p, %p, %d, %p, %08x\n", secret, debugstr_w(deriv_func), parameter, derived, derived_size, result, flags );
+
+    if (!(strcmpW(deriv_func, BCRYPT_KDF_RAW_SECRET)))
+    {
+        ULONG n;
+        ULONG secret_length = secret->len;
+
+        if (!derived)
+        {
+            *result = secret_length;
+            return STATUS_SUCCESS;
+        }
+
+        /* outputs in little endian for some reason */
+        for (n = 0; n < secret_length && n < derived_size; n++)
+        {
+            derived[n] = secret->data[secret_length - n - 1];
+        }
+
+        *result = n;
+        return STATUS_SUCCESS;
+    }
+    FIXME( "Derivation function %s not supported.\n", debugstr_w(deriv_func) );
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
@@ -1728,6 +1777,9 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
         DisableThreadLibraryCalls( hinst );
 #ifdef HAVE_GNUTLS_CIPHER_INIT
         gnutls_initialize();
+#ifdef SONAME_LIBGCRYPT
+        gcrypt_initialize();
+#endif
 #endif
         break;
 
@@ -1735,6 +1787,9 @@ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
         if (reserved) break;
 #ifdef HAVE_GNUTLS_CIPHER_INIT
         gnutls_uninitialize();
+#ifdef SONAME_LIBGCRYPT
+    gcrypt_uninitialize();
+#endif
 #endif
         break;
     }
diff --git a/dlls/bcrypt/gcrypt.c b/dlls/bcrypt/gcrypt.c
new file mode 100644
index 0000000000..53c39e0b48
--- /dev/null
+++ b/dlls/bcrypt/gcrypt.c
@@ -0,0 +1,337 @@
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+#ifdef HAVE_GNUTLS_CIPHER_INIT
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
+#ifdef HAVE_LIBGCRYPT
+#include <libgcrypt/libgcrypt.h>
+#endif
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "bcrypt.h"
+
+#include "bcrypt_internal.h"
+
+#include "wine/debug.h"
+#include "wine/heap.h"
+#include "wine/library.h"
+#include "wine/unicode.h"
+
+#if defined(HAVE_GNUTLS_CIPHER_INIT) &&  defined(SONAME_LIBGCRYPT)
+WINE_DEFAULT_DEBUG_CHANNEL(bcrypt);
+WINE_DECLARE_DEBUG_CHANNEL(winediag);
+
+static void *libgcrypt_handle;
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f
+MAKE_FUNCPTR(gcry_sexp_build);
+MAKE_FUNCPTR(gcry_pk_encrypt);
+MAKE_FUNCPTR(gcry_mpi_new);
+MAKE_FUNCPTR(gcry_mpi_print);
+MAKE_FUNCPTR(gcry_sexp_release);
+MAKE_FUNCPTR(gcry_mpi_release);
+MAKE_FUNCPTR(gcry_strsource);
+MAKE_FUNCPTR(gcry_strerror);
+#if (GCRYPT_VERSION_NUMBER >= 0x010700)
+MAKE_FUNCPTR(gcry_sexp_extract_param);
+MAKE_FUNCPTR(gcry_mpi_point_new);
+MAKE_FUNCPTR(gcry_mpi_ec_decode_point);
+MAKE_FUNCPTR(gcry_mpi_point_snatch_get);
+#else
+MAKE_FUNCPTR(gcry_sexp_find_token);
+MAKE_FUNCPTR(gcry_sexp_nth_mpi);
+#endif
+#undef MAKE_FUNCPTR
+
+BOOL gcrypt_initialize(void)
+{
+    if (!(libgcrypt_handle = wine_dlopen( SONAME_LIBGCRYPT, RTLD_NOW, NULL, 0 )))
+    {
+        ERR_(winediag)( "failed to load libgcrypt, no support for diffie hellman key exchange\n" );
+        return FALSE;
+    }
+
+#define LOAD_FUNCPTR(f) \
+    if (!(p##f = wine_dlsym( libgcrypt_handle, #f, NULL, 0 ))) \
+    { \
+        ERR( "failed to load %s\n", #f ); \
+        goto fail; \
+    }
+
+    LOAD_FUNCPTR(gcry_sexp_build);
+    LOAD_FUNCPTR(gcry_pk_encrypt);
+    LOAD_FUNCPTR(gcry_mpi_new);
+    LOAD_FUNCPTR(gcry_mpi_print);
+    LOAD_FUNCPTR(gcry_sexp_release);
+    LOAD_FUNCPTR(gcry_mpi_release);
+    LOAD_FUNCPTR(gcry_strsource);
+    LOAD_FUNCPTR(gcry_strerror);
+#if (GCRYPT_VERSION_NUMBER >= 0x010700)
+    LOAD_FUNCPTR(gcry_sexp_extract_param);
+    LOAD_FUNCPTR(gcry_mpi_point_new);
+    LOAD_FUNCPTR(gcry_mpi_ec_decode_point);
+    LOAD_FUNCPTR(gcry_mpi_point_snatch_get);
+#else
+    LOAD_FUNCPTR(gcry_sexp_find_token);
+    LOAD_FUNCPTR(gcry_sexp_nth_mpi);
+#endif
+#undef LOAD_FUNCPTR
+
+    return TRUE;
+
+fail:
+    wine_dlclose( libgcrypt_handle, NULL, 0 );
+    libgcrypt_handle = NULL;
+    return FALSE;
+}
+
+
+void gcrypt_uninitialize(void)
+{
+    wine_dlclose( libgcrypt_handle, NULL, 0 );
+    libgcrypt_handle = NULL;
+}
+
+NTSTATUS extract_result_into_secret(gcry_sexp_t result, struct secret *secret)
+{
+    gcry_error_t err;
+    NTSTATUS status = STATUS_SUCCESS;
+#if (GCRYPT_VERSION_NUMBER >= 0x010700)
+    gcry_mpi_t result_mpi = NULL;
+    gcry_mpi_point_t result_point = NULL;
+    gcry_mpi_t secret_key = NULL;
+    UCHAR *tmp_buffer;
+    size_t size;
+
+    if ((err = pgcry_sexp_extract_param(result, "", "s", &result_mpi, NULL)))
+    {
+        ERR("Failed to extract result of key exchange\n");
+        goto done;
+    }
+
+    result_point = pgcry_mpi_point_new(0);
+
+    if ((err = pgcry_mpi_ec_decode_point(result_point, result_mpi, NULL)))
+    {
+        ERR("Failed decoding point from result\n");
+        goto done;
+    }
+
+    secret_key = pgcry_mpi_new(0);
+    if (!secret_key)
+    {
+        status = STATUS_NO_MEMORY;
+        goto done;
+    }
+
+    /* releases result_point, (we don't need to free it later) */
+    pgcry_mpi_point_snatch_get(secret_key, NULL, NULL, result_point);
+
+    if ((err = pgcry_mpi_print(GCRYMPI_FMT_STD, NULL, 0, &size, secret_key)))
+    {
+        goto done;
+    }
+
+    tmp_buffer = heap_alloc(size);
+    if ((err = pgcry_mpi_print(GCRYMPI_FMT_STD, tmp_buffer, size, NULL, secret_key)))
+    {
+        heap_free(tmp_buffer);
+        goto done;
+    }
+
+    if (size % 2 == 0)
+    {
+        secret->data = tmp_buffer;
+        secret->len = size;
+    }
+    else
+    {
+        secret->data = heap_alloc(size - 1);
+        memcpy(secret->data, tmp_buffer + 1, size - 1);
+        secret->len = size - 1;
+        heap_free(tmp_buffer);
+    }
+
+    done:
+    pgcry_mpi_release(result_mpi);
+    pgcry_mpi_release(secret_key);
+#else
+    gcry_sexp_t fragment = NULL;
+    gcry_mpi_t fullcoords = NULL;
+    UCHAR *tmp_buffer, *pos;
+    size_t size;
+
+    fragment = pgcry_sexp_find_token(result, "s", 0);
+    if (!fragment)
+    {
+        status = STATUS_NO_MEMORY;
+        goto done;
+    }
+
+    fullcoords = pgcry_sexp_nth_mpi(fragment, 1, GCRYMPI_FMT_USG);
+    if (!fullcoords)
+    {
+        status = STATUS_NO_MEMORY;
+        goto done;
+    }
+
+    if ((err = pgcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &size, fullcoords)))
+    {
+        goto done;
+    }
+
+    tmp_buffer = heap_alloc(size);
+    if ((err = pgcry_mpi_print(GCRYMPI_FMT_STD, tmp_buffer, size, NULL, fullcoords)))
+    {
+        goto done;
+    }
+
+    secret->data = heap_alloc(size / 2);
+    memcpy(secret->data, tmp_buffer + size % 2, size / 2);
+    secret->len = size / 2;
+
+    done:
+    if (tmp_buffer)
+    {
+        heap_free(tmp_buffer);
+    }
+    pgcry_mpi_release(fullcoords);
+    pgcry_sexp_release(fragment);
+#endif
+    if (status)
+    {
+        return status;
+    }
+    if (err)
+    {
+        ERR("Error = %s/%s\n", pgcry_strsource (err), pgcry_strerror (err));
+        return STATUS_INTERNAL_ERROR;
+    }
+    return STATUS_SUCCESS;
+}
+
+/* this is necessary since GNUTLS doesn't support ECDH public key encryption, maybe we can replace this when it does:
+   https://github.com/gnutls/gnutls/blob/cdc4fc288d87f91f974aa23b6e8595a53970ce00/lib/nettle/pk.c#L495 */
+NTSTATUS compute_secret_ecc (struct key *privkey_in, struct key *pubkey_in, struct secret *secret)
+{
+    const char *pubkey_format;
+    DWORD key_size;
+    gcry_sexp_t pubkey = NULL;
+    gcry_sexp_t privkey = NULL;
+    gcry_sexp_t xchg_result = NULL;
+    gcry_error_t err;
+    NTSTATUS status = STATUS_SUCCESS;
+
+    switch (pubkey_in->alg_id)
+    {
+        case ALG_ID_ECDH_P256:
+            pubkey_format = "NIST P-256";
+            key_size = 32;
+            break;
+        default:
+            FIXME("Unsupported algorithm id: %u\n", pubkey_in->alg_id);
+            return STATUS_INTERNAL_ERROR;
+    }
+
+    /* import public key -
+       copy public key into temporary buffer so we can prepend 0x04 (to indicate it is uncompressed) */
+    {
+        UCHAR *public_key_raw = heap_alloc((key_size * 2) + 1);
+        public_key_raw[0] = 0x04;
+        memcpy(public_key_raw + 1, pubkey_in->u.a.pubkey + sizeof(BCRYPT_ECCKEY_BLOB), key_size * 2);
+
+        err = pgcry_sexp_build(&pubkey, NULL,
+                            "(key-data(public-key(ecdh(curve %s)(q %b))))",
+                            pubkey_format,
+                            (key_size * 2) + 1,
+                            public_key_raw);
+
+        heap_free(public_key_raw);
+    }
+
+    if (err)
+    {
+        ERR("Failed to build gcrypt public key\n");
+        goto done;
+    }
+
+    /* import private key */
+    /* extract private key from blob structure */
+    {
+        UCHAR *private_key_raw = heap_alloc(key_size);
+        UCHAR *key_blob;
+        ULONG blob_size;
+
+        status = key_export_ecc( privkey_in, NULL, 0, &blob_size );
+        if (status)
+            goto done;
+
+        key_blob = heap_alloc(blob_size);
+        status = key_export_ecc( privkey_in, key_blob, blob_size, &blob_size);
+        if (status)
+        {
+            heap_free(private_key_raw);
+            heap_free(key_blob);
+            goto done;
+        }
+
+        memcpy(private_key_raw, key_blob + sizeof(BCRYPT_ECCKEY_BLOB) + key_size * 2, key_size);
+        heap_free(key_blob);
+
+        err = pgcry_sexp_build(&privkey, NULL,
+                            "(data(flags raw)(value %b))",
+                            key_size,
+                            private_key_raw);
+
+        heap_free(private_key_raw);
+    }
+
+    if (err)
+    {
+        ERR("Failed to build gcrypt private key data\n");
+        goto done;
+    }
+
+    if ((err = pgcry_pk_encrypt(&xchg_result, privkey, pubkey)))
+    {
+        ERR("Failed to perform key exchange\n");
+        goto done;
+    }
+
+    status = extract_result_into_secret(xchg_result, secret);
+    if (status)
+    {
+        ERR("Failed to extract secret key\n");
+        goto done;
+    }
+
+    if (secret->len != key_size)
+    {
+        ERR("got secret size %u, expected %u\n", secret->len, key_size);
+        status = STATUS_INTERNAL_ERROR;
+        goto done;
+    }
+
+    done:
+    pgcry_sexp_release(pubkey);
+    pgcry_sexp_release(privkey);
+    pgcry_sexp_release(xchg_result);
+
+    if (status)
+    {
+        return status;
+    }
+    if (err)
+    {
+        ERR("Error = %s/%s\n", pgcry_strsource (err), pgcry_strerror (err));
+        return STATUS_INTERNAL_ERROR;
+    }
+    return STATUS_SUCCESS;
+}
+#endif
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c
index 868f898bbb..d9680cb130 100644
--- a/dlls/bcrypt/gnutls.c
+++ b/dlls/bcrypt/gnutls.c
@@ -1297,4 +1297,13 @@ NTSTATUS key_destroy( struct key *key )
     heap_free( key );
     return STATUS_SUCCESS;
 }
+
+#ifndef SONAME_LIBGCRYPT
+NTSTATUS compute_secret_ecc (struct key *pubkey_in, struct key *privkey_in, struct secret *secret)
+{
+    ERR("support for secrets not available without gcrypt\n");
+    return STATUS_NOT_IMPLEMENTED;
+}
+#endif
+
 #endif
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c
index d26150f469..985a6f379b 100644
--- a/dlls/bcrypt/tests/bcrypt.c
+++ b/dlls/bcrypt/tests/bcrypt.c
@@ -2054,10 +2054,10 @@ static void test_ECDH(void)
     ok(status == STATUS_SUCCESS, "got %08x\n", status);
 
     status = pBCryptSecretAgreement(privkey, pubkey, &secret, 0);
-    ok(status == STATUS_SUCCESS, "got %08x\n", status);
 
     if (status != STATUS_SUCCESS)
     {
+        skip("Secret key derivation not suppoted\n");
         goto derive_end;
     }
 
@@ -2070,7 +2070,7 @@ static void test_ECDH(void)
         goto raw_secret_end;
     }
 
-    todo_wine ok(status == STATUS_SUCCESS, "got %08x\n", status);
+    ok(status == STATUS_SUCCESS, "got %08x\n", status);
 
     if (status != STATUS_SUCCESS)
     {
-- 
2.25.0




More information about the wine-devel mailing list