[PATCH 1/2] bcrypt: Move GnuTLS support to a new file.

Hans Leidekker hans at codeweavers.com
Wed Jul 18 04:20:11 CDT 2018


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/bcrypt/Makefile.in       |    1 +
 dlls/bcrypt/bcrypt_internal.h |  137 +++++
 dlls/bcrypt/bcrypt_main.c     | 1189 ++++++++---------------------------------
 dlls/bcrypt/gnutls.c          |  632 ++++++++++++++++++++++
 4 files changed, 1004 insertions(+), 955 deletions(-)
 create mode 100644 dlls/bcrypt/gnutls.c

diff --git a/dlls/bcrypt/Makefile.in b/dlls/bcrypt/Makefile.in
index 3d081c8ab5..63a731fa9d 100644
--- a/dlls/bcrypt/Makefile.in
+++ b/dlls/bcrypt/Makefile.in
@@ -5,6 +5,7 @@ EXTRAINCL = $(GNUTLS_CFLAGS)
 
 C_SRCS = \
 	bcrypt_main.c \
+	gnutls.c \
 	md2.c \
 	sha256.c \
 	sha512.c
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h
index a75f92a90b..11c8b30416 100644
--- a/dlls/bcrypt/bcrypt_internal.h
+++ b/dlls/bcrypt/bcrypt_internal.h
@@ -21,9 +21,18 @@
 #define __BCRYPT_INTERNAL_H
 
 #include <stdarg.h>
+#ifdef HAVE_GNUTLS_CIPHER_INIT
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
+#elif HAVE_COMMONCRYPTO_COMMONCRYPTOR_H
+#include <AvailabilityMacros.h>
+#include <CommonCrypto/CommonCryptor.h>
+#endif
 
 #include "windef.h"
 #include "winbase.h"
+#include "bcrypt.h"
 
 typedef struct
 {
@@ -96,4 +105,132 @@ VOID WINAPI A_SHAInit(SHA_CTX *ctx);
 VOID WINAPI A_SHAUpdate(SHA_CTX *ctx, const UCHAR *buffer, UINT size);
 VOID WINAPI A_SHAFinal(SHA_CTX *ctx, PULONG result);
 
+struct buffer
+{
+    BYTE  *buffer;
+    DWORD  length;
+    DWORD  pos;
+    BOOL   error;
+};
+
+void buffer_init( struct buffer * ) DECLSPEC_HIDDEN;
+void buffer_free( struct buffer * ) DECLSPEC_HIDDEN;
+void buffer_append_asn1_r_s( struct buffer *, BYTE *, DWORD, BYTE *, DWORD ) DECLSPEC_HIDDEN;
+
+#define MAGIC_ALG  (('A' << 24) | ('L' << 16) | ('G' << 8) | '0')
+#define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H')
+#define MAGIC_KEY  (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0')
+struct object
+{
+    ULONG magic;
+};
+
+enum alg_id
+{
+    ALG_ID_AES,
+    ALG_ID_MD2,
+    ALG_ID_MD4,
+    ALG_ID_MD5,
+    ALG_ID_RNG,
+    ALG_ID_RSA,
+    ALG_ID_SHA1,
+    ALG_ID_SHA256,
+    ALG_ID_SHA384,
+    ALG_ID_SHA512,
+    ALG_ID_ECDSA_P256,
+    ALG_ID_ECDSA_P384,
+};
+
+enum mode_id
+{
+    MODE_ID_ECB,
+    MODE_ID_CBC,
+    MODE_ID_GCM
+};
+
+struct algorithm
+{
+    struct object hdr;
+    enum alg_id   id;
+    enum mode_id  mode;
+    BOOL          hmac;
+};
+
+#if defined(HAVE_GNUTLS_CIPHER_INIT)
+struct key_symmetric
+{
+    enum mode_id        mode;
+    ULONG               block_size;
+    gnutls_cipher_hd_t  handle;
+    UCHAR              *secret;
+    ULONG               secret_len;
+};
+
+struct key_asymmetric
+{
+    UCHAR *pubkey;
+    ULONG  pubkey_len;
+};
+
+struct key
+{
+    struct object hdr;
+    enum alg_id   alg_id;
+    union
+    {
+        struct key_symmetric  s;
+        struct key_asymmetric a;
+    } u;
+};
+#elif defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+struct key_symmetric
+{
+    enum mode_id   mode;
+    ULONG          block_size;
+    CCCryptorRef   ref_encrypt;
+    CCCryptorRef   ref_decrypt;
+    UCHAR         *secret;
+    ULONG          secret_len;
+};
+
+struct key_asymmetric
+{
+    UCHAR *pubkey;
+    ULONG  pubkey_len;
+};
+
+struct key
+{
+    struct object hdr;
+    enum alg_id   alg_id;
+    union
+    {
+        struct key_symmetric  s;
+        struct key_asymmetric a;
+    } u;
+};
+#else
+struct key
+{
+    struct object hdr;
+};
+#endif
+
+NTSTATUS get_alg_property( const struct algorithm *, const WCHAR *, UCHAR *, ULONG, ULONG * ) DECLSPEC_HIDDEN;
+
+NTSTATUS key_set_property( struct key *, const WCHAR *, UCHAR *, ULONG, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_symmetric_init( struct key *, struct algorithm *, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_symmetric_set_params( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_symmetric_set_auth_data( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_symmetric_encrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_symmetric_decrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_symmetric_get_tag( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_asymmetric_init( struct key *, struct algorithm *, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_asymmetric_verify( struct key *, void *, UCHAR *, ULONG, UCHAR *, ULONG, DWORD ) DECLSPEC_HIDDEN;
+NTSTATUS key_destroy( struct key * ) DECLSPEC_HIDDEN;
+BOOL key_is_symmetric( struct key * ) DECLSPEC_HIDDEN;
+
+BOOL gnutls_initialize(void) DECLSPEC_HIDDEN;
+void gnutls_uninitialize(void) DECLSPEC_HIDDEN;
+
 #endif /* __BCRYPT_INTERNAL_H */
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 29a0a785d9..e35f00aa2c 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -24,10 +24,6 @@
 #ifdef HAVE_COMMONCRYPTO_COMMONCRYPTOR_H
 #include <AvailabilityMacros.h>
 #include <CommonCrypto/CommonCryptor.h>
-#elif defined(HAVE_GNUTLS_CIPHER_INIT)
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
-#include <gnutls/abstract.h>
 #endif
 
 #include "ntstatus.h"
@@ -48,179 +44,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(bcrypt);
 
 static HINSTANCE instance;
 
-#if defined(HAVE_GNUTLS_CIPHER_INIT) && !defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H)
-WINE_DECLARE_DEBUG_CHANNEL(winediag);
-
-#if GNUTLS_VERSION_MAJOR < 3
-#define GNUTLS_CIPHER_AES_192_CBC 92
-#define GNUTLS_CIPHER_AES_128_GCM 93
-#define GNUTLS_CIPHER_AES_256_GCM 94
-#define GNUTLS_PK_ECC 4
-
-typedef enum
-{
-    GNUTLS_ECC_CURVE_INVALID,
-    GNUTLS_ECC_CURVE_SECP224R1,
-    GNUTLS_ECC_CURVE_SECP256R1,
-    GNUTLS_ECC_CURVE_SECP384R1,
-    GNUTLS_ECC_CURVE_SECP521R1,
-} gnutls_ecc_curve_t;
-#endif
-
-/* Not present in gnutls version < 3.0 */
-static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t);
-static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t);
-static int (*pgnutls_pubkey_import_ecc_raw)(gnutls_pubkey_t, gnutls_ecc_curve_t,
-                                            const gnutls_datum_t *, const gnutls_datum_t *);
-static gnutls_sign_algorithm_t (*pgnutls_pk_to_sign)(gnutls_pk_algorithm_t, gnutls_digest_algorithm_t);
-static int (*pgnutls_pubkey_verify_hash2)(gnutls_pubkey_t, gnutls_sign_algorithm_t, unsigned int,
-                                          const gnutls_datum_t *, const gnutls_datum_t *);
-
-/* Not present in gnutls version < 2.11.0 */
-static int (*pgnutls_pubkey_import_rsa_raw)(gnutls_pubkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e);
-
-static void *libgnutls_handle;
-#define MAKE_FUNCPTR(f) static typeof(f) * p##f
-MAKE_FUNCPTR(gnutls_cipher_decrypt2);
-MAKE_FUNCPTR(gnutls_cipher_deinit);
-MAKE_FUNCPTR(gnutls_cipher_encrypt2);
-MAKE_FUNCPTR(gnutls_cipher_init);
-MAKE_FUNCPTR(gnutls_global_deinit);
-MAKE_FUNCPTR(gnutls_global_init);
-MAKE_FUNCPTR(gnutls_global_set_log_function);
-MAKE_FUNCPTR(gnutls_global_set_log_level);
-MAKE_FUNCPTR(gnutls_perror);
-MAKE_FUNCPTR(gnutls_pubkey_init);
-MAKE_FUNCPTR(gnutls_pubkey_deinit);
-#undef MAKE_FUNCPTR
-
-static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static int compat_gnutls_cipher_add_auth(gnutls_cipher_hd_t handle, const void *ptext, size_t ptext_size)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static int compat_gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t curve,
-                                               const gnutls_datum_t *x, const gnutls_datum_t *y)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static gnutls_sign_algorithm_t compat_gnutls_pk_to_sign(gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t hash)
-{
-    return GNUTLS_SIGN_UNKNOWN;
-}
-
-static int compat_gnutls_pubkey_verify_hash2(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo,
-                                             unsigned int flags, const gnutls_datum_t *hash,
-                                             const gnutls_datum_t *signature)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static int compat_gnutls_pubkey_import_rsa_raw(gnutls_pubkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e)
-{
-    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
-}
-
-static void gnutls_log( int level, const char *msg )
-{
-    TRACE( "<%d> %s", level, msg );
-}
-
-static BOOL gnutls_initialize(void)
-{
-    int ret;
-
-    if (!(libgnutls_handle = wine_dlopen( SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0 )))
-    {
-        ERR_(winediag)( "failed to load libgnutls, no support for encryption\n" );
-        return FALSE;
-    }
-
-#define LOAD_FUNCPTR(f) \
-    if (!(p##f = wine_dlsym( libgnutls_handle, #f, NULL, 0 ))) \
-    { \
-        ERR( "failed to load %s\n", #f ); \
-        goto fail; \
-    }
-
-    LOAD_FUNCPTR(gnutls_cipher_decrypt2)
-    LOAD_FUNCPTR(gnutls_cipher_deinit)
-    LOAD_FUNCPTR(gnutls_cipher_encrypt2)
-    LOAD_FUNCPTR(gnutls_cipher_init)
-    LOAD_FUNCPTR(gnutls_global_deinit)
-    LOAD_FUNCPTR(gnutls_global_init)
-    LOAD_FUNCPTR(gnutls_global_set_log_function)
-    LOAD_FUNCPTR(gnutls_global_set_log_level)
-    LOAD_FUNCPTR(gnutls_perror)
-    LOAD_FUNCPTR(gnutls_pubkey_init);
-    LOAD_FUNCPTR(gnutls_pubkey_deinit);
-#undef LOAD_FUNCPTR
-
-    if (!(pgnutls_cipher_tag = wine_dlsym( libgnutls_handle, "gnutls_cipher_tag", NULL, 0 )))
-    {
-        WARN("gnutls_cipher_tag not found\n");
-        pgnutls_cipher_tag = compat_gnutls_cipher_tag;
-    }
-    if (!(pgnutls_cipher_add_auth = wine_dlsym( libgnutls_handle, "gnutls_cipher_add_auth", NULL, 0 )))
-    {
-        WARN("gnutls_cipher_add_auth not found\n");
-        pgnutls_cipher_add_auth = compat_gnutls_cipher_add_auth;
-    }
-
-    if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS)
-    {
-        pgnutls_perror( ret );
-        goto fail;
-    }
-    if (!(pgnutls_pubkey_import_ecc_raw = wine_dlsym( libgnutls_handle, "gnutls_pubkey_import_ecc_raw", NULL, 0 )))
-    {
-        WARN("gnutls_pubkey_import_ecc_raw not found\n");
-        pgnutls_pubkey_import_ecc_raw = compat_gnutls_pubkey_import_ecc_raw;
-    }
-    if (!(pgnutls_pk_to_sign = wine_dlsym( libgnutls_handle, "gnutls_pk_to_sign", NULL, 0 )))
-    {
-        WARN("gnutls_pk_to_sign not found\n");
-        pgnutls_pk_to_sign = compat_gnutls_pk_to_sign;
-    }
-    if (!(pgnutls_pubkey_verify_hash2 = wine_dlsym( libgnutls_handle, "gnutls_pubkey_verify_hash2", NULL, 0 )))
-    {
-        WARN("gnutls_pubkey_verify_hash2 not found\n");
-        pgnutls_pubkey_verify_hash2 = compat_gnutls_pubkey_verify_hash2;
-    }
-    if (!(pgnutls_pubkey_import_rsa_raw = wine_dlsym( libgnutls_handle, "gnutls_pubkey_import_rsa_raw", NULL, 0 )))
-    {
-        WARN("gnutls_pubkey_import_rsa_raw not found\n");
-        pgnutls_pubkey_import_rsa_raw = compat_gnutls_pubkey_import_rsa_raw;
-    }
-
-    if (TRACE_ON( bcrypt ))
-    {
-        pgnutls_global_set_log_level( 4 );
-        pgnutls_global_set_log_function( gnutls_log );
-    }
-
-    return TRUE;
-
-fail:
-    wine_dlclose( libgnutls_handle, NULL, 0 );
-    libgnutls_handle = NULL;
-    return FALSE;
-}
-
-static void gnutls_uninitialize(void)
-{
-    pgnutls_global_deinit();
-    wine_dlclose( libgnutls_handle, NULL, 0 );
-    libgnutls_handle = NULL;
-}
-#endif /* HAVE_GNUTLS_CIPHER_INIT && !HAVE_COMMONCRYPTO_COMMONCRYPTOR_H */
-
 NTSTATUS WINAPI BCryptAddContextFunction(ULONG table, LPCWSTR context, ULONG iface, LPCWSTR function, ULONG pos)
 {
     FIXME("%08x, %s, %08x, %s, %u: stub\n", table, debugstr_w(context), iface, debugstr_w(function), pos);
@@ -268,37 +91,6 @@ NTSTATUS WINAPI BCryptEnumAlgorithms(ULONG dwAlgOperations, ULONG *pAlgCount,
     return STATUS_NOT_IMPLEMENTED;
 }
 
-#define MAGIC_ALG  (('A' << 24) | ('L' << 16) | ('G' << 8) | '0')
-#define MAGIC_HASH (('H' << 24) | ('A' << 16) | ('S' << 8) | 'H')
-#define MAGIC_KEY  (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0')
-struct object
-{
-    ULONG magic;
-};
-
-enum alg_id
-{
-    ALG_ID_AES,
-    ALG_ID_MD2,
-    ALG_ID_MD4,
-    ALG_ID_MD5,
-    ALG_ID_RNG,
-    ALG_ID_RSA,
-    ALG_ID_SHA1,
-    ALG_ID_SHA256,
-    ALG_ID_SHA384,
-    ALG_ID_SHA512,
-    ALG_ID_ECDSA_P256,
-    ALG_ID_ECDSA_P384,
-};
-
-enum mode_id
-{
-    MODE_ID_ECB,
-    MODE_ID_CBC,
-    MODE_ID_GCM
-};
-
 #define MAX_HASH_OUTPUT_BYTES 64
 #define MAX_HASH_BLOCK_BITS 1024
 
@@ -326,14 +118,6 @@ alg_props[] =
     /* ALG_ID_ECDSA_P384 */ { 0,   0,     0, BCRYPT_ECDSA_P384_ALGORITHM, FALSE  },
 };
 
-struct algorithm
-{
-    struct object hdr;
-    enum alg_id   id;
-    enum mode_id  mode;
-    BOOL hmac;
-};
-
 NTSTATUS WINAPI BCryptGenRandom(BCRYPT_ALG_HANDLE handle, UCHAR *buffer, ULONG count, ULONG flags)
 {
     const DWORD supported_flags = BCRYPT_USE_SYSTEM_PREFERRED_RNG;
@@ -629,7 +413,7 @@ static NTSTATUS generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
+NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size )
 {
     NTSTATUS status;
 
@@ -934,110 +718,105 @@ NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE algorithm, UCHAR *secret, ULONG se
     return BCryptDestroyHash( handle );
 }
 
-#if defined(HAVE_GNUTLS_CIPHER_INIT) && !defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H)
-struct key_symmetric
+#if defined(HAVE_GNUTLS_CIPHER_INIT)
+void buffer_init( struct buffer *buffer )
 {
-    enum mode_id        mode;
-    ULONG               block_size;
-    gnutls_cipher_hd_t  handle;
-    UCHAR              *secret;
-    ULONG               secret_len;
-};
+    buffer->buffer = NULL;
+    buffer->length = 0;
+    buffer->pos    = 0;
+    buffer->error  = FALSE;
+}
 
-struct key_asymmetric
+void buffer_free( struct buffer *buffer )
 {
-    UCHAR *pubkey;
-    ULONG  pubkey_len;
-};
+    heap_free( buffer->buffer );
+}
 
-struct key
+static void buffer_append( struct buffer *buffer, BYTE *data, DWORD len )
 {
-    struct object hdr;
-    enum alg_id   alg_id;
-    union
+    if (!len) return;
+
+    if (buffer->pos + len > buffer->length)
     {
-        struct key_symmetric  s;
-        struct key_asymmetric a;
-    } u;
-};
+        DWORD new_length = max( max( buffer->pos + len, buffer->length * 2 ), 64 );
+        BYTE *new_buffer;
 
-#elif defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
-struct key_symmetric
-{
-    enum mode_id   mode;
-    ULONG          block_size;
-    CCCryptorRef   ref_encrypt;
-    CCCryptorRef   ref_decrypt;
-    UCHAR         *secret;
-    ULONG          secret_len;
-};
+        if (!(new_buffer = heap_realloc( buffer->buffer, new_length )))
+        {
+            ERR( "out of memory\n" );
+            buffer->error = TRUE;
+            return;
+        }
 
-struct key_asymmetric
-{
-    UCHAR *pubkey;
-    ULONG  pubkey_len;
-};
+        buffer->buffer = new_buffer;
+        buffer->length = new_length;
+    }
 
-struct key
-{
-    struct object hdr;
-    enum alg_id   alg_id;
-    union
-    {
-        struct key_symmetric  s;
-        struct key_asymmetric a;
-    } u;
-};
-#else
-struct key
-{
-    struct object hdr;
-};
-#endif
+    memcpy( &buffer->buffer[buffer->pos], data, len );
+    buffer->pos += len;
+}
 
-#if defined(HAVE_GNUTLS_CIPHER_INIT) && !defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H)
-static inline BOOL key_is_symmetric( struct key *key )
+static void buffer_append_byte( struct buffer *buffer, BYTE value )
 {
-    return alg_props[key->alg_id].symmetric;
+    buffer_append( buffer, &value, sizeof(value) );
 }
 
-static ULONG get_block_size( struct algorithm *alg )
+static void buffer_append_asn1_length( struct buffer *buffer, DWORD length )
 {
-    ULONG ret = 0, size = sizeof(ret);
-    get_alg_property( alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&ret, sizeof(ret), &size );
-    return ret;
+    DWORD num_bytes;
+
+    if (length < 128)
+    {
+        buffer_append_byte( buffer, length );
+        return;
+    }
+
+    if (length <= 0xff) num_bytes = 1;
+    else if (length <= 0xffff) num_bytes = 2;
+    else if (length <= 0xffffff) num_bytes = 3;
+    else num_bytes = 4;
+
+    buffer_append_byte( buffer, 0x80 | num_bytes );
+    while (num_bytes--) buffer_append_byte( buffer, length >> (num_bytes * 8) );
 }
 
-static NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR *secret, ULONG secret_len )
+static void buffer_append_asn1_integer( struct buffer *buffer, BYTE *data, DWORD len )
 {
-    UCHAR *buffer;
+    DWORD leading_zero = (*data & 0x80) != 0;
 
-    if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
+    buffer_append_byte( buffer, 0x02 );  /* tag */
+    buffer_append_asn1_length( buffer, len + leading_zero );
+    if (leading_zero) buffer_append_byte( buffer, 0 );
+    buffer_append( buffer, data, len );
+}
 
-    switch (alg->id)
+static void buffer_append_asn1_sequence( struct buffer *buffer, struct buffer *content )
+{
+    if (content->error)
     {
-    case ALG_ID_AES:
-        break;
-
-    default:
-        FIXME( "algorithm %u not supported\n", alg->id );
-        return STATUS_NOT_SUPPORTED;
+        buffer->error = TRUE;
+        return;
     }
 
-    if (!(key->u.s.block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER;
-    if (!(buffer = heap_alloc( secret_len ))) return STATUS_NO_MEMORY;
-    memcpy( buffer, secret, secret_len );
+    buffer_append_byte( buffer, 0x30 );  /* tag */
+    buffer_append_asn1_length( buffer, content->pos );
+    buffer_append( buffer, content->buffer, content->pos );
+}
 
-    key->alg_id         = alg->id;
-    key->u.s.mode       = alg->mode;
-    key->u.s.handle     = 0;        /* initialized on first use */
-    key->u.s.secret     = buffer;
-    key->u.s.secret_len = secret_len;
+void buffer_append_asn1_r_s( struct buffer *buffer, BYTE *r, DWORD r_len, BYTE *s, DWORD s_len )
+{
+    struct buffer value;
 
-    return STATUS_SUCCESS;
+    buffer_init( &value );
+    buffer_append_asn1_integer( &value, r, r_len );
+    buffer_append_asn1_integer( &value, s, s_len );
+    buffer_append_asn1_sequence( buffer, &value );
+    buffer_free( &value );
 }
+#endif
 
-static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
+#if defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+NTSTATUS key_set_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
 {
     if (!strcmpW( prop, BCRYPT_CHAINING_MODE ))
     {
@@ -1051,11 +830,6 @@ static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *val
             key->u.s.mode = MODE_ID_CBC;
             return STATUS_SUCCESS;
         }
-        else if (!strncmpW( (WCHAR *)value, BCRYPT_CHAIN_MODE_GCM, size ))
-        {
-            key->u.s.mode = MODE_ID_GCM;
-            return STATUS_SUCCESS;
-        }
         else
         {
             FIXME( "unsupported mode %s\n", debugstr_wn( (WCHAR *)value, size ) );
@@ -1067,646 +841,152 @@ static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *val
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key )
+static ULONG get_block_size( struct algorithm *alg )
+{
+    ULONG ret = 0, size = sizeof(ret);
+    get_alg_property( alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&ret, sizeof(ret), &size );
+    return ret;
+}
+
+NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR *secret, ULONG secret_len )
 {
-    switch (key->alg_id)
+    UCHAR *buffer;
+
+    switch (alg->id)
     {
     case ALG_ID_AES:
-        WARN( "handle block size\n" );
-        switch (key->u.s.mode)
+        switch (alg->mode)
         {
-        case MODE_ID_GCM:
-            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_GCM;
-            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_GCM;
-            break;
-        case MODE_ID_ECB: /* can be emulated with CBC + empty IV */
+        case MODE_ID_ECB:
         case MODE_ID_CBC:
-            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_CBC;
-            if (key->u.s.secret_len == 24) return GNUTLS_CIPHER_AES_192_CBC;
-            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_CBC;
             break;
         default:
-            break;
+            FIXME( "mode %u not supported\n", alg->mode );
+            return STATUS_NOT_SUPPORTED;
         }
-        FIXME( "AES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len );
-        return GNUTLS_CIPHER_UNKNOWN;
+        break;
 
     default:
-        FIXME( "algorithm %u not supported\n", key->alg_id );
-        return GNUTLS_CIPHER_UNKNOWN;
+        FIXME( "algorithm %u not supported\n", alg->id );
+        return STATUS_NOT_SUPPORTED;
     }
+
+    if (!(key->u.s.block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER;
+    if (!(buffer = heap_alloc( secret_len ))) return STATUS_NO_MEMORY;
+    memcpy( buffer, secret, secret_len );
+
+    key->alg_id          = alg->id;
+    key->u.s.mode        = alg->mode;
+    key->u.s.ref_encrypt = NULL;        /* initialized on first use */
+    key->u.s.ref_decrypt = NULL;
+    key->u.s.secret      = buffer;
+    key->u.s.secret_len  = secret_len;
+
+    return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
+static CCMode get_cryptor_mode( struct key *key )
 {
-    gnutls_cipher_algorithm_t cipher;
-    gnutls_datum_t secret, vector;
-    int ret;
-
-    if (key->u.s.handle)
+    switch (key->u.s.mode)
     {
-        pgnutls_cipher_deinit( key->u.s.handle );
-        key->u.s.handle = NULL;
+    case MODE_ID_ECB: return kCCModeECB;
+    case MODE_ID_CBC: return kCCModeCBC;
+    default:
+        FIXME( "unsupported mode %u\n", key->u.s.mode );
+        return 0;
     }
+}
 
-    if ((cipher = get_gnutls_cipher( key )) == GNUTLS_CIPHER_UNKNOWN)
-        return STATUS_NOT_SUPPORTED;
+NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
+{
+    CCCryptorStatus status;
+    CCMode mode;
+
+    if (!(mode = get_cryptor_mode( key ))) return STATUS_NOT_SUPPORTED;
 
-    secret.data = key->u.s.secret;
-    secret.size = key->u.s.secret_len;
-    if (iv)
+    if (key->u.s.ref_encrypt)
+    {
+        CCCryptorRelease( key->u.s.ref_encrypt );
+        key->u.s.ref_encrypt = NULL;
+    }
+    if (key->u.s.ref_decrypt)
     {
-        vector.data = iv;
-        vector.size = iv_len;
+        CCCryptorRelease( key->u.s.ref_decrypt );
+        key->u.s.ref_decrypt = NULL;
     }
 
-    if ((ret = pgnutls_cipher_init( &key->u.s.handle, cipher, &secret, iv ? &vector : NULL )))
+    if ((status = CCCryptorCreateWithMode( kCCEncrypt, mode, kCCAlgorithmAES128, ccNoPadding, iv, key->u.s.secret,
+                                           key->u.s.secret_len, NULL, 0, 0, 0, &key->u.s.ref_encrypt )) != kCCSuccess)
+    {
+        WARN( "CCCryptorCreateWithMode failed %d\n", status );
+        return STATUS_INTERNAL_ERROR;
+    }
+    if ((status = CCCryptorCreateWithMode( kCCDecrypt, mode, kCCAlgorithmAES128, ccNoPadding, iv, key->u.s.secret,
+                                           key->u.s.secret_len, NULL, 0, 0, 0, &key->u.s.ref_decrypt )) != kCCSuccess)
     {
-        pgnutls_perror( ret );
+        WARN( "CCCryptorCreateWithMode failed %d\n", status );
+        CCCryptorRelease( key->u.s.ref_encrypt );
+        key->u.s.ref_encrypt = NULL;
         return STATUS_INTERNAL_ERROR;
     }
 
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG len )
+NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG len )
 {
-    int ret;
+    FIXME( "not implemented on Mac\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
 
-    if (!auth_data) return STATUS_SUCCESS;
+NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
+                                ULONG output_len  )
+{
+    CCCryptorStatus status;
 
-    if ((ret = pgnutls_cipher_add_auth( key->u.s.handle, auth_data, len )))
+    if ((status = CCCryptorUpdate( key->u.s.ref_encrypt, input, input_len, output, output_len, NULL  )) != kCCSuccess)
     {
-        pgnutls_perror( ret );
+        WARN( "CCCryptorUpdate failed %d\n", status );
         return STATUS_INTERNAL_ERROR;
     }
 
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
-                                       ULONG output_len )
+NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
+                                ULONG output_len )
 {
-    int ret;
+    CCCryptorStatus status;
 
-    if ((ret = pgnutls_cipher_encrypt2( key->u.s.handle, input, input_len, output, output_len )))
+    if ((status = CCCryptorUpdate( key->u.s.ref_decrypt, input, input_len, output, output_len, NULL  )) != kCCSuccess)
     {
-        pgnutls_perror( ret );
+        WARN( "CCCryptorUpdate failed %d\n", status );
         return STATUS_INTERNAL_ERROR;
     }
 
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
-                                       ULONG output_len  )
-{
-    int ret;
-
-    if ((ret = pgnutls_cipher_decrypt2( key->u.s.handle, input, input_len, output, output_len )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
-{
-    int ret;
-
-    if ((ret = pgnutls_cipher_tag( key->u.s.handle, tag, len )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
-{
-    UCHAR *buffer;
-
-    if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
-
-    switch (alg->id)
-    {
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    case ALG_ID_RSA:
-        break;
-
-    default:
-        FIXME( "algorithm %u not supported\n", alg->id );
-        return STATUS_NOT_SUPPORTED;
-    }
-
-    if (!(buffer = heap_alloc( pubkey_len ))) return STATUS_NO_MEMORY;
-    memcpy( buffer, pubkey, pubkey_len );
-
-    key->alg_id         = alg->id;
-    key->u.a.pubkey     = buffer;
-    key->u.a.pubkey_len = pubkey_len;
-
-    return STATUS_SUCCESS;
-}
-
-struct buffer
-{
-    BYTE  *buffer;
-    DWORD  length;
-    DWORD  pos;
-    BOOL   error;
-};
-
-static void buffer_init( struct buffer *buffer )
-{
-    buffer->buffer = NULL;
-    buffer->length = 0;
-    buffer->pos    = 0;
-    buffer->error  = FALSE;
-}
-
-static void buffer_free( struct buffer *buffer )
-{
-    heap_free( buffer->buffer );
-}
-
-static void buffer_append( struct buffer *buffer, BYTE *data, DWORD len )
-{
-    if (!len) return;
-
-    if (buffer->pos + len > buffer->length)
-    {
-        DWORD new_length = max( max( buffer->pos + len, buffer->length * 2 ), 64 );
-        BYTE *new_buffer;
-
-        if (!(new_buffer = heap_realloc( buffer->buffer, new_length )))
-        {
-            ERR( "out of memory\n" );
-            buffer->error = TRUE;
-            return;
-        }
-
-        buffer->buffer = new_buffer;
-        buffer->length = new_length;
-    }
-
-    memcpy( &buffer->buffer[buffer->pos], data, len );
-    buffer->pos += len;
-}
-
-static void buffer_append_byte( struct buffer *buffer, BYTE value )
-{
-    buffer_append( buffer, &value, sizeof(value) );
-}
-
-static void buffer_append_asn1_length( struct buffer *buffer, DWORD length )
-{
-    DWORD num_bytes;
-
-    if (length < 128)
-    {
-        buffer_append_byte( buffer, length );
-        return;
-    }
-
-    if (length <= 0xff) num_bytes = 1;
-    else if (length <= 0xffff) num_bytes = 2;
-    else if (length <= 0xffffff) num_bytes = 3;
-    else num_bytes = 4;
-
-    buffer_append_byte( buffer, 0x80 | num_bytes );
-    while (num_bytes--) buffer_append_byte( buffer, length >> (num_bytes * 8) );
-}
-
-static void buffer_append_asn1_integer( struct buffer *buffer, BYTE *data, DWORD len )
-{
-    DWORD leading_zero = (*data & 0x80) != 0;
-
-    buffer_append_byte( buffer, 0x02 );  /* tag */
-    buffer_append_asn1_length( buffer, len + leading_zero );
-    if (leading_zero) buffer_append_byte( buffer, 0 );
-    buffer_append( buffer, data, len );
-}
-
-static void buffer_append_asn1_sequence( struct buffer *buffer, struct buffer *content )
-{
-    if (content->error)
-    {
-        buffer->error = TRUE;
-        return;
-    }
-
-    buffer_append_byte( buffer, 0x30 );  /* tag */
-    buffer_append_asn1_length( buffer, content->pos );
-    buffer_append( buffer, content->buffer, content->pos );
-}
-
-static void buffer_append_asn1_r_s( struct buffer *buffer, BYTE *r, DWORD r_len, BYTE *s, DWORD s_len )
-{
-    struct buffer value;
-
-    buffer_init( &value );
-    buffer_append_asn1_integer( &value, r, r_len );
-    buffer_append_asn1_integer( &value, s, s_len );
-    buffer_append_asn1_sequence( buffer, &value );
-    buffer_free( &value );
-}
-
-static NTSTATUS import_gnutls_pubkey_ecc( struct key *key, gnutls_pubkey_t *gnutls_key )
-{
-    BCRYPT_ECCKEY_BLOB *ecc_blob;
-    gnutls_ecc_curve_t curve;
-    gnutls_datum_t x, y;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDSA_P256: curve = GNUTLS_ECC_CURVE_SECP256R1; break;
-    case ALG_ID_ECDSA_P384: curve = GNUTLS_ECC_CURVE_SECP384R1; break;
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((ret = pgnutls_pubkey_init( gnutls_key )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    ecc_blob = (BCRYPT_ECCKEY_BLOB *)key->u.a.pubkey;
-    x.data = key->u.a.pubkey + sizeof(*ecc_blob);
-    x.size = ecc_blob->cbKey;
-    y.data = key->u.a.pubkey + sizeof(*ecc_blob) + ecc_blob->cbKey;
-    y.size = ecc_blob->cbKey;
-
-    if ((ret = pgnutls_pubkey_import_ecc_raw( *gnutls_key, curve, &x, &y )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( *gnutls_key );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS import_gnutls_pubkey_rsa( struct key *key, gnutls_pubkey_t *gnutls_key )
-{
-    BCRYPT_RSAKEY_BLOB *rsa_blob;
-    gnutls_datum_t m, e;
-    int ret;
-
-    if ((ret = pgnutls_pubkey_init( gnutls_key )))
-    {
-        pgnutls_perror( ret );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    rsa_blob = (BCRYPT_RSAKEY_BLOB *)key->u.a.pubkey;
-    e.data = key->u.a.pubkey + sizeof(*rsa_blob);
-    e.size = rsa_blob->cbPublicExp;
-    m.data = key->u.a.pubkey + sizeof(*rsa_blob) + rsa_blob->cbPublicExp;
-    m.size = rsa_blob->cbModulus;
-
-    if ((ret = pgnutls_pubkey_import_rsa_raw( *gnutls_key, &m, &e )))
-    {
-        pgnutls_perror( ret );
-        pgnutls_pubkey_deinit( *gnutls_key );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS import_gnutls_pubkey( struct key *key, gnutls_pubkey_t *gnutls_key )
-{
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-        return import_gnutls_pubkey_ecc( key, gnutls_key );
-
-    case ALG_ID_RSA:
-        return import_gnutls_pubkey_rsa( key, gnutls_key );
-
-    default:
-        FIXME("algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-}
-
-static NTSTATUS prepare_gnutls_signature_ecc( struct key *key, UCHAR *signature, ULONG signature_len,
-                                              gnutls_datum_t *gnutls_signature )
-{
-    struct buffer buffer;
-    DWORD r_len = signature_len / 2;
-    DWORD s_len = r_len;
-    BYTE *r = signature;
-    BYTE *s = signature + r_len;
-
-    buffer_init( &buffer );
-    buffer_append_asn1_r_s( &buffer, r, r_len, s, s_len );
-    if (buffer.error)
-    {
-        buffer_free( &buffer );
-        return STATUS_NO_MEMORY;
-    }
-
-    gnutls_signature->data = buffer.buffer;
-    gnutls_signature->size = buffer.pos;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS prepare_gnutls_signature_rsa( struct key *key, UCHAR *signature, ULONG signature_len,
-                                              gnutls_datum_t *gnutls_signature )
-{
-    gnutls_signature->data = signature;
-    gnutls_signature->size = signature_len;
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULONG signature_len,
-                                          gnutls_datum_t *gnutls_signature )
-{
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-        return prepare_gnutls_signature_ecc( key, signature, signature_len, gnutls_signature );
-
-    case ALG_ID_RSA:
-        return prepare_gnutls_signature_rsa( key, signature, signature_len, gnutls_signature );
-
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-}
-
-static NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len,
-                                       UCHAR *signature, ULONG signature_len, DWORD flags )
-{
-    gnutls_digest_algorithm_t hash_alg;
-    gnutls_sign_algorithm_t sign_alg;
-    gnutls_datum_t gnutls_hash, gnutls_signature;
-    gnutls_pk_algorithm_t pk_alg;
-    gnutls_pubkey_t gnutls_key;
-    NTSTATUS status;
-    int ret;
-
-    switch (key->alg_id)
-    {
-    case ALG_ID_ECDSA_P256:
-    case ALG_ID_ECDSA_P384:
-    {
-        if (flags) FIXME( "flags %08x not supported\n", flags );
-
-        /* only the hash size must match, not the actual hash function */
-        switch (hash_len)
-        {
-        case 32: hash_alg = GNUTLS_DIG_SHA256; break;
-        case 48: hash_alg = GNUTLS_DIG_SHA384; break;
-
-        default:
-            FIXME( "hash size %u not yet supported\n", hash_len );
-            return STATUS_INVALID_SIGNATURE;
-        }
-        pk_alg = GNUTLS_PK_ECC;
-        break;
-    }
-    case ALG_ID_RSA:
-    {
-        BCRYPT_PKCS1_PADDING_INFO *info = (BCRYPT_PKCS1_PADDING_INFO *)padding;
-
-        if (!(flags & BCRYPT_PAD_PKCS1) || !info) return STATUS_INVALID_PARAMETER;
-        if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE;
-
-        if (!strcmpW( info->pszAlgId, BCRYPT_SHA1_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA1;
-        else if (!strcmpW( info->pszAlgId, BCRYPT_SHA256_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA256;
-        else if (!strcmpW( info->pszAlgId, BCRYPT_SHA384_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA384;
-        else if (!strcmpW( info->pszAlgId, BCRYPT_SHA512_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA512;
-        else
-        {
-            FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) );
-            return STATUS_NOT_SUPPORTED;
-        }
-        pk_alg = GNUTLS_PK_RSA;
-        break;
-    }
-    default:
-        FIXME( "algorithm %u not yet supported\n", key->alg_id );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((sign_alg = pgnutls_pk_to_sign( pk_alg, hash_alg )) == GNUTLS_SIGN_UNKNOWN)
-    {
-        FIXME("GnuTLS does not support algorithm %u with hash len %u\n", key->alg_id, hash_len );
-        return STATUS_NOT_IMPLEMENTED;
-    }
-
-    if ((status = import_gnutls_pubkey( key, &gnutls_key ))) return status;
-    if ((status = prepare_gnutls_signature( key, signature, signature_len, &gnutls_signature )))
-    {
-        pgnutls_pubkey_deinit( gnutls_key );
-        return status;
-    }
-
-    gnutls_hash.data = hash;
-    gnutls_hash.size = hash_len;
-    ret = pgnutls_pubkey_verify_hash2( gnutls_key, sign_alg, 0, &gnutls_hash, &gnutls_signature );
-
-    if (gnutls_signature.data != signature) heap_free( gnutls_signature.data );
-    pgnutls_pubkey_deinit( gnutls_key );
-    return (ret < 0) ? STATUS_INVALID_SIGNATURE : STATUS_SUCCESS;
-}
-
-static NTSTATUS key_destroy( struct key *key )
-{
-    if (key_is_symmetric( key ))
-    {
-        if (key->u.s.handle) pgnutls_cipher_deinit( key->u.s.handle );
-        heap_free( key->u.s.secret );
-    }
-    else heap_free( key->u.a.pubkey );
-    heap_free( key );
-    return STATUS_SUCCESS;
-}
-#elif defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
-static inline BOOL key_is_symmetric( struct key *key )
-{
-    return alg_props[key->alg_id].symmetric;
-}
-
-static ULONG get_block_size( struct algorithm *alg )
-{
-    ULONG ret = 0, size = sizeof(ret);
-    get_alg_property( alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&ret, sizeof(ret), &size );
-    return ret;
-}
-
-static NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR *secret, ULONG secret_len )
-{
-    UCHAR *buffer;
-
-    switch (alg->id)
-    {
-    case ALG_ID_AES:
-        switch (alg->mode)
-        {
-        case MODE_ID_ECB:
-        case MODE_ID_CBC:
-            break;
-        default:
-            FIXME( "mode %u not supported\n", alg->mode );
-            return STATUS_NOT_SUPPORTED;
-        }
-        break;
-
-    default:
-        FIXME( "algorithm %u not supported\n", alg->id );
-        return STATUS_NOT_SUPPORTED;
-    }
-
-    if (!(key->u.s.block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER;
-    if (!(buffer = heap_alloc( secret_len ))) return STATUS_NO_MEMORY;
-    memcpy( buffer, secret, secret_len );
-
-    key->alg_id          = alg->id;
-    key->u.s.mode        = alg->mode;
-    key->u.s.ref_encrypt = NULL;        /* initialized on first use */
-    key->u.s.ref_decrypt = NULL;
-    key->u.s.secret      = buffer;
-    key->u.s.secret_len  = secret_len;
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
-{
-    if (!strcmpW( prop, BCRYPT_CHAINING_MODE ))
-    {
-        if (!strncmpW( (WCHAR *)value, BCRYPT_CHAIN_MODE_ECB, size ))
-        {
-            key->u.s.mode = MODE_ID_ECB;
-            return STATUS_SUCCESS;
-        }
-        else if (!strncmpW( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC, size ))
-        {
-            key->u.s.mode = MODE_ID_CBC;
-            return STATUS_SUCCESS;
-        }
-        else
-        {
-            FIXME( "unsupported mode %s\n", debugstr_wn( (WCHAR *)value, size ) );
-            return STATUS_NOT_IMPLEMENTED;
-        }
-    }
-
-    FIXME( "unsupported key property %s\n", debugstr_w(prop) );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static CCMode get_cryptor_mode( struct key *key )
-{
-    switch (key->u.s.mode)
-    {
-    case MODE_ID_ECB: return kCCModeECB;
-    case MODE_ID_CBC: return kCCModeCBC;
-    default:
-        FIXME( "unsupported mode %u\n", key->u.s.mode );
-        return 0;
-    }
-}
-
-static NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
-{
-    CCCryptorStatus status;
-    CCMode mode;
-
-    if (!(mode = get_cryptor_mode( key ))) return STATUS_NOT_SUPPORTED;
-
-    if (key->u.s.ref_encrypt)
-    {
-        CCCryptorRelease( key->u.s.ref_encrypt );
-        key->u.s.ref_encrypt = NULL;
-    }
-    if (key->u.s.ref_decrypt)
-    {
-        CCCryptorRelease( key->u.s.ref_decrypt );
-        key->u.s.ref_decrypt = NULL;
-    }
-
-    if ((status = CCCryptorCreateWithMode( kCCEncrypt, mode, kCCAlgorithmAES128, ccNoPadding, iv, key->u.s.secret,
-                                           key->u.s.secret_len, NULL, 0, 0, 0, &key->u.s.ref_encrypt )) != kCCSuccess)
-    {
-        WARN( "CCCryptorCreateWithMode failed %d\n", status );
-        return STATUS_INTERNAL_ERROR;
-    }
-    if ((status = CCCryptorCreateWithMode( kCCDecrypt, mode, kCCAlgorithmAES128, ccNoPadding, iv, key->u.s.secret,
-                                           key->u.s.secret_len, NULL, 0, 0, 0, &key->u.s.ref_decrypt )) != kCCSuccess)
-    {
-        WARN( "CCCryptorCreateWithMode failed %d\n", status );
-        CCCryptorRelease( key->u.s.ref_encrypt );
-        key->u.s.ref_encrypt = NULL;
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG len )
+NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
 {
     FIXME( "not implemented on Mac\n" );
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
-                                       ULONG output_len  )
-{
-    CCCryptorStatus status;
-
-    if ((status = CCCryptorUpdate( key->u.s.ref_encrypt, input, input_len, output, output_len, NULL  )) != kCCSuccess)
-    {
-        WARN( "CCCryptorUpdate failed %d\n", status );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output,
-                                       ULONG output_len )
-{
-    CCCryptorStatus status;
-
-    if ((status = CCCryptorUpdate( key->u.s.ref_decrypt, input, input_len, output, output_len, NULL  )) != kCCSuccess)
-    {
-        WARN( "CCCryptorUpdate failed %d\n", status );
-        return STATUS_INTERNAL_ERROR;
-    }
-
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
+NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
 {
     FIXME( "not implemented on Mac\n" );
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len,
-                                       UCHAR *signature, ULONG signature_len, DWORD flags )
+NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len, UCHAR *signature,
+                                ULONG signature_len, DWORD flags )
 {
     FIXME( "not implemented on Mac\n" );
     return STATUS_NOT_IMPLEMENTED;
 }
 
-static NTSTATUS key_destroy( struct key *key )
+NTSTATUS key_destroy( struct key *key )
 {
     if (key->u.s.ref_encrypt) CCCryptorRelease( key->u.s.ref_encrypt );
     if (key->u.s.ref_decrypt) CCCryptorRelease( key->u.s.ref_decrypt );
@@ -1714,86 +994,14 @@ static NTSTATUS key_destroy( struct key *key )
     heap_free( key );
     return STATUS_SUCCESS;
 }
+#endif
 
-static NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
-{
-    FIXME( "not implemented on Mac\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-#else
-static NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR *secret, ULONG secret_len )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_duplicate( struct key *key_orig, struct key *key_copy )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len,
-                                       UCHAR *signature, ULONG signature_len, DWORD flags )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_import( BCRYPT_ALG_HANDLE algorithm, const WCHAR *type, BCRYPT_KEY_HANDLE *key, UCHAR *object,
-                            ULONG object_len, UCHAR *input, ULONG input_len )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_destroy( struct key *key )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static inline BOOL key_is_symmetric( struct key *key )
-{
-    ERR( "support for keys not available at build time\n" );
-    return FALSE;
-}
-
-static NTSTATUS key_encrypt( struct key *key,  UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
-                             ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_decrypt( struct key *key, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
-                             ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
-{
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
-}
-
-static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY_HANDLE *ret_key, UCHAR *input,
-                                 ULONG input_len )
+#if defined(HAVE_GNUTLS_CIPHER_INIT) || defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+BOOL key_is_symmetric( struct key *key )
 {
-    ERR( "support for keys not available at build time\n" );
-    return STATUS_NOT_IMPLEMENTED;
+    return alg_props[key->alg_id].symmetric;
 }
-#endif
 
-#if defined(HAVE_GNUTLS_CIPHER_INIT) || defined(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
 static NTSTATUS key_import( BCRYPT_ALG_HANDLE algorithm, const WCHAR *type, BCRYPT_KEY_HANDLE *key, UCHAR *object,
                             ULONG object_len, UCHAR *input, ULONG input_len )
 {
@@ -2108,6 +1316,77 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
     FIXME( "unsupported key type %s\n", debugstr_w(type) );
     return STATUS_NOT_SUPPORTED;
 }
+#else
+NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR *secret, ULONG secret_len )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+BOOL key_is_symmetric( struct key *key )
+{
+    ERR( "support for keys not available at build time\n" );
+    return FALSE;
+}
+
+NTSTATUS key_set_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS key_duplicate( struct key *key_orig, struct key *key_copy )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len, UCHAR *signature,
+                                ULONG signature_len, DWORD flags )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS key_import( BCRYPT_ALG_HANDLE algorithm, const WCHAR *type, BCRYPT_KEY_HANDLE *key, UCHAR *object,
+                            ULONG object_len, UCHAR *input, ULONG input_len )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, ULONG output_len, ULONG *size )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS key_destroy( struct key *key )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS key_encrypt( struct key *key,  UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
+                             ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS key_decrypt( struct key *key, UCHAR *input, ULONG input_len, void *padding, UCHAR *iv,
+                             ULONG iv_len, UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYPT_KEY_HANDLE *ret_key, UCHAR *input,
+                                 ULONG input_len )
+{
+    ERR( "support for keys not available at build time\n" );
+    return STATUS_NOT_IMPLEMENTED;
+}
 #endif
 
 NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE algorithm, BCRYPT_KEY_HANDLE *handle,
@@ -2307,7 +1586,7 @@ NTSTATUS WINAPI BCryptSetProperty( BCRYPT_HANDLE handle, const WCHAR *prop, UCHA
     case MAGIC_KEY:
     {
         struct key *key = (struct key *)object;
-        return set_key_property( key, prop, value, size, flags );
+        return key_set_property( key, prop, value, size, flags );
     }
     default:
         WARN( "unknown magic %08x\n", object->magic );
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c
new file mode 100644
index 0000000000..f191d2c8dd
--- /dev/null
+++ b/dlls/bcrypt/gnutls.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright 2009 Henri Verbeet for CodeWeavers
+ * Copyright 2018 Hans Leidekker for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#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>
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "ntsecapi.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(HAVE_COMMONCRYPTO_COMMONCRYPTOR_H)
+WINE_DEFAULT_DEBUG_CHANNEL(bcrypt);
+WINE_DECLARE_DEBUG_CHANNEL(winediag);
+
+#if GNUTLS_VERSION_MAJOR < 3
+#define GNUTLS_CIPHER_AES_192_CBC 92
+#define GNUTLS_CIPHER_AES_128_GCM 93
+#define GNUTLS_CIPHER_AES_256_GCM 94
+#define GNUTLS_PK_ECC 4
+
+typedef enum
+{
+    GNUTLS_ECC_CURVE_INVALID,
+    GNUTLS_ECC_CURVE_SECP224R1,
+    GNUTLS_ECC_CURVE_SECP256R1,
+    GNUTLS_ECC_CURVE_SECP384R1,
+    GNUTLS_ECC_CURVE_SECP521R1,
+} gnutls_ecc_curve_t;
+#endif
+
+/* Not present in gnutls version < 3.0 */
+static int (*pgnutls_cipher_tag)(gnutls_cipher_hd_t, void *, size_t);
+static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t);
+static int (*pgnutls_pubkey_import_ecc_raw)(gnutls_pubkey_t, gnutls_ecc_curve_t,
+                                            const gnutls_datum_t *, const gnutls_datum_t *);
+static gnutls_sign_algorithm_t (*pgnutls_pk_to_sign)(gnutls_pk_algorithm_t, gnutls_digest_algorithm_t);
+static int (*pgnutls_pubkey_verify_hash2)(gnutls_pubkey_t, gnutls_sign_algorithm_t, unsigned int,
+                                          const gnutls_datum_t *, const gnutls_datum_t *);
+
+/* Not present in gnutls version < 2.11.0 */
+static int (*pgnutls_pubkey_import_rsa_raw)(gnutls_pubkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e);
+
+static void *libgnutls_handle;
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f
+MAKE_FUNCPTR(gnutls_cipher_decrypt2);
+MAKE_FUNCPTR(gnutls_cipher_deinit);
+MAKE_FUNCPTR(gnutls_cipher_encrypt2);
+MAKE_FUNCPTR(gnutls_cipher_init);
+MAKE_FUNCPTR(gnutls_global_deinit);
+MAKE_FUNCPTR(gnutls_global_init);
+MAKE_FUNCPTR(gnutls_global_set_log_function);
+MAKE_FUNCPTR(gnutls_global_set_log_level);
+MAKE_FUNCPTR(gnutls_perror);
+MAKE_FUNCPTR(gnutls_pubkey_init);
+MAKE_FUNCPTR(gnutls_pubkey_deinit);
+#undef MAKE_FUNCPTR
+
+static int compat_gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size)
+{
+    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+static int compat_gnutls_cipher_add_auth(gnutls_cipher_hd_t handle, const void *ptext, size_t ptext_size)
+{
+    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+static int compat_gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t curve,
+                                               const gnutls_datum_t *x, const gnutls_datum_t *y)
+{
+    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+static gnutls_sign_algorithm_t compat_gnutls_pk_to_sign(gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t hash)
+{
+    return GNUTLS_SIGN_UNKNOWN;
+}
+
+static int compat_gnutls_pubkey_verify_hash2(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo,
+                                             unsigned int flags, const gnutls_datum_t *hash,
+                                             const gnutls_datum_t *signature)
+{
+    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+static int compat_gnutls_pubkey_import_rsa_raw(gnutls_pubkey_t key, const gnutls_datum_t *m, const gnutls_datum_t *e)
+{
+    return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+static void gnutls_log( int level, const char *msg )
+{
+    TRACE( "<%d> %s", level, msg );
+}
+
+BOOL gnutls_initialize(void)
+{
+    int ret;
+
+    if (!(libgnutls_handle = wine_dlopen( SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0 )))
+    {
+        ERR_(winediag)( "failed to load libgnutls, no support for encryption\n" );
+        return FALSE;
+    }
+
+#define LOAD_FUNCPTR(f) \
+    if (!(p##f = wine_dlsym( libgnutls_handle, #f, NULL, 0 ))) \
+    { \
+        ERR( "failed to load %s\n", #f ); \
+        goto fail; \
+    }
+
+    LOAD_FUNCPTR(gnutls_cipher_decrypt2)
+    LOAD_FUNCPTR(gnutls_cipher_deinit)
+    LOAD_FUNCPTR(gnutls_cipher_encrypt2)
+    LOAD_FUNCPTR(gnutls_cipher_init)
+    LOAD_FUNCPTR(gnutls_global_deinit)
+    LOAD_FUNCPTR(gnutls_global_init)
+    LOAD_FUNCPTR(gnutls_global_set_log_function)
+    LOAD_FUNCPTR(gnutls_global_set_log_level)
+    LOAD_FUNCPTR(gnutls_perror)
+    LOAD_FUNCPTR(gnutls_pubkey_init);
+    LOAD_FUNCPTR(gnutls_pubkey_deinit);
+#undef LOAD_FUNCPTR
+
+    if (!(pgnutls_cipher_tag = wine_dlsym( libgnutls_handle, "gnutls_cipher_tag", NULL, 0 )))
+    {
+        WARN("gnutls_cipher_tag not found\n");
+        pgnutls_cipher_tag = compat_gnutls_cipher_tag;
+    }
+    if (!(pgnutls_cipher_add_auth = wine_dlsym( libgnutls_handle, "gnutls_cipher_add_auth", NULL, 0 )))
+    {
+        WARN("gnutls_cipher_add_auth not found\n");
+        pgnutls_cipher_add_auth = compat_gnutls_cipher_add_auth;
+    }
+
+    if ((ret = pgnutls_global_init()) != GNUTLS_E_SUCCESS)
+    {
+        pgnutls_perror( ret );
+        goto fail;
+    }
+    if (!(pgnutls_pubkey_import_ecc_raw = wine_dlsym( libgnutls_handle, "gnutls_pubkey_import_ecc_raw", NULL, 0 )))
+    {
+        WARN("gnutls_pubkey_import_ecc_raw not found\n");
+        pgnutls_pubkey_import_ecc_raw = compat_gnutls_pubkey_import_ecc_raw;
+    }
+    if (!(pgnutls_pk_to_sign = wine_dlsym( libgnutls_handle, "gnutls_pk_to_sign", NULL, 0 )))
+    {
+        WARN("gnutls_pk_to_sign not found\n");
+        pgnutls_pk_to_sign = compat_gnutls_pk_to_sign;
+    }
+    if (!(pgnutls_pubkey_verify_hash2 = wine_dlsym( libgnutls_handle, "gnutls_pubkey_verify_hash2", NULL, 0 )))
+    {
+        WARN("gnutls_pubkey_verify_hash2 not found\n");
+        pgnutls_pubkey_verify_hash2 = compat_gnutls_pubkey_verify_hash2;
+    }
+    if (!(pgnutls_pubkey_import_rsa_raw = wine_dlsym( libgnutls_handle, "gnutls_pubkey_import_rsa_raw", NULL, 0 )))
+    {
+        WARN("gnutls_pubkey_import_rsa_raw not found\n");
+        pgnutls_pubkey_import_rsa_raw = compat_gnutls_pubkey_import_rsa_raw;
+    }
+
+    if (TRACE_ON( bcrypt ))
+    {
+        pgnutls_global_set_log_level( 4 );
+        pgnutls_global_set_log_function( gnutls_log );
+    }
+
+    return TRUE;
+
+fail:
+    wine_dlclose( libgnutls_handle, NULL, 0 );
+    libgnutls_handle = NULL;
+    return FALSE;
+}
+
+void gnutls_uninitialize(void)
+{
+    pgnutls_global_deinit();
+    wine_dlclose( libgnutls_handle, NULL, 0 );
+    libgnutls_handle = NULL;
+}
+
+NTSTATUS key_set_property( struct key *key, const WCHAR *prop, UCHAR *value, ULONG size, ULONG flags )
+{
+    if (!strcmpW( prop, BCRYPT_CHAINING_MODE ))
+    {
+        if (!strncmpW( (WCHAR *)value, BCRYPT_CHAIN_MODE_ECB, size ))
+        {
+            key->u.s.mode = MODE_ID_ECB;
+            return STATUS_SUCCESS;
+        }
+        else if (!strncmpW( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC, size ))
+        {
+            key->u.s.mode = MODE_ID_CBC;
+            return STATUS_SUCCESS;
+        }
+        else if (!strncmpW( (WCHAR *)value, BCRYPT_CHAIN_MODE_GCM, size ))
+        {
+            key->u.s.mode = MODE_ID_GCM;
+            return STATUS_SUCCESS;
+        }
+        else
+        {
+            FIXME( "unsupported mode %s\n", debugstr_wn( (WCHAR *)value, size ) );
+            return STATUS_NOT_IMPLEMENTED;
+        }
+    }
+
+    FIXME( "unsupported key property %s\n", debugstr_w(prop) );
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+static ULONG get_block_size( struct algorithm *alg )
+{
+    ULONG ret = 0, size = sizeof(ret);
+    get_alg_property( alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&ret, sizeof(ret), &size );
+    return ret;
+}
+
+NTSTATUS key_symmetric_init( struct key *key, struct algorithm *alg, const UCHAR *secret, ULONG secret_len )
+{
+    if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
+
+    switch (alg->id)
+    {
+    case ALG_ID_AES:
+        break;
+
+    default:
+        FIXME( "algorithm %u not supported\n", alg->id );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    if (!(key->u.s.block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER;
+    if (!(key->u.s.secret = heap_alloc( secret_len ))) return STATUS_NO_MEMORY;
+    memcpy( key->u.s.secret, secret, secret_len );
+    key->u.s.secret_len = secret_len;
+
+    key->alg_id         = alg->id;
+    key->u.s.mode       = alg->mode;
+    key->u.s.handle     = 0;        /* initialized on first use */
+
+    return STATUS_SUCCESS;
+}
+
+static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key )
+{
+    switch (key->alg_id)
+    {
+    case ALG_ID_AES:
+        WARN( "handle block size\n" );
+        switch (key->u.s.mode)
+        {
+        case MODE_ID_GCM:
+            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_GCM;
+            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_GCM;
+            break;
+        case MODE_ID_ECB: /* can be emulated with CBC + empty IV */
+        case MODE_ID_CBC:
+            if (key->u.s.secret_len == 16) return GNUTLS_CIPHER_AES_128_CBC;
+            if (key->u.s.secret_len == 24) return GNUTLS_CIPHER_AES_192_CBC;
+            if (key->u.s.secret_len == 32) return GNUTLS_CIPHER_AES_256_CBC;
+            break;
+        default:
+            break;
+        }
+        FIXME( "AES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len );
+        return GNUTLS_CIPHER_UNKNOWN;
+
+    default:
+        FIXME( "algorithm %u not supported\n", key->alg_id );
+        return GNUTLS_CIPHER_UNKNOWN;
+    }
+}
+
+NTSTATUS key_symmetric_set_params( struct key *key, UCHAR *iv, ULONG iv_len )
+{
+    gnutls_cipher_algorithm_t cipher;
+    gnutls_datum_t secret, vector;
+    int ret;
+
+    if (key->u.s.handle)
+    {
+        pgnutls_cipher_deinit( key->u.s.handle );
+        key->u.s.handle = NULL;
+    }
+
+    if ((cipher = get_gnutls_cipher( key )) == GNUTLS_CIPHER_UNKNOWN)
+        return STATUS_NOT_SUPPORTED;
+
+    secret.data = key->u.s.secret;
+    secret.size = key->u.s.secret_len;
+    if (iv)
+    {
+        vector.data = iv;
+        vector.size = iv_len;
+    }
+
+    if ((ret = pgnutls_cipher_init( &key->u.s.handle, cipher, &secret, iv ? &vector : NULL )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS key_symmetric_set_auth_data( struct key *key, UCHAR *auth_data, ULONG len )
+{
+    int ret;
+    if (!auth_data) return STATUS_SUCCESS;
+    if ((ret = pgnutls_cipher_add_auth( key->u.s.handle, auth_data, len )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS key_symmetric_encrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len )
+{
+    int ret;
+    if ((ret = pgnutls_cipher_encrypt2( key->u.s.handle, input, input_len, output, output_len )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS key_symmetric_decrypt( struct key *key, const UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len  )
+{
+    int ret;
+    if ((ret = pgnutls_cipher_decrypt2( key->u.s.handle, input, input_len, output, output_len )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
+{
+    int ret;
+    if ((ret = pgnutls_cipher_tag( key->u.s.handle, tag, len )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
+{
+    if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
+
+    switch (alg->id)
+    {
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    case ALG_ID_RSA:
+        break;
+
+    default:
+        FIXME( "algorithm %u not supported\n", alg->id );
+        return STATUS_NOT_SUPPORTED;
+    }
+
+    if (!(key->u.a.pubkey = heap_alloc( pubkey_len ))) return STATUS_NO_MEMORY;
+    memcpy( key->u.a.pubkey, pubkey, pubkey_len );
+    key->u.a.pubkey_len = pubkey_len;
+    key->alg_id         = alg->id;
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS import_gnutls_pubkey_ecc( struct key *key, gnutls_pubkey_t *gnutls_key )
+{
+    BCRYPT_ECCKEY_BLOB *ecc_blob;
+    gnutls_ecc_curve_t curve;
+    gnutls_datum_t x, y;
+    int ret;
+
+    switch (key->alg_id)
+    {
+    case ALG_ID_ECDSA_P256: curve = GNUTLS_ECC_CURVE_SECP256R1; break;
+    case ALG_ID_ECDSA_P384: curve = GNUTLS_ECC_CURVE_SECP384R1; break;
+
+    default:
+        FIXME( "algorithm %u not yet supported\n", key->alg_id );
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    if ((ret = pgnutls_pubkey_init( gnutls_key )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    ecc_blob = (BCRYPT_ECCKEY_BLOB *)key->u.a.pubkey;
+    x.data = key->u.a.pubkey + sizeof(*ecc_blob);
+    x.size = ecc_blob->cbKey;
+    y.data = key->u.a.pubkey + sizeof(*ecc_blob) + ecc_blob->cbKey;
+    y.size = ecc_blob->cbKey;
+
+    if ((ret = pgnutls_pubkey_import_ecc_raw( *gnutls_key, curve, &x, &y )))
+    {
+        pgnutls_perror( ret );
+        pgnutls_pubkey_deinit( *gnutls_key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS import_gnutls_pubkey_rsa( struct key *key, gnutls_pubkey_t *gnutls_key )
+{
+    BCRYPT_RSAKEY_BLOB *rsa_blob;
+    gnutls_datum_t m, e;
+    int ret;
+
+    if ((ret = pgnutls_pubkey_init( gnutls_key )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    rsa_blob = (BCRYPT_RSAKEY_BLOB *)key->u.a.pubkey;
+    e.data = key->u.a.pubkey + sizeof(*rsa_blob);
+    e.size = rsa_blob->cbPublicExp;
+    m.data = key->u.a.pubkey + sizeof(*rsa_blob) + rsa_blob->cbPublicExp;
+    m.size = rsa_blob->cbModulus;
+
+    if ((ret = pgnutls_pubkey_import_rsa_raw( *gnutls_key, &m, &e )))
+    {
+        pgnutls_perror( ret );
+        pgnutls_pubkey_deinit( *gnutls_key );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS import_gnutls_pubkey( struct key *key, gnutls_pubkey_t *gnutls_key )
+{
+    switch (key->alg_id)
+    {
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+        return import_gnutls_pubkey_ecc( key, gnutls_key );
+
+    case ALG_ID_RSA:
+        return import_gnutls_pubkey_rsa( key, gnutls_key );
+
+    default:
+        FIXME("algorithm %u not yet supported\n", key->alg_id );
+        return STATUS_NOT_IMPLEMENTED;
+    }
+}
+
+static NTSTATUS prepare_gnutls_signature_ecc( struct key *key, UCHAR *signature, ULONG signature_len,
+                                              gnutls_datum_t *gnutls_signature )
+{
+    struct buffer buffer;
+    DWORD r_len = signature_len / 2;
+    DWORD s_len = r_len;
+    BYTE *r = signature;
+    BYTE *s = signature + r_len;
+
+    buffer_init( &buffer );
+    buffer_append_asn1_r_s( &buffer, r, r_len, s, s_len );
+    if (buffer.error)
+    {
+        buffer_free( &buffer );
+        return STATUS_NO_MEMORY;
+    }
+
+    gnutls_signature->data = buffer.buffer;
+    gnutls_signature->size = buffer.pos;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS prepare_gnutls_signature_rsa( struct key *key, UCHAR *signature, ULONG signature_len,
+                                              gnutls_datum_t *gnutls_signature )
+{
+    gnutls_signature->data = signature;
+    gnutls_signature->size = signature_len;
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULONG signature_len,
+                                          gnutls_datum_t *gnutls_signature )
+{
+    switch (key->alg_id)
+    {
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+        return prepare_gnutls_signature_ecc( key, signature, signature_len, gnutls_signature );
+
+    case ALG_ID_RSA:
+        return prepare_gnutls_signature_rsa( key, signature, signature_len, gnutls_signature );
+
+    default:
+        FIXME( "algorithm %u not yet supported\n", key->alg_id );
+        return STATUS_NOT_IMPLEMENTED;
+    }
+}
+
+NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len, UCHAR *signature,
+                                ULONG signature_len, DWORD flags )
+{
+    gnutls_digest_algorithm_t hash_alg;
+    gnutls_sign_algorithm_t sign_alg;
+    gnutls_datum_t gnutls_hash, gnutls_signature;
+    gnutls_pk_algorithm_t pk_alg;
+    gnutls_pubkey_t gnutls_key;
+    NTSTATUS status;
+    int ret;
+
+    switch (key->alg_id)
+    {
+    case ALG_ID_ECDSA_P256:
+    case ALG_ID_ECDSA_P384:
+    {
+        if (flags) FIXME( "flags %08x not supported\n", flags );
+
+        /* only the hash size must match, not the actual hash function */
+        switch (hash_len)
+        {
+        case 32: hash_alg = GNUTLS_DIG_SHA256; break;
+        case 48: hash_alg = GNUTLS_DIG_SHA384; break;
+
+        default:
+            FIXME( "hash size %u not yet supported\n", hash_len );
+            return STATUS_INVALID_SIGNATURE;
+        }
+        pk_alg = GNUTLS_PK_ECC;
+        break;
+    }
+    case ALG_ID_RSA:
+    {
+        BCRYPT_PKCS1_PADDING_INFO *info = (BCRYPT_PKCS1_PADDING_INFO *)padding;
+
+        if (!(flags & BCRYPT_PAD_PKCS1) || !info) return STATUS_INVALID_PARAMETER;
+        if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE;
+
+        if (!strcmpW( info->pszAlgId, BCRYPT_SHA1_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA1;
+        else if (!strcmpW( info->pszAlgId, BCRYPT_SHA256_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA256;
+        else if (!strcmpW( info->pszAlgId, BCRYPT_SHA384_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA384;
+        else if (!strcmpW( info->pszAlgId, BCRYPT_SHA512_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA512;
+        else
+        {
+            FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) );
+            return STATUS_NOT_SUPPORTED;
+        }
+        pk_alg = GNUTLS_PK_RSA;
+        break;
+    }
+    default:
+        FIXME( "algorithm %u not yet supported\n", key->alg_id );
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    if ((sign_alg = pgnutls_pk_to_sign( pk_alg, hash_alg )) == GNUTLS_SIGN_UNKNOWN)
+    {
+        FIXME("GnuTLS does not support algorithm %u with hash len %u\n", key->alg_id, hash_len );
+        return STATUS_NOT_IMPLEMENTED;
+    }
+
+    if ((status = import_gnutls_pubkey( key, &gnutls_key ))) return status;
+    if ((status = prepare_gnutls_signature( key, signature, signature_len, &gnutls_signature )))
+    {
+        pgnutls_pubkey_deinit( gnutls_key );
+        return status;
+    }
+
+    gnutls_hash.data = hash;
+    gnutls_hash.size = hash_len;
+    ret = pgnutls_pubkey_verify_hash2( gnutls_key, sign_alg, 0, &gnutls_hash, &gnutls_signature );
+
+    if (gnutls_signature.data != signature) heap_free( gnutls_signature.data );
+    pgnutls_pubkey_deinit( gnutls_key );
+    return (ret < 0) ? STATUS_INVALID_SIGNATURE : STATUS_SUCCESS;
+}
+
+NTSTATUS key_destroy( struct key *key )
+{
+    if (key_is_symmetric( key ))
+    {
+        if (key->u.s.handle) pgnutls_cipher_deinit( key->u.s.handle );
+        heap_free( key->u.s.secret );
+    }
+    else heap_free( key->u.a.pubkey );
+    heap_free( key );
+    return STATUS_SUCCESS;
+}
+#endif
-- 
2.11.0




More information about the wine-devel mailing list