Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/bcrypt/tests/bcrypt.c | 75 ++++++++++++++++++++++++++++++++++---- include/bcrypt.h | 12 ++++++ 2 files changed, 80 insertions(+), 7 deletions(-)
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 198acbf721..47bb58d3d1 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -62,6 +62,9 @@ static NTSTATUS (WINAPI *pBCryptOpenAlgorithmProvider)(BCRYPT_ALG_HANDLE *, LPCW static NTSTATUS (WINAPI *pBCryptSetProperty)(BCRYPT_HANDLE, LPCWSTR, PUCHAR, ULONG, ULONG); static NTSTATUS (WINAPI *pBCryptSignHash)(BCRYPT_KEY_HANDLE, void *, UCHAR *, ULONG, UCHAR *, ULONG, ULONG *, ULONG); static NTSTATUS (WINAPI *pBCryptVerifySignature)(BCRYPT_KEY_HANDLE, VOID *, UCHAR *, ULONG, UCHAR *, ULONG, ULONG); +static NTSTATUS (WINAPI *pBCryptSecretAgreement)(BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE, BCRYPT_SECRET_HANDLE *, ULONG); +static NTSTATUS (WINAPI *pBCryptDestroySecret)(BCRYPT_SECRET_HANDLE); +static NTSTATUS (WINAPI *pBCryptDeriveKey)(BCRYPT_SECRET_HANDLE, LPCWSTR, BCryptBufferDesc *, PUCHAR, ULONG, ULONG *, ULONG);
static void test_BCryptGenRandom(void) { @@ -1923,13 +1926,29 @@ static void test_RSA_SIGN(void)
static BYTE eccprivkey[] = { - 0x45, 0x43, 0x4b, 0x32, 0x20, 0x00, 0x00, 0x00, 0xfb, 0xbd, 0x3d, 0x20, 0x1b, 0x6d, 0x66, 0xb3, - 0x7c, 0x9f, 0x89, 0xf3, 0xe4, 0x41, 0x16, 0xa5, 0x68, 0x52, 0x77, 0xac, 0xab, 0x55, 0xb2, 0x6c, - 0xb0, 0x23, 0x55, 0xcb, 0x96, 0x14, 0xfd, 0x0b, 0x1c, 0xef, 0xdf, 0x07, 0x6d, 0x31, 0xaf, 0x39, - 0xce, 0x8c, 0x8f, 0x9d, 0x75, 0xd0, 0x7b, 0xea, 0x81, 0xdc, 0x40, 0x21, 0x1f, 0x58, 0x22, 0x5f, - 0x72, 0x55, 0xfc, 0x58, 0x8a, 0xeb, 0x88, 0x5d, 0x02, 0x09, 0x90, 0xd2, 0xe3, 0x36, 0xac, 0xfe, - 0x83, 0x13, 0x6c, 0x88, 0x1a, 0xab, 0x9b, 0xdd, 0xaa, 0x8a, 0xee, 0x69, 0x9a, 0x6a, 0x62, 0x86, - 0x6a, 0x13, 0x69, 0x88, 0xb7, 0xd5, 0xa3, 0xcd + 0x45, 0x43, 0x4b, 0x32, 0x20, 0x00, 0x00, 0x00, + 0xfb, 0xbd, 0x3d, 0x20, 0x1b, 0x6d, 0x66, 0xb3, 0x7c, 0x9f, 0x89, 0xf3, 0xe4, 0x41, 0x16, 0xa5, + 0x68, 0x52, 0x77, 0xac, 0xab, 0x55, 0xb2, 0x6c, 0xb0, 0x23, 0x55, 0xcb, 0x96, 0x14, 0xfd, 0x0b, + 0x1c, 0xef, 0xdf, 0x07, 0x6d, 0x31, 0xaf, 0x39, 0xce, 0x8c, 0x8f, 0x9d, 0x75, 0xd0, 0x7b, 0xea, + 0x81, 0xdc, 0x40, 0x21, 0x1f, 0x58, 0x22, 0x5f, 0x72, 0x55, 0xfc, 0x58, 0x8a, 0xeb, 0x88, 0x5d, + 0x02, 0x09, 0x90, 0xd2, 0xe3, 0x36, 0xac, 0xfe, 0x83, 0x13, 0x6c, 0x88, 0x1a, 0xab, 0x9b, 0xdd, + 0xaa, 0x8a, 0xee, 0x69, 0x9a, 0x6a, 0x62, 0x86, 0x6a, 0x13, 0x69, 0x88, 0xb7, 0xd5, 0xa3, 0xcd +}; + +static BYTE ecdh_pubkey[] = +{ + 0x45, 0x43, 0x4b, 0x31, 0x20, 0x00, 0x00, 0x00, + 0x07, 0x61, 0x9d, 0x49, 0x63, 0x6b, 0x96, 0x94, 0xd1, 0x8f, 0xd1, 0x48, 0xcc, 0xcf, 0x72, 0x4d, + 0xff, 0x43, 0xf4, 0x97, 0x0f, 0xa3, 0x8a, 0x72, 0xe9, 0xe0, 0xba, 0x87, 0x6d, 0xc3, 0x62, 0x15, + 0xae, 0x65, 0xdd, 0x31, 0x51, 0xfc, 0x3b, 0xc9, 0x59, 0xa1, 0x0a, 0x92, 0x17, 0x2b, 0x64, 0x55, + 0x03, 0x3e, 0x62, 0x1d, 0xac, 0x3e, 0x37, 0x40, 0x6a, 0x4c, 0xb6, 0x21, 0x3f, 0x73, 0x5c, 0xf5 +}; + +/* little endian */ +static BYTE ecdh_secret[] = +{ + 0x48, 0xb0, 0x11, 0xdb, 0x69, 0x4e, 0xb4, 0xf4, 0xf5, 0x3e, 0xe1, 0x9b, 0xca, 0x00, 0x04, 0xc8, + 0x9b, 0x69, 0xaf, 0xd1, 0xaf, 0x1f, 0xc2, 0xd7, 0x83, 0x0a, 0xb7, 0xf8, 0x4f, 0x24, 0x32, 0x8e, };
static void test_ECDH(void) @@ -1938,6 +1957,7 @@ static void test_ECDH(void) BCRYPT_ECCKEY_BLOB *ecckey; BCRYPT_ALG_HANDLE alg; BCRYPT_KEY_HANDLE key, privkey, pubkey; + BCRYPT_SECRET_HANDLE secret; NTSTATUS status; ULONG size;
@@ -2008,6 +2028,44 @@ static void test_ECDH(void) ok(!memcmp(buf, eccprivkey, size), "wrong data\n"); HeapFree(GetProcessHeap(), 0, buf);
+ status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, ecdh_pubkey, sizeof(ecdh_pubkey), 0); + 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) + { + goto derive_end; + } + + /* verify result on windows 10 */ + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &size, 0); + + if (status == STATUS_NOT_SUPPORTED) + { + win_skip("BCRYPT_KDF_RAW_SECRET not supported\n"); + goto derive_end; + } + + todo_wine ok(status == STATUS_SUCCESS, "got %08x\n", status); + + if (status != STATUS_SUCCESS) + { + goto derive_end; + } + + ok(size == 32, "size of secret key incorrect, got %u, expected 32\n", size); + buf = HeapAlloc(GetProcessHeap(), 0, size); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(!(memcmp(ecdh_secret, buf, size)), "wrong data\n"); + HeapFree(GetProcessHeap(), 0, buf); + + derive_end: + + pBCryptDestroySecret(secret); + pBCryptDestroyKey(pubkey); pBCryptDestroyKey(privkey); pBCryptCloseAlgorithmProvider(alg, 0); } @@ -2284,6 +2342,9 @@ START_TEST(bcrypt) pBCryptSetProperty = (void *)GetProcAddress(module, "BCryptSetProperty"); pBCryptSignHash = (void *)GetProcAddress(module, "BCryptSignHash"); pBCryptVerifySignature = (void *)GetProcAddress(module, "BCryptVerifySignature"); + pBCryptSecretAgreement = (void *)GetProcAddress(module, "BCryptSecretAgreement"); + pBCryptDestroySecret = (void *)GetProcAddress(module, "BCryptDestroySecret"); + pBCryptDeriveKey = (void *)GetProcAddress(module, "BCryptDeriveKey");
test_BCryptGenRandom(); test_BCryptGetFipsAlgorithmMode(); diff --git a/include/bcrypt.h b/include/bcrypt.h index 4546aea394..f393dc6e5c 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -92,6 +92,12 @@ typedef LONG NTSTATUS; #define BCRYPT_CHAIN_MODE_CFB L"ChainingModeCFB" #define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM" #define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM" + +#define BCRYPT_KDF_HASH L"HASH" +#define BCRYPT_KDF_HMAC L"HMAC" +#define BCRYPT_KDF_TLS_PRF L"TLS_PRF" +#define BCRYPT_KDF_SP80056A_CONCAT L"SP800_56A_CONCAT" +#define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" #else static const WCHAR BCRYPT_ALGORITHM_NAME[] = {'A','l','g','o','r','i','t','h','m','N','a','m','e',0}; static const WCHAR BCRYPT_AUTH_TAG_LENGTH[] = {'A','u','t','h','T','a','g','L','e','n','g','t','h',0}; @@ -146,6 +152,12 @@ static const WCHAR BCRYPT_CHAIN_MODE_ECB[] = {'C','h','a','i','n','i','n','g','M static const WCHAR BCRYPT_CHAIN_MODE_CFB[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','F','B',0}; static const WCHAR BCRYPT_CHAIN_MODE_CCM[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','C','M',0}; static const WCHAR BCRYPT_CHAIN_MODE_GCM[] = {'C','h','a','i','n','i','n','g','M','o','d','e','G','C','M',0}; + +static const WCHAR BCRYPT_KDF_HASH[] = {'H','A','S','H',0}; +static const WCHAR BCRYPT_KDF_HMAC[] = {'H','M','A','C',0}; +static const WCHAR BCRYPT_KDF_TLS_PRF[] = {'T','L','S','_','P','R','F',0}; +static const WCHAR BCRYPT_KDF_SP80056A_CONCAT[] = {'S','P','8','0','0','_','5','6','A','_','C','O','N','C','A','T',0}; +static const WCHAR BCRYPT_KDF_RAW_SECRET[] = {'T','R','U','N','C','A','T','E',0}; #endif
#define BCRYPT_ECDSA_PUBLIC_P256_MAGIC 0x31534345
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/bcrypt/tests/bcrypt.c | 60 ++++++++++++++++++++++++++++++++++++-- include/bcrypt.h | 6 ++++ 2 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 47bb58d3d1..d26150f469 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -1951,6 +1951,28 @@ static BYTE ecdh_secret[] = 0x9b, 0x69, 0xaf, 0xd1, 0xaf, 0x1f, 0xc2, 0xd7, 0x83, 0x0a, 0xb7, 0xf8, 0x4f, 0x24, 0x32, 0x8e, };
+BCryptBuffer hash_param_buffers[] = +{ +{ + sizeof(BCRYPT_SHA1_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA1_ALGORITHM, +} +}; + +BCryptBufferDesc hash_params = +{ + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(hash_param_buffers), + hash_param_buffers, +}; + +static BYTE hashed_secret[] = +{ + 0x1b, 0xe7, 0xbf, 0x0f, 0x65, 0x1e, 0xd0, 0x07, 0xf9, 0xf4, 0x77, 0x48, 0x48, 0x39, 0xd0, 0xf8, + 0xf3, 0xce, 0xfc, 0x89 +}; + static void test_ECDH(void) { BYTE *buf; @@ -2045,14 +2067,14 @@ static void test_ECDH(void) if (status == STATUS_NOT_SUPPORTED) { win_skip("BCRYPT_KDF_RAW_SECRET not supported\n"); - goto derive_end; + goto raw_secret_end; }
todo_wine ok(status == STATUS_SUCCESS, "got %08x\n", status);
if (status != STATUS_SUCCESS) { - goto derive_end; + goto raw_secret_end; }
ok(size == 32, "size of secret key incorrect, got %u, expected 32\n", size); @@ -2062,6 +2084,40 @@ static void test_ECDH(void) ok(!(memcmp(ecdh_secret, buf, size)), "wrong data\n"); HeapFree(GetProcessHeap(), 0, buf);
+ 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); + + if (status != STATUS_SUCCESS) + { + goto derive_end; + } + + ok (size == 20, "got %u\n", size); + buf = HeapAlloc(GetProcessHeap(), 0, size); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(!(memcmp(hashed_secret, buf, size)), "wrong data\n"); + HeapFree(GetProcessHeap(), 0, buf); + + /* ulVersion is not verified */ + hash_params.ulVersion = 0xdeadbeef; + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + ok (status == STATUS_SUCCESS, "got %08x\n", status); + + hash_params.ulVersion = BCRYPTBUFFER_VERSION; + hash_param_buffers[0].pvBuffer = (void*) L"INVALID"; + hash_param_buffers[0].cbBuffer = sizeof(L"INVALID"); + + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + ok (status == STATUS_NOT_SUPPORTED || broken (status == STATUS_NOT_FOUND) /* < win8 */, "got %08x\n", status); + + hash_param_buffers[0].pvBuffer = (void*) BCRYPT_RNG_ALGORITHM; + hash_param_buffers[0].cbBuffer = sizeof(BCRYPT_RNG_ALGORITHM); + status = pBCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, NULL, 0, &size, 0); + ok (status == STATUS_NOT_SUPPORTED, "got %08x\n", status); + derive_end:
pBCryptDestroySecret(secret); diff --git a/include/bcrypt.h b/include/bcrypt.h index f393dc6e5c..0bc4dea910 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -286,6 +286,10 @@ typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER ULONG cbKeyData; } BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER;
+#define KDF_HASH_ALGORITHM 0x00000000 +#define KDF_SECRET_PREPEND 0x00000001 +#define KDF_SECRET_APPEND 0x00000002 + typedef struct _BCryptBuffer { ULONG cbBuffer; @@ -293,6 +297,8 @@ typedef struct _BCryptBuffer void *pvBuffer; } BCryptBuffer, *PBCryptBuffer;
+#define BCRYPTBUFFER_VERSION 0 + typedef struct _BCryptBufferDesc { ULONG ulVersion;
Signed-off-by: Hans Leidekker hans@codeweavers.com
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- configure.ac | 14 ++ dlls/bcrypt/Makefile.in | 1 + dlls/bcrypt/bcrypt_internal.h | 13 ++ dlls/bcrypt/bcrypt_main.c | 75 ++++++++-- dlls/bcrypt/gcrypt.c | 263 ++++++++++++++++++++++++++++++++++ dlls/bcrypt/gnutls.c | 9 ++ dlls/bcrypt/tests/bcrypt.c | 2 +- 7 files changed, 366 insertions(+), 11 deletions(-) create mode 100644 dlls/bcrypt/gcrypt.c
diff --git a/configure.ac b/configure.ac index 5e36331596..e4a6e85af8 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]) @@ -1996,6 +1997,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..9a3b164f61 --- /dev/null +++ b/dlls/bcrypt/gcrypt.c @@ -0,0 +1,263 @@ +#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_sexp_extract_param); +MAKE_FUNCPTR(gcry_mpi_point_new); +MAKE_FUNCPTR(gcry_mpi_ec_decode_point); +MAKE_FUNCPTR(gcry_mpi_new); +MAKE_FUNCPTR(gcry_mpi_point_snatch_get); +MAKE_FUNCPTR(gcry_mpi_print); +MAKE_FUNCPTR(gcry_sexp_release); +MAKE_FUNCPTR(gcry_mpi_release); +MAKE_FUNCPTR(gcry_mpi_point_release); +MAKE_FUNCPTR(gcry_strsource); +MAKE_FUNCPTR(gcry_strerror); +#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_sexp_extract_param); + LOAD_FUNCPTR(gcry_mpi_point_new); + LOAD_FUNCPTR(gcry_mpi_ec_decode_point); + LOAD_FUNCPTR(gcry_mpi_new); + LOAD_FUNCPTR(gcry_mpi_point_snatch_get); + LOAD_FUNCPTR(gcry_mpi_print); + LOAD_FUNCPTR(gcry_sexp_release); + LOAD_FUNCPTR(gcry_mpi_release); + LOAD_FUNCPTR(gcry_mpi_point_release); + LOAD_FUNCPTR(gcry_strsource); + LOAD_FUNCPTR(gcry_strerror); +#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; +} + +/* 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/cdc4fc288d87f91f974aa23b6e8595a53970ce... */ +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_mpi_t result_mpi = NULL; + gcry_mpi_point_t result_point = NULL; + gcry_mpi_t secret_key = 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; + } + + if ((err = pgcry_sexp_extract_param(xchg_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); + + { + size_t secret_size; + UCHAR *tmp_buffer; + + if ((err = pgcry_mpi_print(GCRYMPI_FMT_STD, NULL, 0, &secret_size, secret_key))) + { + goto done; + } + + if (secret_size != key_size && secret_size != key_size + 1) + { + ERR("got secret size %lu, expected %u\n", secret_size, key_size); + status = STATUS_INTERNAL_ERROR; + goto done; + } + + tmp_buffer = heap_alloc(secret_size); + if ((err = pgcry_mpi_print(GCRYMPI_FMT_STD, tmp_buffer, secret_size, NULL, secret_key))) + { + goto done; + } + + if (secret_size % 2 != 0) /* remove prepended 0 */ + { + UCHAR *secret_buffer = heap_alloc(secret_size - 1); + memcpy(secret_buffer, tmp_buffer + 1, secret_size - 1); + heap_free(tmp_buffer); + secret->data = secret_buffer; + secret->len = secret_size - 1; + } + else + { + secret->data = tmp_buffer; + secret->len = secret_size; + } + } + + done: + pgcry_sexp_release(pubkey); + pgcry_sexp_release(privkey); + pgcry_sexp_release(xchg_result); + pgcry_mpi_release(result_mpi); + pgcry_mpi_release(secret_key); + + 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..f62669982c 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"); + return STATUS_NOT_IMPLEMENTED; +} +#endif + #endif diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index d26150f469..0f0771bedc 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -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) {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=62996
Your paranoid android.
=== debian10 (32 bit report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit French report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit Japanese:Japan report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit Chinese:China report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (64 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
On 2020-01-07 14:57, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=62996
Your paranoid android.
=== debian10 (32 bit report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit French report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit Japanese:Japan report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit Chinese:China report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (64 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
These tests failed because libgcrypt wasn't available on the testbot. I'm not sure what the correct way to deal with this is.
On Tue, 7 Jan 2020, Derek Lesho wrote: [...]
=== debian10 (64 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
These tests failed because libgcrypt wasn't available on the testbot. I'm not sure what the correct way to deal with this is.
I added libgcrypt-dev to the debian10 VM. Let me know if there are still issues.
On 2020-01-24 08:51, Francois Gouget wrote:
On Tue, 7 Jan 2020, Derek Lesho wrote: [...]
=== debian10 (64 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
These tests failed because libgcrypt wasn't available on the testbot. I'm not sure what the correct way to deal with this is.
I added libgcrypt-dev to the debian10 VM. Let me know if there are still issues.
Thanks Francois, I just resent the patches.
On 2020-01-24 09:31, Derek Lesho wrote:
On 2020-01-24 08:51, Francois Gouget wrote:
On Tue, 7 Jan 2020, Derek Lesho wrote: [...]
=== debian10 (64 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
These tests failed because libgcrypt wasn't available on the testbot. I'm not sure what the correct way to deal with this is.
I added libgcrypt-dev to the debian10 VM. Let me know if there are still issues.
Thanks Francois, I just resent the patches.
checking gcrypt.h usability... no checking gcrypt.h presence... no checking for gcrypt.h... no
I just checked the build log for v4 of my patch, and it looks like gcrypt still isn't being picked up. I did add a skip() to the tests though so that it doesn't manifest as a test failure.
On Fri, 24 Jan 2020, Derek Lesho wrote: [...]
I just checked the build log for v4 of my patch, and it looks like gcrypt still isn't being picked up. I did add a skip() to the tests though so that it doesn't manifest as a test failure.
Turns out libgpg-error-dev is yet another Debian package that does not support multiarch (despite multiarch support being a release goal for Debian 7!).
So I sent a fix for libgpg-error-dev and applied it to the TestBot VM: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=933713
Libgcrypt-dev is broken too but the workaround is simpler and the right fix harder :-(. So I'm sticking with the workaround for now.
I sent a job to test this and I think it works: https://testbot.winehq.org/JobDetails.pl?Key=63981
On 2020-01-28 09:00, Francois Gouget wrote:
On Fri, 24 Jan 2020, Derek Lesho wrote: [...]
I just checked the build log for v4 of my patch, and it looks like gcrypt still isn't being picked up. I did add a skip() to the tests though so that it doesn't manifest as a test failure.
Turns out libgpg-error-dev is yet another Debian package that does not support multiarch (despite multiarch support being a release goal for Debian 7!).
So I sent a fix for libgpg-error-dev and applied it to the TestBot VM: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=933713
Libgcrypt-dev is broken too but the workaround is simpler and the right fix harder :-(. So I'm sticking with the workaround for now.
I sent a job to test this and I think it works: https://testbot.winehq.org/JobDetails.pl?Key=63981
Thanks!
Alexandre said he prefers that we don't add another dependency, so at some point I'll try redoing this patch with GnuTLS, but if that's not possible this will be useful.
On Tue, 2020-01-07 at 14:22 -0600, Derek Lesho wrote:
+/* this is necessary since GNUTLS doesn't support ECDH public key encryption, maybe we can replace this when it does:
+NTSTATUS compute_secret_ecc (struct key *privkey_in, struct key *pubkey_in, struct secret *secret)
It would be nice if we could avoid adding another dependency. Has any effort been made to add this feature to GnuTLS?
On 2020-01-08 04:10, Hans Leidekker wrote:
On Tue, 2020-01-07 at 14:22 -0600, Derek Lesho wrote:
+/* this is necessary since GNUTLS doesn't support ECDH public key encryption, maybe we can replace this when it does:
+NTSTATUS compute_secret_ecc (struct key *privkey_in, struct key *pubkey_in, struct secret *secret)
It would be nice if we could avoid adding another dependency. Has any effort been made to add this feature to GnuTLS?
Not that I know of. I just took a second look, and I think the functionality does exist in nettle, so I might be able to add support for it in GNUTLS. However, that would take a very long time to trickle down into the libraries shipped by most distros
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47699 Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/bcrypt/bcrypt_main.c | 100 +++++++++++++++++++++++++++++++++++++ dlls/bcrypt/tests/bcrypt.c | 2 +- 2 files changed, 101 insertions(+), 1 deletion(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index ed64cd1965..ed09ffafb3 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1744,6 +1744,106 @@ NTSTATUS WINAPI BCryptDeriveKey(BCRYPT_SECRET_HANDLE hSecret, LPCWSTR deriv_func
TRACE( "%p, %s, %p, %p, %d, %p, %08x\n", secret, debugstr_w(deriv_func), parameter, derived, derived_size, result, flags );
+ if (flags) + { + FIXME("flags ignored: %08x\n", flags); + } + + if (!(strcmpW(deriv_func, 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 = ¶meter->pBuffers[i]; + switch(cur_buffer->BufferType) + { + case KDF_HASH_ALGORITHM: + hash_algorithm = cur_buffer; + break; + case KDF_SECRET_PREPEND: + secret_prepend = cur_buffer; + break; + case KDF_SECRET_APPEND: + secret_append = cur_buffer; + 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)) + { + 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(deriv_func, BCRYPT_KDF_RAW_SECRET))) { ULONG n; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 0f0771bedc..214ac92b44 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2087,7 +2087,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) {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=62997
Your paranoid android.
=== debian10 (32 bit report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit French report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit Japanese:Japan report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit Chinese:China report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (32 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002
=== debian10 (64 bit WoW report) ===
bcrypt: bcrypt.c:2057: Test failed: got c0000002