From 35221608696ca0e4680b15089ae6cb8f7bf368cc Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 25 May 2020 22:06:22 +0100 Subject: [PATCH 1/3] libssh2: Add support for Windows 10 Unix domain sockets --- vendor/libssh2/configure | 2 +- vendor/libssh2/configure.ac | 2 +- vendor/libssh2/example/CMakeLists.txt | 1 + vendor/libssh2/example/libssh2_config.h.in | 3 +++ vendor/libssh2/example/libssh2_config_cmake.h.in | 1 + vendor/libssh2/src/CMakeLists.txt | 1 + vendor/libssh2/src/agent.c | 6 ++++-- vendor/libssh2/src/libssh2_config_cmake.h.in | 1 + vendor/libssh2/tests/CMakeLists.txt | 1 + vendor/libssh2/tests/libssh2_config_cmake.h.in | 1 + vendor/libssh2/win32/libssh2_config.h | 1 + vendor/static_config/libssh2/win32/libssh2_config.h | 1 + 12 files changed, 17 insertions(+), 4 deletions(-) diff --git a/vendor/libssh2/configure b/vendor/libssh2/configure index a983da2ce..fee9804e8 100755 --- a/vendor/libssh2/configure +++ b/vendor/libssh2/configure @@ -18017,7 +18017,7 @@ case $host in # These are POSIX-like systems using BSD-like sockets API. ;; *) - for ac_header in windows.h winsock2.h ws2tcpip.h + for ac_header in windows.h winsock2.h ws2tcpip.h afunix.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" diff --git a/vendor/libssh2/configure.ac b/vendor/libssh2/configure.ac index fe5054a09..fd788f879 100644 --- a/vendor/libssh2/configure.ac +++ b/vendor/libssh2/configure.ac @@ -297,7 +297,7 @@ case $host in # These are POSIX-like systems using BSD-like sockets API. ;; *) - AC_CHECK_HEADERS([windows.h winsock2.h ws2tcpip.h]) + AC_CHECK_HEADERS([windows.h winsock2.h ws2tcpip.h afunix.h]) ;; esac diff --git a/vendor/libssh2/example/CMakeLists.txt b/vendor/libssh2/example/CMakeLists.txt index f77033f75..ac93a7e4a 100644 --- a/vendor/libssh2/example/CMakeLists.txt +++ b/vendor/libssh2/example/CMakeLists.txt @@ -86,6 +86,7 @@ check_include_files(sys/time.h HAVE_SYS_TIME_H) check_include_files(arpa/inet.h HAVE_ARPA_INET_H) check_include_files(netinet/in.h HAVE_NETINET_IN_H) check_include_files(winsock2.h HAVE_WINSOCK2_H) +check_include_files("winsock2.h;afunix.h" HAVE_AFUNIX_H) check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) check_symbol_exists(_stricmp string.h HAVE__STRICMP) diff --git a/vendor/libssh2/example/libssh2_config.h.in b/vendor/libssh2/example/libssh2_config.h.in index 307c62553..b7fd080c6 100644 --- a/vendor/libssh2/example/libssh2_config.h.in +++ b/vendor/libssh2/example/libssh2_config.h.in @@ -157,6 +157,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_WS2TCPIP_H +/* Define to 1 if you have the header file. */ +#undef HAVE_AFUNIX_H + /* to make a symbol visible */ #undef LIBSSH2_API diff --git a/vendor/libssh2/example/libssh2_config_cmake.h.in b/vendor/libssh2/example/libssh2_config_cmake.h.in index 12264f7fe..3a528c5be 100644 --- a/vendor/libssh2/example/libssh2_config_cmake.h.in +++ b/vendor/libssh2/example/libssh2_config_cmake.h.in @@ -44,6 +44,7 @@ #cmakedefine HAVE_ARPA_INET_H #cmakedefine HAVE_NETINET_IN_H #cmakedefine HAVE_WINSOCK2_H +#cmakedefine HAVE_AFUNIX_H /* Functions */ #cmakedefine HAVE_STRCASECMP diff --git a/vendor/libssh2/src/CMakeLists.txt b/vendor/libssh2/src/CMakeLists.txt index 2eaf4cc2c..0aae1be3d 100644 --- a/vendor/libssh2/src/CMakeLists.txt +++ b/vendor/libssh2/src/CMakeLists.txt @@ -297,6 +297,7 @@ check_include_files(sys/un.h HAVE_SYS_UN_H) check_include_files(windows.h HAVE_WINDOWS_H) check_include_files(ws2tcpip.h HAVE_WS2TCPIP_H) check_include_files(winsock2.h HAVE_WINSOCK2_H) +check_include_files("winsock2.h;afunix.h" HAVE_AFUNIX_H) check_type_size("long long" LONGLONG) diff --git a/vendor/libssh2/src/agent.c b/vendor/libssh2/src/agent.c index 0c8d88166..95e245766 100644 --- a/vendor/libssh2/src/agent.c +++ b/vendor/libssh2/src/agent.c @@ -42,10 +42,12 @@ #include #ifdef HAVE_SYS_UN_H #include +#elif defined(HAVE_AFUNIX_H) +#include #else -/* Use the existence of sys/un.h as a test if Unix domain socket is +/* Use the existence of sys/un.h or afunix.h as a test if Unix domain socket is supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually - support them. */ + support them before Windows 10 1703. */ #undef PF_UNIX #endif #include "userauth.h" diff --git a/vendor/libssh2/src/libssh2_config_cmake.h.in b/vendor/libssh2/src/libssh2_config_cmake.h.in index 62723ede8..d4b620c67 100644 --- a/vendor/libssh2/src/libssh2_config_cmake.h.in +++ b/vendor/libssh2/src/libssh2_config_cmake.h.in @@ -48,6 +48,7 @@ #cmakedefine HAVE_WINDOWS_H #cmakedefine HAVE_WS2TCPIP_H #cmakedefine HAVE_WINSOCK2_H +#cmakedefine HAVE_AFUNIX_H #cmakedefine HAVE_NTDEF_H #cmakedefine HAVE_NTSTATUS_H diff --git a/vendor/libssh2/tests/CMakeLists.txt b/vendor/libssh2/tests/CMakeLists.txt index c8c5253df..0f7cb9d2a 100644 --- a/vendor/libssh2/tests/CMakeLists.txt +++ b/vendor/libssh2/tests/CMakeLists.txt @@ -48,6 +48,7 @@ check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) check_include_files(arpa/inet.h HAVE_ARPA_INET_H) check_include_files(windows.h HAVE_WINDOWS_H) check_include_files(winsock2.h HAVE_WINSOCK2_H) +check_include_files("winsock2.h;afunix.h" HAVE_AFUNIX_H) check_include_files(netinet/in.h HAVE_NETINET_IN_H) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/libssh2_config_cmake.h.in" diff --git a/vendor/libssh2/tests/libssh2_config_cmake.h.in b/vendor/libssh2/tests/libssh2_config_cmake.h.in index 4df27ecdc..1221f0ee9 100644 --- a/vendor/libssh2/tests/libssh2_config_cmake.h.in +++ b/vendor/libssh2/tests/libssh2_config_cmake.h.in @@ -43,6 +43,7 @@ #cmakedefine HAVE_NETINET_IN_H #cmakedefine HAVE_WINDOWS_H #cmakedefine HAVE_WINSOCK2_H +#cmakedefine HAVE_AFUNIX_H #cmakedefine HAVE_SNPRINTF /* snprintf not in Visual Studio CRT and _snprintf dangerously incompatible. diff --git a/vendor/libssh2/win32/libssh2_config.h b/vendor/libssh2/win32/libssh2_config.h index 6ac2ef43e..fc320ae6c 100644 --- a/vendor/libssh2/win32/libssh2_config.h +++ b/vendor/libssh2/win32/libssh2_config.h @@ -20,6 +20,7 @@ #define HAVE_LIBCRYPT32 #define HAVE_WINSOCK2_H +#define HAVE_AFUNIX_H #define HAVE_IOCTLSOCKET #define HAVE_SELECT diff --git a/vendor/static_config/libssh2/win32/libssh2_config.h b/vendor/static_config/libssh2/win32/libssh2_config.h index b6af97806..87d1cd728 100644 --- a/vendor/static_config/libssh2/win32/libssh2_config.h +++ b/vendor/static_config/libssh2/win32/libssh2_config.h @@ -20,6 +20,7 @@ #define LIBSSH2_OPENSSL #define HAVE_WINSOCK2_H +#define HAVE_AFUNIX_H #define HAVE_IOCTLSOCKET #define HAVE_SELECT From 518f6a4bb608d9aef628d9a9730ed63f30185a39 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 25 May 2020 22:07:57 +0100 Subject: [PATCH 2/3] libssh2: Add support for OpenSSH-Win32 ssh-agent --- vendor/libssh2/src/agent.c | 94 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/vendor/libssh2/src/agent.c b/vendor/libssh2/src/agent.c index 95e245766..33af26cef 100644 --- a/vendor/libssh2/src/agent.c +++ b/vendor/libssh2/src/agent.c @@ -364,6 +364,99 @@ struct agent_ops agent_ops_pageant = { agent_transact_pageant, agent_disconnect_pageant }; + +static int +agent_connect_opensshwin32(LIBSSH2_AGENT *agent) +{ + HANDLE pipe = CreateFileA( + "\\\\.\\pipe\\openssh-ssh-agent", + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + if(pipe == INVALID_HANDLE_VALUE) + return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET, + "failed creating socket"); + agent->fd = (libssh2_socket_t)pipe; + return LIBSSH2_ERROR_NONE; +} + +static int +agent_transact_opensshwin32(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) +{ + unsigned char buf[4]; + BOOL rc; + + /* Send the length of the request */ + if(transctx->state == agent_NB_state_request_created) { + _libssh2_htonu32(buf, transctx->request_len); + DWORD written = 0; + rc = WriteFile((HANDLE)agent->fd, buf, (DWORD)sizeof buf, &written, NULL); + if(!rc || written != (DWORD)sizeof buf) + return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND, + "agent send failed"); + transctx->state = agent_NB_state_request_length_sent; + } + + /* Send the request body */ + if(transctx->state == agent_NB_state_request_length_sent) { + DWORD written = 0; + rc = WriteFile((HANDLE)agent->fd, transctx->request, + (DWORD)transctx->request_len, &written, NULL); + if(!rc || written != (DWORD)transctx->request_len) + return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND, + "agent send failed"); + transctx->state = agent_NB_state_request_sent; + } + + /* Receive the length of a response */ + if(transctx->state == agent_NB_state_request_sent) { + DWORD read = 0; + rc = ReadFile((HANDLE)agent->fd, buf, sizeof buf, &read, NULL); + if(!rc || read != (DWORD)sizeof buf) + return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV, + "agent recv failed"); + transctx->response_len = _libssh2_ntohu32(buf); + transctx->response = LIBSSH2_ALLOC(agent->session, + transctx->response_len); + if(!transctx->response) + return LIBSSH2_ERROR_ALLOC; + + transctx->state = agent_NB_state_response_length_received; + } + + /* Receive the response body */ + if(transctx->state == agent_NB_state_response_length_received) { + DWORD read = 0; + rc = ReadFile((HANDLE)agent->fd, transctx->response, + (DWORD)transctx->response_len, &read, NULL); + if(!rc || read != (DWORD)transctx->response_len) + return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND, + "agent recv failed"); + transctx->state = agent_NB_state_response_received; + } + + return 0; +} + +static int +agent_disconnect_opensshwin32(LIBSSH2_AGENT *agent) +{ + if(CloseHandle((HANDLE)agent->fd)) + agent->fd = LIBSSH2_INVALID_SOCKET; + else + return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT, + "failed closing the agent socket"); + return LIBSSH2_ERROR_NONE; +} + +struct agent_ops agent_ops_opensshwin32 = { + agent_connect_opensshwin32, + agent_transact_opensshwin32, + agent_disconnect_opensshwin32 +}; #endif /* WIN32 */ static struct { @@ -372,6 +465,7 @@ static struct { } supported_backends[] = { #ifdef WIN32 {"Pageant", &agent_ops_pageant}, + {"OpenSSH-Win32", &agent_ops_opensshwin32}, #endif /* WIN32 */ #ifdef PF_UNIX {"Unix", &agent_ops_unix}, From 57c26bf718b8c8ca9c17d80c155e4756b0ad2e25 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 25 May 2020 22:09:29 +0100 Subject: [PATCH 3/3] libssh2: Use newer DH API for Windows 10 1903+ --- vendor/libssh2/src/wincng.c | 317 ++++++++++++++++++++++++++++++++++-- vendor/libssh2/src/wincng.h | 21 ++- 2 files changed, 326 insertions(+), 12 deletions(-) diff --git a/vendor/libssh2/src/wincng.c b/vendor/libssh2/src/wincng.c index 4bebc6407..7ad0b47aa 100755 --- a/vendor/libssh2/src/wincng.c +++ b/vendor/libssh2/src/wincng.c @@ -291,6 +291,11 @@ _libssh2_wincng_init(void) 0); } } + +#if LIBSSH2_USE_BCRYPT_DH + (void)BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDH, + BCRYPT_DH_ALGORITHM, NULL, 0); +#endif } void @@ -310,6 +315,9 @@ _libssh2_wincng_free(void) (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0); (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0); +#if LIBSSH2_USE_BCRYPT_DH + (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDH, 0); +#endif memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng)); } @@ -2126,6 +2134,40 @@ _libssh2_wincng_bignum_free(_libssh2_bn *bn) } } +#if LIBSSH2_USE_BCRYPT_DH +/* We provide our own prototype for RtlGetVersion as the availability + * of the header that is documented to provide it is patchy across + * the various environments that the libssh2 CI builds on, and + * because the stdcall convention is important for the linker to + * be able to resolve the function in 32-bit MSVC compiler + * environments. */ +static int is_windows_10_1903_or_later(void) +{ + static NTSTATUS (NTAPI *RtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation) = NULL; + static int is_windows_10_1903_or_later_set = 0, is_windows_10_1903_or_later_value = 0; + if(!RtlGetVersion) { + *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), + "RtlGetVersion"); + if(!RtlGetVersion) { + return 0; + } + } + + if(!is_windows_10_1903_or_later_set) { + OSVERSIONINFOW vers = {sizeof(vers), 0, 0, 0, 0, {0}}; + if(RtlGetVersion(&vers) != 0) { + return 0; + } + + is_windows_10_1903_or_later_set = 1; + is_windows_10_1903_or_later_value = + (vers.dwMajorVersion == 10 && vers.dwBuildNumber >= 18362) || + vers.dwMajorVersion > 10; + } + + return is_windows_10_1903_or_later_value; +} +#endif /* * Windows CNG backend: Diffie-Hellman support. @@ -2134,35 +2176,288 @@ _libssh2_wincng_bignum_free(_libssh2_bn *bn) void _libssh2_dh_init(_libssh2_dh_ctx *dhctx) { - *dhctx = _libssh2_wincng_bignum_init(); /* Random from client */ +#if LIBSSH2_USE_BCRYPT_DH + if(is_windows_10_1903_or_later()) { + dhctx->dh_handle = NULL; + dhctx->dh_params = NULL; + return; + } +#endif + /* Random from client */ + dhctx->bn = _libssh2_wincng_bignum_init(); +} + +void +_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) +{ +#if LIBSSH2_USE_BCRYPT_DH + if(is_windows_10_1903_or_later()) { + if(dhctx->dh_handle) { + BCryptDestroyKey(dhctx->dh_handle); + dhctx->dh_handle = NULL; + } + if(dhctx->dh_params) { + /* Since dh_params were shared in clear text, we don't need + * to securely zero them out here */ + free(dhctx->dh_params); + dhctx->dh_params = NULL; + } + return; + } +#endif + if(dhctx->bn) { + _libssh2_wincng_bignum_free(dhctx->bn); + dhctx->bn = NULL; + } +} + +/* Copy a big endian set of bits from src to dest. + * if the size of src is smaller than dest then pad the "left" (MSB) + * end with zeroes and copy the bits into the "right" (LSB) end. */ +static void +memcpy_with_be_padding(unsigned char *dest, unsigned long dest_len, + unsigned char *src, unsigned long src_len) +{ + if(dest_len > src_len) { + memset(dest, 0, dest_len - src_len); + } + memcpy(dest + dest_len - src_len, src, src_len); +} + +static int +round_down(int number, int multiple) +{ + return (number / multiple) * multiple; } +/* Generates a Diffie-Hellman key pair using base `g', prime `p' and the given + * `group_order'. Can use the given big number context `bnctx' if needed. The + * private key is stored as opaque in the Diffie-Hellman context `*dhctx' and + * the public key is returned in `public'. 0 is returned upon success, else + * -1. */ int _libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, _libssh2_bn *g, _libssh2_bn *p, int group_order) { +#if LIBSSH2_USE_BCRYPT_DH + if(is_windows_10_1903_or_later()) { + BCRYPT_DH_PARAMETER_HEADER *dh_params = NULL; + unsigned long dh_params_len; + unsigned char *blob = NULL; + int status; + DWORD public_key_len_bytes = 0; + /* Note that the DH provider requires that keys be multiples of 64 bits + * in length. At the time of writing a practical observed group_order + * value is 257, so we need to round down to 8 bytes of length (64/8) + * in order for kex to succeed */ + DWORD key_length_bytes = max(round_down(group_order, 8), + max(g->length, p->length)); + unsigned char *public_blob = NULL; + BCRYPT_DH_KEY_BLOB *dh_key_blob; + + /* Prepare a key pair; pass the in the bit length of the key, + * but the key is not ready for consumption until it is finalized. */ + status = BCryptGenerateKeyPair(_libssh2_wincng.hAlgDH, + &dhctx->dh_handle, + key_length_bytes * 8, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + dh_params_len = sizeof(*dh_params) + 2 * key_length_bytes; + blob = malloc(dh_params_len); + if(!blob) { + return -1; + } + + /* Populate DH parameters blob; after the header follows the `p` + * value and the `g` value. */ + dh_params = (BCRYPT_DH_PARAMETER_HEADER*)blob; + dh_params->cbLength = dh_params_len; + dh_params->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + dh_params->cbKeyLength = key_length_bytes; + memcpy_with_be_padding(blob + sizeof(*dh_params), key_length_bytes, + p->bignum, p->length); + memcpy_with_be_padding(blob + sizeof(*dh_params) + key_length_bytes, + key_length_bytes, g->bignum, g->length); + + status = BCryptSetProperty(dhctx->dh_handle, BCRYPT_DH_PARAMETERS, + blob, dh_params_len, 0); + /* Pass ownership to dhctx; these parameters will be freed when + * the context is destroyed. We need to keep the parameters more + * easily available so that we have access to the `g` value when + * _libssh2_dh_secret is called later. */ + dhctx->dh_params = dh_params; + blob = NULL; + + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + status = BCryptFinalizeKeyPair(dhctx->dh_handle, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + /* Now we need to extract the public portion of the key so that we + * set it in the `public` bignum to satisfy our caller. + * First measure up the size of the required buffer. */ + status = BCryptExportKey(dhctx->dh_handle, NULL, BCRYPT_DH_PUBLIC_BLOB, + NULL, 0, &public_key_len_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + public_blob = malloc(public_key_len_bytes); + + status = BCryptExportKey(dhctx->dh_handle, NULL, BCRYPT_DH_PUBLIC_BLOB, + public_blob, public_key_len_bytes, + &public_key_len_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + return -1; + } + + /* BCRYPT_DH_PUBLIC_BLOB corresponds to a BCRYPT_DH_KEY_BLOB header + * followed by the Modulus, Generator and Public data. Those components + * each have equal size, specified by dh_key_blob->cbKey. */ + dh_key_blob = (BCRYPT_DH_KEY_BLOB*)public_blob; + if(_libssh2_wincng_bignum_resize(public, dh_key_blob->cbKey)) { + return -1; + } + + /* Copy the public key data into the bignum data buffer */ + memcpy(public->bignum, + public_blob + sizeof(*dh_key_blob) + 2 * dh_key_blob->cbKey, + dh_key_blob->cbKey); + + return 0; + } +#endif /* Generate x and e */ - if(_libssh2_wincng_bignum_rand(*dhctx, group_order * 8 - 1, 0, -1)) + if(_libssh2_wincng_bignum_rand(dhctx->bn, group_order * 8 - 1, 0, -1)) return -1; - if(_libssh2_wincng_bignum_mod_exp(public, g, *dhctx, p)) + if(_libssh2_wincng_bignum_mod_exp(public, g, dhctx->bn, p)) return -1; return 0; } +/* Computes the Diffie-Hellman secret from the previously created context + * `*dhctx', the public key `f' from the other party and the same prime `p' + * used at context creation. The result is stored in `secret'. 0 is returned + * upon success, else -1. */ int _libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, _libssh2_bn *f, _libssh2_bn *p) { +#if LIBSSH2_USE_BCRYPT_DH + if(is_windows_10_1903_or_later()) { + BCRYPT_KEY_HANDLE peer_public = NULL; + BCRYPT_SECRET_HANDLE agreement = NULL; + ULONG secret_len_bytes = 0; + unsigned char *blob; + int status; + unsigned char *start, *end; + BCRYPT_DH_KEY_BLOB *public_blob = NULL; + DWORD key_length_bytes = max(f->length, dhctx->dh_params->cbKeyLength); + DWORD public_blob_len = sizeof(*public_blob) + 3 * key_length_bytes; + + { + /* Populate a BCRYPT_DH_KEY_BLOB; after the header follows the + * Modulus, Generator and Public data. Those components must have + * equal size in this representation. */ + unsigned char *dest; + unsigned char *src; + + blob = malloc(public_blob_len); + if(!blob) { + return -1; + } + public_blob = (BCRYPT_DH_KEY_BLOB*)blob; + public_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + public_blob->cbKey = key_length_bytes; + + dest = (unsigned char *)(public_blob + 1); + src = (unsigned char *)(dhctx->dh_params + 1); + + /* Modulus (the p-value from the first call) */ + memcpy_with_be_padding(dest, key_length_bytes, src, + dhctx->dh_params->cbKeyLength); + /* Generator (the g-value from the first call) */ + memcpy_with_be_padding(dest + key_length_bytes, key_length_bytes, + src + dhctx->dh_params->cbKeyLength, + dhctx->dh_params->cbKeyLength); + /* Public from the peer */ + memcpy_with_be_padding(dest + 2*key_length_bytes, key_length_bytes, + f->bignum, f->length); + } + + /* Import the peer public key information */ + status = BCryptImportKeyPair(_libssh2_wincng.hAlgDH, NULL, + BCRYPT_DH_PUBLIC_BLOB, &peer_public, blob, + public_blob_len, 0); + if(!BCRYPT_SUCCESS(status)) { + goto out; + } + + /* Set up a handle that we can use to establish the shared secret + * between ourselves (our saved dh_handle) and the peer. */ + status = BCryptSecretAgreement(dhctx->dh_handle, peer_public, + &agreement, 0); + if(!BCRYPT_SUCCESS(status)) { + goto out; + } + + /* Compute the size of the buffer that is needed to hold the derived + * shared secret. */ + status = BCryptDeriveKey(agreement, BCRYPT_KDF_RAW_SECRET, NULL, NULL, + 0, &secret_len_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + goto out; + } + + /* Expand the secret bignum to be ready to receive the derived secret + * */ + if(_libssh2_wincng_bignum_resize(secret, secret_len_bytes)) { + status = STATUS_NO_MEMORY; + goto out; + } + + /* And populate the secret bignum */ + status = BCryptDeriveKey(agreement, BCRYPT_KDF_RAW_SECRET, NULL, + secret->bignum, secret_len_bytes, + &secret_len_bytes, 0); + if(!BCRYPT_SUCCESS(status)) { + goto out; + } + + /* Counter to all the other data in the BCrypt APIs, the raw secret is + * returned to us in host byte order, so we need to swap it to big + * endian order. */ + start = secret->bignum; + end = secret->bignum + secret->length - 1; + while(start < end) { + unsigned char tmp = *end; + *end = *start; + *start = tmp; + start++; + end--; + } + + status = 0; + +out: + if(peer_public) { + BCryptDestroyKey(peer_public); + } + if(agreement) { + BCryptDestroySecret(agreement); + } + return BCRYPT_SUCCESS(status) ? 0 : -1; + } +#endif /* Compute the shared secret */ - _libssh2_wincng_bignum_mod_exp(secret, f, *dhctx, p); + _libssh2_wincng_bignum_mod_exp(secret, f, dhctx->bn, p); return 0; } -void -_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) -{ - _libssh2_wincng_bignum_free(*dhctx); - *dhctx = NULL; -} - #endif /* LIBSSH2_WINCNG */ diff --git a/vendor/libssh2/src/wincng.h b/vendor/libssh2/src/wincng.h index f5838d0e6..349d3bab4 100755 --- a/vendor/libssh2/src/wincng.h +++ b/vendor/libssh2/src/wincng.h @@ -47,6 +47,12 @@ #include #include +#if defined(BCRYPT_KDF_RAW_SECRET) && defined(BCRYPT_DH_ALGORITHM) +/* BCRYPT_KDF_RAW_SECRET is available from Windows 8.1 and onwards */ +#define LIBSSH2_USE_BCRYPT_DH 1 +#else +#define LIBSSH2_USE_BCRYPT_DH 0 +#endif #define LIBSSH2_MD5 1 @@ -99,6 +105,9 @@ struct _libssh2_wincng_ctx { BCRYPT_ALG_HANDLE hAlgAES_ECB; BCRYPT_ALG_HANDLE hAlgRC4_NA; BCRYPT_ALG_HANDLE hAlg3DES_CBC; +#if LIBSSH2_USE_BCRYPT_DH + BCRYPT_ALG_HANDLE hAlgDH; +#endif }; struct _libssh2_wincng_ctx _libssh2_wincng; @@ -385,7 +394,17 @@ _libssh2_bn *_libssh2_wincng_bignum_init(void); * Windows CNG backend: Diffie-Hellman support */ -#define _libssh2_dh_ctx struct _libssh2_wincng_bignum * +typedef struct { +#if LIBSSH2_USE_BCRYPT_DH + /* holds our private+public key components */ + BCRYPT_KEY_HANDLE dh_handle; + /* records the parsed out modulus and generator parameters that are shared + * with the peer */ + BCRYPT_DH_PARAMETER_HEADER *dh_params; +#endif + /* fallback if the newer DH api doesn't work on this system */ + struct _libssh2_wincng_bignum *bn; +} _libssh2_dh_ctx; #define libssh2_dh_init(dhctx) _libssh2_dh_init(dhctx) #define libssh2_dh_key_pair(dhctx, public, g, p, group_order, bnctx) \ _libssh2_dh_key_pair(dhctx, public, g, p, group_order)