summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <george.hazan@gmail.com>2024-10-20 17:59:03 +0300
committerGeorge Hazan <george.hazan@gmail.com>2024-10-20 17:59:07 +0300
commit4da5235106afefe68a0a37cdd083241ecda9344b (patch)
tree8ebd7ef73d0ee68ebd4896f3917a0a8fce6035dc
parent91d9a431ee26ef580715a72b48d61634e0e4dec7 (diff)
fixes #4752 (Update libssh2 to 1.11.1)
-rw-r--r--libs/libssh2/include/libssh2.h8
-rw-r--r--libs/libssh2/include/libssh2_sftp.h11
-rw-r--r--libs/libssh2/libssh2.vcxproj12
-rw-r--r--libs/libssh2/libssh2.vcxproj.filters18
-rw-r--r--libs/libssh2/src/.gitignore7
-rw-r--r--libs/libssh2/src/CMakeLists.txt181
-rw-r--r--libs/libssh2/src/Makefile.am2
-rw-r--r--libs/libssh2/src/Makefile.inc6
-rw-r--r--libs/libssh2/src/bcrypt_pbkdf.c2
-rw-r--r--libs/libssh2/src/chacha.c224
-rw-r--r--libs/libssh2/src/chacha.h33
-rw-r--r--libs/libssh2/src/channel.c2
-rw-r--r--libs/libssh2/src/cipher-chachapoly.c134
-rw-r--r--libs/libssh2/src/cipher-chachapoly.h41
-rw-r--r--libs/libssh2/src/crypt.c166
-rw-r--r--libs/libssh2/src/crypto_config.h2
-rw-r--r--libs/libssh2/src/hostkey.c34
-rw-r--r--libs/libssh2/src/kex.c456
-rw-r--r--libs/libssh2/src/knownhost.c16
-rw-r--r--libs/libssh2/src/libgcrypt.h2
-rw-r--r--libs/libssh2/src/libssh2_priv.h37
-rw-r--r--libs/libssh2/src/mac.c44
-rw-r--r--libs/libssh2/src/mbedtls.c1533
-rw-r--r--libs/libssh2/src/mbedtls.h514
-rw-r--r--libs/libssh2/src/misc.c34
-rw-r--r--libs/libssh2/src/misc.h1
-rw-r--r--libs/libssh2/src/openssl.c133
-rw-r--r--libs/libssh2/src/openssl.h35
-rw-r--r--libs/libssh2/src/pem.c112
-rw-r--r--libs/libssh2/src/poly1305.c206
-rw-r--r--libs/libssh2/src/poly1305.h20
-rw-r--r--libs/libssh2/src/scp.c2
-rw-r--r--libs/libssh2/src/session.c55
-rw-r--r--libs/libssh2/src/sftp.c187
-rw-r--r--libs/libssh2/src/sftp.h10
-rw-r--r--libs/libssh2/src/transport.c419
-rw-r--r--libs/libssh2/src/userauth.c90
-rw-r--r--libs/libssh2/src/wincng.c4185
-rw-r--r--libs/libssh2/src/wincng.h561
39 files changed, 9072 insertions, 463 deletions
diff --git a/libs/libssh2/include/libssh2.h b/libs/libssh2/include/libssh2.h
index 8bc8a1380e..f3ba804a44 100644
--- a/libs/libssh2/include/libssh2.h
+++ b/libs/libssh2/include/libssh2.h
@@ -48,7 +48,7 @@
to make the BANNER define (used by src/session.c) be a valid SSH
banner. Release versions have no appended strings and may of course not
have dashes either. */
-#define LIBSSH2_VERSION "1.11.1_DEV"
+#define LIBSSH2_VERSION "1.11.1"
/* The numeric version number is also available "in parts" by using these
defines: */
@@ -292,7 +292,7 @@ typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT
typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE
{
char *text;
- unsigned int length;
+ unsigned int length; /* FIXME: change type to size_t */
} LIBSSH2_USERAUTH_KBDINT_RESPONSE;
typedef struct _LIBSSH2_SK_SIG_INFO {
@@ -502,7 +502,7 @@ typedef struct _LIBSSH2_POLLFD {
/* Hostkey Types */
#define LIBSSH2_HOSTKEY_TYPE_UNKNOWN 0
#define LIBSSH2_HOSTKEY_TYPE_RSA 1
-#define LIBSSH2_HOSTKEY_TYPE_DSS 2
+#define LIBSSH2_HOSTKEY_TYPE_DSS 2 /* deprecated */
#define LIBSSH2_HOSTKEY_TYPE_ECDSA_256 3
#define LIBSSH2_HOSTKEY_TYPE_ECDSA_384 4
#define LIBSSH2_HOSTKEY_TYPE_ECDSA_521 5
@@ -1147,7 +1147,7 @@ libssh2_knownhost_init(LIBSSH2_SESSION *session);
#define LIBSSH2_KNOWNHOST_KEY_SHIFT 18
#define LIBSSH2_KNOWNHOST_KEY_RSA1 (1<<18)
#define LIBSSH2_KNOWNHOST_KEY_SSHRSA (2<<18)
-#define LIBSSH2_KNOWNHOST_KEY_SSHDSS (3<<18)
+#define LIBSSH2_KNOWNHOST_KEY_SSHDSS (3<<18) /* deprecated */
#define LIBSSH2_KNOWNHOST_KEY_ECDSA_256 (4<<18)
#define LIBSSH2_KNOWNHOST_KEY_ECDSA_384 (5<<18)
#define LIBSSH2_KNOWNHOST_KEY_ECDSA_521 (6<<18)
diff --git a/libs/libssh2/include/libssh2_sftp.h b/libs/libssh2/include/libssh2_sftp.h
index 9d7a8fbb66..ab7b0af4de 100644
--- a/libs/libssh2/include/libssh2_sftp.h
+++ b/libs/libssh2/include/libssh2_sftp.h
@@ -303,11 +303,20 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp,
LIBSSH2_SFTP_RENAME_ATOMIC | \
LIBSSH2_SFTP_RENAME_NATIVE)
+LIBSSH2_API int libssh2_sftp_posix_rename_ex(LIBSSH2_SFTP *sftp,
+ const char *source_filename,
+ size_t srouce_filename_len,
+ const char *dest_filename,
+ size_t dest_filename_len);
+#define libssh2_sftp_posix_rename(sftp, sourcefile, destfile) \
+ libssh2_sftp_posix_rename_ex((sftp), (sourcefile), strlen(sourcefile), \
+ (destfile), strlen(destfile))
+
LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp,
const char *filename,
unsigned int filename_len);
#define libssh2_sftp_unlink(sftp, filename) \
- libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename))
+ libssh2_sftp_unlink_ex((sftp), (filename), (unsigned int)strlen(filename))
LIBSSH2_API int libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle,
LIBSSH2_SFTP_STATVFS *st);
diff --git a/libs/libssh2/libssh2.vcxproj b/libs/libssh2/libssh2.vcxproj
index 3d69cf0fd9..e38490eb09 100644
--- a/libs/libssh2/libssh2.vcxproj
+++ b/libs/libssh2/libssh2.vcxproj
@@ -58,9 +58,15 @@
<ClCompile Include="src\blowfish.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\chacha.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\channel.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\cipher-chachapoly.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\comp.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -100,6 +106,9 @@
<ClCompile Include="src\pem.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\poly1305.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\publickey.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -124,7 +133,9 @@
<ClCompile Include="src\version.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClInclude Include="src\chacha.h" />
<ClInclude Include="src\channel.h" />
+ <ClInclude Include="src\cipher-chachapoly.h" />
<ClInclude Include="src\comp.h" />
<ClInclude Include="src\crypto.h" />
<ClInclude Include="src\libgcrypt.h" />
@@ -134,6 +145,7 @@
<ClInclude Include="src\misc.h" />
<ClInclude Include="src\openssl.h" />
<ClInclude Include="src\packet.h" />
+ <ClInclude Include="src\poly1305.h" />
<ClInclude Include="src\session.h" />
<ClInclude Include="src\sftp.h" />
<ClInclude Include="src\transport.h" />
diff --git a/libs/libssh2/libssh2.vcxproj.filters b/libs/libssh2/libssh2.vcxproj.filters
index 37987969d3..a517eb2502 100644
--- a/libs/libssh2/libssh2.vcxproj.filters
+++ b/libs/libssh2/libssh2.vcxproj.filters
@@ -80,6 +80,15 @@
<ClCompile Include="src\userauth_kbd_packet.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\chacha.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\cipher-chachapoly.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\poly1305.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\channel.h">
@@ -127,5 +136,14 @@
<ClInclude Include="src\userauth_kbd_packet.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\chacha.h">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\cipher-chachapoly.h">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\poly1305.h">
+ <Filter>Source Files</Filter>
+ </ClInclude>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/libs/libssh2/src/.gitignore b/libs/libssh2/src/.gitignore
new file mode 100644
index 0000000000..91aa7f0be0
--- /dev/null
+++ b/libs/libssh2/src/.gitignore
@@ -0,0 +1,7 @@
+.deps
+.libs
+Makefile.inc.cmake
+libssh2.pc
+libssh2_config.h
+libssh2_config.h.in
+stamp-h1
diff --git a/libs/libssh2/src/CMakeLists.txt b/libs/libssh2/src/CMakeLists.txt
index bca1b23d5b..512c64ed3c 100644
--- a/libs/libssh2/src/CMakeLists.txt
+++ b/libs/libssh2/src/CMakeLists.txt
@@ -36,8 +36,8 @@
#
# SPDX-License-Identifier: BSD-3-Clause
-set(LIBSSH2_SOVERSION 1)
-set(LIBSSH2_LIBVERSION 1.0.1)
+set(_libssh2_soversion 1)
+set(_libssh2_libversion 1.0.1)
if(CRYPTO_BACKEND)
list(APPEND PRIVATE_COMPILE_DEFINITIONS ${CRYPTO_BACKEND_DEFINE})
@@ -49,9 +49,11 @@ endif()
## Options
+unset(_libssh2_definitions)
+
option(CLEAR_MEMORY "Enable clearing of memory before being freed" ON)
if(NOT CLEAR_MEMORY)
- list(APPEND libssh2_DEFINITIONS "LIBSSH2_NO_CLEAR_MEMORY")
+ list(APPEND _libssh2_definitions "LIBSSH2_NO_CLEAR_MEMORY")
endif()
option(ENABLE_ZLIB_COMPRESSION "Use zlib for compression" OFF)
@@ -61,19 +63,14 @@ if(ENABLE_ZLIB_COMPRESSION)
find_package(ZLIB REQUIRED)
list(APPEND libssh2_INCLUDE_DIRS ${ZLIB_INCLUDE_DIRS})
- list(APPEND LIBRARIES ${ZLIB_LIBRARIES})
- list(APPEND LIBSSH2_PC_LIBS_PRIVATE "-lz")
+ list(APPEND LIBSSH2_LIBS ${ZLIB_LIBRARIES})
list(APPEND LIBSSH2_PC_REQUIRES_PRIVATE "zlib")
if(ZLIB_FOUND)
- list(APPEND libssh2_DEFINITIONS "LIBSSH2_HAVE_ZLIB")
+ list(APPEND _libssh2_definitions "LIBSSH2_HAVE_ZLIB")
endif()
endif()
-list(APPEND LIBRARIES ${SOCKET_LIBRARIES})
-
-if(WIN32)
- list(APPEND LIBSSH2_PC_LIBS_PRIVATE "-lws2_32")
-endif()
+list(APPEND LIBSSH2_LIBS ${LIBSSH2_LIBS_SOCKET})
# to find generated header
list(APPEND libssh2_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR})
@@ -89,7 +86,7 @@ include(GNUInstallDirs)
transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
# Get 'CSOURCES' and 'HHEADERS' variables
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
-set(SOURCES ${CSOURCES} ${HHEADERS})
+set(_sources ${CSOURCES} ${HHEADERS})
## Library definition
@@ -103,40 +100,48 @@ if(WIN32 AND BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS AND
set(STATIC_LIB_SUFFIX "_static")
endif()
+unset(_libssh2_export)
+
# we want it to be called libssh2 on all platforms
if(BUILD_STATIC_LIBS)
- list(APPEND libssh2_export ${LIB_STATIC})
- add_library(${LIB_STATIC} STATIC ${SOURCES})
+ list(APPEND _libssh2_export ${LIB_STATIC})
+ add_library(${LIB_STATIC} STATIC ${_sources})
add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
- target_compile_definitions(${LIB_STATIC} PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} ${libssh2_DEFINITIONS})
- target_link_libraries(${LIB_STATIC} PRIVATE ${LIBRARIES})
+ target_compile_definitions(${LIB_STATIC} PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} ${_libssh2_definitions})
+ target_link_libraries(${LIB_STATIC} PRIVATE ${LIBSSH2_LIBS})
set_target_properties(${LIB_STATIC} PROPERTIES
- PREFIX "" OUTPUT_NAME "libssh2" SOVERSION "${LIBSSH2_SOVERSION}" VERSION "${LIBSSH2_LIBVERSION}"
+ PREFIX "" OUTPUT_NAME "libssh2" SOVERSION "${_libssh2_soversion}" VERSION "${_libssh2_libversion}"
SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
target_include_directories(${LIB_STATIC}
- PRIVATE "${PROJECT_SOURCE_DIR}/include/" ${libssh2_INCLUDE_DIRS} ${PRIVATE_INCLUDE_DIRECTORIES}
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/include"
+ ${libssh2_INCLUDE_DIRS}
+ ${PRIVATE_INCLUDE_DIRECTORIES}
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
endif()
if(BUILD_SHARED_LIBS)
- list(APPEND libssh2_export ${LIB_SHARED})
- add_library(${LIB_SHARED} SHARED ${SOURCES})
+ list(APPEND _libssh2_export ${LIB_SHARED})
+ add_library(${LIB_SHARED} SHARED ${_sources})
add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
if(WIN32)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libssh2.rc")
endif()
- target_compile_definitions(${LIB_SHARED} PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} ${libssh2_DEFINITIONS} ${LIB_SHARED_DEFINITIONS})
+ target_compile_definitions(${LIB_SHARED} PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} ${_libssh2_definitions} ${LIB_SHARED_DEFINITIONS})
target_compile_options(${LIB_SHARED} PRIVATE ${LIB_SHARED_C_FLAGS})
- target_link_libraries(${LIB_SHARED} PRIVATE ${LIBRARIES})
+ target_link_libraries(${LIB_SHARED} PRIVATE ${LIBSSH2_LIBS})
set_target_properties(${LIB_SHARED} PROPERTIES
- PREFIX "" OUTPUT_NAME "libssh2" SOVERSION "${LIBSSH2_SOVERSION}" VERSION "${LIBSSH2_LIBVERSION}"
+ PREFIX "" OUTPUT_NAME "libssh2" SOVERSION "${_libssh2_soversion}" VERSION "${_libssh2_libversion}"
IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
POSITION_INDEPENDENT_CODE ON)
target_include_directories(${LIB_SHARED}
- PRIVATE "${PROJECT_SOURCE_DIR}/include/" ${libssh2_INCLUDE_DIRS} ${PRIVATE_INCLUDE_DIRECTORIES}
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/include"
+ ${libssh2_INCLUDE_DIRS}
+ ${PRIVATE_INCLUDE_DIRECTORIES}
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}>")
@@ -171,7 +176,7 @@ if(BUILD_SHARED_LIBS)
endif()
set(RUNTIME_DEPENDENCIES ${_RUNTIME_DEPENDENCIES} CACHE INTERNAL
- "Files that must be in the same directory as the executables at runtime.")
+ "Files that must be in the same directory as the executables at runtime.")
# Package config
@@ -182,16 +187,96 @@ install(EXPORT "${PROJECT_NAME}-targets"
## During build, register directly from build tree
# create libssh2-targets.cmake
-export(TARGETS ${libssh2_export} NAMESPACE "${PROJECT_NAME}::" FILE "${PROJECT_NAME}-targets.cmake")
+export(TARGETS ${_libssh2_export} NAMESPACE "${PROJECT_NAME}::" FILE "${PROJECT_NAME}-targets.cmake")
export(PACKAGE ${PROJECT_NAME}) # register it
-# Generate libssh2-config.cmake into build tree and install it
+# Generate libssh2-config.cmake into build tree and install it with dependencies
configure_file("${PROJECT_SOURCE_DIR}/cmake/libssh2-config.cmake.in" "${PROJECT_NAME}-config.cmake" @ONLY)
install(
- FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
+ "${PROJECT_SOURCE_DIR}/cmake/FindLibgcrypt.cmake"
+ "${PROJECT_SOURCE_DIR}/cmake/FindMbedTLS.cmake"
+ "${PROJECT_SOURCE_DIR}/cmake/FindWolfSSL.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
-## Export a .pc file for client projects not using CMaek
+# Calculate variables for pkg-config
+set(LIBSSH2_PC_LIBS_PRIVATE "")
+
+if(WIN32)
+ list(APPEND LIBSSH2_PC_LIBS_PRIVATE "-lws2_32")
+endif()
+
+set(_ldflags "")
+
+# Avoid getting unnecessary -L options for known system directories.
+unset(_sys_libdirs)
+foreach(_libdir IN LISTS CMAKE_SYSTEM_PREFIX_PATH)
+ if(_libdir MATCHES "/$")
+ set(_libdir "${_libdir}lib")
+ else()
+ set(_libdir "${_libdir}/lib")
+ endif()
+ if(IS_DIRECTORY "${_libdir}")
+ list(APPEND _sys_libdirs "${_libdir}")
+ endif()
+ if(DEFINED CMAKE_LIBRARY_ARCHITECTURE)
+ set(_libdir "${_libdir}/${CMAKE_LIBRARY_ARCHITECTURE}")
+ if(IS_DIRECTORY "${_libdir}")
+ list(APPEND _sys_libdirs "${_libdir}")
+ endif()
+ endif()
+endforeach()
+
+foreach(_libdir IN LISTS LIBSSH2_LIBDIRS)
+ list(FIND _sys_libdirs "${_libdir}" _libdir_index)
+ if(_libdir_index LESS 0)
+ list(APPEND _ldflags "-L${_libdir}")
+ endif()
+endforeach()
+
+unset(_implicit_libs)
+if(NOT MINGW AND NOT UNIX)
+ set(_implicit_libs ${CMAKE_C_IMPLICIT_LINK_LIBRARIES})
+endif()
+
+foreach(_lib IN LISTS _implicit_libs LIBSSH2_LIBS)
+ if(TARGET "${_lib}")
+ set(_libname "${_lib}")
+ get_target_property(_imported "${_libname}" IMPORTED)
+ if(NOT _imported)
+ # Reading the LOCATION property on non-imported target will error out.
+ # Assume the user will not need this information in the .pc file.
+ continue()
+ endif()
+ get_target_property(_lib "${_libname}" LOCATION)
+ if(NOT _lib)
+ message(WARNING "Bad lib in library list: ${_libname}")
+ continue()
+ endif()
+ endif()
+ if(_lib MATCHES "^-")
+ list(APPEND _ldflags "${_lib}")
+ elseif(_lib MATCHES ".*/.*")
+ # This gets a bit more complex, because we want to specify the
+ # directory separately, and only once per directory
+ get_filename_component(_libdir ${_lib} DIRECTORY)
+ get_filename_component(_libname ${_lib} NAME_WE)
+ if(_libname MATCHES "^lib")
+ list(FIND _sys_libdirs "${_libdir}" _libdir_index)
+ if(_libdir_index LESS 0)
+ list(APPEND _ldflags "-L${_libdir}")
+ endif()
+ string(REGEX REPLACE "^lib" "" _libname "${_libname}")
+ list(APPEND LIBSSH2_PC_LIBS_PRIVATE "-l${_libname}")
+ else()
+ list(APPEND LIBSSH2_PC_LIBS_PRIVATE "${_lib}")
+ endif()
+ else()
+ list(APPEND LIBSSH2_PC_LIBS_PRIVATE "-l${_lib}")
+ endif()
+endforeach()
+
if(LIBSSH2_PC_REQUIRES_PRIVATE)
string(REPLACE ";" "," LIBSSH2_PC_REQUIRES_PRIVATE "${LIBSSH2_PC_REQUIRES_PRIVATE}")
endif()
@@ -199,7 +284,14 @@ if(LIBSSH2_PC_LIBS_PRIVATE)
list(REMOVE_DUPLICATES LIBSSH2_PC_LIBS_PRIVATE)
string(REPLACE ";" " " LIBSSH2_PC_LIBS_PRIVATE "${LIBSSH2_PC_LIBS_PRIVATE}")
endif()
-# merge the pkg-config private fields into public ones when static-only
+if(_ldflags)
+ list(REMOVE_DUPLICATES _ldflags)
+ string(REPLACE ";" " " _ldflags "${_ldflags}")
+ set(LIBSSH2_PC_LIBS_PRIVATE "${_ldflags} ${LIBSSH2_PC_LIBS_PRIVATE}")
+ string(STRIP "${LIBSSH2_PC_LIBS_PRIVATE}" LIBSSH2_PC_LIBS_PRIVATE)
+endif()
+
+# Merge pkg-config private fields into public ones when static-only
if(BUILD_SHARED_LIBS)
set(LIBSSH2_PC_REQUIRES "")
set(LIBSSH2_PC_LIBS "")
@@ -207,10 +299,31 @@ else()
set(LIBSSH2_PC_REQUIRES "${LIBSSH2_PC_REQUIRES_PRIVATE}")
set(LIBSSH2_PC_LIBS "${LIBSSH2_PC_LIBS_PRIVATE}")
endif()
-set(prefix ${CMAKE_INSTALL_PREFIX})
-set(exec_prefix "\${prefix}")
-set(libdir "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
-set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+
+set(prefix "${CMAKE_INSTALL_PREFIX}")
+set(exec_prefix "\${prefix}")
+if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
+ set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+else()
+ set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+endif()
+if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
+ set(libdir "${CMAKE_INSTALL_LIBDIR}")
+else()
+ set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+# Generate a pkg-config file for client projects not using CMake.
+# Consumed variables:
+# exec_prefix
+# includedir
+# LIBSSH2_PC_LIBS
+# LIBSSH2_PC_LIBS_PRIVATE
+# LIBSSH2_PC_REQUIRES
+# LIBSSH2_PC_REQUIRES_PRIVATE
+# LIBSSH2_VERSION
+# libdir
+# prefix
configure_file("${PROJECT_SOURCE_DIR}/libssh2.pc.in" "libssh2.pc" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/libssh2.pc"
diff --git a/libs/libssh2/src/Makefile.am b/libs/libssh2/src/Makefile.am
index 45b7a17275..9f148823d1 100644
--- a/libs/libssh2/src/Makefile.am
+++ b/libs/libssh2/src/Makefile.am
@@ -20,7 +20,7 @@ lib_LTLIBRARIES = libssh2.la
AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/include
# This might hold -Werror
-CFLAGS += @LIBSSH2_CFLAG_EXTRAS@
+AM_CFLAGS = @LIBSSH2_CFLAG_EXTRAS@
VERSION=-version-info 1:1:0
diff --git a/libs/libssh2/src/Makefile.inc b/libs/libssh2/src/Makefile.inc
index 9133819a62..5cf070cc85 100644
--- a/libs/libssh2/src/Makefile.inc
+++ b/libs/libssh2/src/Makefile.inc
@@ -5,6 +5,8 @@ CSOURCES = \
bcrypt_pbkdf.c \
channel.c \
comp.c \
+ chacha.c \
+ cipher-chachapoly.c \
crypt.c \
crypto.c \
global.c \
@@ -16,6 +18,7 @@ CSOURCES = \
misc.c \
packet.c \
pem.c \
+ poly1305.c \
publickey.c \
scp.c \
session.c \
@@ -26,7 +29,9 @@ CSOURCES = \
version.c
HHEADERS = \
+ chacha.h \
channel.h \
+ cipher-chachapoly.h \
comp.h \
crypto.h \
crypto_config.h \
@@ -39,6 +44,7 @@ HHEADERS = \
openssl.h \
os400qc3.h \
packet.h \
+ poly1305.h \
session.h \
sftp.h \
transport.h \
diff --git a/libs/libssh2/src/bcrypt_pbkdf.c b/libs/libssh2/src/bcrypt_pbkdf.c
index 414c5e4e36..73cda43576 100644
--- a/libs/libssh2/src/bcrypt_pbkdf.c
+++ b/libs/libssh2/src/bcrypt_pbkdf.c
@@ -116,7 +116,7 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt,
if(rounds < 1)
return -1;
if(passlen == 0 || saltlen == 0 || keylen == 0 ||
- keylen > sizeof(out) * sizeof(out) || saltlen > 1<<20)
+ keylen > sizeof(out) * sizeof(out) || saltlen > 1 << 20)
return -1;
countsalt = calloc(1, saltlen + 4);
if(!countsalt)
diff --git a/libs/libssh2/src/chacha.c b/libs/libssh2/src/chacha.c
new file mode 100644
index 0000000000..72560f8610
--- /dev/null
+++ b/libs/libssh2/src/chacha.c
@@ -0,0 +1,224 @@
+/*
+ * chacha-merged.c version 20080118
+ * D. J. Bernstein
+ * Public domain.
+ * Copyright not intended 2024.
+ *
+ * SPDX-License-Identifier: SAX-PD-2.0
+ */
+
+#include "libssh2_priv.h"
+
+#include "chacha.h"
+
+/* $OpenBSD: chacha.c,v 1.1 2013/11/21 00:45:44 djm Exp $ */
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+
+typedef struct chacha_ctx chacha_ctx;
+
+#define U8C(v) (v##U)
+#define U32C(v) (v##U)
+
+#define U8V(v) ((u8)(v) & U8C(0xFF))
+#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
+
+#define ROTL32(v, n) \
+ (U32V((v) << (n)) | ((v) >> (32 - (n))))
+
+#define U8TO32_LITTLE(p) \
+ (((u32)((p)[0]) ) | \
+ ((u32)((p)[1]) << 8) | \
+ ((u32)((p)[2]) << 16) | \
+ ((u32)((p)[3]) << 24))
+
+#define U32TO8_LITTLE(p, v) \
+ do { \
+ (p)[0] = U8V((v) ); \
+ (p)[1] = U8V((v) >> 8); \
+ (p)[2] = U8V((v) >> 16); \
+ (p)[3] = U8V((v) >> 24); \
+ } while (0)
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v),1))
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static const char sigma[17] = "expand 32-byte k";
+static const char tau[17] = "expand 16-byte k";
+
+void
+chacha_keysetup(chacha_ctx *x, const u8 *k, u32 kbits)
+{
+ const char *constants;
+
+ x->input[4] = U8TO32_LITTLE(k + 0);
+ x->input[5] = U8TO32_LITTLE(k + 4);
+ x->input[6] = U8TO32_LITTLE(k + 8);
+ x->input[7] = U8TO32_LITTLE(k + 12);
+ if(kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ }
+ else { /* kbits == 128 */
+ constants = tau;
+ }
+ x->input[8] = U8TO32_LITTLE(k + 0);
+ x->input[9] = U8TO32_LITTLE(k + 4);
+ x->input[10] = U8TO32_LITTLE(k + 8);
+ x->input[11] = U8TO32_LITTLE(k + 12);
+ x->input[0] = U8TO32_LITTLE(constants + 0);
+ x->input[1] = U8TO32_LITTLE(constants + 4);
+ x->input[2] = U8TO32_LITTLE(constants + 8);
+ x->input[3] = U8TO32_LITTLE(constants + 12);
+}
+
+void
+chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter)
+{
+ x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0);
+ x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4);
+ x->input[14] = U8TO32_LITTLE(iv + 0);
+ x->input[15] = U8TO32_LITTLE(iv + 4);
+}
+
+void
+chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, u32 bytes)
+{
+ u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ u8 *ctarget = NULL;
+ u8 tmp[64];
+ u_int i;
+
+ if(!bytes)
+ return;
+
+ j0 = x->input[0];
+ j1 = x->input[1];
+ j2 = x->input[2];
+ j3 = x->input[3];
+ j4 = x->input[4];
+ j5 = x->input[5];
+ j6 = x->input[6];
+ j7 = x->input[7];
+ j8 = x->input[8];
+ j9 = x->input[9];
+ j10 = x->input[10];
+ j11 = x->input[11];
+ j12 = x->input[12];
+ j13 = x->input[13];
+ j14 = x->input[14];
+ j15 = x->input[15];
+
+ for(;;) {
+ if(bytes < 64) {
+ for(i = 0; i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for(i = 20; i > 0; i -= 2) {
+ QUARTERROUND(x0, x4, x8, x12)
+ QUARTERROUND(x1, x5, x9, x13)
+ QUARTERROUND(x2, x6, x10, x14)
+ QUARTERROUND(x3, x7, x11, x15)
+ QUARTERROUND(x0, x5, x10, x15)
+ QUARTERROUND(x1, x6, x11, x12)
+ QUARTERROUND(x2, x7, x8, x13)
+ QUARTERROUND(x3, x4, x9, x14)
+ }
+ x0 = PLUS(x0, j0);
+ x1 = PLUS(x1, j1);
+ x2 = PLUS(x2, j2);
+ x3 = PLUS(x3, j3);
+ x4 = PLUS(x4, j4);
+ x5 = PLUS(x5, j5);
+ x6 = PLUS(x6, j6);
+ x7 = PLUS(x7, j7);
+ x8 = PLUS(x8, j8);
+ x9 = PLUS(x9, j9);
+ x10 = PLUS(x10, j10);
+ x11 = PLUS(x11, j11);
+ x12 = PLUS(x12, j12);
+ x13 = PLUS(x13, j13);
+ x14 = PLUS(x14, j14);
+ x15 = PLUS(x15, j15);
+
+ x0 = XOR(x0, U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1, U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2, U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3, U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4, U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5, U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6, U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7, U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8, U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9, U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10, U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11, U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12, U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13, U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14, U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15, U8TO32_LITTLE(m + 60));
+
+ j12 = PLUSONE(j12);
+ if(!j12) {
+ j13 = PLUSONE(j13);
+ /* stopping at 2^70 bytes per nonce is user's responsibility */
+ }
+
+ U32TO8_LITTLE(c + 0, x0);
+ U32TO8_LITTLE(c + 4, x1);
+ U32TO8_LITTLE(c + 8, x2);
+ U32TO8_LITTLE(c + 12, x3);
+ U32TO8_LITTLE(c + 16, x4);
+ U32TO8_LITTLE(c + 20, x5);
+ U32TO8_LITTLE(c + 24, x6);
+ U32TO8_LITTLE(c + 28, x7);
+ U32TO8_LITTLE(c + 32, x8);
+ U32TO8_LITTLE(c + 36, x9);
+ U32TO8_LITTLE(c + 40, x10);
+ U32TO8_LITTLE(c + 44, x11);
+ U32TO8_LITTLE(c + 48, x12);
+ U32TO8_LITTLE(c + 52, x13);
+ U32TO8_LITTLE(c + 56, x14);
+ U32TO8_LITTLE(c + 60, x15);
+
+ if(bytes <= 64) {
+ if(bytes < 64) {
+ for(i = 0; i < bytes;++i) ctarget[i] = c[i];
+ }
+ x->input[12] = j12;
+ x->input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ m += 64;
+ }
+}
diff --git a/libs/libssh2/src/chacha.h b/libs/libssh2/src/chacha.h
new file mode 100644
index 0000000000..b17ecd5c6e
--- /dev/null
+++ b/libs/libssh2/src/chacha.h
@@ -0,0 +1,33 @@
+/* $OpenBSD: chacha.h,v 1.4 2016/08/27 04:04:56 guenther Exp $ */
+
+/*
+ * chacha-merged.c version 20080118
+ * D. J. Bernstein
+ * Public domain.
+ * Copyright not intended 2024.
+ *
+ * SPDX-License-Identifier: SAX-PD-2.0
+ */
+
+#ifndef CHACHA_H
+#define CHACHA_H
+
+#include <stdlib.h>
+
+struct chacha_ctx {
+ u_int input[16];
+};
+
+#define CHACHA_MINKEYLEN 16
+#define CHACHA_NONCELEN 8
+#define CHACHA_CTRLEN 8
+#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN)
+#define CHACHA_BLOCKLEN 64
+
+void chacha_keysetup(struct chacha_ctx *x, const u_char *k, u_int kbits);
+void chacha_ivsetup(struct chacha_ctx *x, const u_char *iv, const u_char *ctr);
+void chacha_encrypt_bytes(struct chacha_ctx *x, const u_char *m,
+ u_char *c, u_int bytes);
+
+#endif /* CHACHA_H */
+
diff --git a/libs/libssh2/src/channel.c b/libs/libssh2/src/channel.c
index 2f7d162c72..529c2ef820 100644
--- a/libs/libssh2/src/channel.c
+++ b/libs/libssh2/src/channel.c
@@ -2180,7 +2180,7 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
"channel_read() got %ld of data from %u/%u/%d%s",
(long)bytes_want, channel->local.id,
channel->remote.id, stream_id,
- unlink_packet?" [ul]":""));
+ unlink_packet ? " [ul]" : ""));
/* copy data from this struct to the target buffer */
memcpy(&buf[bytes_read],
diff --git a/libs/libssh2/src/cipher-chachapoly.c b/libs/libssh2/src/cipher-chachapoly.c
new file mode 100644
index 0000000000..9e4b117008
--- /dev/null
+++ b/libs/libssh2/src/cipher-chachapoly.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2013 Damien Miller <djm@mindrot.org>
+ *
+ * Adapted by Will Cosgrove <will@panic.com> for libssh2
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/* $OpenBSD: cipher-chachapoly.c,v 1.8 2016/08/03 05:41:57 djm Exp $ */
+
+#include "libssh2_priv.h"
+#include "misc.h"
+#include "cipher-chachapoly.h"
+
+int
+chachapoly_timingsafe_bcmp(const void *b1, const void *b2, size_t n);
+
+int
+chachapoly_init(struct chachapoly_ctx *ctx, const u_char *key, u_int keylen)
+{
+ if(keylen != (32 + 32)) /* 2 x 256 bit keys */
+ return LIBSSH2_ERROR_INVAL;
+ chacha_keysetup(&ctx->main_ctx, key, 256);
+ chacha_keysetup(&ctx->header_ctx, key + 32, 256);
+ return 0;
+}
+
+/*
+ * chachapoly_crypt() operates as following:
+ * En/decrypt with header key 'aadlen' bytes from 'src', storing result
+ * to 'dest'. The ciphertext here is treated as additional authenticated
+ * data for MAC calculation.
+ * En/decrypt 'len' bytes at offset 'aadlen' from 'src' to 'dest'. Use
+ * POLY1305_TAGLEN bytes at offset 'len'+'aadlen' as the authentication
+ * tag. This tag is written on encryption and verified on decryption.
+ */
+int
+chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
+ const u_char *src, u_int len, u_int aadlen, int do_encrypt)
+{
+ u_char seqbuf[8];
+ const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
+ u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
+ int r = LIBSSH2_ERROR_INVAL;
+ unsigned char *ptr = NULL;
+
+ /*
+ * Run ChaCha20 once to generate the Poly1305 key. The IV is the
+ * packet sequence number.
+ */
+ memset(poly_key, 0, sizeof(poly_key));
+ ptr = &seqbuf[0];
+ _libssh2_store_u64(&ptr, seqnr);
+ chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
+ chacha_encrypt_bytes(&ctx->main_ctx,
+ poly_key, poly_key, sizeof(poly_key));
+
+ /* If decrypting, check tag before anything else */
+ if(!do_encrypt) {
+ const u_char *tag = src + aadlen + len;
+
+ poly1305_auth(expected_tag, src, aadlen + len, poly_key);
+ if(chachapoly_timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN)
+ != 0) {
+ r = LIBSSH2_ERROR_DECRYPT;
+ goto out;
+ }
+ }
+
+ /* Crypt additional data */
+ if(aadlen) {
+ chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
+ chacha_encrypt_bytes(&ctx->header_ctx, src, dest, aadlen);
+ }
+
+ /* Set Chacha's block counter to 1 */
+ chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
+ chacha_encrypt_bytes(&ctx->main_ctx, src + aadlen,
+ dest + aadlen, len);
+
+ /* If encrypting, calculate and append tag */
+ if(do_encrypt) {
+ poly1305_auth(dest + aadlen + len, dest, aadlen + len,
+ poly_key);
+ }
+ r = 0;
+out:
+ memset(expected_tag, 0, sizeof(expected_tag));
+ memset(seqbuf, 0, sizeof(seqbuf));
+ memset(poly_key, 0, sizeof(poly_key));
+ return r;
+}
+
+/* Decrypt and extract the encrypted packet length */
+int
+chachapoly_get_length(struct chachapoly_ctx *ctx, unsigned int *plenp,
+ unsigned int seqnr, const unsigned char *cp,
+ unsigned int len)
+{
+ u_char buf[4], seqbuf[8];
+ unsigned char *ptr = NULL;
+
+ if(len < 4)
+ return -1;
+ ptr = &seqbuf[0];
+ _libssh2_store_u64(&ptr, seqnr);
+ chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
+ chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4);
+ *plenp = _libssh2_ntohu32(buf);
+ return 0;
+}
+
+int
+chachapoly_timingsafe_bcmp(const void *b1, const void *b2, size_t n)
+{
+ const unsigned char *p1 = b1, *p2 = b2;
+ int ret = 0;
+
+ for(; n > 0; n--)
+ ret |= *p1++ ^ *p2++;
+ return (ret != 0);
+}
diff --git a/libs/libssh2/src/cipher-chachapoly.h b/libs/libssh2/src/cipher-chachapoly.h
new file mode 100644
index 0000000000..4d0015cb79
--- /dev/null
+++ b/libs/libssh2/src/cipher-chachapoly.h
@@ -0,0 +1,41 @@
+/* $OpenBSD: cipher-chachapoly.h,v 1.4 2014/06/24 01:13:21 djm Exp $ */
+
+/*
+ * Copyright (c) Damien Miller 2013 <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#ifndef CHACHA_POLY_AEAD_H
+#define CHACHA_POLY_AEAD_H
+
+#include "chacha.h"
+#include "poly1305.h"
+
+#define CHACHA_KEYLEN 32 /* Only 256 bit keys used here */
+
+struct chachapoly_ctx {
+ struct chacha_ctx main_ctx, header_ctx;
+};
+
+int chachapoly_init(struct chachapoly_ctx *cpctx,
+ const u_char *key, u_int keylen);
+int chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr,
+ u_char *dest, const u_char *src, u_int len, u_int aadlen,
+ int do_encrypt);
+int chachapoly_get_length(struct chachapoly_ctx *cpctx,
+ u_int *plenp, u_int seqnr, const u_char *cp,
+ u_int len);
+
+#endif /* CHACHA_POLY_AEAD_H */
diff --git a/libs/libssh2/src/crypt.c b/libs/libssh2/src/crypt.c
index 2fb661fd60..6c816eb447 100644
--- a/libs/libssh2/src/crypt.c
+++ b/libs/libssh2/src/crypt.c
@@ -39,6 +39,9 @@
*/
#include "libssh2_priv.h"
+#include "cipher-chachapoly.h"
+
+#include <assert.h>
#if defined(LIBSSH2DEBUG) && defined(LIBSSH2_CRYPT_NONE_INSECURE)
/* crypt_none_crypt
@@ -55,8 +58,12 @@
*
*/
static int
-crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf,
- void **abstract, int firstlast)
+crypt_none_crypt(LIBSSH2_SESSION * session,
+ unsigned int seqno,
+ unsigned char *buf,
+ size_t buf_len,
+ void **abstract,
+ int firstlast)
{
/* Do nothing to the data! */
return 0;
@@ -80,6 +87,7 @@ struct crypt_ctx
int encrypt;
_libssh2_cipher_type(algo);
_libssh2_cipher_ctx h;
+ struct chachapoly_ctx chachapoly_ctx;
};
static int
@@ -107,13 +115,18 @@ crypt_init(LIBSSH2_SESSION * session,
}
static int
-crypt_encrypt(LIBSSH2_SESSION * session, unsigned char *block,
- size_t blocksize, void **abstract, int firstlast)
+crypt_encrypt(LIBSSH2_SESSION * session,
+ unsigned int seqno,
+ unsigned char *buf,
+ size_t buf_len,
+ void **abstract,
+ int firstlast)
{
struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
(void) session;
- return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
- blocksize, firstlast);
+ (void) seqno;
+ return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, buf,
+ buf_len, firstlast);
}
static int
@@ -135,8 +148,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_gcm = {
16, /* blocksize */
12, /* initial value length */
32, /* secret length -- 32*8 == 256bit */
+ 16, /* length of the authentication tag */
LIBSSH2_CRYPT_FLAG_INTEGRATED_MAC | LIBSSH2_CRYPT_FLAG_PKTLEN_AAD,
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes256gcm
@@ -148,8 +163,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_gcm = {
16, /* blocksize */
12, /* initial value length */
16, /* secret length -- 16*8 == 128bit */
+ 16, /* length of the authentication tag */
LIBSSH2_CRYPT_FLAG_INTEGRATED_MAC | LIBSSH2_CRYPT_FLAG_PKTLEN_AAD,
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes128gcm
@@ -163,8 +180,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
16, /* blocksize */
16, /* initial value length */
16, /* secret length -- 16*8 == 128bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes128ctr
@@ -176,8 +195,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = {
16, /* blocksize */
16, /* initial value length */
24, /* secret length -- 24*8 == 192bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes192ctr
@@ -189,8 +210,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = {
16, /* blocksize */
16, /* initial value length */
32, /* secret length -- 32*8 == 256bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes256ctr
@@ -204,8 +227,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = {
16, /* blocksize */
16, /* initial value length */
16, /* secret length -- 16*8 == 128bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes128
@@ -217,8 +242,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = {
16, /* blocksize */
16, /* initial value length */
24, /* secret length -- 24*8 == 192bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes192
@@ -230,8 +257,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = {
16, /* blocksize */
16, /* initial value length */
32, /* secret length -- 32*8 == 256bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes256
@@ -245,8 +274,10 @@ static const LIBSSH2_CRYPT_METHOD
16, /* blocksize */
16, /* initial value length */
32, /* secret length -- 32*8 == 256bit */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_aes256
@@ -260,8 +291,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = {
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_blowfish
@@ -275,8 +308,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = {
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_arcfour
@@ -312,8 +347,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour128 = {
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init_arcfour128,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_arcfour
@@ -327,8 +364,10 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = {
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_cast5
@@ -342,17 +381,132 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = {
8, /* blocksize */
8, /* initial value length */
24, /* secret length */
+ 0, /* length of the authentication tag */
0, /* flags */
&crypt_init,
+ NULL,
&crypt_encrypt,
&crypt_dtor,
_libssh2_cipher_3des
};
#endif
+static int
+crypt_init_chacha20_poly(LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method,
+ unsigned char *iv, int *free_iv,
+ unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract)
+{
+ struct crypt_ctx *ctx = LIBSSH2_ALLOC(session,
+ sizeof(struct crypt_ctx));
+
+ (void)iv;
+
+ if(!ctx)
+ return LIBSSH2_ERROR_ALLOC;
+
+ ctx->encrypt = encrypt;
+ ctx->algo = method->algo;
+
+ if(chachapoly_init(&ctx->chachapoly_ctx, secret, method->secret_len)) {
+ LIBSSH2_FREE(session, ctx);
+ return -1;
+ }
+
+ *abstract = ctx;
+ *free_iv = 1;
+ *free_secret = 1;
+ return 0;
+}
+
+
+static int
+crypt_encrypt_chacha20_poly_buffer(LIBSSH2_SESSION * session,
+ unsigned int seqno,
+ unsigned char *buf,
+ size_t buf_len,
+ void **abstract,
+ int firstlast)
+{
+ int ret = 1;
+ struct crypt_ctx *ctx = *(struct crypt_ctx **) abstract;
+
+ (void)session;
+ (void)firstlast;
+
+ if(ctx) {
+ if(ctx->encrypt) {
+ /* requires out_buf to be large enough to hold encrypted output
+ plus auth tag (auth len)
+
+ buf is a full packet so we need to subtract packet length from
+ length
+ */
+ ret = chachapoly_crypt(&ctx->chachapoly_ctx, seqno, buf, buf,
+ ((u_int)buf_len) - 4, 4, ctx->encrypt);
+ }
+ else {
+ /* buf is full packet including size and auth tag but buf_len
+ doesn't include size */
+ ret = chachapoly_crypt(&ctx->chachapoly_ctx, seqno, buf, buf,
+ ((u_int)buf_len), 4, ctx->encrypt);
+
+ /* the api expects the size field to already be removed
+ from the decrypted packet so we'll help it out */
+ if(ret == 0) {
+ memmove(buf, buf + 4, buf_len - 4);
+ }
+ }
+ }
+
+ return (ret == 0 ? 0 : 1);
+}
+
+static int
+crypt_get_length_chacha20_poly(LIBSSH2_SESSION * session, unsigned int seqno,
+ unsigned char *data, size_t data_size,
+ unsigned int *len, void **abstract)
+{
+ struct crypt_ctx *ctx = *(struct crypt_ctx **) abstract;
+
+ (void)session;
+
+ return chachapoly_get_length(&ctx->chachapoly_ctx, len, seqno, data,
+ (u_int)data_size);
+}
+
+static int
+crypt_dtor_chacha20_poly(LIBSSH2_SESSION * session, void **abstract)
+{
+ struct crypt_ctx **cctx = (struct crypt_ctx **) abstract;
+ if(cctx && *cctx) {
+ LIBSSH2_FREE(session, *cctx);
+ *abstract = NULL;
+ }
+ return 0;
+}
+
+static const LIBSSH2_CRYPT_METHOD
+ libssh2_crypt_method_chacha20_poly1305_openssh = {
+ "chacha20-poly1305@openssh.com",
+ "",
+ 8, /* blocksize */
+ 0, /* initial value length */
+ 64, /* secret length */
+ 16, /* length of the auth_tag */
+ LIBSSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET, /* flags */
+ &crypt_init_chacha20_poly,
+ &crypt_get_length_chacha20_poly,
+ &crypt_encrypt_chacha20_poly_buffer,
+ &crypt_dtor_chacha20_poly,
+ _libssh2_cipher_chacha20 /* not actually used */
+};
+
/* These are the crypt methods that are available to be negotiated. Methods
towards the start are chosen in preference to ones further down the list. */
static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
+ &libssh2_crypt_method_chacha20_poly1305_openssh,
#if LIBSSH2_AES_GCM
&libssh2_crypt_method_aes256_gcm,
&libssh2_crypt_method_aes128_gcm,
diff --git a/libs/libssh2/src/crypto_config.h b/libs/libssh2/src/crypto_config.h
index 5934e140b6..885a6a918d 100644
--- a/libs/libssh2/src/crypto_config.h
+++ b/libs/libssh2/src/crypto_config.h
@@ -20,7 +20,7 @@
#define LIBSSH2_HMAC_RIPEMD 0
#endif
-#ifdef LIBSSH2_NO_DSA
+#if !defined(LIBSSH2_DSA_ENABLE)
#undef LIBSSH2_DSA
#define LIBSSH2_DSA 0
#endif
diff --git a/libs/libssh2/src/hostkey.c b/libs/libssh2/src/hostkey.c
index b28b4e6995..99eaf3e07f 100644
--- a/libs/libssh2/src/hostkey.c
+++ b/libs/libssh2/src/hostkey.c
@@ -498,6 +498,34 @@ static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_cert = {
#endif /* LIBSSH2_RSA_SHA1 */
+#if LIBSSH2_RSA_SHA2
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_sha2_256_cert = {
+ "rsa-sha2-256-cert-v01@openssh.com",
+ SHA256_DIGEST_LENGTH,
+ NULL,
+ hostkey_method_ssh_rsa_initPEM,
+ hostkey_method_ssh_rsa_initPEMFromMemory,
+ NULL,
+ hostkey_method_ssh_rsa_sha2_256_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_rsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_sha2_512_cert = {
+ "rsa-sha2-512-cert-v01@openssh.com",
+ SHA512_DIGEST_LENGTH,
+ NULL,
+ hostkey_method_ssh_rsa_initPEM,
+ hostkey_method_ssh_rsa_initPEMFromMemory,
+ NULL,
+ hostkey_method_ssh_rsa_sha2_512_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_rsa_dtor,
+};
+
+#endif /* LIBSSH2_RSA_SHA2 */
+
#endif /* LIBSSH2_RSA */
#if LIBSSH2_DSA
@@ -1332,6 +1360,8 @@ static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = {
#if LIBSSH2_RSA_SHA2
&hostkey_method_ssh_rsa_sha2_512,
&hostkey_method_ssh_rsa_sha2_256,
+ &hostkey_method_ssh_rsa_sha2_512_cert,
+ &hostkey_method_ssh_rsa_sha2_256_cert,
#endif /* LIBSSH2_RSA_SHA2 */
#if LIBSSH2_RSA_SHA1
&hostkey_method_ssh_rsa,
@@ -1386,9 +1416,11 @@ static int hostkey_type(const unsigned char *hostkey, size_t len)
static const unsigned char rsa[] = {
0, 0, 0, 0x07, 's', 's', 'h', '-', 'r', 's', 'a'
};
+#if LIBSSH2_DSA
static const unsigned char dss[] = {
0, 0, 0, 0x07, 's', 's', 'h', '-', 'd', 's', 's'
};
+#endif
static const unsigned char ecdsa_256[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
'n', 'i', 's', 't', 'p', '2', '5', '6'
@@ -1411,8 +1443,10 @@ static int hostkey_type(const unsigned char *hostkey, size_t len)
if(!memcmp(rsa, hostkey, 11))
return LIBSSH2_HOSTKEY_TYPE_RSA;
+#if LIBSSH2_DSA
if(!memcmp(dss, hostkey, 11))
return LIBSSH2_HOSTKEY_TYPE_DSS;
+#endif
if(len < 15)
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
diff --git a/libs/libssh2/src/kex.c b/libs/libssh2/src/kex.c
index 8c65a0fee6..4ca9c34c74 100644
--- a/libs/libssh2/src/kex.c
+++ b/libs/libssh2/src/kex.c
@@ -228,6 +228,60 @@ static void _libssh2_sha_algo_value_hash(int sha_algo,
}
+static void
+diffie_hellman_state_cleanup(LIBSSH2_SESSION * session,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ libssh2_dh_dtor(&exchange_state->x);
+ _libssh2_bn_free(exchange_state->e);
+ exchange_state->e = NULL;
+ _libssh2_bn_free(exchange_state->f);
+ exchange_state->f = NULL;
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+ _libssh2_bn_ctx_free(exchange_state->ctx);
+ exchange_state->ctx = NULL;
+
+ if(exchange_state->e_packet) {
+ LIBSSH2_FREE(session, exchange_state->e_packet);
+ exchange_state->e_packet = NULL;
+ }
+
+ if(exchange_state->s_packet) {
+ LIBSSH2_FREE(session, exchange_state->s_packet);
+ exchange_state->s_packet = NULL;
+ }
+
+ if(exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+}
+
+static void
+kex_diffie_hellman_cleanup(LIBSSH2_SESSION * session,
+ key_exchange_state_low_t * key_state) {
+ if(key_state->state != libssh2_NB_state_idle) {
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+
+ if(key_state->data) {
+ LIBSSH2_FREE(session, key_state->data);
+ key_state->data = NULL;
+ }
+ key_state->state = libssh2_NB_state_idle;
+ }
+
+ if(key_state->exchange_state.state != libssh2_NB_state_idle) {
+ diffie_hellman_state_cleanup(session, &key_state->exchange_state);
+ }
+}
+
+
/*!
* @function diffie_hellman_sha_algo
* @abstract Diffie Hellman Key Exchange, Group Agnostic,
@@ -316,13 +370,21 @@ static int diffie_hellman_sha_algo(LIBSSH2_SESSION *session,
_libssh2_htonu32(exchange_state->e_packet + 1,
(uint32_t)(exchange_state->e_packet_len - 5));
if(_libssh2_bn_bits(exchange_state->e) % 8) {
- _libssh2_bn_to_bin(exchange_state->e,
- exchange_state->e_packet + 5);
+ if(_libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 5)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->e");
+ goto clean_exit;
+ }
}
else {
exchange_state->e_packet[5] = 0;
- _libssh2_bn_to_bin(exchange_state->e,
- exchange_state->e_packet + 6);
+ if(_libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 6)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->e");
+ goto clean_exit;
+ }
}
_libssh2_debug((session, LIBSSH2_TRACE_KEX, "Sending KEX packet %u",
@@ -407,8 +469,11 @@ static int diffie_hellman_sha_algo(LIBSSH2_SESSION *session,
buf.dataptr = buf.data;
buf.dataptr++; /* advance past type */
- if(session->server_hostkey)
+ if(session->server_hostkey) {
LIBSSH2_FREE(session, session->server_hostkey);
+ session->server_hostkey = NULL;
+ session->server_hostkey_len = 0;
+ }
if(_libssh2_copy_string(session, &buf, &(session->server_hostkey),
&host_key_len)) {
@@ -522,8 +587,13 @@ static int diffie_hellman_sha_algo(LIBSSH2_SESSION *session,
goto clean_exit;
}
- _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len,
- exchange_state->f_value);
+ if(_libssh2_bn_from_bin(exchange_state->f,
+ exchange_state->f_value_len,
+ exchange_state->f_value)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Invalid DH-SHA f value");
+ goto clean_exit;
+ }
if(_libssh2_get_string(&buf, &(exchange_state->h_sig),
&(exchange_state->h_sig_len))) {
@@ -550,11 +620,21 @@ static int diffie_hellman_sha_algo(LIBSSH2_SESSION *session,
_libssh2_htonu32(exchange_state->k_value,
(uint32_t)(exchange_state->k_value_len - 4));
if(_libssh2_bn_bits(exchange_state->k) % 8) {
- _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ if(_libssh2_bn_to_bin(exchange_state->k,
+ exchange_state->k_value + 4)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->k");
+ goto clean_exit;
+ }
}
else {
exchange_state->k_value[4] = 0;
- _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ if(_libssh2_bn_to_bin(exchange_state->k,
+ exchange_state->k_value + 5)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->k");
+ goto clean_exit;
+ }
}
exchange_state->exchange_hash = (void *)&exchange_hash_ctx;
@@ -933,32 +1013,7 @@ static int diffie_hellman_sha_algo(LIBSSH2_SESSION *session,
}
clean_exit:
- libssh2_dh_dtor(&exchange_state->x);
- _libssh2_bn_free(exchange_state->e);
- exchange_state->e = NULL;
- _libssh2_bn_free(exchange_state->f);
- exchange_state->f = NULL;
- _libssh2_bn_free(exchange_state->k);
- exchange_state->k = NULL;
- _libssh2_bn_ctx_free(exchange_state->ctx);
- exchange_state->ctx = NULL;
-
- if(exchange_state->e_packet) {
- LIBSSH2_FREE(session, exchange_state->e_packet);
- exchange_state->e_packet = NULL;
- }
-
- if(exchange_state->s_packet) {
- LIBSSH2_FREE(session, exchange_state->s_packet);
- exchange_state->s_packet = NULL;
- }
-
- if(exchange_state->k_value) {
- LIBSSH2_FREE(session, exchange_state->k_value);
- exchange_state->k_value = NULL;
- }
-
- exchange_state->state = libssh2_NB_state_idle;
+ diffie_hellman_state_cleanup(session, exchange_state);
return ret;
}
@@ -1002,8 +1057,16 @@ kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session,
key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
/* Initialize P and G */
- _libssh2_bn_set_word(key_state->g, 2);
- _libssh2_bn_from_bin(key_state->p, 128, p_value);
+ if(!key_state->g || _libssh2_bn_set_word(key_state->g, 2)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state g.");
+ goto clean_exit;
+ }
+ if(!key_state->p || _libssh2_bn_from_bin(key_state->p, 128, p_value)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state p.");
+ goto clean_exit;
+ }
_libssh2_debug((session, LIBSSH2_TRACE_KEX,
"Initiating Diffie-Hellman Group1 Key Exchange"));
@@ -1019,11 +1082,8 @@ kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session,
return ret;
}
- _libssh2_bn_free(key_state->p);
- key_state->p = NULL;
- _libssh2_bn_free(key_state->g);
- key_state->g = NULL;
- key_state->state = libssh2_NB_state_idle;
+clean_exit:
+ kex_diffie_hellman_cleanup(session, key_state);
return ret;
}
@@ -1095,8 +1155,17 @@ kex_method_diffie_hellman_group14_key_exchange(LIBSSH2_SESSION *session,
/* g == 2 */
/* Initialize P and G */
- _libssh2_bn_set_word(key_state->g, 2);
- _libssh2_bn_from_bin(key_state->p, 256, p_value);
+ if(!key_state->g || _libssh2_bn_set_word(key_state->g, 2)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state g.");
+ goto clean_exit;
+ }
+ else if(!key_state->p ||
+ _libssh2_bn_from_bin(key_state->p, 256, p_value)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state p.");
+ goto clean_exit;
+ }
_libssh2_debug((session, LIBSSH2_TRACE_KEX,
"Initiating Diffie-Hellman Group14 Key Exchange"));
@@ -1110,11 +1179,8 @@ kex_method_diffie_hellman_group14_key_exchange(LIBSSH2_SESSION *session,
return ret;
}
- key_state->state = libssh2_NB_state_idle;
- _libssh2_bn_free(key_state->p);
- key_state->p = NULL;
- _libssh2_bn_free(key_state->g);
- key_state->g = NULL;
+clean_exit:
+ kex_diffie_hellman_cleanup(session, key_state);
return ret;
}
@@ -1216,8 +1282,16 @@ kex_method_diffie_hellman_group16_sha512_key_exchange(LIBSSH2_SESSION *session,
/* g == 2 */
/* Initialize P and G */
- _libssh2_bn_set_word(key_state->g, 2);
- _libssh2_bn_from_bin(key_state->p, 512, p_value);
+ if(!key_state->g || _libssh2_bn_set_word(key_state->g, 2)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state g.");
+ goto clean_exit;
+ }
+ if(!key_state->p || _libssh2_bn_from_bin(key_state->p, 512, p_value)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state p.");
+ goto clean_exit;
+ }
_libssh2_debug((session, LIBSSH2_TRACE_KEX,
"Initiating Diffie-Hellman Group16 Key Exchange"));
@@ -1233,11 +1307,8 @@ kex_method_diffie_hellman_group16_sha512_key_exchange(LIBSSH2_SESSION *session,
return ret;
}
- key_state->state = libssh2_NB_state_idle;
- _libssh2_bn_free(key_state->p);
- key_state->p = NULL;
- _libssh2_bn_free(key_state->g);
- key_state->g = NULL;
+clean_exit:
+ kex_diffie_hellman_cleanup(session, key_state);
return ret;
}
@@ -1348,8 +1419,17 @@ kex_method_diffie_hellman_group18_sha512_key_exchange(LIBSSH2_SESSION *session,
/* g == 2 */
/* Initialize P and G */
- _libssh2_bn_set_word(key_state->g, 2);
- _libssh2_bn_from_bin(key_state->p, 1024, p_value);
+ if(!key_state->g || _libssh2_bn_set_word(key_state->g, 2)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state g.");
+ goto clean_exit;
+ }
+ else if(!key_state->p ||
+ _libssh2_bn_from_bin(key_state->p, 1024, p_value)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to allocate key state p.");
+ goto clean_exit;
+ }
_libssh2_debug((session, LIBSSH2_TRACE_KEX,
"Initiating Diffie-Hellman Group18 Key Exchange"));
@@ -1365,11 +1445,8 @@ kex_method_diffie_hellman_group18_sha512_key_exchange(LIBSSH2_SESSION *session,
return ret;
}
- key_state->state = libssh2_NB_state_idle;
- _libssh2_bn_free(key_state->p);
- key_state->p = NULL;
- _libssh2_bn_free(key_state->g);
- key_state->g = NULL;
+clean_exit:
+ kex_diffie_hellman_cleanup(session, key_state);
return ret;
}
@@ -1462,8 +1539,17 @@ kex_method_diffie_hellman_group_exchange_sha1_key_exchange(
goto dh_gex_clean_exit;
}
- _libssh2_bn_from_bin(key_state->p, p_len, p);
- _libssh2_bn_from_bin(key_state->g, g_len, g);
+ if(_libssh2_bn_from_bin(key_state->p, p_len, p)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid DH-SHA1 p");
+ goto dh_gex_clean_exit;
+ }
+
+ if(_libssh2_bn_from_bin(key_state->g, g_len, g)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid DH-SHA1 g");
+ goto dh_gex_clean_exit;
+ }
ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p,
(int)p_len, 1,
@@ -1476,16 +1562,10 @@ kex_method_diffie_hellman_group_exchange_sha1_key_exchange(
if(ret == LIBSSH2_ERROR_EAGAIN) {
return ret;
}
-
- LIBSSH2_FREE(session, key_state->data);
}
dh_gex_clean_exit:
- key_state->state = libssh2_NB_state_idle;
- _libssh2_bn_free(key_state->g);
- key_state->g = NULL;
- _libssh2_bn_free(key_state->p);
- key_state->p = NULL;
+ kex_diffie_hellman_cleanup(session, key_state);
return ret;
}
@@ -1581,8 +1661,17 @@ kex_method_diffie_hellman_group_exchange_sha256_key_exchange(
goto dh_gex_clean_exit;
}
- _libssh2_bn_from_bin(key_state->p, p_len, p);
- _libssh2_bn_from_bin(key_state->g, g_len, g);
+ if(_libssh2_bn_from_bin(key_state->p, p_len, p)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid DH-SHA256 p");
+ goto dh_gex_clean_exit;
+ }
+
+ if(_libssh2_bn_from_bin(key_state->g, g_len, g)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid DH-SHA256 g");
+ goto dh_gex_clean_exit;
+ }
ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p,
(int)p_len, 256,
@@ -1595,16 +1684,10 @@ kex_method_diffie_hellman_group_exchange_sha256_key_exchange(
if(ret == LIBSSH2_ERROR_EAGAIN) {
return ret;
}
-
- LIBSSH2_FREE(session, key_state->data);
}
dh_gex_clean_exit:
- key_state->state = libssh2_NB_state_idle;
- _libssh2_bn_free(key_state->g);
- key_state->g = NULL;
- _libssh2_bn_free(key_state->p);
- key_state->p = NULL;
+ kex_diffie_hellman_cleanup(session, key_state);
return ret;
}
@@ -1755,6 +1838,49 @@ kex_session_ecdh_curve_type(const char *name, libssh2_curve_type *out_type)
}
+static void
+ecdh_exchange_state_cleanup(LIBSSH2_SESSION * session,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+
+ if(exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+}
+
+
+static void
+kex_method_ecdh_cleanup
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ if(key_state->public_key_oct) {
+ LIBSSH2_FREE(session, key_state->public_key_oct);
+ key_state->public_key_oct = NULL;
+ }
+
+ if(key_state->private_key) {
+ _libssh2_ecdsa_free(key_state->private_key);
+ key_state->private_key = NULL;
+ }
+
+ if(key_state->data) {
+ LIBSSH2_FREE(session, key_state->data);
+ key_state->data = NULL;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+
+ if(key_state->exchange_state.state != libssh2_NB_state_idle) {
+ ecdh_exchange_state_cleanup(session, &key_state->exchange_state);
+ }
+}
+
+
/* ecdh_sha2_nistp
* Elliptic Curve Diffie Hellman Key Exchange
*/
@@ -1941,11 +2067,21 @@ static int ecdh_sha2_nistp(LIBSSH2_SESSION *session, libssh2_curve_type type,
_libssh2_htonu32(exchange_state->k_value,
(uint32_t)(exchange_state->k_value_len - 4));
if(_libssh2_bn_bits(exchange_state->k) % 8) {
- _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ if(_libssh2_bn_to_bin(exchange_state->k,
+ exchange_state->k_value + 4)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->k");
+ goto clean_exit;
+ }
}
else {
exchange_state->k_value[4] = 0;
- _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ if(_libssh2_bn_to_bin(exchange_state->k,
+ exchange_state->k_value + 5)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->e");
+ goto clean_exit;
+ }
}
/* verify hash */
@@ -2231,15 +2367,7 @@ static int ecdh_sha2_nistp(LIBSSH2_SESSION *session, libssh2_curve_type type,
}
clean_exit:
- _libssh2_bn_free(exchange_state->k);
- exchange_state->k = NULL;
-
- if(exchange_state->k_value) {
- LIBSSH2_FREE(session, exchange_state->k_value);
- exchange_state->k_value = NULL;
- }
-
- exchange_state->state = libssh2_NB_state_idle;
+ ecdh_exchange_state_cleanup(session, exchange_state);
return ret;
}
@@ -2347,23 +2475,11 @@ kex_method_ecdh_key_exchange
if(ret == LIBSSH2_ERROR_EAGAIN) {
return ret;
}
-
- LIBSSH2_FREE(session, key_state->data);
}
ecdh_clean_exit:
- if(key_state->public_key_oct) {
- LIBSSH2_FREE(session, key_state->public_key_oct);
- key_state->public_key_oct = NULL;
- }
-
- if(key_state->private_key) {
- _libssh2_ecdsa_free(key_state->private_key);
- key_state->private_key = NULL;
- }
-
- key_state->state = libssh2_NB_state_idle;
+ kex_method_ecdh_cleanup(session, key_state);
return ret;
}
@@ -2373,6 +2489,51 @@ ecdh_clean_exit:
#if LIBSSH2_ED25519
+static void
+curve25519_exchange_state_cleanup(LIBSSH2_SESSION * session,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+
+ if(exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+}
+
+static void
+kex_method_curve25519_cleanup
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ if(key_state->curve25519_public_key) {
+ _libssh2_explicit_zero(key_state->curve25519_public_key,
+ LIBSSH2_ED25519_KEY_LEN);
+ LIBSSH2_FREE(session, key_state->curve25519_public_key);
+ key_state->curve25519_public_key = NULL;
+ }
+
+ if(key_state->curve25519_private_key) {
+ _libssh2_explicit_zero(key_state->curve25519_private_key,
+ LIBSSH2_ED25519_KEY_LEN);
+ LIBSSH2_FREE(session, key_state->curve25519_private_key);
+ key_state->curve25519_private_key = NULL;
+ }
+
+ if(key_state->data) {
+ LIBSSH2_FREE(session, key_state->data);
+ key_state->data = NULL;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+
+ if(key_state->exchange_state.state != libssh2_NB_state_idle) {
+ curve25519_exchange_state_cleanup(session, &key_state->exchange_state);
+ }
+}
+
/* curve25519_sha256
* Elliptic Curve Key Exchange
*/
@@ -2579,11 +2740,21 @@ curve25519_sha256(LIBSSH2_SESSION *session, unsigned char *data,
_libssh2_htonu32(exchange_state->k_value,
(uint32_t)(exchange_state->k_value_len - 4));
if(_libssh2_bn_bits(exchange_state->k) % 8) {
- _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ if(_libssh2_bn_to_bin(exchange_state->k,
+ exchange_state->k_value + 4)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->e");
+ goto clean_exit;
+ }
}
else {
exchange_state->k_value[4] = 0;
- _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ if(_libssh2_bn_to_bin(exchange_state->k,
+ exchange_state->k_value + 5)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Can't write exchange_state->e");
+ goto clean_exit;
+ }
}
/*/ verify hash */
@@ -2845,15 +3016,7 @@ curve25519_sha256(LIBSSH2_SESSION *session, unsigned char *data,
}
clean_exit:
- _libssh2_bn_free(exchange_state->k);
- exchange_state->k = NULL;
-
- if(exchange_state->k_value) {
- LIBSSH2_FREE(session, exchange_state->k_value);
- exchange_state->k_value = NULL;
- }
-
- exchange_state->state = libssh2_NB_state_idle;
+ curve25519_exchange_state_cleanup(session, exchange_state);
return ret;
}
@@ -2952,27 +3115,11 @@ kex_method_curve25519_key_exchange
if(ret == LIBSSH2_ERROR_EAGAIN) {
return ret;
}
-
- LIBSSH2_FREE(session, key_state->data);
}
clean_exit:
- if(key_state->curve25519_public_key) {
- _libssh2_explicit_zero(key_state->curve25519_public_key,
- LIBSSH2_ED25519_KEY_LEN);
- LIBSSH2_FREE(session, key_state->curve25519_public_key);
- key_state->curve25519_public_key = NULL;
- }
-
- if(key_state->curve25519_private_key) {
- _libssh2_explicit_zero(key_state->curve25519_private_key,
- LIBSSH2_ED25519_KEY_LEN);
- LIBSSH2_FREE(session, key_state->curve25519_private_key);
- key_state->curve25519_private_key = NULL;
- }
-
- key_state->state = libssh2_NB_state_idle;
+ kex_method_curve25519_cleanup(session, key_state);
return ret;
}
@@ -2987,30 +3134,35 @@ clean_exit:
static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group1_sha1 = {
"diffie-hellman-group1-sha1",
kex_method_diffie_hellman_group1_sha1_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group14_sha1 = {
"diffie-hellman-group14-sha1",
kex_method_diffie_hellman_group14_sha1_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group14_sha256 = {
"diffie-hellman-group14-sha256",
kex_method_diffie_hellman_group14_sha256_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group16_sha512 = {
"diffie-hellman-group16-sha512",
kex_method_diffie_hellman_group16_sha512_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group18_sha512 = {
"diffie-hellman-group18-sha512",
kex_method_diffie_hellman_group18_sha512_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
@@ -3018,6 +3170,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_diffie_helman_group_exchange_sha1 = {
"diffie-hellman-group-exchange-sha1",
kex_method_diffie_hellman_group_exchange_sha1_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
@@ -3025,6 +3178,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_diffie_helman_group_exchange_sha256 = {
"diffie-hellman-group-exchange-sha256",
kex_method_diffie_hellman_group_exchange_sha256_key_exchange,
+ kex_diffie_hellman_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
@@ -3033,6 +3187,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_ecdh_sha2_nistp256 = {
"ecdh-sha2-nistp256",
kex_method_ecdh_key_exchange,
+ kex_method_ecdh_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
@@ -3040,6 +3195,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_ecdh_sha2_nistp384 = {
"ecdh-sha2-nistp384",
kex_method_ecdh_key_exchange,
+ kex_method_ecdh_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
@@ -3047,6 +3203,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_ecdh_sha2_nistp521 = {
"ecdh-sha2-nistp521",
kex_method_ecdh_key_exchange,
+ kex_method_ecdh_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
#endif
@@ -3056,12 +3213,14 @@ static const LIBSSH2_KEX_METHOD
kex_method_ssh_curve25519_sha256_libssh = {
"curve25519-sha256@libssh.org",
kex_method_curve25519_key_exchange,
+ kex_method_curve25519_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
static const LIBSSH2_KEX_METHOD
kex_method_ssh_curve25519_sha256 = {
"curve25519-sha256",
kex_method_curve25519_key_exchange,
+ kex_method_curve25519_cleanup,
LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
};
#endif
@@ -3074,6 +3233,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_extension_negotiation = {
"ext-info-c",
NULL,
+ NULL,
0,
};
@@ -3081,6 +3241,7 @@ static const LIBSSH2_KEX_METHOD
kex_method_strict_client_extension = {
"kex-strict-c-v00@openssh.com",
NULL,
+ NULL,
0,
};
@@ -3900,6 +4061,10 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
session->state |= LIBSSH2_STATE_EXCHANGING_KEYS;
if(reexchange) {
+ if(session->kex && session->kex->cleanup) {
+ session->kex->cleanup(session, &key_state->key_state_low);
+ }
+
session->kex = NULL;
if(session->hostkey && session->hostkey->dtor) {
@@ -3970,9 +4135,10 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
}
session->remote.kexinit = key_state->data;
session->remote.kexinit_len = key_state->data_len;
+ key_state->data = NULL;
- if(kex_agree_methods(session, key_state->data,
- key_state->data_len))
+ if(kex_agree_methods(session, session->remote.kexinit,
+ session->remote.kexinit_len))
rc = LIBSSH2_ERROR_KEX_FAILURE;
key_state->state = libssh2_NB_state_sent2;
@@ -4027,13 +4193,26 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
const char *prefs)
{
char **prefvar, *s, *newprefs;
+ char *tmpprefs = NULL;
size_t prefs_len = strlen(prefs);
const LIBSSH2_COMMON_METHOD **mlist;
+ const char *kex_extensions = "ext-info-c,kex-strict-c-v00@openssh.com,";
+ size_t kex_extensions_len = strlen(kex_extensions);
switch(method_type) {
case LIBSSH2_METHOD_KEX:
prefvar = &session->kex_prefs;
mlist = (const LIBSSH2_COMMON_METHOD **)libssh2_kex_methods;
+ tmpprefs = LIBSSH2_ALLOC(session, kex_extensions_len + prefs_len + 1);
+ if(!tmpprefs) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Error allocated space for kex method"
+ " preferences");
+ }
+ memcpy(tmpprefs, kex_extensions, kex_extensions_len);
+ memcpy(tmpprefs + kex_extensions_len, prefs, prefs_len + 1);
+ prefs = tmpprefs;
+ prefs_len = strlen(prefs);
break;
case LIBSSH2_METHOD_HOSTKEY:
@@ -4093,6 +4272,9 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
s = newprefs = LIBSSH2_ALLOC(session, prefs_len + 1);
if(!newprefs) {
+ if(tmpprefs) {
+ LIBSSH2_FREE(session, tmpprefs);
+ }
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Error allocated space for method preferences");
}
@@ -4121,6 +4303,10 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
}
}
+ if(tmpprefs) {
+ LIBSSH2_FREE(session, tmpprefs);
+ }
+
if(!*newprefs) {
LIBSSH2_FREE(session, newprefs);
return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
diff --git a/libs/libssh2/src/knownhost.c b/libs/libssh2/src/knownhost.c
index 5eba6a108c..79500ec555 100644
--- a/libs/libssh2/src/knownhost.c
+++ b/libs/libssh2/src/knownhost.c
@@ -779,18 +779,20 @@ static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
}
key_type_len = key - key_type_name;
- if(!strncmp(key_type_name, "ssh-dss", key_type_len))
- key_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
- else if(!strncmp(key_type_name, "ssh-rsa", key_type_len))
- key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ if(!strncmp(key_type_name, "ssh-ed25519", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_ED25519;
else if(!strncmp(key_type_name, "ecdsa-sha2-nistp256", key_type_len))
key_type = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
else if(!strncmp(key_type_name, "ecdsa-sha2-nistp384", key_type_len))
key_type = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
else if(!strncmp(key_type_name, "ecdsa-sha2-nistp521", key_type_len))
key_type = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
- else if(!strncmp(key_type_name, "ssh-ed25519", key_type_len))
- key_type = LIBSSH2_KNOWNHOST_KEY_ED25519;
+ else if(!strncmp(key_type_name, "ssh-rsa", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+#if LIBSSH2_DSA
+ else if(!strncmp(key_type_name, "ssh-dss", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+#endif
else
key_type = LIBSSH2_KNOWNHOST_KEY_UNKNOWN;
@@ -1026,10 +1028,12 @@ knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
key_type_name = "ssh-rsa";
key_type_len = 7;
break;
+#if LIBSSH2_DSA
case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
key_type_name = "ssh-dss";
key_type_len = 7;
break;
+#endif
case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
key_type_name = "ecdsa-sha2-nistp256";
key_type_len = 19;
diff --git a/libs/libssh2/src/libgcrypt.h b/libs/libssh2/src/libgcrypt.h
index 7fc8148720..e8efc4be4e 100644
--- a/libs/libssh2/src/libgcrypt.h
+++ b/libs/libssh2/src/libgcrypt.h
@@ -184,6 +184,8 @@
_libssh2_gcry_ciphermode(GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC)
#define _libssh2_cipher_3des \
_libssh2_gcry_ciphermode(GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC)
+#define _libssh2_cipher_chacha20 \
+ _libssh2_gcry_ciphermode(GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_STREAM)
#define _libssh2_cipher_dtor(ctx) gcry_cipher_close(*(ctx))
diff --git a/libs/libssh2/src/libssh2_priv.h b/libs/libssh2/src/libssh2_priv.h
index 4e6995deda..3c0f048e06 100644
--- a/libs/libssh2/src/libssh2_priv.h
+++ b/libs/libssh2/src/libssh2_priv.h
@@ -93,6 +93,19 @@
#include "libssh2_sftp.h"
#include "misc.h"
+#ifdef _WIN32
+/* Detect Windows App environment which has a restricted access
+ to the Win32 APIs. */
+# if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \
+ defined(WINAPI_FAMILY)
+# include <winapifamily.h>
+# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
+ !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define LIBSSH2_WINDOWS_UWP
+# endif
+# endif
+#endif
+
#ifndef FALSE
#define FALSE 0
#endif
@@ -401,12 +414,6 @@ typedef struct packet_authagent_state_t
LIBSSH2_CHANNEL *channel;
} packet_authagent_state_t;
-typedef enum
-{
- libssh2_requires_size_decryption = (1 << 0),
- libssh2_requires_size_field_in_packet = (1 << 1)
-} libssh2_crypt_flags;
-
struct _LIBSSH2_PACKET
{
struct list_node node; /* linked list header */
@@ -767,7 +774,7 @@ struct _LIBSSH2_SESSION
/* State variables used in libssh2_banner_send() */
libssh2_nonblocking_states banner_TxRx_state;
- char banner_TxRx_banner[256];
+ char banner_TxRx_banner[8192];
ssize_t banner_TxRx_total_send;
/* State variables used in libssh2_kexinit() */
@@ -971,6 +978,9 @@ struct _LIBSSH2_KEX_METHOD
int (*exchange_keys) (LIBSSH2_SESSION * session,
key_exchange_state_low_t * key_state);
+ void (*cleanup) (LIBSSH2_SESSION * session,
+ key_exchange_state_low_t * key_state);
+
long flags;
};
@@ -1011,14 +1021,21 @@ struct _LIBSSH2_CRYPT_METHOD
int iv_len;
int secret_len;
+ /* length of the authentication tag */
+ int auth_len;
+
long flags;
int (*init) (LIBSSH2_SESSION * session,
const LIBSSH2_CRYPT_METHOD * method, unsigned char *iv,
int *free_iv, unsigned char *secret, int *free_secret,
int encrypt, void **abstract);
- int (*crypt) (LIBSSH2_SESSION * session, unsigned char *block,
- size_t blocksize, void **abstract, int firstlast);
+ int (*get_len) (LIBSSH2_SESSION * session, unsigned int seqno,
+ unsigned char *data, size_t data_size, unsigned int *len,
+ void **abstract);
+ int (*crypt) (LIBSSH2_SESSION * session, unsigned int seqno,
+ unsigned char *block, size_t blocksize, void **abstract,
+ int firstlast);
int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
_libssh2_cipher_type(algo);
@@ -1030,6 +1047,8 @@ struct _LIBSSH2_CRYPT_METHOD
#define LIBSSH2_CRYPT_FLAG_INTEGRATED_MAC 1
/* Crypto method does not encrypt the packet length */
#define LIBSSH2_CRYPT_FLAG_PKTLEN_AAD 2
+/* Crypto method must encrypt and decrypt entire messages */
+#define LIBSSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET 4
/* Convenience macros for accessing crypt flags */
/* Local crypto flags */
diff --git a/libs/libssh2/src/mac.c b/libs/libssh2/src/mac.c
index 03ecd329ee..00d3c21fba 100644
--- a/libs/libssh2/src/mac.c
+++ b/libs/libssh2/src/mac.c
@@ -485,6 +485,44 @@ _libssh2_mac_methods(void)
}
#if LIBSSH2_AES_GCM
+static int
+mac_method_none_init(LIBSSH2_SESSION * session, unsigned char *key,
+ int *free_key, void **abstract)
+{
+ (void)session;
+ (void)key;
+ (void)free_key;
+ (void)abstract;
+ return 0;
+}
+
+static int
+mac_method_hmac_none_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ size_t packet_len,
+ const unsigned char *addtl,
+ size_t addtl_len, void **abstract)
+{
+ (void)session;
+ (void)buf;
+ (void)seqno;
+ (void)packet;
+ (void)packet_len;
+ (void)addtl;
+ (void)addtl_len;
+ (void)abstract;
+ return 0;
+}
+
+static int
+mac_method_none_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ (void)session;
+ (void)abstract;
+ return 0;
+}
+
/* Stub for aes256-gcm@openssh.com crypto type, which has an integrated
HMAC method. This must not be added to mac_methods[] since it cannot be
negotiated separately. */
@@ -492,9 +530,9 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_aesgcm = {
"INTEGRATED-AES-GCM", /* made up name for display only */
16,
16,
- NULL,
- NULL,
- NULL,
+ mac_method_none_init,
+ mac_method_hmac_none_hash,
+ mac_method_none_dtor,
0
};
#endif /* LIBSSH2_AES_GCM */
diff --git a/libs/libssh2/src/mbedtls.c b/libs/libssh2/src/mbedtls.c
new file mode 100644
index 0000000000..a66e36c5ab
--- /dev/null
+++ b/libs/libssh2/src/mbedtls.c
@@ -0,0 +1,1533 @@
+/* Copyright (C) Art <https://github.com/wildart>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifdef LIBSSH2_CRYPTO_C /* Compile this via crypto.c */
+
+#include <stdlib.h>
+
+#if MBEDTLS_VERSION_NUMBER < 0x03000000
+#define mbedtls_cipher_info_get_key_bitlen(c) (c->key_bitlen)
+#define mbedtls_cipher_info_get_iv_size(c) (c->iv_size)
+#define mbedtls_rsa_get_len(rsa) (rsa->len)
+
+#define MBEDTLS_PRIVATE(m) m
+#endif
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Global context handles
+ */
+
+static mbedtls_entropy_context _libssh2_mbedtls_entropy;
+static mbedtls_ctr_drbg_context _libssh2_mbedtls_ctr_drbg;
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Generic functions
+ */
+
+void
+_libssh2_mbedtls_init(void)
+{
+ int ret;
+
+ mbedtls_entropy_init(&_libssh2_mbedtls_entropy);
+ mbedtls_ctr_drbg_init(&_libssh2_mbedtls_ctr_drbg);
+
+ ret = mbedtls_ctr_drbg_seed(&_libssh2_mbedtls_ctr_drbg,
+ mbedtls_entropy_func,
+ &_libssh2_mbedtls_entropy, NULL, 0);
+ if(ret)
+ mbedtls_ctr_drbg_free(&_libssh2_mbedtls_ctr_drbg);
+}
+
+void
+_libssh2_mbedtls_free(void)
+{
+ mbedtls_ctr_drbg_free(&_libssh2_mbedtls_ctr_drbg);
+ mbedtls_entropy_free(&_libssh2_mbedtls_entropy);
+}
+
+int
+_libssh2_mbedtls_random(unsigned char *buf, size_t len)
+{
+ int ret;
+ ret = mbedtls_ctr_drbg_random(&_libssh2_mbedtls_ctr_drbg, buf, len);
+ return ret == 0 ? 0 : -1;
+}
+
+static void
+_libssh2_mbedtls_safe_free(void *buf, size_t len)
+{
+ if(!buf)
+ return;
+
+ if(len > 0)
+ _libssh2_explicit_zero(buf, len);
+
+ mbedtls_free(buf);
+}
+
+int
+_libssh2_mbedtls_cipher_init(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv,
+ unsigned char *secret,
+ int encrypt)
+{
+ const mbedtls_cipher_info_t *cipher_info;
+ int ret, op;
+
+ if(!ctx)
+ return -1;
+
+ op = encrypt == 0 ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT;
+
+ cipher_info = mbedtls_cipher_info_from_type(algo);
+ if(!cipher_info)
+ return -1;
+
+ mbedtls_cipher_init(ctx);
+ ret = mbedtls_cipher_setup(ctx, cipher_info);
+ if(!ret)
+ ret = mbedtls_cipher_setkey(ctx,
+ secret,
+ (int)mbedtls_cipher_info_get_key_bitlen(cipher_info),
+ op);
+
+ if(!ret)
+ ret = mbedtls_cipher_set_iv(ctx, iv,
+ mbedtls_cipher_info_get_iv_size(cipher_info));
+
+ return ret == 0 ? 0 : -1;
+}
+
+int
+_libssh2_mbedtls_cipher_crypt(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt,
+ unsigned char *block,
+ size_t blocklen, int firstlast)
+{
+ int ret;
+ unsigned char *output;
+ size_t osize, olen, finish_olen;
+
+ (void)encrypt;
+ (void)algo;
+ (void)firstlast;
+
+ osize = blocklen + mbedtls_cipher_get_block_size(ctx);
+
+ output = (unsigned char *)mbedtls_calloc(osize, sizeof(char));
+ if(output) {
+ ret = mbedtls_cipher_reset(ctx);
+
+ if(!ret)
+ ret = mbedtls_cipher_update(ctx, block, blocklen, output, &olen);
+
+ if(!ret)
+ ret = mbedtls_cipher_finish(ctx, output + olen, &finish_olen);
+
+ if(!ret) {
+ olen += finish_olen;
+ memcpy(block, output, olen);
+ }
+
+ _libssh2_mbedtls_safe_free(output, osize);
+ }
+ else
+ ret = -1;
+
+ return ret == 0 ? 0 : -1;
+}
+
+void
+_libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx)
+{
+ mbedtls_cipher_free(ctx);
+}
+
+
+int
+_libssh2_mbedtls_hash_init(mbedtls_md_context_t *ctx,
+ mbedtls_md_type_t mdtype,
+ const unsigned char *key, size_t keylen)
+{
+ const mbedtls_md_info_t *md_info;
+ int ret, hmac;
+
+ md_info = mbedtls_md_info_from_type(mdtype);
+ if(!md_info)
+ return 0;
+
+ hmac = key ? 1 : 0;
+
+ mbedtls_md_init(ctx);
+ ret = mbedtls_md_setup(ctx, md_info, hmac);
+ if(!ret) {
+ if(hmac)
+ ret = mbedtls_md_hmac_starts(ctx, key, keylen);
+ else
+ ret = mbedtls_md_starts(ctx);
+ }
+
+ return ret == 0 ? 1 : 0;
+}
+
+int
+_libssh2_mbedtls_hash_final(mbedtls_md_context_t *ctx, unsigned char *hash)
+{
+ int ret;
+
+ ret = mbedtls_md_finish(ctx, hash);
+ mbedtls_md_free(ctx);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int
+_libssh2_mbedtls_hash(const unsigned char *data, size_t datalen,
+ mbedtls_md_type_t mdtype, unsigned char *hash)
+{
+ const mbedtls_md_info_t *md_info;
+ int ret;
+
+ md_info = mbedtls_md_info_from_type(mdtype);
+ if(!md_info)
+ return 0;
+
+ ret = mbedtls_md(md_info, data, datalen, hash);
+
+ return ret == 0 ? 0 : -1;
+}
+
+int _libssh2_hmac_ctx_init(libssh2_hmac_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ return 1;
+}
+
+#if LIBSSH2_MD5
+int _libssh2_hmac_md5_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ return _libssh2_mbedtls_hash_init(ctx, MBEDTLS_MD_MD5, key, keylen);
+}
+#endif
+
+#if LIBSSH2_HMAC_RIPEMD
+int _libssh2_hmac_ripemd160_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ return _libssh2_mbedtls_hash_init(ctx, MBEDTLS_MD_RIPEMD160, key, keylen);
+}
+#endif
+
+int _libssh2_hmac_sha1_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ return _libssh2_mbedtls_hash_init(ctx, MBEDTLS_MD_SHA1, key, keylen);
+}
+
+int _libssh2_hmac_sha256_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ return _libssh2_mbedtls_hash_init(ctx, MBEDTLS_MD_SHA256, key, keylen);
+}
+
+int _libssh2_hmac_sha512_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ return _libssh2_mbedtls_hash_init(ctx, MBEDTLS_MD_SHA512, key, keylen);
+}
+
+int _libssh2_hmac_update(libssh2_hmac_ctx *ctx,
+ const void *data, size_t datalen)
+{
+ int ret = mbedtls_md_hmac_update(ctx, data, datalen);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int _libssh2_hmac_final(libssh2_hmac_ctx *ctx, void *data)
+{
+ int ret = mbedtls_md_hmac_finish(ctx, data);
+
+ return ret == 0 ? 1 : 0;
+}
+
+void _libssh2_hmac_cleanup(libssh2_hmac_ctx *ctx)
+{
+ mbedtls_md_free(ctx);
+}
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: BigNumber functions
+ */
+
+_libssh2_bn *
+_libssh2_mbedtls_bignum_init(void)
+{
+ _libssh2_bn *bignum;
+
+ bignum = (_libssh2_bn *)mbedtls_calloc(1, sizeof(_libssh2_bn));
+ if(bignum) {
+ mbedtls_mpi_init(bignum);
+ }
+
+ return bignum;
+}
+
+void
+_libssh2_mbedtls_bignum_free(_libssh2_bn *bn)
+{
+ if(bn) {
+ mbedtls_mpi_free(bn);
+ mbedtls_free(bn);
+ }
+}
+
+static int
+_libssh2_mbedtls_bignum_random(_libssh2_bn *bn, int bits, int top, int bottom)
+{
+ size_t len;
+ int err;
+ size_t i;
+
+ if(!bn || bits <= 0)
+ return -1;
+
+ len = (bits + 7) >> 3;
+ err = mbedtls_mpi_fill_random(bn, len, mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg);
+ if(err)
+ return -1;
+
+ /* Zero unused bits above the most significant bit */
+ for(i = len*8 - 1; (size_t)bits <= i; --i) {
+ err = mbedtls_mpi_set_bit(bn, i, 0);
+ if(err)
+ return -1;
+ }
+
+ /* If `top` is -1, the most significant bit of the random number can be
+ zero. If top is 0, the most significant bit of the random number is
+ set to 1, and if top is 1, the two most significant bits of the number
+ will be set to 1, so that the product of two such random numbers will
+ always have 2*bits length.
+ */
+ if(top >= 0) {
+ for(i = 0; i <= (size_t)top; ++i) {
+ err = mbedtls_mpi_set_bit(bn, bits-i-1, 1);
+ if(err)
+ return -1;
+ }
+ }
+
+ /* make odd by setting first bit in least significant byte */
+ if(bottom) {
+ err = mbedtls_mpi_set_bit(bn, 0, 1);
+ if(err)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: RSA functions
+ */
+
+int
+_libssh2_mbedtls_rsa_new(libssh2_rsa_ctx **rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata,
+ unsigned long coefflen)
+{
+ int ret;
+ libssh2_rsa_ctx *ctx;
+
+ ctx = (libssh2_rsa_ctx *) mbedtls_calloc(1, sizeof(libssh2_rsa_ctx));
+ if(ctx) {
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ mbedtls_rsa_init(ctx);
+#else
+ mbedtls_rsa_init(ctx, MBEDTLS_RSA_PKCS_V15, 0);
+#endif
+ }
+ else
+ return -1;
+
+ /* !checksrc! disable ASSIGNWITHINCONDITION 1 */
+ if((ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(E)),
+ edata, elen)) ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(N)),
+ ndata, nlen))) {
+ ret = -1;
+ }
+
+ if(!ret) {
+ ctx->MBEDTLS_PRIVATE(len) =
+ mbedtls_mpi_size(&(ctx->MBEDTLS_PRIVATE(N)));
+ }
+
+ if(!ret && ddata) {
+ /* !checksrc! disable ASSIGNWITHINCONDITION 1 */
+ if((ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(D)),
+ ddata, dlen)) ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(P)),
+ pdata, plen)) ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(Q)),
+ qdata, qlen)) ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(DP)),
+ e1data, e1len)) ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(DQ)),
+ e2data, e2len)) ||
+ (ret = mbedtls_mpi_read_binary(&(ctx->MBEDTLS_PRIVATE(QP)),
+ coeffdata, coefflen))) {
+ ret = -1;
+ }
+ ret = mbedtls_rsa_check_privkey(ctx);
+ }
+ else if(!ret) {
+ ret = mbedtls_rsa_check_pubkey(ctx);
+ }
+
+ if(ret && ctx) {
+ _libssh2_mbedtls_rsa_free(ctx);
+ ctx = NULL;
+ }
+ *rsa = ctx;
+ return ret;
+}
+
+int
+_libssh2_mbedtls_rsa_new_private(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase)
+{
+ int ret;
+ mbedtls_pk_context pkey;
+ mbedtls_rsa_context *pk_rsa;
+
+ *rsa = (libssh2_rsa_ctx *) LIBSSH2_ALLOC(session, sizeof(libssh2_rsa_ctx));
+ if(!*rsa)
+ return -1;
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ mbedtls_rsa_init(*rsa);
+#else
+ mbedtls_rsa_init(*rsa, MBEDTLS_RSA_PKCS_V15, 0);
+#endif
+ mbedtls_pk_init(&pkey);
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_pk_parse_keyfile(&pkey, filename, (char *)passphrase,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg);
+#else
+ ret = mbedtls_pk_parse_keyfile(&pkey, filename, (char *)passphrase);
+#endif
+ if(ret || mbedtls_pk_get_type(&pkey) != MBEDTLS_PK_RSA) {
+ mbedtls_pk_free(&pkey);
+ mbedtls_rsa_free(*rsa);
+ LIBSSH2_FREE(session, *rsa);
+ *rsa = NULL;
+ return -1;
+ }
+
+ pk_rsa = mbedtls_pk_rsa(pkey);
+ mbedtls_rsa_copy(*rsa, pk_rsa);
+ mbedtls_pk_free(&pkey);
+
+ return 0;
+}
+
+int
+_libssh2_mbedtls_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ int ret;
+ mbedtls_pk_context pkey;
+ mbedtls_rsa_context *pk_rsa;
+ void *filedata_nullterm;
+ size_t pwd_len;
+
+ *rsa = (libssh2_rsa_ctx *) mbedtls_calloc(1, sizeof(libssh2_rsa_ctx));
+ if(!*rsa)
+ return -1;
+
+ /*
+ mbedtls checks in "mbedtls/pkparse.c:1184" if "key[keylen - 1] != '\0'"
+ private-key from memory will fail if the last byte is not a null byte
+ */
+ filedata_nullterm = mbedtls_calloc(filedata_len + 1, 1);
+ if(!filedata_nullterm) {
+ return -1;
+ }
+ memcpy(filedata_nullterm, filedata, filedata_len);
+
+ mbedtls_pk_init(&pkey);
+
+ pwd_len = passphrase ? strlen((const char *)passphrase) : 0;
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)filedata_nullterm,
+ filedata_len + 1,
+ passphrase, pwd_len,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg);
+#else
+ ret = mbedtls_pk_parse_key(&pkey, (unsigned char *)filedata_nullterm,
+ filedata_len + 1,
+ passphrase, pwd_len);
+#endif
+ _libssh2_mbedtls_safe_free(filedata_nullterm, filedata_len);
+
+ if(ret || mbedtls_pk_get_type(&pkey) != MBEDTLS_PK_RSA) {
+ mbedtls_pk_free(&pkey);
+ mbedtls_rsa_free(*rsa);
+ LIBSSH2_FREE(session, *rsa);
+ *rsa = NULL;
+ return -1;
+ }
+
+ pk_rsa = mbedtls_pk_rsa(pkey);
+ mbedtls_rsa_copy(*rsa, pk_rsa);
+ mbedtls_pk_free(&pkey);
+
+ return 0;
+}
+
+int
+_libssh2_mbedtls_rsa_sha2_verify(libssh2_rsa_ctx * rsactx,
+ size_t hash_len,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len)
+{
+ int ret;
+ int md_type;
+ unsigned char *hash;
+
+ if(sig_len < mbedtls_rsa_get_len(rsactx))
+ return -1;
+
+ hash = malloc(hash_len);
+ if(!hash)
+ return -1;
+
+ if(hash_len == SHA_DIGEST_LENGTH) {
+ md_type = MBEDTLS_MD_SHA1;
+ }
+ else if(hash_len == SHA256_DIGEST_LENGTH) {
+ md_type = MBEDTLS_MD_SHA256;
+ }
+ else if(hash_len == SHA512_DIGEST_LENGTH) {
+ md_type = MBEDTLS_MD_SHA512;
+ }
+ else{
+ free(hash);
+ return -1; /* unsupported digest */
+ }
+ ret = _libssh2_mbedtls_hash(m, m_len, md_type, hash);
+
+ if(ret) {
+ free(hash);
+ return -1; /* failure */
+ }
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_rsa_pkcs1_verify(rsactx,
+ md_type, (unsigned int)hash_len,
+ hash, sig);
+#else
+ ret = mbedtls_rsa_pkcs1_verify(rsactx, NULL, NULL, MBEDTLS_RSA_PUBLIC,
+ md_type, (unsigned int)hash_len,
+ hash, sig);
+#endif
+ free(hash);
+
+ return (ret == 0) ? 0 : -1;
+}
+
+int
+_libssh2_mbedtls_rsa_sha1_verify(libssh2_rsa_ctx * rsactx,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len)
+{
+ return _libssh2_mbedtls_rsa_sha2_verify(rsactx, SHA_DIGEST_LENGTH,
+ sig, sig_len, m, m_len);
+}
+
+int
+_libssh2_mbedtls_rsa_sha2_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ int ret;
+ unsigned char *sig;
+ size_t sig_len;
+ int md_type;
+
+ sig_len = mbedtls_rsa_get_len(rsa);
+ sig = LIBSSH2_ALLOC(session, sig_len);
+ if(!sig) {
+ return -1;
+ }
+ ret = 0;
+ if(hash_len == SHA_DIGEST_LENGTH) {
+ md_type = MBEDTLS_MD_SHA1;
+ }
+ else if(hash_len == SHA256_DIGEST_LENGTH) {
+ md_type = MBEDTLS_MD_SHA256;
+ }
+ else if(hash_len == SHA512_DIGEST_LENGTH) {
+ md_type = MBEDTLS_MD_SHA512;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unsupported hash digest length");
+ ret = -1;
+ }
+ if(ret == 0) {
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_rsa_pkcs1_sign(rsa,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg,
+ md_type, (unsigned int)hash_len,
+ hash, sig);
+#else
+ ret = mbedtls_rsa_pkcs1_sign(rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE,
+ md_type, (unsigned int)hash_len,
+ hash, sig);
+#endif
+ }
+ if(ret) {
+ LIBSSH2_FREE(session, sig);
+ return -1;
+ }
+
+ *signature = sig;
+ *signature_len = sig_len;
+
+ return (ret == 0) ? 0 : -1;
+}
+
+int
+_libssh2_mbedtls_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ return _libssh2_mbedtls_rsa_sha2_sign(session, rsactx, hash, hash_len,
+ signature, signature_len);
+}
+
+void
+_libssh2_mbedtls_rsa_free(libssh2_rsa_ctx *ctx)
+{
+ mbedtls_rsa_free(ctx);
+ mbedtls_free(ctx);
+}
+
+static unsigned char *
+gen_publickey_from_rsa(LIBSSH2_SESSION *session,
+ mbedtls_rsa_context *rsa,
+ size_t *keylen)
+{
+ uint32_t e_bytes, n_bytes;
+ uint32_t len;
+ unsigned char *key;
+ unsigned char *p;
+
+ e_bytes = (uint32_t)mbedtls_mpi_size(&rsa->MBEDTLS_PRIVATE(E));
+ n_bytes = (uint32_t)mbedtls_mpi_size(&rsa->MBEDTLS_PRIVATE(N));
+
+ /* Key form is "ssh-rsa" + e + n. */
+ len = 4 + 7 + 4 + e_bytes + 4 + n_bytes;
+
+ key = LIBSSH2_ALLOC(session, len);
+ if(!key) {
+ return NULL;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ _libssh2_htonu32(p, 7); /* Key type. */
+ p += 4;
+ memcpy(p, "ssh-rsa", 7);
+ p += 7;
+
+ _libssh2_htonu32(p, e_bytes);
+ p += 4;
+ mbedtls_mpi_write_binary(&rsa->MBEDTLS_PRIVATE(E), p, e_bytes);
+
+ _libssh2_htonu32(p, n_bytes);
+ p += 4;
+ mbedtls_mpi_write_binary(&rsa->MBEDTLS_PRIVATE(N), p, n_bytes);
+
+ *keylen = (size_t)(p - key);
+ return key;
+}
+
+static int
+_libssh2_mbedtls_pub_priv_key(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ mbedtls_pk_context *pkey)
+{
+ unsigned char *key = NULL, *mth = NULL;
+ size_t keylen = 0, mthlen = 0;
+ int ret;
+ mbedtls_rsa_context *rsa;
+
+ if(mbedtls_pk_get_type(pkey) != MBEDTLS_PK_RSA) {
+ mbedtls_pk_free(pkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Key type not supported");
+ }
+
+ /* write method */
+ mthlen = 7;
+ mth = LIBSSH2_ALLOC(session, mthlen);
+ if(mth) {
+ memcpy(mth, "ssh-rsa", mthlen);
+ }
+ else {
+ ret = -1;
+ }
+
+ rsa = mbedtls_pk_rsa(*pkey);
+ key = gen_publickey_from_rsa(session, rsa, &keylen);
+ if(!key) {
+ ret = -1;
+ }
+
+ /* write output */
+ if(ret) {
+ if(mth)
+ LIBSSH2_FREE(session, mth);
+ if(key)
+ LIBSSH2_FREE(session, key);
+ }
+ else {
+ *method = mth;
+ *method_len = mthlen;
+ *pubkeydata = key;
+ *pubkeydata_len = keylen;
+ }
+
+ return ret;
+}
+
+int
+_libssh2_mbedtls_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+ mbedtls_pk_context pkey;
+ char buf[1024];
+ int ret;
+
+ mbedtls_pk_init(&pkey);
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_pk_parse_keyfile(&pkey, privatekey, passphrase,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg);
+#else
+ ret = mbedtls_pk_parse_keyfile(&pkey, privatekey, passphrase);
+#endif
+ if(ret) {
+ mbedtls_strerror(ret, (char *)buf, sizeof(buf));
+ mbedtls_pk_free(&pkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE, buf);
+ }
+
+ ret = _libssh2_mbedtls_pub_priv_key(session, method, method_len,
+ pubkeydata, pubkeydata_len, &pkey);
+
+ mbedtls_pk_free(&pkey);
+
+ return ret;
+}
+
+int
+_libssh2_mbedtls_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ mbedtls_pk_context pkey;
+ char buf[1024];
+ int ret;
+ void *privatekeydata_nullterm;
+ size_t pwd_len;
+
+ /*
+ mbedtls checks in "mbedtls/pkparse.c:1184" if "key[keylen - 1] != '\0'"
+ private-key from memory will fail if the last byte is not a null byte
+ */
+ privatekeydata_nullterm = mbedtls_calloc(privatekeydata_len + 1, 1);
+ if(!privatekeydata_nullterm) {
+ return -1;
+ }
+ memcpy(privatekeydata_nullterm, privatekeydata, privatekeydata_len);
+
+ mbedtls_pk_init(&pkey);
+
+ pwd_len = passphrase ? strlen((const char *)passphrase) : 0;
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ ret = mbedtls_pk_parse_key(&pkey,
+ (unsigned char *)privatekeydata_nullterm,
+ privatekeydata_len + 1,
+ (const unsigned char *)passphrase, pwd_len,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg);
+#else
+ ret = mbedtls_pk_parse_key(&pkey,
+ (unsigned char *)privatekeydata_nullterm,
+ privatekeydata_len + 1,
+ (const unsigned char *)passphrase, pwd_len);
+#endif
+ _libssh2_mbedtls_safe_free(privatekeydata_nullterm, privatekeydata_len);
+
+ if(ret) {
+ mbedtls_strerror(ret, (char *)buf, sizeof(buf));
+ mbedtls_pk_free(&pkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE, buf);
+ }
+
+ ret = _libssh2_mbedtls_pub_priv_key(session, method, method_len,
+ pubkeydata, pubkeydata_len, &pkey);
+
+ mbedtls_pk_free(&pkey);
+
+ return ret;
+}
+
+int
+_libssh2_mbedtls_sk_pub_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ int *algorithm,
+ unsigned char *flags,
+ const char **application,
+ const unsigned char **key_handle,
+ size_t *handle_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ (void)method;
+ (void)method_len;
+ (void)pubkeydata;
+ (void)pubkeydata_len;
+ (void)algorithm;
+ (void)flags;
+ (void)application;
+ (void)key_handle;
+ (void)handle_len;
+ (void)privatekeydata;
+ (void)privatekeydata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public SK key from private key file: "
+ "Method unimplemented in mbedTLS backend");
+}
+
+void _libssh2_init_aes_ctr(void)
+{
+ /* no implementation */
+}
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Diffie-Hellman functions
+ */
+
+void
+_libssh2_dh_init(_libssh2_dh_ctx *dhctx)
+{
+ *dhctx = _libssh2_mbedtls_bignum_init(); /* Random from client */
+}
+
+int
+_libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public,
+ _libssh2_bn *g, _libssh2_bn *p, int group_order)
+{
+ /* Generate x and e */
+ _libssh2_mbedtls_bignum_random(*dhctx, group_order * 8 - 1, 0, -1);
+ mbedtls_mpi_exp_mod(public, g, *dhctx, p, NULL);
+ return 0;
+}
+
+int
+_libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret,
+ _libssh2_bn *f, _libssh2_bn *p)
+{
+ /* Compute the shared secret */
+ mbedtls_mpi_exp_mod(secret, f, *dhctx, p, NULL);
+ return 0;
+}
+
+void
+_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx)
+{
+ _libssh2_mbedtls_bignum_free(*dhctx);
+ *dhctx = NULL;
+}
+
+#if LIBSSH2_ECDSA
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: ECDSA functions
+ */
+
+/*
+ * _libssh2_ecdsa_create_key
+ *
+ * Creates a local private key based on input curve
+ * and returns octal value and octal length
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_create_key(LIBSSH2_SESSION *session,
+ _libssh2_ec_key **privkey,
+ unsigned char **pubkey_oct,
+ size_t *pubkey_oct_len,
+ libssh2_curve_type curve)
+{
+ size_t plen = 0;
+
+ *privkey = LIBSSH2_ALLOC(session, sizeof(mbedtls_ecp_keypair));
+
+ if(!*privkey)
+ goto failed;
+
+ mbedtls_ecdsa_init(*privkey);
+
+ if(mbedtls_ecdsa_genkey(*privkey, (mbedtls_ecp_group_id)curve,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg))
+ goto failed;
+
+ plen = 2 * mbedtls_mpi_size(&(*privkey)->MBEDTLS_PRIVATE(grp).P) + 1;
+ *pubkey_oct = LIBSSH2_ALLOC(session, plen);
+
+ if(!*pubkey_oct)
+ goto failed;
+
+ if(mbedtls_ecp_point_write_binary(&(*privkey)->MBEDTLS_PRIVATE(grp),
+ &(*privkey)->MBEDTLS_PRIVATE(Q),
+ MBEDTLS_ECP_PF_UNCOMPRESSED,
+ pubkey_oct_len, *pubkey_oct, plen) == 0)
+ return 0;
+
+failed:
+
+ _libssh2_mbedtls_ecdsa_free(*privkey);
+ _libssh2_mbedtls_safe_free(*pubkey_oct, plen);
+ *privkey = NULL;
+
+ return -1;
+}
+
+/* _libssh2_ecdsa_curve_name_with_octal_new
+ *
+ * Creates a new public key given an octal string, length and type
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx **ctx,
+ const unsigned char *k,
+ size_t k_len,
+ libssh2_curve_type curve)
+{
+ *ctx = mbedtls_calloc(1, sizeof(mbedtls_ecp_keypair));
+
+ if(!*ctx)
+ goto failed;
+
+ mbedtls_ecdsa_init(*ctx);
+
+ if(mbedtls_ecp_group_load(&(*ctx)->MBEDTLS_PRIVATE(grp),
+ (mbedtls_ecp_group_id)curve))
+ goto failed;
+
+ if(mbedtls_ecp_point_read_binary(&(*ctx)->MBEDTLS_PRIVATE(grp),
+ &(*ctx)->MBEDTLS_PRIVATE(Q),
+ k, k_len))
+ goto failed;
+
+ if(mbedtls_ecp_check_pubkey(&(*ctx)->MBEDTLS_PRIVATE(grp),
+ &(*ctx)->MBEDTLS_PRIVATE(Q)) == 0)
+ return 0;
+
+failed:
+
+ _libssh2_mbedtls_ecdsa_free(*ctx);
+ *ctx = NULL;
+
+ return -1;
+}
+
+/* _libssh2_ecdh_gen_k
+ *
+ * Computes the shared secret K given a local private key,
+ * remote public key and length
+ */
+
+int
+_libssh2_mbedtls_ecdh_gen_k(_libssh2_bn **k,
+ _libssh2_ec_key *privkey,
+ const unsigned char *server_pubkey,
+ size_t server_pubkey_len)
+{
+ mbedtls_ecp_point pubkey;
+ int rc = 0;
+
+ if(!*k)
+ return -1;
+
+ mbedtls_ecp_point_init(&pubkey);
+
+ if(mbedtls_ecp_point_read_binary(&privkey->MBEDTLS_PRIVATE(grp),
+ &pubkey,
+ server_pubkey, server_pubkey_len)) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ if(mbedtls_ecdh_compute_shared(&privkey->MBEDTLS_PRIVATE(grp), *k,
+ &pubkey,
+ &privkey->MBEDTLS_PRIVATE(d),
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg)) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ if(mbedtls_ecp_check_privkey(&privkey->MBEDTLS_PRIVATE(grp), *k))
+ rc = -1;
+
+cleanup:
+
+ mbedtls_ecp_point_free(&pubkey);
+
+ return rc;
+}
+
+#define LIBSSH2_MBEDTLS_ECDSA_VERIFY(digest_type) \
+ do { \
+ unsigned char hsh[SHA##digest_type##_DIGEST_LENGTH]; \
+ \
+ if(libssh2_sha##digest_type(m, m_len, hsh) == 0) { \
+ rc = mbedtls_ecdsa_verify(&ctx->MBEDTLS_PRIVATE(grp), hsh, \
+ SHA##digest_type##_DIGEST_LENGTH, \
+ &ctx->MBEDTLS_PRIVATE(Q), &pr, &ps); \
+ } \
+ } while(0)
+
+/* _libssh2_ecdsa_verify
+ *
+ * Verifies the ECDSA signature of a hashed message
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_verify(libssh2_ecdsa_ctx *ctx,
+ const unsigned char *r, size_t r_len,
+ const unsigned char *s, size_t s_len,
+ const unsigned char *m, size_t m_len)
+{
+ mbedtls_mpi pr, ps;
+ int rc = -1;
+
+ mbedtls_mpi_init(&pr);
+ mbedtls_mpi_init(&ps);
+
+ if(mbedtls_mpi_read_binary(&pr, r, r_len))
+ goto cleanup;
+
+ if(mbedtls_mpi_read_binary(&ps, s, s_len))
+ goto cleanup;
+
+ switch(_libssh2_ecdsa_get_curve_type(ctx)) {
+ case LIBSSH2_EC_CURVE_NISTP256:
+ LIBSSH2_MBEDTLS_ECDSA_VERIFY(256);
+ break;
+ case LIBSSH2_EC_CURVE_NISTP384:
+ LIBSSH2_MBEDTLS_ECDSA_VERIFY(384);
+ break;
+ case LIBSSH2_EC_CURVE_NISTP521:
+ LIBSSH2_MBEDTLS_ECDSA_VERIFY(512);
+ break;
+ default:
+ rc = -1;
+ }
+
+cleanup:
+
+ mbedtls_mpi_free(&pr);
+ mbedtls_mpi_free(&ps);
+
+ return (rc == 0) ? 0 : -1;
+}
+
+static int
+_libssh2_mbedtls_parse_eckey(libssh2_ecdsa_ctx **ctx,
+ mbedtls_pk_context *pkey,
+ LIBSSH2_SESSION *session,
+ const unsigned char *data,
+ size_t data_len,
+ const unsigned char *pwd)
+{
+ size_t pwd_len;
+
+ pwd_len = pwd ? strlen((const char *) pwd) : 0;
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+ if(mbedtls_pk_parse_key(pkey, data, data_len, pwd, pwd_len,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg))
+
+ goto failed;
+#else
+ if(mbedtls_pk_parse_key(pkey, data, data_len, pwd, pwd_len))
+ goto failed;
+#endif
+
+ if(mbedtls_pk_get_type(pkey) != MBEDTLS_PK_ECKEY)
+ goto failed;
+
+ *ctx = LIBSSH2_ALLOC(session, sizeof(libssh2_ecdsa_ctx));
+
+ if(!*ctx)
+ goto failed;
+
+ mbedtls_ecdsa_init(*ctx);
+
+ if(mbedtls_ecdsa_from_keypair(*ctx, mbedtls_pk_ec(*pkey)) == 0)
+ return 0;
+
+failed:
+
+ _libssh2_mbedtls_ecdsa_free(*ctx);
+ *ctx = NULL;
+
+ return -1;
+}
+
+static int
+_libssh2_mbedtls_parse_openssh_key(libssh2_ecdsa_ctx **ctx,
+ LIBSSH2_SESSION *session,
+ const unsigned char *data,
+ size_t data_len,
+ const unsigned char *pwd)
+{
+ libssh2_curve_type type;
+ unsigned char *name = NULL;
+ struct string_buf *decrypted = NULL;
+ size_t curvelen, exponentlen, pointlen;
+ unsigned char *curve, *exponent, *point_buf;
+
+ if(_libssh2_openssh_pem_parse_memory(session, pwd,
+ (const char *)data, data_len,
+ &decrypted))
+ goto failed;
+
+ if(_libssh2_get_string(decrypted, &name, NULL))
+ goto failed;
+
+ if(_libssh2_mbedtls_ecdsa_curve_type_from_name((const char *)name,
+ &type))
+ goto failed;
+
+ if(_libssh2_get_string(decrypted, &curve, &curvelen))
+ goto failed;
+
+ if(_libssh2_get_string(decrypted, &point_buf, &pointlen))
+ goto failed;
+
+ if(_libssh2_get_bignum_bytes(decrypted, &exponent, &exponentlen))
+ goto failed;
+
+ *ctx = LIBSSH2_ALLOC(session, sizeof(libssh2_ecdsa_ctx));
+
+ if(!*ctx)
+ goto failed;
+
+ mbedtls_ecdsa_init(*ctx);
+
+ if(mbedtls_ecp_group_load(&(*ctx)->MBEDTLS_PRIVATE(grp),
+ (mbedtls_ecp_group_id)type))
+ goto failed;
+
+ if(mbedtls_mpi_read_binary(&(*ctx)->MBEDTLS_PRIVATE(d),
+ exponent, exponentlen))
+ goto failed;
+
+ if(mbedtls_ecp_mul(&(*ctx)->MBEDTLS_PRIVATE(grp),
+ &(*ctx)->MBEDTLS_PRIVATE(Q),
+ &(*ctx)->MBEDTLS_PRIVATE(d),
+ &(*ctx)->MBEDTLS_PRIVATE(grp).G,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg))
+ goto failed;
+
+ if(mbedtls_ecp_check_privkey(&(*ctx)->MBEDTLS_PRIVATE(grp),
+ &(*ctx)->MBEDTLS_PRIVATE(d)) == 0)
+ goto cleanup;
+
+failed:
+
+ _libssh2_mbedtls_ecdsa_free(*ctx);
+ *ctx = NULL;
+
+cleanup:
+
+ if(decrypted) {
+ _libssh2_string_buf_free(session, decrypted);
+ }
+
+ return *ctx ? 0 : -1;
+}
+
+/* Force-expose internal mbedTLS function */
+#if MBEDTLS_VERSION_NUMBER >= 0x03060000
+int mbedtls_pk_load_file(const char *path, unsigned char **buf, size_t *n);
+#endif
+
+/* _libssh2_ecdsa_new_private
+ *
+ * Creates a new private key given a file path and password
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_new_private(libssh2_ecdsa_ctx **ctx,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *pwd)
+{
+ mbedtls_pk_context pkey;
+ unsigned char *data = NULL;
+ size_t data_len = 0;
+
+ mbedtls_pk_init(&pkey);
+
+ /* FIXME: Reimplement this functionality via a public API. */
+ if(mbedtls_pk_load_file(filename, &data, &data_len))
+ goto cleanup;
+
+ if(_libssh2_mbedtls_parse_eckey(ctx, &pkey, session,
+ data, data_len, pwd) == 0)
+ goto cleanup;
+
+ _libssh2_mbedtls_parse_openssh_key(ctx, session, data, data_len, pwd);
+
+cleanup:
+
+ mbedtls_pk_free(&pkey);
+
+ _libssh2_mbedtls_safe_free(data, data_len);
+
+ return *ctx ? 0 : -1;
+}
+
+/* _libssh2_ecdsa_new_private
+ *
+ * Creates a new private key given a file data and password
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx **ctx,
+ LIBSSH2_SESSION *session,
+ const char *data,
+ size_t data_len,
+ const unsigned char *pwd)
+{
+ unsigned char *ntdata;
+ mbedtls_pk_context pkey;
+
+ mbedtls_pk_init(&pkey);
+
+ ntdata = LIBSSH2_ALLOC(session, data_len + 1);
+
+ if(!ntdata)
+ goto cleanup;
+
+ memcpy(ntdata, data, data_len);
+
+ if(_libssh2_mbedtls_parse_eckey(ctx, &pkey, session,
+ ntdata, data_len + 1, pwd) == 0)
+ goto cleanup;
+
+ _libssh2_mbedtls_parse_openssh_key(ctx, session,
+ ntdata, data_len + 1, pwd);
+
+cleanup:
+
+ mbedtls_pk_free(&pkey);
+
+ _libssh2_mbedtls_safe_free(ntdata, data_len);
+
+ return *ctx ? 0 : -1;
+}
+
+static unsigned char *
+_libssh2_mbedtls_mpi_write_binary(unsigned char *buf,
+ const mbedtls_mpi *mpi,
+ size_t bytes)
+{
+ unsigned char *p = buf;
+ uint32_t size = (uint32_t)bytes;
+
+ if(sizeof(&p) / sizeof(p[0]) < 4) {
+ goto done;
+ }
+
+ p += 4;
+ *p = 0;
+
+ if(size > 0) {
+ mbedtls_mpi_write_binary(mpi, p + 1, size - 1);
+ }
+
+ if(size > 0 && !(*(p + 1) & 0x80)) {
+ memmove(p, p + 1, --size);
+ }
+
+ _libssh2_htonu32(p - 4, size);
+
+done:
+
+ return p + size;
+}
+
+/* _libssh2_ecdsa_sign
+ *
+ * Computes the ECDSA signature of a previously-hashed message
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_sign(LIBSSH2_SESSION *session,
+ libssh2_ecdsa_ctx *ctx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **sign,
+ size_t *sign_len)
+{
+ size_t r_len, s_len, tmp_sign_len = 0;
+ unsigned char *sp, *tmp_sign = NULL;
+ mbedtls_mpi pr, ps;
+
+ mbedtls_mpi_init(&pr);
+ mbedtls_mpi_init(&ps);
+
+ if(mbedtls_ecdsa_sign(&ctx->MBEDTLS_PRIVATE(grp), &pr, &ps,
+ &ctx->MBEDTLS_PRIVATE(d),
+ hash, hash_len,
+ mbedtls_ctr_drbg_random,
+ &_libssh2_mbedtls_ctr_drbg))
+ goto cleanup;
+
+ r_len = mbedtls_mpi_size(&pr) + 1;
+ s_len = mbedtls_mpi_size(&ps) + 1;
+ tmp_sign_len = r_len + s_len + 8;
+
+ tmp_sign = LIBSSH2_CALLOC(session, tmp_sign_len);
+
+ if(!tmp_sign)
+ goto cleanup;
+
+ sp = tmp_sign;
+ sp = _libssh2_mbedtls_mpi_write_binary(sp, &pr, r_len);
+ sp = _libssh2_mbedtls_mpi_write_binary(sp, &ps, s_len);
+
+ *sign_len = (size_t)(sp - tmp_sign);
+
+ *sign = LIBSSH2_CALLOC(session, *sign_len);
+
+ if(!*sign)
+ goto cleanup;
+
+ memcpy(*sign, tmp_sign, *sign_len);
+
+cleanup:
+
+ mbedtls_mpi_free(&pr);
+ mbedtls_mpi_free(&ps);
+
+ _libssh2_mbedtls_safe_free(tmp_sign, tmp_sign_len);
+
+ return *sign ? 0 : -1;
+}
+
+/* _libssh2_ecdsa_get_curve_type
+ *
+ * returns key curve type that maps to libssh2_curve_type
+ *
+ */
+
+libssh2_curve_type
+_libssh2_mbedtls_ecdsa_get_curve_type(libssh2_ecdsa_ctx *ctx)
+{
+ return (libssh2_curve_type) ctx->MBEDTLS_PRIVATE(grp).id;
+}
+
+/* _libssh2_ecdsa_curve_type_from_name
+ *
+ * returns 0 for success, key curve type that maps to libssh2_curve_type
+ *
+ */
+
+int
+_libssh2_mbedtls_ecdsa_curve_type_from_name(const char *name,
+ libssh2_curve_type *out_type)
+{
+ int ret = 0;
+ libssh2_curve_type type;
+
+ if(!name || strlen(name) != 19)
+ return -1;
+
+ if(strcmp(name, "ecdsa-sha2-nistp256") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP256;
+ else if(strcmp(name, "ecdsa-sha2-nistp384") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP384;
+ else if(strcmp(name, "ecdsa-sha2-nistp521") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP521;
+ else {
+ ret = -1;
+ }
+
+ if(ret == 0 && out_type) {
+ *out_type = type;
+ }
+
+ return ret;
+}
+
+void
+_libssh2_mbedtls_ecdsa_free(libssh2_ecdsa_ctx *ctx)
+{
+ mbedtls_ecdsa_free(ctx);
+ mbedtls_free(ctx);
+}
+#endif /* LIBSSH2_ECDSA */
+
+
+/* _libssh2_supported_key_sign_algorithms
+ *
+ * Return supported key hash algo upgrades, see crypto.h
+ *
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len)
+{
+ (void)session;
+
+#if LIBSSH2_RSA_SHA2
+ if(key_method_len == 7 &&
+ memcmp(key_method, "ssh-rsa", key_method_len) == 0) {
+ return "rsa-sha2-512,rsa-sha2-256"
+#if LIBSSH2_RSA_SHA1
+ ",ssh-rsa"
+#endif
+ ;
+ }
+#endif
+
+ return NULL;
+}
+
+#endif /* LIBSSH2_CRYPTO_C */
diff --git a/libs/libssh2/src/mbedtls.h b/libs/libssh2/src/mbedtls.h
new file mode 100644
index 0000000000..b1e6726ebd
--- /dev/null
+++ b/libs/libssh2/src/mbedtls.h
@@ -0,0 +1,514 @@
+#ifndef LIBSSH2_MBEDTLS_H
+#define LIBSSH2_MBEDTLS_H
+/* Copyright (C) Art <https://github.com/wildart>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define LIBSSH2_CRYPTO_ENGINE libssh2_mbedtls
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+/* mbedTLS (as of v3.5.1) has a `[-Werror=arith-conversion]`
+ warning in its public headers. */
+#if !defined(__clang__) && __GNUC__ >= 10
+#pragma GCC diagnostic ignored "-Warith-conversion"
+#endif
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+/* mbedTLS (as of v3.5.1) has a duplicate function declaration
+ in its public headers. Disable the warning that detects it. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+#include <mbedtls/version.h>
+#include <mbedtls/platform.h>
+#include <mbedtls/md.h>
+#include <mbedtls/rsa.h>
+#include <mbedtls/bignum.h>
+#include <mbedtls/cipher.h>
+#ifdef MBEDTLS_ECDH_C
+# include <mbedtls/ecdh.h>
+#endif
+#ifdef MBEDTLS_ECDSA_C
+# include <mbedtls/ecdsa.h>
+#endif
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/pk.h>
+#include <mbedtls/error.h>
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+/* Define which features are supported. */
+#define LIBSSH2_MD5 1
+
+#define LIBSSH2_HMAC_RIPEMD 1
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#define LIBSSH2_AES_CBC 1
+#define LIBSSH2_AES_CTR 1
+#define LIBSSH2_AES_GCM 0
+#ifdef MBEDTLS_CIPHER_BLOWFISH_CBC
+# define LIBSSH2_BLOWFISH 1
+#else
+# define LIBSSH2_BLOWFISH 0
+#endif
+#ifdef MBEDTLS_CIPHER_ARC4_128
+# define LIBSSH2_RC4 1
+#else
+# define LIBSSH2_RC4 0
+#endif
+#define LIBSSH2_CAST 0
+#define LIBSSH2_3DES 1
+
+#define LIBSSH2_RSA 1
+#define LIBSSH2_RSA_SHA1 1
+#define LIBSSH2_RSA_SHA2 1
+#define LIBSSH2_DSA 0
+#ifdef MBEDTLS_ECDSA_C
+# define LIBSSH2_ECDSA 1
+#else
+# define LIBSSH2_ECDSA 0
+#endif
+#define LIBSSH2_ED25519 0
+
+#include "crypto_config.h"
+
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+#define MD5_DIGEST_LENGTH 16
+#endif
+#define SHA_DIGEST_LENGTH 20
+#define SHA256_DIGEST_LENGTH 32
+#define SHA384_DIGEST_LENGTH 48
+#define SHA512_DIGEST_LENGTH 64
+
+#define EC_MAX_POINT_LEN ((528 * 2 / 8) + 1)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Generic functions
+ */
+
+#define libssh2_crypto_init() \
+ _libssh2_mbedtls_init()
+#define libssh2_crypto_exit() \
+ _libssh2_mbedtls_free()
+
+#define _libssh2_random(buf, len) \
+ _libssh2_mbedtls_random(buf, len)
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: HMAC functions
+ */
+
+#define libssh2_hmac_ctx mbedtls_md_context_t
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA1 functions
+ */
+
+#define libssh2_sha1_ctx mbedtls_md_context_t
+
+#define libssh2_sha1_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA1, NULL, 0)
+#define libssh2_sha1_update(ctx, data, datalen) \
+ (mbedtls_md_update(&ctx, (const unsigned char *) data, datalen) == 0)
+#define libssh2_sha1_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha1(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA1, hash)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA256 functions
+ */
+
+#define libssh2_sha256_ctx mbedtls_md_context_t
+
+#define libssh2_sha256_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA256, NULL, 0)
+#define libssh2_sha256_update(ctx, data, datalen) \
+ (mbedtls_md_update(&ctx, (const unsigned char *) data, datalen) == 0)
+#define libssh2_sha256_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha256(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA256, hash)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA384 functions
+ */
+
+#define libssh2_sha384_ctx mbedtls_md_context_t
+
+#define libssh2_sha384_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA384, NULL, 0)
+#define libssh2_sha384_update(ctx, data, datalen) \
+ (mbedtls_md_update(&ctx, (const unsigned char *) data, datalen) == 0)
+#define libssh2_sha384_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha384(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA384, hash)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: SHA512 functions
+ */
+
+#define libssh2_sha512_ctx mbedtls_md_context_t
+
+#define libssh2_sha512_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_SHA512, NULL, 0)
+#define libssh2_sha512_update(ctx, data, datalen) \
+ (mbedtls_md_update(&ctx, (const unsigned char *) data, datalen) == 0)
+#define libssh2_sha512_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#define libssh2_sha512(data, datalen, hash) \
+ _libssh2_mbedtls_hash(data, datalen, MBEDTLS_MD_SHA512, hash)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: MD5 functions
+ */
+
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+#define libssh2_md5_ctx mbedtls_md_context_t
+
+#define libssh2_md5_init(pctx) \
+ _libssh2_mbedtls_hash_init(pctx, MBEDTLS_MD_MD5, NULL, 0)
+#define libssh2_md5_update(ctx, data, datalen) \
+ (mbedtls_md_update(&ctx, (const unsigned char *) data, datalen) == 0)
+#define libssh2_md5_final(ctx, hash) \
+ _libssh2_mbedtls_hash_final(&ctx, hash)
+#endif
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: RSA functions
+ */
+
+#define libssh2_rsa_ctx mbedtls_rsa_context
+
+#define _libssh2_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len) \
+ _libssh2_mbedtls_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len)
+
+#define _libssh2_rsa_new_private(rsactx, s, filename, passphrase) \
+ _libssh2_mbedtls_rsa_new_private(rsactx, s, filename, passphrase)
+
+#define _libssh2_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_mbedtls_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase)
+
+#define _libssh2_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len) \
+ _libssh2_mbedtls_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len)
+
+#define _libssh2_rsa_sha2_sign(s, rsactx, hash, hash_len, sig, sig_len) \
+ _libssh2_mbedtls_rsa_sha2_sign(s, rsactx, hash, hash_len, sig, sig_len)
+
+
+#define _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len) \
+ _libssh2_mbedtls_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len)
+
+#define _libssh2_rsa_sha2_verify(rsactx, hash_len, sig, sig_len, m, m_len) \
+ _libssh2_mbedtls_rsa_sha2_verify(rsactx, hash_len, sig, sig_len, m, m_len)
+
+#define _libssh2_rsa_free(rsactx) \
+ _libssh2_mbedtls_rsa_free(rsactx)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: ECDSA structures
+ */
+
+#if LIBSSH2_ECDSA
+
+typedef enum {
+#ifdef MBEDTLS_ECP_DP_SECP256R1_ENABLED
+ LIBSSH2_EC_CURVE_NISTP256 = MBEDTLS_ECP_DP_SECP256R1,
+#else
+ LIBSSH2_EC_CURVE_NISTP256 = MBEDTLS_ECP_DP_NONE,
+#endif
+#ifdef MBEDTLS_ECP_DP_SECP384R1_ENABLED
+ LIBSSH2_EC_CURVE_NISTP384 = MBEDTLS_ECP_DP_SECP384R1,
+#else
+ LIBSSH2_EC_CURVE_NISTP384 = MBEDTLS_ECP_DP_NONE,
+#endif
+#ifdef MBEDTLS_ECP_DP_SECP521R1_ENABLED
+ LIBSSH2_EC_CURVE_NISTP521 = MBEDTLS_ECP_DP_SECP521R1
+#else
+ LIBSSH2_EC_CURVE_NISTP521 = MBEDTLS_ECP_DP_NONE,
+#endif
+} libssh2_curve_type;
+
+# define _libssh2_ec_key mbedtls_ecp_keypair
+#else
+# define _libssh2_ec_key void
+#endif /* LIBSSH2_ECDSA */
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: ECDSA functions
+ */
+
+#if LIBSSH2_ECDSA
+
+#define libssh2_ecdsa_ctx mbedtls_ecdsa_context
+
+#define _libssh2_ecdsa_create_key(session, privkey, pubkey_octal, \
+ pubkey_octal_len, curve) \
+ _libssh2_mbedtls_ecdsa_create_key(session, privkey, pubkey_octal, \
+ pubkey_octal_len, curve)
+
+#define _libssh2_ecdsa_curve_name_with_octal_new(ctx, k, k_len, curve) \
+ _libssh2_mbedtls_ecdsa_curve_name_with_octal_new(ctx, k, k_len, curve)
+
+#define _libssh2_ecdh_gen_k(k, privkey, server_pubkey, server_pubkey_len) \
+ _libssh2_mbedtls_ecdh_gen_k(k, privkey, server_pubkey, server_pubkey_len)
+
+#define _libssh2_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len) \
+ _libssh2_mbedtls_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len)
+
+#define _libssh2_ecdsa_new_private(ctx, session, filename, passphrase) \
+ _libssh2_mbedtls_ecdsa_new_private(ctx, session, filename, passphrase)
+
+#define _libssh2_ecdsa_new_private_frommemory(ctx, session, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_mbedtls_ecdsa_new_private_frommemory(ctx, session, filedata, \
+ filedata_len, passphrase)
+
+#define _libssh2_ecdsa_sign(session, ctx, hash, hash_len, sign, sign_len) \
+ _libssh2_mbedtls_ecdsa_sign(session, ctx, hash, hash_len, sign, sign_len)
+
+#define _libssh2_ecdsa_get_curve_type(ctx) \
+ _libssh2_mbedtls_ecdsa_get_curve_type(ctx)
+
+#define _libssh2_ecdsa_free(ctx) \
+ _libssh2_mbedtls_ecdsa_free(ctx)
+
+#endif /* LIBSSH2_ECDSA */
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Key functions
+ */
+
+#define _libssh2_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw) \
+ _libssh2_mbedtls_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw)
+#define _libssh2_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw) \
+ _libssh2_mbedtls_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw)
+#define _libssh2_sk_pub_keyfilememory(s, m, m_len, p, p_len, alg, app, \
+ f, kh, kh_len, pk, pk_len, pw) \
+ _libssh2_mbedtls_sk_pub_keyfilememory(s, m, m_len, p, p_len, alg, app, \
+ f, kh, kh_len, pk, pk_len, pw)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Cipher Context structure
+ */
+
+#define _libssh2_cipher_ctx mbedtls_cipher_context_t
+
+#define _libssh2_cipher_type(algo) mbedtls_cipher_type_t algo
+
+#define _libssh2_cipher_aes256ctr MBEDTLS_CIPHER_AES_256_CTR
+#define _libssh2_cipher_aes192ctr MBEDTLS_CIPHER_AES_192_CTR
+#define _libssh2_cipher_aes128ctr MBEDTLS_CIPHER_AES_128_CTR
+#define _libssh2_cipher_aes256 MBEDTLS_CIPHER_AES_256_CBC
+#define _libssh2_cipher_aes192 MBEDTLS_CIPHER_AES_192_CBC
+#define _libssh2_cipher_aes128 MBEDTLS_CIPHER_AES_128_CBC
+#ifdef MBEDTLS_CIPHER_BLOWFISH_CBC
+#define _libssh2_cipher_blowfish MBEDTLS_CIPHER_BLOWFISH_CBC
+#endif
+#ifdef MBEDTLS_CIPHER_ARC4_128
+#define _libssh2_cipher_arcfour MBEDTLS_CIPHER_ARC4_128
+#endif
+#define _libssh2_cipher_3des MBEDTLS_CIPHER_DES_EDE3_CBC
+#define _libssh2_cipher_chacha20 MBEDTLS_CIPHER_CHACHA20_POLY1305
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Cipher functions
+ */
+
+#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
+ _libssh2_mbedtls_cipher_init(ctx, type, iv, secret, encrypt)
+#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen, fl) \
+ _libssh2_mbedtls_cipher_crypt(ctx, type, encrypt, block, blocklen, fl)
+#define _libssh2_cipher_dtor(ctx) \
+ _libssh2_mbedtls_cipher_dtor(ctx)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: BigNumber Support
+ */
+
+#define _libssh2_bn_ctx int /* not used */
+#define _libssh2_bn_ctx_new() 0 /* not used */
+#define _libssh2_bn_ctx_free(bnctx) ((void)0) /* not used */
+
+#define _libssh2_bn mbedtls_mpi
+
+#define _libssh2_bn_init() \
+ _libssh2_mbedtls_bignum_init()
+#define _libssh2_bn_init_from_bin() \
+ _libssh2_mbedtls_bignum_init()
+#define _libssh2_bn_set_word(bn, word) \
+ mbedtls_mpi_lset(bn, word)
+#define _libssh2_bn_from_bin(bn, len, bin) \
+ mbedtls_mpi_read_binary(bn, bin, len)
+#define _libssh2_bn_to_bin(bn, bin) \
+ mbedtls_mpi_write_binary(bn, bin, mbedtls_mpi_size(bn))
+#define _libssh2_bn_bytes(bn) \
+ mbedtls_mpi_size(bn)
+#define _libssh2_bn_bits(bn) \
+ mbedtls_mpi_bitlen(bn)
+#define _libssh2_bn_free(bn) \
+ _libssh2_mbedtls_bignum_free(bn)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: Diffie-Hellman support.
+ */
+
+/* Default generate and safe prime sizes for
+ diffie-hellman-group-exchange-sha1 */
+#define LIBSSH2_DH_GEX_MINGROUP 2048
+#define LIBSSH2_DH_GEX_OPTGROUP 4096
+#define LIBSSH2_DH_GEX_MAXGROUP 8192
+
+#define LIBSSH2_DH_MAX_MODULUS_BITS 16384
+
+#define _libssh2_dh_ctx mbedtls_mpi *
+#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)
+#define libssh2_dh_secret(dhctx, secret, f, p, bnctx) \
+ _libssh2_dh_secret(dhctx, secret, f, p)
+#define libssh2_dh_dtor(dhctx) _libssh2_dh_dtor(dhctx)
+
+
+/*******************************************************************/
+/*
+ * mbedTLS backend: forward declarations
+ */
+
+void
+_libssh2_mbedtls_init(void);
+
+void
+_libssh2_mbedtls_free(void);
+
+int
+_libssh2_mbedtls_random(unsigned char *buf, size_t len);
+
+void
+_libssh2_mbedtls_cipher_dtor(_libssh2_cipher_ctx *ctx);
+
+int
+_libssh2_mbedtls_hash_init(mbedtls_md_context_t *ctx,
+ mbedtls_md_type_t mdtype,
+ const unsigned char *key, size_t keylen);
+
+int
+_libssh2_mbedtls_hash_final(mbedtls_md_context_t *ctx, unsigned char *hash);
+int
+_libssh2_mbedtls_hash(const unsigned char *data, size_t datalen,
+ mbedtls_md_type_t mdtype, unsigned char *hash);
+
+_libssh2_bn *
+_libssh2_mbedtls_bignum_init(void);
+
+void
+_libssh2_mbedtls_bignum_free(_libssh2_bn *bn);
+
+void
+_libssh2_mbedtls_rsa_free(libssh2_rsa_ctx *rsa);
+
+#if LIBSSH2_ECDSA
+libssh2_curve_type
+_libssh2_mbedtls_ecdsa_key_get_curve_type(libssh2_ecdsa_ctx *ctx);
+int
+_libssh2_mbedtls_ecdsa_curve_type_from_name(const char *name,
+ libssh2_curve_type *type);
+void
+_libssh2_mbedtls_ecdsa_free(libssh2_ecdsa_ctx *ctx);
+#endif /* LIBSSH2_ECDSA */
+
+extern void
+_libssh2_init_aes_ctr(void);
+extern void
+_libssh2_dh_init(_libssh2_dh_ctx *dhctx);
+extern int
+_libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public,
+ _libssh2_bn *g, _libssh2_bn *p, int group_order);
+extern int
+_libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret,
+ _libssh2_bn *f, _libssh2_bn *p);
+extern void
+_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx);
+
+#endif /* LIBSSH2_MBEDTLS_H */
diff --git a/libs/libssh2/src/misc.c b/libs/libssh2/src/misc.c
index 0dee48f5f5..6c28327c2d 100644
--- a/libs/libssh2/src/misc.c
+++ b/libs/libssh2/src/misc.c
@@ -72,14 +72,7 @@ int _libssh2_snprintf(char *cp, size_t cp_max_len, const char *fmt, ...)
if(cp_max_len < 2)
return 0;
va_start(args, fmt);
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wformat-nonliteral"
-#endif
n = vsnprintf(cp, cp_max_len, fmt, args);
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
va_end(args);
return (n < (int)cp_max_len) ? n : (int)(cp_max_len - 1);
}
@@ -273,6 +266,24 @@ void _libssh2_store_u32(unsigned char **buf, uint32_t value)
*buf += sizeof(uint32_t);
}
+/* _libssh2_store_u64
+ */
+void _libssh2_store_u64(unsigned char **buf, libssh2_uint64_t value)
+{
+ unsigned char *ptr = *buf;
+
+ ptr[0] = (unsigned char)((value >> 56) & 0xFF);
+ ptr[1] = (unsigned char)((value >> 48) & 0xFF);
+ ptr[2] = (unsigned char)((value >> 40) & 0xFF);
+ ptr[3] = (unsigned char)((value >> 32) & 0xFF);
+ ptr[4] = (unsigned char)((value >> 24) & 0xFF);
+ ptr[5] = (unsigned char)((value >> 16) & 0xFF);
+ ptr[6] = (unsigned char)((value >> 8) & 0xFF);
+ ptr[7] = (unsigned char)(value & 0xFF);
+
+ *buf += sizeof(libssh2_uint64_t);
+}
+
/* _libssh2_store_str
*/
int _libssh2_store_str(unsigned char **buf, const char *str, size_t len)
@@ -303,7 +314,7 @@ int _libssh2_store_bignum2_bytes(unsigned char **buf,
extraByte = (len > 0 && (p[0] & 0x80) != 0);
len_stored = (uint32_t)len;
- if(extraByte && len_stored == 0xffffffff)
+ if(extraByte && len_stored == UINT32_MAX)
len_stored--;
_libssh2_store_u32(buf, len_stored + extraByte);
@@ -575,14 +586,7 @@ _libssh2_debug_low(LIBSSH2_SESSION * session, int context, const char *format,
buflen -= len;
msglen = len;
va_start(vargs, format);
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wformat-nonliteral"
-#endif
len = vsnprintf(buffer + msglen, buflen, format, vargs);
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
va_end(vargs);
msglen += len < buflen ? len : buflen - 1;
}
diff --git a/libs/libssh2/src/misc.h b/libs/libssh2/src/misc.h
index c7b090f1b8..eac2e064be 100644
--- a/libs/libssh2/src/misc.h
+++ b/libs/libssh2/src/misc.h
@@ -112,6 +112,7 @@ uint32_t _libssh2_ntohu32(const unsigned char *buf);
libssh2_uint64_t _libssh2_ntohu64(const unsigned char *buf);
void _libssh2_htonu32(unsigned char *buf, uint32_t val);
void _libssh2_store_u32(unsigned char **buf, uint32_t value);
+void _libssh2_store_u64(unsigned char **buf, libssh2_uint64_t value);
int _libssh2_store_str(unsigned char **buf, const char *str, size_t len);
int _libssh2_store_bignum2_bytes(unsigned char **buf,
const unsigned char *bytes,
diff --git a/libs/libssh2/src/openssl.c b/libs/libssh2/src/openssl.c
index 31220f9cc8..eba05031b1 100644
--- a/libs/libssh2/src/openssl.c
+++ b/libs/libssh2/src/openssl.c
@@ -155,7 +155,7 @@ int _libssh2_hmac_update(libssh2_hmac_ctx *ctx,
#ifdef USE_OPENSSL_3
return EVP_MAC_update(*ctx, data, datalen);
#elif defined(HAVE_OPAQUE_STRUCTS)
-/* FIXME: upstream bug as of v5.6.0: datalen is int instead of size_t */
+/* FIXME: upstream bug as of v5.7.0: datalen is int instead of size_t */
#if defined(LIBSSH2_WOLFSSL)
return HMAC_Update(*ctx, data, (int)datalen);
#else /* !LIBSSH2_WOLFSSL */
@@ -348,6 +348,8 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
if(dbuf)
OPENSSL_clear_free(dbuf, dlen);
+ EVP_PKEY_CTX_free(ctx);
+
return (ret == 1) ? 0 : -1;
#else
BIGNUM * e;
@@ -798,6 +800,9 @@ _libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx,
char *group_name = NULL;
unsigned char *data = NULL;
+ if(!ctx)
+ return -1;
+
if(n) {
group_name = OPENSSL_zalloc(strlen(n) + 1);
}
@@ -822,17 +827,22 @@ _libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx,
params[2] = OSSL_PARAM_construct_end();
- if(EVP_PKEY_fromdata_init(ctx) > 0) {
+ if(EVP_PKEY_fromdata_init(ctx) > 0)
ret = EVP_PKEY_fromdata(ctx, ec_ctx, EVP_PKEY_PUBLIC_KEY,
params);
- }
+ else
+ ret = -1;
+ }
+ else
+ ret = -1;
- if(group_name)
- OPENSSL_clear_free(group_name, strlen(n));
+ if(group_name)
+ OPENSSL_clear_free(group_name, strlen(n));
- if(data)
- OPENSSL_clear_free(data, k_len);
- }
+ if(data)
+ OPENSSL_clear_free(data, k_len);
+
+ EVP_PKEY_CTX_free(ctx);
#else
EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve);
@@ -842,15 +852,26 @@ _libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx,
ec_group = EC_KEY_get0_group(ec_key);
point = EC_POINT_new(ec_group);
- ret = EC_POINT_oct2point(ec_group, point, k, k_len, NULL);
- ret = EC_KEY_set_public_key(ec_key, point);
- if(point)
+ if(point) {
+ ret = EC_POINT_oct2point(ec_group, point, k, k_len, NULL);
+ if(ret == 1)
+ ret = EC_KEY_set_public_key(ec_key, point);
+
EC_POINT_free(point);
+ }
+ else
+ ret = -1;
- if(ec_ctx)
+ if(ret == 1 && ec_ctx)
*ec_ctx = ec_key;
+ else {
+ EC_KEY_free(ec_key);
+ ret = -1;
+ }
}
+ else
+ ret = -1;
#endif
return (ret == 1) ? 0 : -1;
@@ -916,7 +937,16 @@ _libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ecdsa_ctx,
#ifdef USE_OPENSSL_3
ctx = EVP_PKEY_CTX_new(ecdsa_ctx, NULL);
+ if(!ctx) {
+ ret = -1;
+ goto cleanup;
+ }
+
der_len = i2d_ECDSA_SIG(ecdsa_sig, &der);
+ if(der_len <= 0) {
+ ret = -1;
+ goto cleanup;
+ }
#endif
if(type == LIBSSH2_EC_CURVE_NISTP256) {
@@ -929,12 +959,24 @@ _libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ecdsa_ctx,
LIBSSH2_ECDSA_VERIFY(512);
}
+#ifdef USE_OPENSSL_3
+cleanup:
+
+ if(ctx)
+ EVP_PKEY_CTX_free(ctx);
+
+ if(der)
+ OPENSSL_free(der);
+#endif
+
#ifdef HAVE_OPAQUE_STRUCTS
if(ecdsa_sig)
ECDSA_SIG_free(ecdsa_sig);
#else
- BN_clear_free(ecdsa_sig_.s);
- BN_clear_free(ecdsa_sig_.r);
+ if(ecdsa_sig_.s)
+ BN_clear_free(ecdsa_sig_.s);
+ if(ecdsa_sig_.r)
+ BN_clear_free(ecdsa_sig_.r);
#endif
return (ret == 1) ? 0 : -1;
@@ -1056,7 +1098,13 @@ _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
decrypt: verify tag, if applicable
in!=NULL is equivalent to EVP_CipherUpdate
in==NULL is equivalent to EVP_CipherFinal */
-#ifdef HAVE_OPAQUE_STRUCTS
+#if defined(LIBSSH2_WOLFSSL) && LIBWOLFSSL_VERSION_HEX < 0x05007000
+ /* Workaround for wolfSSL bug fixed in v5.7.0:
+ https://github.com/wolfSSL/wolfssl/pull/7143 */
+ unsigned char buf2[EVP_MAX_BLOCK_LENGTH];
+ int outb;
+ ret = EVP_CipherFinal(*ctx, buf2, &outb);
+#elif defined(HAVE_OPAQUE_STRUCTS)
ret = EVP_Cipher(*ctx, NULL, NULL, 0); /* final */
#else
ret = EVP_Cipher(ctx, NULL, NULL, 0); /* final */
@@ -1226,7 +1274,7 @@ gen_publickey_from_rsa(LIBSSH2_SESSION *session, libssh2_rsa_ctx *rsa,
{
int e_bytes, n_bytes;
unsigned long len;
- unsigned char *key;
+ unsigned char *key = NULL;
unsigned char *p;
#ifdef USE_OPENSSL_3
@@ -1249,7 +1297,7 @@ gen_publickey_from_rsa(LIBSSH2_SESSION *session, libssh2_rsa_ctx *rsa,
#endif
#endif
if(!e || !n) {
- return NULL;
+ goto fail;
}
e_bytes = BN_num_bytes(e) + 1;
@@ -1260,7 +1308,7 @@ gen_publickey_from_rsa(LIBSSH2_SESSION *session, libssh2_rsa_ctx *rsa,
key = LIBSSH2_ALLOC(session, len);
if(!key) {
- return NULL;
+ goto fail;
}
/* Process key encoding. */
@@ -1275,6 +1323,11 @@ gen_publickey_from_rsa(LIBSSH2_SESSION *session, libssh2_rsa_ctx *rsa,
p = write_bn(p, n, n_bytes);
*key_len = (size_t)(p - key);
+fail:
+#ifdef USE_OPENSSL_3
+ BN_clear_free(e);
+ BN_clear_free(n);
+#endif
return key;
}
@@ -1661,18 +1714,18 @@ gen_publickey_from_dsa(LIBSSH2_SESSION* session, libssh2_dsa_ctx *dsa,
{
int p_bytes, q_bytes, g_bytes, k_bytes;
unsigned long len;
- unsigned char *key;
+ unsigned char *key = NULL;
unsigned char *p;
#ifdef USE_OPENSSL_3
- BIGNUM * p_bn;
- BIGNUM * q;
- BIGNUM * g;
- BIGNUM * pub_key;
+ BIGNUM * p_bn = NULL;
+ BIGNUM * q = NULL;
+ BIGNUM * g = NULL;
+ BIGNUM * pub_key = NULL;
EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_P, &p_bn);
- EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_G, &q);
- EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_Q, &g);
+ EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_Q, &q);
+ EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_FFC_G, &g);
EVP_PKEY_get_bn_param(dsa, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
#else
const BIGNUM * p_bn;
@@ -1703,7 +1756,7 @@ gen_publickey_from_dsa(LIBSSH2_SESSION* session, libssh2_dsa_ctx *dsa,
key = LIBSSH2_ALLOC(session, len);
if(!key) {
- return NULL;
+ goto fail;
}
/* Process key encoding. */
@@ -1720,6 +1773,13 @@ gen_publickey_from_dsa(LIBSSH2_SESSION* session, libssh2_dsa_ctx *dsa,
p = write_bn(p, pub_key, k_bytes);
*key_len = (size_t)(p - key);
+fail:
+#ifdef USE_OPENSSL_3
+ BN_clear_free(p_bn);
+ BN_clear_free(q);
+ BN_clear_free(g);
+ BN_clear_free(pub_key);
+#endif
return key;
}
@@ -2717,6 +2777,7 @@ _libssh2_rsa_sha2_sign(LIBSSH2_SESSION * session,
if(EVP_PKEY_get_bn_param(rsactx, OSSL_PKEY_PARAM_RSA_N, &n) > 0) {
sig_len = BN_num_bytes(n);
+ BN_clear_free(n);
}
if(sig_len > 0)
@@ -4025,7 +4086,7 @@ _libssh2_ecdsa_create_key(LIBSSH2_SESSION *session,
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
if(ctx &&
- EVP_PKEY_keygen_init(ctx) >0 &&
+ EVP_PKEY_keygen_init(ctx) > 0 &&
EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_type) > 0) {
ret = EVP_PKEY_keygen(ctx, &private_key);
}
@@ -5120,6 +5181,16 @@ _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx)
*dhctx = NULL;
}
+int
+_libssh2_bn_from_bin(_libssh2_bn *bn, size_t len, const unsigned char *val)
+{
+ if(!BN_bin2bn(val, (int)len, bn)) {
+ return -1;
+ }
+
+ return 0;
+}
+
/* _libssh2_supported_key_sign_algorithms
*
* Return supported key hash algo upgrades, see crypto.h
@@ -5134,8 +5205,12 @@ _libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
(void)session;
#if LIBSSH2_RSA_SHA2
- if(key_method_len == 7 &&
- memcmp(key_method, "ssh-rsa", key_method_len) == 0) {
+ if((key_method_len == 7 &&
+ memcmp(key_method, "ssh-rsa", key_method_len) == 0) ||
+ (key_method_len == 28 &&
+ memcmp(key_method, "ssh-rsa-cert-v01@openssh.com",
+ key_method_len) == 0)
+ ) {
return "rsa-sha2-512,rsa-sha2-256"
#if LIBSSH2_RSA_SHA1
",ssh-rsa"
diff --git a/libs/libssh2/src/openssl.h b/libs/libssh2/src/openssl.h
index e44c87498c..e41c1df470 100644
--- a/libs/libssh2/src/openssl.h
+++ b/libs/libssh2/src/openssl.h
@@ -48,7 +48,7 @@
#ifdef LIBSSH2_WOLFSSL
#include <wolfssl/options.h>
-#include <openssl/ecdh.h>
+#include <wolfssl/openssl/ecdh.h>
#if defined(NO_DSA) || defined(HAVE_FIPS)
#define OPENSSL_NO_DSA
@@ -76,7 +76,23 @@
/* wolfSSL has no engine framework. */
#define OPENSSL_NO_ENGINE
-#endif /* LIBSSH2_WOLFSSL */
+#include <wolfssl/openssl/opensslconf.h>
+#include <wolfssl/openssl/sha.h>
+#include <wolfssl/openssl/rsa.h>
+#ifndef OPENSSL_NO_DSA
+#include <wolfssl/openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_MD5
+#include <wolfssl/openssl/md5.h>
+#endif
+#include <wolfssl/openssl/err.h>
+#include <wolfssl/openssl/evp.h>
+#include <wolfssl/openssl/hmac.h>
+#include <wolfssl/openssl/bn.h>
+#include <wolfssl/openssl/pem.h>
+#include <wolfssl/openssl/rand.h>
+
+#else /* !LIBSSH2_WOLFSSL */
#include <openssl/opensslconf.h>
#include <openssl/sha.h>
@@ -102,6 +118,8 @@
#include <openssl/core_names.h>
#endif
+#endif /* LIBSSH2_WOLFSSL */
+
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && \
!defined(LIBRESSL_VERSION_NUMBER)) || defined(LIBSSH2_WOLFSSL) || \
(defined(LIBRESSL_VERSION_NUMBER) && \
@@ -167,8 +185,11 @@
# define LIBSSH2_AES_CBC 0
#endif
+/* wolfSSL v5.4.0 is required due to possibly this bug:
+ https://github.com/wolfSSL/wolfssl/pull/5205
+ Before this release, all libssh2 tests crash with AES-GCM enabled */
#if (OPENSSL_VERSION_NUMBER >= 0x01010100fL && !defined(OPENSSL_NO_AES)) || \
- (defined(LIBSSH2_WOLFSSL) && \
+ (defined(LIBSSH2_WOLFSSL) && LIBWOLFSSL_VERSION_HEX >= 0x05004000 && \
defined(HAVE_AESGCM) && defined(WOLFSSL_AESGCM_STREAM))
# define LIBSSH2_AES_GCM 1
#else
@@ -386,6 +407,7 @@ libssh2_curve_type;
#define _libssh2_cipher_arcfour EVP_rc4
#define _libssh2_cipher_cast5 EVP_cast5_cbc
#define _libssh2_cipher_3des EVP_des_ede3_cbc
+#define _libssh2_cipher_chacha20 NULL
#ifdef HAVE_OPAQUE_STRUCTS
#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_free(*(ctx))
@@ -399,9 +421,10 @@ libssh2_curve_type;
#define _libssh2_bn_ctx_free(bnctx) BN_CTX_free(bnctx)
#define _libssh2_bn_init() BN_new()
#define _libssh2_bn_init_from_bin() _libssh2_bn_init()
-#define _libssh2_bn_set_word(bn, val) BN_set_word(bn, val)
-#define _libssh2_bn_from_bin(bn, len, val) BN_bin2bn(val, (int)len, bn)
-#define _libssh2_bn_to_bin(bn, val) BN_bn2bin(bn, val)
+#define _libssh2_bn_set_word(bn, val) !BN_set_word(bn, val)
+extern int _libssh2_bn_from_bin(_libssh2_bn *bn, size_t len,
+ const unsigned char *v);
+#define _libssh2_bn_to_bin(bn, val) (BN_bn2bin(bn, val) <= 0)
#define _libssh2_bn_bytes(bn) BN_num_bytes(bn)
#define _libssh2_bn_bits(bn) BN_num_bits(bn)
#define _libssh2_bn_free(bn) BN_clear_free(bn)
diff --git a/libs/libssh2/src/pem.c b/libs/libssh2/src/pem.c
index c658896809..c9ab774690 100644
--- a/libs/libssh2/src/pem.c
+++ b/libs/libssh2/src/pem.c
@@ -126,8 +126,7 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
if(readline(line, LINE_SIZE, fp)) {
return -1;
}
- }
- while(strcmp(line, headerbegin) != 0);
+ } while(strcmp(line, headerbegin) != 0);
if(readline(line, LINE_SIZE, fp)) {
return -1;
@@ -263,13 +262,8 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
goto out;
}
- while(len_decrypted <= (int)*datalen - blocksize) {
- if(method->crypt(session, *data + len_decrypted, blocksize,
- &abstract,
- len_decrypted == 0 ? FIRST_BLOCK :
- ((len_decrypted == (int)*datalen - blocksize) ?
- LAST_BLOCK : MIDDLE_BLOCK)
- )) {
+ if(method->flags & LIBSSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET) {
+ if(method->crypt(session, 0, *data, *datalen, &abstract, 0)) {
ret = LIBSSH2_ERROR_DECRYPT;
_libssh2_explicit_zero((char *)secret, sizeof(secret));
method->dtor(session, &abstract);
@@ -277,8 +271,25 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session,
LIBSSH2_FREE(session, *data);
goto out;
}
+ }
+ else {
+ while(len_decrypted <= (int)*datalen - blocksize) {
+ if(method->crypt(session, 0, *data + len_decrypted, blocksize,
+ &abstract,
+ len_decrypted == 0 ? FIRST_BLOCK :
+ ((len_decrypted == (int)*datalen - blocksize) ?
+ LAST_BLOCK : MIDDLE_BLOCK)
+ )) {
+ ret = LIBSSH2_ERROR_DECRYPT;
+ _libssh2_explicit_zero((char *)secret, sizeof(secret));
+ method->dtor(session, &abstract);
+ _libssh2_explicit_zero(*data, *datalen);
+ LIBSSH2_FREE(session, *data);
+ goto out;
+ }
- len_decrypted += blocksize;
+ len_decrypted += blocksize;
+ }
}
/* Account for padding */
@@ -323,8 +334,7 @@ _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
return -1;
}
- }
- while(strcmp(line, headerbegin) != 0);
+ } while(strcmp(line, headerbegin) != 0);
*line = '\0';
@@ -599,46 +609,58 @@ _libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
goto out;
}
- while((size_t)len_decrypted <= decrypted.len - blocksize) {
- /* We always pass MIDDLE_BLOCK here because OpenSSH Key Files
- * do not use AAD to authenticate the length.
- * Furthermore, the authentication tag is appended after the
- * encrypted key, and the length of the authentication tag is
- * not included in the key length, so we check it after the
- * loop.
- */
- if(method->crypt(session, decrypted.data + len_decrypted,
- blocksize,
+ if(method->flags & LIBSSH2_CRYPT_FLAG_REQUIRES_FULL_PACKET) {
+ if(method->crypt(session, 0, decrypted.data,
+ decrypted.len,
&abstract,
MIDDLE_BLOCK)) {
ret = LIBSSH2_ERROR_DECRYPT;
method->dtor(session, &abstract);
goto out;
}
-
- len_decrypted += blocksize;
}
+ else {
+ while((size_t)len_decrypted <= decrypted.len - blocksize) {
+ /* We always pass MIDDLE_BLOCK here because OpenSSH Key Files
+ * do not use AAD to authenticate the length.
+ * Furthermore, the authentication tag is appended after the
+ * encrypted key, and the length of the authentication tag is
+ * not included in the key length, so we check it after the
+ * loop.
+ */
+ if(method->crypt(session, 0, decrypted.data + len_decrypted,
+ blocksize,
+ &abstract,
+ MIDDLE_BLOCK)) {
+ ret = LIBSSH2_ERROR_DECRYPT;
+ method->dtor(session, &abstract);
+ goto out;
+ }
- /* No padding */
-
- /* for the AES GCM methods, the 16 byte authentication tag is
- * appended to the encrypted key */
- if(strcmp(method->name, "aes256-gcm@openssh.com") == 0 ||
- strcmp(method->name, "aes128-gcm@openssh.com") == 0) {
- if(!_libssh2_check_length(&decoded, 16)) {
- ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
- "GCM auth tag missing");
- method->dtor(session, &abstract);
- goto out;
+ len_decrypted += blocksize;
}
- if(method->crypt(session, decoded.dataptr, 16, &abstract,
- LAST_BLOCK)) {
- ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
- "GCM auth tag invalid");
- method->dtor(session, &abstract);
- goto out;
+
+ /* No padding */
+
+ /* for the AES GCM methods, the 16 byte authentication tag is
+ * appended to the encrypted key */
+ if(strcmp(method->name, "aes256-gcm@openssh.com") == 0 ||
+ strcmp(method->name, "aes128-gcm@openssh.com") == 0) {
+ if(!_libssh2_check_length(&decoded, 16)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "GCM auth tag missing");
+ method->dtor(session, &abstract);
+ goto out;
+ }
+ if(method->crypt(session, 0, decoded.dataptr, 16, &abstract,
+ LAST_BLOCK)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
+ "GCM auth tag invalid");
+ method->dtor(session, &abstract);
+ goto out;
+ }
+ decoded.dataptr += 16;
}
- decoded.dataptr += 16;
}
method->dtor(session, &abstract);
@@ -722,8 +744,7 @@ _libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
if(readline(line, LINE_SIZE, fp)) {
return -1;
}
- }
- while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
+ } while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
if(readline(line, LINE_SIZE, fp)) {
return -1;
@@ -803,8 +824,7 @@ _libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
return -1;
}
- }
- while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
+ } while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
*line = '\0';
diff --git a/libs/libssh2/src/poly1305.c b/libs/libssh2/src/poly1305.c
new file mode 100644
index 0000000000..2d7b23248d
--- /dev/null
+++ b/libs/libssh2/src/poly1305.c
@@ -0,0 +1,206 @@
+/*
+ * Public Domain poly1305 from Andrew Moon
+ * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
+ * Copyright not intended 2024.
+ *
+ * SPDX-License-Identifier: SAX-PD-2.0
+ */
+
+/* $OpenBSD: poly1305.c,v 1.3 2013/12/19 22:57:13 djm Exp $ */
+
+#include "libssh2_priv.h"
+
+#include "poly1305.h"
+
+#define mul32x32_64(a,b) ((uint64_t)(a) * (b))
+
+#define U8TO32_LE(p) \
+ (((uint32_t)((p)[0])) | \
+ ((uint32_t)((p)[1]) << 8) | \
+ ((uint32_t)((p)[2]) << 16) | \
+ ((uint32_t)((p)[3]) << 24))
+
+#define U32TO8_LE(p, v) \
+ do { \
+ (p)[0] = (uint8_t)((v)); \
+ (p)[1] = (uint8_t)((v) >> 8); \
+ (p)[2] = (uint8_t)((v) >> 16); \
+ (p)[3] = (uint8_t)((v) >> 24); \
+ } while (0)
+
+void
+poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m,
+ size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
+ uint32_t t0;
+ uint32_t t1;
+ uint32_t t2;
+ uint32_t t3;
+ uint32_t h0;
+ uint32_t h1;
+ uint32_t h2;
+ uint32_t h3;
+ uint32_t h4;
+ uint32_t r0;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r3;
+ uint32_t r4;
+ uint32_t s1;
+ uint32_t s2;
+ uint32_t s3;
+ uint32_t s4;
+ uint32_t b;
+ uint32_t nb;
+ size_t j;
+ uint64_t t[5];
+ uint64_t f0;
+ uint64_t f1;
+ uint64_t f2;
+ uint64_t f3;
+ uint32_t g0;
+ uint32_t g1;
+ uint32_t g2;
+ uint32_t g3;
+ uint32_t g4;
+ uint64_t c;
+ unsigned char mp[16];
+
+ /* clamp key */
+ t0 = U8TO32_LE(key + 0);
+ t1 = U8TO32_LE(key + 4);
+ t2 = U8TO32_LE(key + 8);
+ t3 = U8TO32_LE(key + 12);
+
+ /* precompute multipliers */
+ r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>= 8;
+ r4 = t3 & 0x00fffff;
+
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ /* init state */
+ h0 = 0;
+ h1 = 0;
+ h2 = 0;
+ h3 = 0;
+ h4 = 0;
+
+ /* full blocks */
+ if(inlen < 16)
+ goto poly1305_donna_atmost15bytes;
+
+poly1305_donna_16bytes:
+ m += 16;
+ inlen -= 16;
+
+ t0 = U8TO32_LE(m-16);
+ t1 = U8TO32_LE(m-12);
+ t2 = U8TO32_LE(m-8);
+ t3 = U8TO32_LE(m-4);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += ((uint32_t)((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff);
+ h2 += ((uint32_t)((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff);
+ h3 += ((uint32_t)((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff);
+ h4 += (t3 >> 8) | (1 << 24);
+
+
+poly1305_donna_mul:
+ t[0] = mul32x32_64(h0, r0) + mul32x32_64(h1, s4) + mul32x32_64(h2, s3) +
+ mul32x32_64(h3, s2) + mul32x32_64(h4, s1);
+ t[1] = mul32x32_64(h0, r1) + mul32x32_64(h1, r0) + mul32x32_64(h2, s4) +
+ mul32x32_64(h3, s3) + mul32x32_64(h4, s2);
+ t[2] = mul32x32_64(h0, r2) + mul32x32_64(h1, r1) + mul32x32_64(h2, r0) +
+ mul32x32_64(h3, s4) + mul32x32_64(h4, s3);
+ t[3] = mul32x32_64(h0, r3) + mul32x32_64(h1, r2) + mul32x32_64(h2, r1) +
+ mul32x32_64(h3, r0) + mul32x32_64(h4, s4);
+ t[4] = mul32x32_64(h0, r4) + mul32x32_64(h1, r3) + mul32x32_64(h2, r2) +
+ mul32x32_64(h3, r1) + mul32x32_64(h4, r0);
+
+ h0 = (uint32_t)t[0] & 0x3ffffff;
+ c = (t[0] >> 26);
+
+ t[1] += c;
+ h1 = (uint32_t)t[1] & 0x3ffffff;
+ b = (uint32_t)(t[1] >> 26);
+
+ t[2] += b;
+ h2 = (uint32_t)t[2] & 0x3ffffff;
+ b = (uint32_t)(t[2] >> 26);
+
+ t[3] += b;
+ h3 = (uint32_t)t[3] & 0x3ffffff;
+ b = (uint32_t)(t[3] >> 26);
+
+ t[4] += b;
+ h4 = (uint32_t)t[4] & 0x3ffffff;
+ b = (uint32_t)(t[4] >> 26);
+
+ h0 += b * 5;
+
+ if(inlen >= 16)
+ goto poly1305_donna_16bytes;
+
+ /* final bytes */
+poly1305_donna_atmost15bytes:
+ if(!inlen)
+ goto poly1305_donna_finish;
+
+ for(j = 0; j < inlen; j++) mp[j] = m[j];
+ mp[j++] = 1;
+ for(; j < 16; j++)
+ mp[j] = 0;
+
+ inlen = 0;
+
+ t0 = U8TO32_LE(mp + 0);
+ t1 = U8TO32_LE(mp + 4);
+ t2 = U8TO32_LE(mp + 8);
+ t3 = U8TO32_LE(mp + 12);
+
+ h0 += t0 & 0x3ffffff;
+ h1 += ((uint32_t)((((uint64_t)t1 << 32) | t0) >> 26) & 0x3ffffff);
+ h2 += ((uint32_t)((((uint64_t)t2 << 32) | t1) >> 20) & 0x3ffffff);
+ h3 += ((uint32_t)((((uint64_t)t3 << 32) | t2) >> 14) & 0x3ffffff);
+ h4 += (t3 >> 8);
+
+ goto poly1305_donna_mul;
+
+poly1305_donna_finish:
+ b = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5; b = h0 >> 26; h0 = h0 & 0x3ffffff;
+ h1 += b;
+
+ g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >> 31) - 1;
+ nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = ((h0 ) | (h1 << 26)) + (uint64_t)U8TO32_LE(&key[16]);
+ f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t)U8TO32_LE(&key[20]);
+ f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t)U8TO32_LE(&key[24]);
+ f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t)U8TO32_LE(&key[28]);
+
+ U32TO8_LE(&out[ 0], f0); f1 += (f0 >> 32);
+ U32TO8_LE(&out[ 4], f1); f2 += (f1 >> 32);
+ U32TO8_LE(&out[ 8], f2); f3 += (f2 >> 32);
+ U32TO8_LE(&out[12], f3);
+}
diff --git a/libs/libssh2/src/poly1305.h b/libs/libssh2/src/poly1305.h
new file mode 100644
index 0000000000..21efca93fe
--- /dev/null
+++ b/libs/libssh2/src/poly1305.h
@@ -0,0 +1,20 @@
+/* $OpenBSD: poly1305.h,v 1.4 2014/05/02 03:27:54 djm Exp $ */
+
+/*
+ * Public Domain poly1305 from Andrew Moon
+ * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna
+ * Copyright not intended 2024.
+ *
+ * SPDX-License-Identifier: SAX-PD-2.0
+ */
+
+#ifndef POLY1305_H
+#define POLY1305_H
+
+#define POLY1305_KEYLEN 32
+#define POLY1305_TAGLEN 16
+
+void poly1305_auth(u_char out[POLY1305_TAGLEN], const u_char *m, size_t inlen,
+ const u_char key[POLY1305_KEYLEN]);
+
+#endif /* POLY1305_H */
diff --git a/libs/libssh2/src/scp.c b/libs/libssh2/src/scp.c
index 0900655aac..8a8e7d9ea7 100644
--- a/libs/libssh2/src/scp.c
+++ b/libs/libssh2/src/scp.c
@@ -784,7 +784,7 @@ scp_recv_error:
tmp_err_code = session->err_code;
tmp_err_msg = session->err_msg;
while(libssh2_channel_free(session->scpRecv_channel) ==
- LIBSSH2_ERROR_EAGAIN);
+ LIBSSH2_ERROR_EAGAIN);
session->err_code = tmp_err_code;
session->err_msg = tmp_err_msg;
session->scpRecv_channel = NULL;
diff --git a/libs/libssh2/src/session.c b/libs/libssh2/src/session.c
index 9d89ade8ec..2d77b05e97 100644
--- a/libs/libssh2/src/session.c
+++ b/libs/libssh2/src/session.c
@@ -474,6 +474,7 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
session->packet_read_timeout = LIBSSH2_DEFAULT_READ_TIMEOUT;
session->flag.quote_paths = 1; /* default behavior is to quote paths
for the scp subsystem */
+ session->kex = NULL;
_libssh2_debug((session, LIBSSH2_TRACE_TRANS,
"New session resource allocated"));
_libssh2_init_if_needed();
@@ -674,13 +675,27 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time)
if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) {
FD_ZERO(&rfd);
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
FD_SET(session->socket_fd, &rfd);
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
readfd = &rfd;
}
if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
FD_ZERO(&wfd);
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
FD_SET(session->socket_fd, &wfd);
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
writefd = &wfd;
}
@@ -929,6 +944,11 @@ session_free(LIBSSH2_SESSION *session)
session->free_state = libssh2_NB_state_sent1;
}
+ if(session->kex && session->kex->cleanup) {
+ session->kex->cleanup(session,
+ &session->startup_key_state.key_state_low);
+ }
+
if(session->state & LIBSSH2_STATE_NEWKEYS) {
/* hostkey */
if(session->hostkey && session->hostkey->dtor) {
@@ -1635,19 +1655,40 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
switch(fds[i].type) {
case LIBSSH2_POLLFD_SOCKET:
if(fds[i].events & LIBSSH2_POLLFD_POLLIN) {
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
FD_SET(fds[i].fd.socket, &rfds);
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
if(fds[i].fd.socket > maxfd)
maxfd = fds[i].fd.socket;
}
if(fds[i].events & LIBSSH2_POLLFD_POLLOUT) {
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
FD_SET(fds[i].fd.socket, &wfds);
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
if(fds[i].fd.socket > maxfd)
maxfd = fds[i].fd.socket;
}
break;
case LIBSSH2_POLLFD_CHANNEL:
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
FD_SET(fds[i].fd.channel->session->socket_fd, &rfds);
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
if(fds[i].fd.channel->session->socket_fd > maxfd)
maxfd = fds[i].fd.channel->session->socket_fd;
if(!session)
@@ -1655,7 +1696,14 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
break;
case LIBSSH2_POLLFD_LISTENER:
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
FD_SET(fds[i].fd.listener->session->socket_fd, &rfds);
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
if(fds[i].fd.listener->session->socket_fd > maxfd)
maxfd = fds[i].fd.listener->session->socket_fd;
if(!session)
@@ -1832,6 +1880,10 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
for(i = 0; i < nfds; i++) {
switch(fds[i].type) {
case LIBSSH2_POLLFD_SOCKET:
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
if(FD_ISSET(fds[i].fd.socket, &rfds)) {
fds[i].revents |= LIBSSH2_POLLFD_POLLIN;
}
@@ -1841,6 +1893,9 @@ libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
if(fds[i].revents) {
active_fds++;
}
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
break;
case LIBSSH2_POLLFD_CHANNEL:
diff --git a/libs/libssh2/src/sftp.c b/libs/libssh2/src/sftp.c
index f8f1323e02..6ede311109 100644
--- a/libs/libssh2/src/sftp.c
+++ b/libs/libssh2/src/sftp.c
@@ -47,6 +47,7 @@
#include "sftp.h"
#include <assert.h>
+#include <stdlib.h> /* strtol() */
/* This release of libssh2 implements Version 5 with automatic downgrade
* based on server's declaration
@@ -102,26 +103,6 @@ static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
size_t *data_len);
static void sftp_packet_flush(LIBSSH2_SFTP *sftp);
-/* _libssh2_store_u64
- */
-static void _libssh2_store_u64(unsigned char **ptr, libssh2_uint64_t value)
-{
- uint32_t msl = (uint32_t)(value >> 32);
- unsigned char *buf = *ptr;
-
- buf[0] = (unsigned char)((msl >> 24) & 0xFF);
- buf[1] = (unsigned char)((msl >> 16) & 0xFF);
- buf[2] = (unsigned char)((msl >> 8) & 0xFF);
- buf[3] = (unsigned char)( msl & 0xFF);
-
- buf[4] = (unsigned char)((value >> 24) & 0xFF);
- buf[5] = (unsigned char)((value >> 16) & 0xFF);
- buf[6] = (unsigned char)((value >> 8) & 0xFF);
- buf[7] = (unsigned char)( value & 0xFF);
-
- *ptr += 8;
-}
-
/*
* Search list of zombied FXP_READ request IDs.
*
@@ -381,8 +362,8 @@ window_adjust:
1, NULL);
/* store the state so that we continue with the correct
operation at next invoke */
- sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN)?
- libssh2_NB_state_sent:
+ sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN) ?
+ libssh2_NB_state_sent :
libssh2_NB_state_idle;
if(rc == LIBSSH2_ERROR_EAGAIN)
@@ -988,20 +969,42 @@ static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
sftp_handle->version));
while(buf.dataptr < endp) {
unsigned char *extname, *extdata;
+ size_t extname_len, extdata_len;
+ uint32_t extversion = 0;
- if(_libssh2_get_string(&buf, &extname, NULL)) {
+ if(_libssh2_get_string(&buf, &extname, &extname_len)) {
LIBSSH2_FREE(session, data);
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Data too short when extracting extname");
goto sftp_init_error;
}
- if(_libssh2_get_string(&buf, &extdata, NULL)) {
+ if(_libssh2_get_string(&buf, &extdata, &extdata_len)) {
LIBSSH2_FREE(session, data);
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Data too short when extracting extdata");
goto sftp_init_error;
}
+
+ if(extdata_len > 0) {
+ char *extversion_str;
+ extversion_str = (char *)LIBSSH2_ALLOC(session, extdata_len + 1);
+ if(!extversion_str) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for SSH_FXP_VERSION "
+ "packet");
+ goto sftp_init_error;
+ }
+ memcpy(extversion_str, extdata, extdata_len);
+ extversion_str[extdata_len] = '\0';
+ extversion = (uint32_t)strtol(extversion_str, NULL, 10);
+ LIBSSH2_FREE(session, extversion_str);
+ }
+ if(extname_len == 24
+ && strncmp("posix-rename@openssh.com", (char *)extname, 24) == 0) {
+ sftp_handle->posix_rename_version = extversion;
+ }
+
}
LIBSSH2_FREE(session, data);
@@ -1163,7 +1166,7 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
/* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
flags(4) */
sftp->open_packet_len = (uint32_t)(filename_len + 13 +
- (open_file? (4 + sftp_attrsize(attrs.flags)) : 0));
+ (open_file ? (4 + sftp_attrsize(attrs.flags)) : 0));
/* surprise! this starts out with nothing sent */
sftp->open_packet_sent = 0;
@@ -1180,7 +1183,7 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
_libssh2_store_u32(&s, sftp->open_packet_len - 4);
- *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
+ *(s++) = open_file ? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
sftp->open_request_id = sftp->request_id++;
_libssh2_store_u32(&s, sftp->open_request_id);
_libssh2_store_str(&s, filename, filename_len);
@@ -1191,7 +1194,7 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
}
_libssh2_debug((session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
- open_file? "file" : "directory"));
+ open_file ? "file" : "directory"));
sftp->open_state = libssh2_NB_state_created;
}
@@ -3015,6 +3018,136 @@ libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
return rc;
}
+static int
+sftp_posix_rename(LIBSSH2_SFTP *sftp, const char *source_filename,
+ size_t source_filename_len,
+ const char *dest_filename,
+ size_t dest_filename_len)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ uint32_t packet_len;
+ size_t data_len = 0;
+ unsigned char *packet, *s, *data = NULL;
+ ssize_t rc;
+ uint32_t retcode;
+
+ if(sftp->posix_rename_version != 1) {
+ return _libssh2_error(session, LIBSSH2_FX_OP_UNSUPPORTED,
+ "Server does not support"
+ "posix-rename@openssh.com");
+ }
+
+ if(source_filename_len > UINT32_MAX ||
+ dest_filename_len > UINT32_MAX ||
+ 45 + source_filename_len + dest_filename_len > UINT32_MAX) {
+ return _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Input too large"
+ "posix-rename@openssh.com");
+ }
+
+ packet_len = (uint32_t)(45 + source_filename_len + dest_filename_len);
+
+ /* 45 = packet_len(4) + packet_type(1) + request_id(4) +
+ string_len(4) + strlen("posix-rename@openssh.com")(24) +
+ oldpath_len(4) + source_filename_len +
+ newpath_len(4) + dest_filename_len */
+
+ if(sftp->posix_rename_state == libssh2_NB_state_idle) {
+ _libssh2_debug((session, LIBSSH2_TRACE_SFTP,
+ "Issuing posix_rename command"));
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->posix_rename_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->posix_rename_request_id);
+ _libssh2_store_str(&s, "posix-rename@openssh.com", 24);
+ _libssh2_store_str(&s, source_filename, source_filename_len);
+ _libssh2_store_str(&s, dest_filename, dest_filename_len);
+
+ sftp->posix_rename_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->posix_rename_packet;
+ }
+
+ if(sftp->posix_rename_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->posix_rename_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->posix_rename_packet = NULL;
+
+ if(rc < 0) {
+ sftp->posix_rename_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->posix_rename_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->posix_rename_request_id,
+ &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return (int)rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP posix_rename packet too short");
+ }
+ else if(rc) {
+ sftp->posix_rename_state = libssh2_NB_state_idle;
+ return (int)_libssh2_error(session, (int)rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ sftp->posix_rename_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(retcode != LIBSSH2_FX_OK) {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "posix_rename failed");
+ }
+
+ return 0;
+}
+
+/* libssh2_sftp_posix_rename_ex
+ * Rename a file on the remote server using the posix-rename@openssh.com
+ * extension.
+ */
+LIBSSH2_API int
+libssh2_sftp_posix_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
+ size_t source_filename_len,
+ const char *dest_filename,
+ size_t dest_filename_len)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_posix_rename(sftp, source_filename, source_filename_len,
+ dest_filename, dest_filename_len));
+ return rc;
+}
+
/* sftp_fstatvfs
* Get file system statistics
*/
diff --git a/libs/libssh2/src/sftp.h b/libs/libssh2/src/sftp.h
index 267c8fef0d..264f823bc0 100644
--- a/libs/libssh2/src/sftp.h
+++ b/libs/libssh2/src/sftp.h
@@ -78,7 +78,8 @@ struct _LIBSSH2_SFTP_PACKET
typedef struct _LIBSSH2_SFTP_PACKET LIBSSH2_SFTP_PACKET;
-#define SFTP_HANDLE_MAXLEN 256 /* according to spec! */
+/* Increasing from 256 to 4092 since OpenSSH doesn't honor it. */
+#define SFTP_HANDLE_MAXLEN 4092 /* according to spec, this should be 256! */
struct _LIBSSH2_SFTP_HANDLE
{
@@ -137,7 +138,7 @@ struct _LIBSSH2_SFTP
{
LIBSSH2_CHANNEL *channel;
- uint32_t request_id, version;
+ uint32_t request_id, version, posix_rename_version;
struct list_head packets;
@@ -202,6 +203,11 @@ struct _LIBSSH2_SFTP
unsigned char *rename_s;
uint32_t rename_request_id;
+ /* State variables used in libssh2_sftp_posix_rename_ex() */
+ libssh2_nonblocking_states posix_rename_state;
+ unsigned char *posix_rename_packet;
+ uint32_t posix_rename_request_id;
+
/* State variables used in libssh2_sftp_fstatvfs() */
libssh2_nonblocking_states fstatvfs_state;
unsigned char *fstatvfs_packet;
diff --git a/libs/libssh2/src/transport.c b/libs/libssh2/src/transport.c
index 531f5aa15a..e1120656c3 100644
--- a/libs/libssh2/src/transport.c
+++ b/libs/libssh2/src/transport.c
@@ -156,7 +156,7 @@ decrypt(LIBSSH2_SESSION * session, unsigned char *source,
lowerfirstlast = LAST_BLOCK;
}
- if(session->remote.crypt->crypt(session, source, decryptlen,
+ if(session->remote.crypt->crypt(session, 0, source, decryptlen,
&session->remote.crypt_abstract,
lowerfirstlast)) {
LIBSSH2_FREE(session, p->payload);
@@ -186,33 +186,39 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
struct transportpacket *p = &session->packet;
int rc;
int compressed;
+ const LIBSSH2_MAC_METHOD *remote_mac = NULL;
uint32_t seq = session->remote.seqno;
+ if(!encrypted || (!CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET) &&
+ !CRYPT_FLAG_R(session, INTEGRATED_MAC))) {
+ remote_mac = session->remote.mac;
+ }
+
if(session->fullpacket_state == libssh2_NB_state_idle) {
session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
session->fullpacket_payload_len = p->packet_length - 1;
- if(encrypted && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
+ if(encrypted && remote_mac) {
/* Calculate MAC hash */
- int etm = session->remote.mac->etm;
- size_t mac_len = session->remote.mac->mac_len;
+ int etm = remote_mac->etm;
+ size_t mac_len = remote_mac->mac_len;
if(etm) {
/* store hash here */
- session->remote.mac->hash(session, macbuf,
- session->remote.seqno,
- p->payload, p->total_num - mac_len,
- NULL, 0,
- &session->remote.mac_abstract);
+ remote_mac->hash(session, macbuf,
+ session->remote.seqno,
+ p->payload, p->total_num - mac_len,
+ NULL, 0,
+ &session->remote.mac_abstract);
}
else {
/* store hash here */
- session->remote.mac->hash(session, macbuf,
- session->remote.seqno,
- p->init, 5,
- p->payload,
- session->fullpacket_payload_len,
- &session->remote.mac_abstract);
+ remote_mac->hash(session, macbuf,
+ session->remote.seqno,
+ p->init, 5,
+ p->payload,
+ session->fullpacket_payload_len,
+ &session->remote.mac_abstract);
}
/* Compare the calculated hash with the MAC we just read from
@@ -272,6 +278,10 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
p->payload = decrypt_buffer;
}
}
+ else if(encrypted && CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ /* etm trim off padding byte from payload */
+ memmove(p->payload, &p->payload[1], p->packet_length - 1);
+ }
session->remote.seqno++;
@@ -370,6 +380,8 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
use them */
int encrypted = 1; /* whether the packet is encrypted or not */
int firstlast = FIRST_BLOCK; /* if the first or last block to decrypt */
+ unsigned int auth_len = 0; /* length of the authentication tag */
+ const LIBSSH2_MAC_METHOD *remote_mac = NULL; /* The remote MAC, if used */
/* default clear the bit */
session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
@@ -425,7 +437,16 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
make the checks below work fine still */
}
- etm = encrypted && session->local.mac ? session->local.mac->etm : 0;
+ if(encrypted) {
+ if(CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ auth_len = session->remote.crypt->auth_len;
+ }
+ else {
+ remote_mac = session->remote.mac;
+ }
+ }
+
+ etm = encrypted && remote_mac ? remote_mac->etm : 0;
/* read/use a whole big chunk into a temporary area stored in
the LIBSSH2_SESSION struct. We will decrypt data from that
@@ -440,7 +461,9 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
/* if remainbuf turns negative we have a bad internal error */
assert(remainbuf >= 0);
- if(remainbuf < blocksize) {
+ if(remainbuf < blocksize ||
+ (CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)
+ && ((ssize_t)p->total_num) > remainbuf)) {
/* If we have less than a blocksize left, it is too
little data to deal with, read more */
ssize_t nread;
@@ -516,7 +539,35 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
}
if(etm) {
- p->packet_length = _libssh2_ntohu32(&p->buf[p->readidx]);
+ /* etm size field is not encrypted */
+ memcpy(block, &p->buf[p->readidx], 4);
+ memcpy(p->init, &p->buf[p->readidx], 4);
+ }
+ else if(encrypted && session->remote.crypt->get_len) {
+ unsigned int len = 0;
+ unsigned char *ptr = NULL;
+
+ rc = session->remote.crypt->get_len(session,
+ session->remote.seqno,
+ &p->buf[p->readidx],
+ numbytes,
+ &len,
+ &session->remote.crypt_abstract);
+
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ if(p->payload)
+ LIBSSH2_FREE(session, p->payload);
+ p->payload = NULL;
+ return rc;
+ }
+
+ /* store size in buffers for use below */
+ ptr = &block[0];
+ _libssh2_store_u32(&ptr, len);
+
+ ptr = &p->init[0];
+ _libssh2_store_u32(&ptr, len);
}
else {
if(encrypted) {
@@ -546,33 +597,61 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
p->packet_length = _libssh2_ntohu32(block);
}
- if(p->packet_length < 1) {
- return LIBSSH2_ERROR_DECRYPT;
- }
- else if(p->packet_length > LIBSSH2_PACKET_MAXPAYLOAD) {
- return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
- }
+ if(!encrypted || !CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ if(p->packet_length < 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ else if(p->packet_length > LIBSSH2_PACKET_MAXPAYLOAD) {
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
- if(etm) {
- /* we collect entire undecrypted packet including the
- packet length field that we run MAC over */
- total_num = 4 + p->packet_length +
- session->remote.mac->mac_len;
+ if(etm) {
+ /* we collect entire undecrypted packet including the
+ packet length field that we run MAC over */
+ p->packet_length = _libssh2_ntohu32(block);
+ total_num = 4 + p->packet_length +
+ remote_mac->mac_len;
+ }
+ else {
+ /* padding_length has not been authenticated yet, but it
+ won't actually be used (except for the sanity check
+ immediately following) until after the entire packet is
+ authenticated, so this is safe. */
+ p->padding_length = block[4];
+ if(p->padding_length > p->packet_length - 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+
+ /* total_num is the number of bytes following the initial
+ (5 bytes) packet length and padding length fields */
+ total_num = p->packet_length - 1 +
+ (encrypted ? remote_mac->mac_len : 0);
+ }
}
else {
- /* padding_length has not been authenticated yet, but it won't
- actually be used (except for the sanity check immediately
- following) until after the entire packet is authenticated,
- so this is safe. */
- p->padding_length = block[4];
- if(p->padding_length > p->packet_length - 1) {
+ /* advance the read pointer past size field if the packet
+ length is not required for decryption */
+
+ /* add size field to be included in total packet size
+ * calculation so it doesn't get dropped off on subsequent
+ * partial reads
+ */
+ total_num = 4;
+
+ p->packet_length = _libssh2_ntohu32(block);
+ if(p->packet_length < 1)
return LIBSSH2_ERROR_DECRYPT;
- }
- /* total_num is the number of bytes following the initial
- (5 bytes) packet length and padding length fields */
- total_num = p->packet_length - 1 +
- (encrypted ? session->remote.mac->mac_len : 0);
+ /* total_num may include size field, however due to existing
+ * logic it needs to be removed after the entire packet is read
+ */
+
+ total_num += p->packet_length +
+ (remote_mac ? remote_mac->mac_len : 0) + auth_len;
+
+ /* don't know what padding is until we decrypt the full
+ packet */
+ p->padding_length = 0;
}
/* RFC4253 section 6.1 Maximum Packet Length says:
@@ -597,32 +676,42 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
/* init write pointer to start of payload buffer */
p->wptr = p->payload;
- if(!etm && blocksize > 5) {
- /* copy the data from index 5 to the end of
- the blocksize from the temporary buffer to
- the start of the decrypted buffer */
- if(blocksize - 5 <= (int) total_num) {
- memcpy(p->wptr, &block[5], blocksize - 5);
- p->wptr += blocksize - 5; /* advance write pointer */
- if(etm) {
- /* advance past unencrypted packet length */
- p->wptr += 4;
+ if(!encrypted || !CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ if(!etm && blocksize > 5) {
+ /* copy the data from index 5 to the end of
+ the blocksize from the temporary buffer to
+ the start of the decrypted buffer */
+ if(blocksize - 5 <= (int) total_num) {
+ memcpy(p->wptr, &block[5], blocksize - 5);
+ p->wptr += blocksize - 5; /* advance write pointer */
+ if(etm) {
+ /* advance past unencrypted packet length */
+ p->wptr += 4;
+ }
+ }
+ else {
+ if(p->payload)
+ LIBSSH2_FREE(session, p->payload);
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
}
}
- else {
- if(p->payload)
- LIBSSH2_FREE(session, p->payload);
- return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
- }
- }
- /* init the data_num field to the number of bytes of
- the package read so far */
- p->data_num = p->wptr - p->payload;
+ /* init the data_num field to the number of bytes of
+ the package read so far */
+ p->data_num = p->wptr - p->payload;
+
+ /* we already dealt with a blocksize worth of data */
+ if(!etm)
+ numbytes -= blocksize;
+ }
+ else {
+ /* haven't started reading payload yet */
+ p->data_num = 0;
- /* we already dealt with a blocksize worth of data */
- if(!etm)
- numbytes -= blocksize;
+ /* we already dealt with packet size worth of data */
+ if(!encrypted)
+ numbytes -= 4;
+ }
}
/* how much there is left to add to the current payload
@@ -635,12 +724,26 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
numbytes = remainpack;
}
+ if(encrypted && CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ if(numbytes < remainpack) {
+ /* need a full packet before checking MAC */
+ session->socket_block_directions |=
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ /* we have a full packet, now remove the size field from numbytes
+ and total_num to process only the packet data */
+ numbytes -= 4;
+ p->total_num -= 4;
+ }
+
if(encrypted && !etm) {
/* At the end of the incoming stream, there is a MAC,
and we don't want to decrypt that since we need it
"raw". We MUST however decrypt the padding data
since it is used for the hash later on. */
- int skip = session->remote.mac->mac_len;
+ int skip = (remote_mac ? remote_mac->mac_len : 0) + auth_len;
if(CRYPT_FLAG_R(session, INTEGRATED_MAC))
/* This crypto method DOES need the MAC to go through
@@ -687,11 +790,44 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
/* if there are bytes to decrypt, do that */
if(numdecrypt > 0) {
/* now decrypt the lot */
- rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt,
- firstlast);
- if(rc != LIBSSH2_ERROR_NONE) {
- p->total_num = 0; /* no packet buffer available */
- return rc;
+ if(CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ rc = session->remote.crypt->crypt(session,
+ session->remote.seqno,
+ &p->buf[p->readidx],
+ numdecrypt,
+ &session->remote.crypt_abstract,
+ 0);
+
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ return rc;
+ }
+
+ memcpy(p->wptr, &p->buf[p->readidx], numbytes);
+
+ /* advance read index past size field now that we've decrypted
+ full packet */
+ p->readidx += 4;
+
+ /* include auth tag in bytes decrypted */
+ numdecrypt += auth_len;
+
+ /* set padding now that the packet has been verified and
+ decrypted */
+ p->padding_length = p->wptr[0];
+
+ if(p->padding_length > p->packet_length - 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ }
+ else {
+ rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt,
+ firstlast);
+
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ return rc;
+ }
}
/* advance the read pointer */
@@ -783,7 +919,8 @@ send_existing(LIBSSH2_SESSION *session, const unsigned char *data,
make the caller really notice his/hers flaw, we return error for
this case */
_libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
- "Address is different, but will resume nonetheless"));
+ "Address is different, returning EAGAIN"));
+ return LIBSSH2_ERROR_EAGAIN;
}
*ret = 1; /* set to make our parent return */
@@ -869,6 +1006,8 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
ssize_t ret;
int rc;
const unsigned char *orgdata = data;
+ const LIBSSH2_MAC_METHOD *local_mac = NULL;
+ unsigned int auth_len = 0;
size_t orgdata_len = data_len;
size_t crypt_offset, etm_crypt_offset;
@@ -908,7 +1047,15 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0;
- etm = encrypted && session->local.mac ? session->local.mac->etm : 0;
+ if(encrypted && session->local.crypt &&
+ CRYPT_FLAG_R(session, REQUIRES_FULL_PACKET)) {
+ auth_len = session->local.crypt->auth_len;
+ }
+ else {
+ local_mac = session->local.mac;
+ }
+
+ etm = encrypted && local_mac ? local_mac->etm : 0;
compressed = session->local.comp &&
session->local.comp->compress &&
@@ -973,7 +1120,8 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
4 for the packet_length field */
/* subtract 4 bytes of the packet_length field when padding AES-GCM
or with ETM */
- crypt_offset = (etm || (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)))
+ crypt_offset = (etm || auth_len ||
+ (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)))
? 4 : 0;
etm_crypt_offset = etm ? 4 : 0;
@@ -1004,7 +1152,9 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
/* append the MAC length to the total_length size */
total_length =
- packet_length + (encrypted ? session->local.mac->mac_len : 0);
+ packet_length + (encrypted && local_mac ? local_mac->mac_len : 0);
+
+ total_length += auth_len;
/* store packet_length, which is the size of the whole packet except
the MAC and the packet_length field itself */
@@ -1027,63 +1177,78 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
fields except the MAC field itself. This is skipped in the
INTEGRATED_MAC case, where the crypto algorithm also does its
own hash. */
- if(!etm && !CRYPT_FLAG_R(session, INTEGRATED_MAC)) {
- if(session->local.mac->hash(session, p->outbuf + packet_length,
- session->local.seqno, p->outbuf,
- packet_length, NULL, 0,
- &session->local.mac_abstract))
+ if(!etm && local_mac && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
+ if(local_mac->hash(session, p->outbuf + packet_length,
+ session->local.seqno, p->outbuf,
+ packet_length, NULL, 0,
+ &session->local.mac_abstract))
return _libssh2_error(session, LIBSSH2_ERROR_MAC_FAILURE,
"Failed to calculate MAC");
}
- /* Encrypt the whole packet data, one block size at a time.
- The MAC field is not encrypted unless INTEGRATED_MAC. */
- /* Some crypto back-ends could handle a single crypt() call for
- encryption, but (presumably) others cannot, so break it up
- into blocksize-sized chunks to satisfy them all. */
- for(i = etm_crypt_offset; i < packet_length;
- i += session->local.crypt->blocksize) {
- unsigned char *ptr = &p->outbuf[i];
- size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
- (int)(packet_length-i));
- /* The INTEGRATED_MAC case always has an extra call below, so it
- will never be LAST_BLOCK up here. */
- int firstlast = i == 0 ? FIRST_BLOCK :
- (!CRYPT_FLAG_L(session, INTEGRATED_MAC)
- && (i == packet_length - session->local.crypt->blocksize)
- ? LAST_BLOCK: MIDDLE_BLOCK);
- /* In the AAD case, the last block would be only 4 bytes because
- everything is offset by 4 since the initial packet_length isn't
- encrypted. In this case, combine that last short packet with the
- previous one since AES-GCM crypt() assumes that the entire MAC
- is available in that packet so it can set that to the
- authentication tag. */
- if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
- if(i > packet_length - 2*bsize) {
- /* increase the final block size */
- bsize = packet_length - i;
- /* advance the loop counter by the extra amount */
- i += bsize - session->local.crypt->blocksize;
- }
- _libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
- "crypting bytes %lu-%lu", (unsigned long)i,
- (unsigned long)(i + bsize - 1)));
- if(session->local.crypt->crypt(session, ptr,
- bsize,
+ if(CRYPT_FLAG_L(session, REQUIRES_FULL_PACKET)) {
+ if(session->local.crypt->crypt(session,
+ session->local.seqno,
+ p->outbuf,
+ packet_length,
&session->local.crypt_abstract,
- firstlast))
- return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ 0)) {
+ return LIBSSH2_ERROR_ENCRYPT;
+ }
}
- /* Call crypt() one last time so it can be filled in with the MAC */
- if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
- int authlen = session->local.mac->mac_len;
- assert((size_t)total_length <=
- packet_length + session->local.crypt->blocksize);
- if(session->local.crypt->crypt(session, &p->outbuf[packet_length],
- authlen,
- &session->local.crypt_abstract,
- LAST_BLOCK))
- return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ else {
+ /* Encrypt the whole packet data, one block size at a time.
+ The MAC field is not encrypted unless INTEGRATED_MAC. */
+ /* Some crypto back-ends could handle a single crypt() call for
+ encryption, but (presumably) others cannot, so break it up
+ into blocksize-sized chunks to satisfy them all. */
+ for(i = etm_crypt_offset; i < packet_length;
+ i += session->local.crypt->blocksize) {
+ unsigned char *ptr = &p->outbuf[i];
+ size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
+ (int)(packet_length-i));
+ /* The INTEGRATED_MAC case always has an extra call below, so
+ it will never be LAST_BLOCK up here. */
+ int firstlast = i == 0 ? FIRST_BLOCK :
+ (!CRYPT_FLAG_L(session, INTEGRATED_MAC)
+ && (i == packet_length - session->local.crypt->blocksize)
+ ? LAST_BLOCK : MIDDLE_BLOCK);
+ /* In the AAD case, the last block would be only 4 bytes
+ because everything is offset by 4 since the initial
+ packet_length isn't encrypted. In this case, combine that last
+ short packet with the previous one since AES-GCM crypt()
+ assumes that the entire MAC is available in that packet so it
+ can set that to the authentication tag. */
+ if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
+ if(i > packet_length - 2*bsize) {
+ /* increase the final block size */
+ bsize = packet_length - i;
+ /* advance the loop counter by the extra amount */
+ i += bsize - session->local.crypt->blocksize;
+ }
+ _libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
+ "crypting bytes %lu-%lu", (unsigned long)i,
+ (unsigned long)(i + bsize - 1)));
+ if(session->local.crypt->crypt(session, 0, ptr,
+ bsize,
+ &session->local.crypt_abstract,
+ firstlast))
+ return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ }
+
+ /* Call crypt one last time so it can be filled in with the MAC */
+ if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
+ int authlen = local_mac->mac_len;
+ assert((size_t)total_length <=
+ packet_length + session->local.crypt->blocksize);
+ if(session->local.crypt->crypt(session,
+ 0,
+ &p->outbuf[packet_length],
+ authlen,
+ &session->local.crypt_abstract,
+ LAST_BLOCK))
+ return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ }
}
if(etm) {
@@ -1092,10 +1257,10 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
calculated on the entire packet (length plain the rest
encrypted), including all fields except the MAC field
itself. */
- if(session->local.mac->hash(session, p->outbuf + packet_length,
- session->local.seqno, p->outbuf,
- packet_length, NULL, 0,
- &session->local.mac_abstract))
+ if(local_mac->hash(session, p->outbuf + packet_length,
+ session->local.seqno, p->outbuf,
+ packet_length, NULL, 0,
+ &session->local.mac_abstract))
return _libssh2_error(session, LIBSSH2_ERROR_MAC_FAILURE,
"Failed to calculate MAC");
}
diff --git a/libs/libssh2/src/userauth.c b/libs/libssh2/src/userauth.c
index 60fd48e47d..0040c3fa30 100644
--- a/libs/libssh2/src/userauth.c
+++ b/libs/libssh2/src/userauth.c
@@ -53,6 +53,8 @@
#include "userauth.h"
#include "userauth_kbd_packet.h"
+#include <stdlib.h> /* strtol() */
+
/* userauth_list
*
* List authentication methods
@@ -1262,6 +1264,15 @@ size_t plain_method(char *method, size_t method_len)
return 7;
}
+ if(!strncmp("rsa-sha2-256-cert-v01@openssh.com",
+ method,
+ method_len) ||
+ !strncmp("rsa-sha2-512-cert-v01@openssh.com",
+ method,
+ method_len)) {
+ return 12;
+ }
+
if(!strncmp("ecdsa-sha2-nistp256-cert-v01@openssh.com",
method,
method_len) ||
@@ -1299,6 +1310,31 @@ size_t plain_method(char *method, size_t method_len)
return method_len;
}
+/* Function to check if the given version is less than pattern (OpenSSH 7.8)
+ * This function expects the input version in x.y* format
+ * (x being openssh major and y being openssh minor version)
+ * Returns 1 if the version is less than OpenSSH_7.8, 0 otherwise
+ */
+static int is_version_less_than_78(const char *version)
+{
+ char *endptr = NULL;
+ long major = 0;
+ int minor = 0;
+
+ if(!version)
+ return 0;
+
+ major = strtol(version, &endptr, 10);
+ if(!endptr || *endptr != '.')
+ return 0; /* Not a valid number */
+ minor = (endptr + 1)[0] - '0';
+ if((major >= 1 && major <= 6) ||
+ (major == 7 && minor >= 0 && minor <= 7)) {
+ return 1; /* Version is in the specified range */
+ }
+ return 0;
+}
+
/**
* @function _libssh2_key_sign_algorithm
* @abstract Upgrades the algorithm used for public key signing RFC 8332
@@ -1326,7 +1362,14 @@ _libssh2_key_sign_algorithm(LIBSSH2_SESSION *session,
size_t f_len = 0;
int rc = 0;
size_t match_len = 0;
+ const size_t suffix_len = 21;
char *filtered_algs = NULL;
+ const char * const certSuffix = "-cert-v01@openssh.com";
+ const char *remote_banner = NULL;
+ const char * const remote_ver_pre = "OpenSSH_";
+ const char *remote_ver_start = NULL;
+ const char *remote_ver = NULL;
+ int SSH_BUG_SIGTYPE = 0;
const char *supported_algs =
_libssh2_supported_key_sign_algorithms(session,
@@ -1345,6 +1388,25 @@ _libssh2_key_sign_algorithm(LIBSSH2_SESSION *session,
return rc;
}
+ /* Set "SSH_BUG_SIGTYPE" flag when the remote server version is OpenSSH 7.7
+ or lower and when the RSA key in question is a certificate to ignore
+ "server-sig-algs" and only offer ssh-rsa signature algorithm for
+ RSA certs */
+ remote_banner = libssh2_session_banner_get(session);
+ /* Extract version information from the banner */
+ if(remote_banner) {
+ remote_ver_start = strstr(remote_banner, remote_ver_pre);
+ if(remote_ver_start) {
+ remote_ver = remote_ver_start + strlen(remote_ver_pre);
+ SSH_BUG_SIGTYPE = is_version_less_than_78(remote_ver);
+ if(SSH_BUG_SIGTYPE && *key_method_len == 28 &&
+ memcmp(key_method, "ssh-rsa-cert-v01@openssh.com",
+ *key_method_len)) {
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+ }
+
s = session->server_sign_algorithms;
i = filtered_algs;
@@ -1412,15 +1474,27 @@ _libssh2_key_sign_algorithm(LIBSSH2_SESSION *session,
}
if(match) {
- if(*key_method)
- LIBSSH2_FREE(session, *key_method);
-
- *key_method = LIBSSH2_ALLOC(session, match_len);
- if(*key_method) {
- memcpy(*key_method, match, match_len);
- *key_method_len = match_len;
+ if(*key_method_len == 28 &&
+ memcmp(key_method, "ssh-rsa-cert-v01@openssh.com", *key_method_len)) {
+ if(*key_method)
+ LIBSSH2_FREE(session, *key_method);
+ *key_method = LIBSSH2_ALLOC(session, match_len + suffix_len);
+ if(*key_method) {
+ memcpy(*key_method, match, match_len);
+ memcpy(*key_method + match_len, certSuffix, suffix_len);
+ *key_method_len = match_len + suffix_len;
+ }
}
else {
+ if(*key_method)
+ LIBSSH2_FREE(session, *key_method);
+ *key_method = LIBSSH2_ALLOC(session, match_len);
+ if(*key_method) {
+ memcpy(*key_method, match, match_len);
+ *key_method_len = match_len;
+ }
+ }
+ if(!key_method) {
*key_method_len = 0;
rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
"Unable to allocate key method upgrade");
@@ -2188,7 +2262,7 @@ userauth_keyboard_interactive(LIBSSH2_SESSION * session,
if(session->userauth_kybd_responses[i].length <=
(SIZE_MAX - 4 - session->userauth_kybd_packet_len)) {
session->userauth_kybd_packet_len +=
- 4 + session->userauth_kybd_responses[i].length;
+ 4 + (size_t)session->userauth_kybd_responses[i].length;
}
else {
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
diff --git a/libs/libssh2/src/wincng.c b/libs/libssh2/src/wincng.c
new file mode 100644
index 0000000000..5d4b36b70b
--- /dev/null
+++ b/libs/libssh2/src/wincng.c
@@ -0,0 +1,4185 @@
+/*
+ * Copyright (C) Marc Hoersken <info@marc-hoersken.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifdef LIBSSH2_CRYPTO_C /* Compile this via crypto.c */
+
+/* required for cross-compilation against the w64 mingw-runtime package */
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
+#undef _WIN32_WINNT
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#if !defined(LIBSSH2_WINCNG_DISABLE_WINCRYPT) && !defined(HAVE_LIBCRYPT32)
+#define HAVE_LIBCRYPT32
+#endif
+
+/* specify the required libraries for dependencies using MSVC */
+#ifdef _MSC_VER
+#pragma comment(lib, "bcrypt.lib")
+#ifdef HAVE_LIBCRYPT32
+#pragma comment(lib, "crypt32.lib")
+#endif
+#endif
+
+#include <windows.h>
+#include <bcrypt.h>
+#include <math.h>
+
+#include <stdlib.h>
+
+#ifdef HAVE_LIBCRYPT32
+#include <wincrypt.h> /* for CryptDecodeObjectEx() */
+#endif
+
+#define PEM_RSA_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_RSA_FOOTER "-----END RSA PRIVATE KEY-----"
+#define PEM_DSA_HEADER "-----BEGIN DSA PRIVATE KEY-----"
+#define PEM_DSA_FOOTER "-----END DSA PRIVATE KEY-----"
+#define PEM_ECDSA_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----"
+#define PEM_ECDSA_FOOTER "-----END OPENSSH PRIVATE KEY-----"
+
+#define OPENSSL_PRIVATEKEY_AUTH_MAGIC "openssh-key-v1"
+
+/* Define these manually to avoid including <ntstatus.h> and thus
+ clashing with <windows.h> symbols. */
+#ifndef STATUS_NOT_SUPPORTED
+#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB)
+#endif
+
+#ifndef STATUS_INVALID_SIGNATURE
+#define STATUS_INVALID_SIGNATURE ((NTSTATUS)0xC000A000)
+#endif
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Missing definitions (for MinGW[-w64])
+ */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
+#endif
+
+#ifndef BCRYPT_RNG_ALGORITHM
+#define BCRYPT_RNG_ALGORITHM L"RNG"
+#endif
+
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+#ifndef BCRYPT_MD5_ALGORITHM
+#define BCRYPT_MD5_ALGORITHM L"MD5"
+#endif
+#endif
+
+#ifndef BCRYPT_SHA1_ALGORITHM
+#define BCRYPT_SHA1_ALGORITHM L"SHA1"
+#endif
+
+#ifndef BCRYPT_SHA256_ALGORITHM
+#define BCRYPT_SHA256_ALGORITHM L"SHA256"
+#endif
+
+#ifndef BCRYPT_SHA384_ALGORITHM
+#define BCRYPT_SHA384_ALGORITHM L"SHA384"
+#endif
+
+#ifndef BCRYPT_SHA512_ALGORITHM
+#define BCRYPT_SHA512_ALGORITHM L"SHA512"
+#endif
+
+#ifndef BCRYPT_RSA_ALGORITHM
+#define BCRYPT_RSA_ALGORITHM L"RSA"
+#endif
+
+#ifndef BCRYPT_DSA_ALGORITHM
+#define BCRYPT_DSA_ALGORITHM L"DSA"
+#endif
+
+#ifndef BCRYPT_AES_ALGORITHM
+#define BCRYPT_AES_ALGORITHM L"AES"
+#endif
+
+#ifndef BCRYPT_RC4_ALGORITHM
+#define BCRYPT_RC4_ALGORITHM L"RC4"
+#endif
+
+#ifndef BCRYPT_3DES_ALGORITHM
+#define BCRYPT_3DES_ALGORITHM L"3DES"
+#endif
+
+#ifndef BCRYPT_DH_ALGORITHM
+#define BCRYPT_DH_ALGORITHM L"DH"
+#endif
+
+/* BCRYPT_KDF_RAW_SECRET is available from Windows 8.1 and onwards */
+#ifndef BCRYPT_KDF_RAW_SECRET
+#define BCRYPT_KDF_RAW_SECRET L"TRUNCATE"
+#endif
+
+#ifndef BCRYPT_ALG_HANDLE_HMAC_FLAG
+#define BCRYPT_ALG_HANDLE_HMAC_FLAG 0x00000008
+#endif
+
+#ifndef BCRYPT_DSA_PUBLIC_BLOB
+#define BCRYPT_DSA_PUBLIC_BLOB L"DSAPUBLICBLOB"
+#endif
+
+#ifndef BCRYPT_DSA_PUBLIC_MAGIC
+#define BCRYPT_DSA_PUBLIC_MAGIC 0x42505344 /* DSPB */
+#endif
+
+#ifndef BCRYPT_DSA_PRIVATE_BLOB
+#define BCRYPT_DSA_PRIVATE_BLOB L"DSAPRIVATEBLOB"
+#endif
+
+#ifndef BCRYPT_DSA_PRIVATE_MAGIC
+#define BCRYPT_DSA_PRIVATE_MAGIC 0x56505344 /* DSPV */
+#endif
+
+#ifndef BCRYPT_RSAPUBLIC_BLOB
+#define BCRYPT_RSAPUBLIC_BLOB L"RSAPUBLICBLOB"
+#endif
+
+#ifndef BCRYPT_RSAPUBLIC_MAGIC
+#define BCRYPT_RSAPUBLIC_MAGIC 0x31415352 /* RSA1 */
+#endif
+
+#ifndef BCRYPT_RSAFULLPRIVATE_BLOB
+#define BCRYPT_RSAFULLPRIVATE_BLOB L"RSAFULLPRIVATEBLOB"
+#endif
+
+#ifndef BCRYPT_RSAFULLPRIVATE_MAGIC
+#define BCRYPT_RSAFULLPRIVATE_MAGIC 0x33415352 /* RSA3 */
+#endif
+
+#ifndef BCRYPT_KEY_DATA_BLOB
+#define BCRYPT_KEY_DATA_BLOB L"KeyDataBlob"
+#endif
+
+#ifndef BCRYPT_MESSAGE_BLOCK_LENGTH
+#define BCRYPT_MESSAGE_BLOCK_LENGTH L"MessageBlockLength"
+#endif
+
+#ifndef BCRYPT_NO_KEY_VALIDATION
+#define BCRYPT_NO_KEY_VALIDATION 0x00000008
+#endif
+
+#ifndef BCRYPT_BLOCK_PADDING
+#define BCRYPT_BLOCK_PADDING 0x00000001
+#endif
+
+#ifndef BCRYPT_PAD_NONE
+#define BCRYPT_PAD_NONE 0x00000001
+#endif
+
+#ifndef BCRYPT_PAD_PKCS1
+#define BCRYPT_PAD_PKCS1 0x00000002
+#endif
+
+#ifndef BCRYPT_PAD_OAEP
+#define BCRYPT_PAD_OAEP 0x00000004
+#endif
+
+#ifndef BCRYPT_PAD_PSS
+#define BCRYPT_PAD_PSS 0x00000008
+#endif
+
+#ifndef CRYPT_STRING_ANY
+#define CRYPT_STRING_ANY 0x00000007
+#endif
+
+#ifndef LEGACY_RSAPRIVATE_BLOB
+#define LEGACY_RSAPRIVATE_BLOB L"CAPIPRIVATEBLOB"
+#endif
+
+#ifndef PKCS_RSA_PRIVATE_KEY
+#define PKCS_RSA_PRIVATE_KEY (LPCSTR)43
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1700
+/* Workaround for warning C4306:
+ 'type cast' : conversion from 'int' to 'LPCSTR' of greater size */
+#undef X509_SEQUENCE_OF_ANY
+#undef X509_MULTI_BYTE_UINT
+#undef PKCS_RSA_PRIVATE_KEY
+#define X509_SEQUENCE_OF_ANY ((LPCSTR)(size_t)34)
+#define X509_MULTI_BYTE_UINT ((LPCSTR)(size_t)38)
+#define PKCS_RSA_PRIVATE_KEY ((LPCSTR)(size_t)43)
+#endif
+
+static int
+_libssh2_wincng_bignum_resize(_libssh2_bn* bn, ULONG length);
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: ECDSA-specific declarations.
+ */
+#if LIBSSH2_ECDSA
+
+typedef enum {
+ WINCNG_ECC_KEYTYPE_ECDSA = 0,
+ WINCNG_ECC_KEYTYPE_ECDH = 1,
+} _libssh2_wincng_ecc_keytype;
+
+typedef struct __libssh2_wincng_ecdsa_algorithm {
+ /* Algorithm name */
+ const char *name;
+
+ /* Key length, in bits */
+ ULONG key_length;
+
+ /* Length of each point, in bytes */
+ ULONG point_length;
+
+ /* Name of CNG algorithm provider, */
+ /* indexed by _libssh2_wincng_ecc_keytype */
+ LPCWSTR provider[2];
+
+ /* Magic for public key import, indexed by _libssh2_wincng_ecc_keytype */
+ ULONG public_import_magic[2];
+
+ /* Magic for private key import, indexed by _libssh2_wincng_ecc_keytype */
+ ULONG private_import_magic[2];
+} _libssh2_wincng_ecdsa_algorithm;
+
+/* Supported algorithms, indexed by libssh2_curve_type */
+static _libssh2_wincng_ecdsa_algorithm _wincng_ecdsa_algorithms[] = {
+ {
+ "ecdsa-sha2-nistp256",
+ 256,
+ 256 / 8,
+ { BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_ECDH_P256_ALGORITHM },
+ { BCRYPT_ECDSA_PUBLIC_P256_MAGIC, BCRYPT_ECDH_PUBLIC_P256_MAGIC },
+ { BCRYPT_ECDSA_PRIVATE_P256_MAGIC, BCRYPT_ECDH_PRIVATE_P256_MAGIC }
+ },
+ {
+ "ecdsa-sha2-nistp384",
+ 384,
+ 384 / 8,
+ { BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_ECDH_P384_ALGORITHM },
+ { BCRYPT_ECDSA_PUBLIC_P384_MAGIC, BCRYPT_ECDH_PUBLIC_P384_MAGIC },
+ { BCRYPT_ECDSA_PRIVATE_P384_MAGIC, BCRYPT_ECDH_PRIVATE_P384_MAGIC }
+ },
+ {
+ "ecdsa-sha2-nistp521",
+ 521,
+ ((521 + 7) & ~7) / 8,
+ { BCRYPT_ECDSA_P521_ALGORITHM, BCRYPT_ECDH_P521_ALGORITHM },
+ { BCRYPT_ECDSA_PUBLIC_P521_MAGIC, BCRYPT_ECDH_PUBLIC_P521_MAGIC },
+ { BCRYPT_ECDSA_PRIVATE_P521_MAGIC, BCRYPT_ECDH_PRIVATE_P521_MAGIC }
+ },
+};
+
+/* An encoded point */
+typedef struct __libssh2_ecdsa_point {
+ libssh2_curve_type curve;
+
+ const unsigned char *x;
+ ULONG x_len;
+
+ const unsigned char *y;
+ ULONG y_len;
+} _libssh2_ecdsa_point;
+
+/* Lookup libssh2_curve_type by name */
+static int
+_libssh2_wincng_ecdsa_curve_type_from_name(IN const char *name,
+ OUT libssh2_curve_type *out_curve);
+
+/* Parse an OpenSSL-formatted ECDSA private key */
+static int
+_libssh2_wincng_parse_ecdsa_privatekey(OUT _libssh2_wincng_ecdsa_key **key,
+ IN unsigned char *privatekey,
+ IN size_t privatekey_len);
+
+#endif
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Generic functions
+ */
+
+struct _libssh2_wincng_ctx _libssh2_wincng;
+
+void
+_libssh2_wincng_init(void)
+{
+ int ret;
+
+#if LIBSSH2_ECDSA
+ unsigned int curve;
+#endif
+
+ memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng));
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRNG,
+ BCRYPT_RNG_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgRNG = NULL;
+ }
+
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashMD5,
+ BCRYPT_MD5_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHashMD5 = NULL;
+ }
+#endif
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA1,
+ BCRYPT_SHA1_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHashSHA1 = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA256,
+ BCRYPT_SHA256_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHashSHA256 = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA384,
+ BCRYPT_SHA384_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHashSHA384 = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHashSHA512,
+ BCRYPT_SHA512_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHashSHA512 = NULL;
+ }
+
+#if LIBSSH2_MD5
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacMD5,
+ BCRYPT_MD5_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHmacMD5 = NULL;
+ }
+#endif
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA1,
+ BCRYPT_SHA1_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHmacSHA1 = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA256,
+ BCRYPT_SHA256_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHmacSHA256 = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA384,
+ BCRYPT_SHA384_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHmacSHA384 = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgHmacSHA512,
+ BCRYPT_SHA512_ALGORITHM, NULL,
+ BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgHmacSHA512 = NULL;
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRSA,
+ BCRYPT_RSA_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgRSA = NULL;
+ }
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDSA,
+ BCRYPT_DSA_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgDSA = NULL;
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgAES_CBC,
+ BCRYPT_AES_ALGORITHM, NULL, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlgAES_CBC,
+ BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_CBC,
+ sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgAES_CBC = NULL;
+ }
+ }
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgAES_ECB,
+ BCRYPT_AES_ALGORITHM, NULL, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlgAES_ECB,
+ BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_ECB,
+ sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_ECB, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgAES_ECB = NULL;
+ }
+ }
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgRC4_NA,
+ BCRYPT_RC4_ALGORITHM, NULL, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlgRC4_NA,
+ BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_NA,
+ sizeof(BCRYPT_CHAIN_MODE_NA), 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgRC4_NA = NULL;
+ }
+ }
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlg3DES_CBC,
+ BCRYPT_3DES_ALGORITHM, NULL, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ ret = BCryptSetProperty(_libssh2_wincng.hAlg3DES_CBC,
+ BCRYPT_CHAINING_MODE,
+ (PBYTE)BCRYPT_CHAIN_MODE_CBC,
+ sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ ret = BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC,
+ 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlg3DES_CBC = NULL;
+ }
+ }
+ }
+
+ ret = BCryptOpenAlgorithmProvider(&_libssh2_wincng.hAlgDH,
+ BCRYPT_DH_ALGORITHM, NULL, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgDH = NULL;
+ }
+
+#if LIBSSH2_ECDSA
+ for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) {
+ BCRYPT_ALG_HANDLE alg_handle_ecdsa;
+ BCRYPT_ALG_HANDLE alg_handle_ecdh;
+
+ ret = BCryptOpenAlgorithmProvider(
+ &alg_handle_ecdsa,
+ _wincng_ecdsa_algorithms[curve].provider[WINCNG_ECC_KEYTYPE_ECDSA],
+ NULL,
+ 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgECDSA[curve] = alg_handle_ecdsa;
+ }
+
+ ret = BCryptOpenAlgorithmProvider(
+ &alg_handle_ecdh,
+ _wincng_ecdsa_algorithms[curve].provider[WINCNG_ECC_KEYTYPE_ECDH],
+ NULL,
+ 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng.hAlgECDH[curve] = alg_handle_ecdh;
+ }
+ }
+#endif
+}
+
+void
+_libssh2_wincng_free(void)
+{
+#if LIBSSH2_ECDSA
+ unsigned int curve;
+#endif
+
+ if(_libssh2_wincng.hAlgRNG)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRNG, 0);
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+ if(_libssh2_wincng.hAlgHashMD5)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashMD5, 0);
+#endif
+ if(_libssh2_wincng.hAlgHashSHA1)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA1, 0);
+ if(_libssh2_wincng.hAlgHashSHA256)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA256, 0);
+ if(_libssh2_wincng.hAlgHashSHA384)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA384, 0);
+ if(_libssh2_wincng.hAlgHashSHA512)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHashSHA512, 0);
+#if LIBSSH2_MD5
+ if(_libssh2_wincng.hAlgHmacMD5)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacMD5, 0);
+#endif
+ if(_libssh2_wincng.hAlgHmacSHA1)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA1, 0);
+ if(_libssh2_wincng.hAlgHmacSHA256)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA256, 0);
+ if(_libssh2_wincng.hAlgHmacSHA384)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA384, 0);
+ if(_libssh2_wincng.hAlgHmacSHA512)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgHmacSHA512, 0);
+ if(_libssh2_wincng.hAlgRSA)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRSA, 0);
+ if(_libssh2_wincng.hAlgDSA)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDSA, 0);
+ if(_libssh2_wincng.hAlgAES_CBC)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgAES_CBC, 0);
+ if(_libssh2_wincng.hAlgRC4_NA)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgRC4_NA, 0);
+ if(_libssh2_wincng.hAlg3DES_CBC)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlg3DES_CBC, 0);
+ if(_libssh2_wincng.hAlgDH)
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgDH, 0);
+
+#if LIBSSH2_ECDSA
+ for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) {
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgECDSA[curve],
+ 0);
+ (void)BCryptCloseAlgorithmProvider(_libssh2_wincng.hAlgECDH[curve],
+ 0);
+ }
+#endif
+
+ memset(&_libssh2_wincng, 0, sizeof(_libssh2_wincng));
+}
+
+int
+_libssh2_wincng_random(void *buf, size_t len)
+{
+ int ret;
+
+ if(len > ULONG_MAX) {
+ return -1;
+ }
+
+ ret = BCryptGenRandom(_libssh2_wincng.hAlgRNG, buf, (ULONG)len, 0);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+static void
+_libssh2_wincng_safe_free(void *buf, size_t len)
+{
+ if(!buf)
+ return;
+
+ if(len > 0)
+ _libssh2_explicit_zero(buf, len);
+
+ free(buf);
+}
+
+/* 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, ULONG dest_len,
+ unsigned char *src, ULONG src_len)
+{
+ if(dest_len > src_len) {
+ memset(dest, 0, dest_len - src_len);
+ }
+ memcpy((dest + dest_len) - src_len, src, src_len);
+}
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Hash functions
+ */
+
+int
+_libssh2_wincng_hash_init(_libssh2_wincng_hash_ctx *ctx,
+ BCRYPT_ALG_HANDLE hAlg, ULONG hashlen,
+ unsigned char *key, ULONG keylen)
+{
+ BCRYPT_HASH_HANDLE hHash;
+ unsigned char *pbHashObject;
+ ULONG dwHashObject, dwHash, cbData;
+ int ret;
+
+ ret = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH,
+ (unsigned char *)&dwHash,
+ sizeof(dwHash),
+ &cbData, 0);
+ if((!BCRYPT_SUCCESS(ret)) || dwHash != hashlen) {
+ return -1;
+ }
+
+ ret = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH,
+ (unsigned char *)&dwHashObject,
+ sizeof(dwHashObject),
+ &cbData, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ return -1;
+ }
+
+ pbHashObject = malloc(dwHashObject);
+ if(!pbHashObject) {
+ return -1;
+ }
+
+
+ ret = BCryptCreateHash(hAlg, &hHash,
+ pbHashObject, dwHashObject,
+ key, keylen, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(pbHashObject, dwHashObject);
+ return -1;
+ }
+
+
+ ctx->hHash = hHash;
+ ctx->pbHashObject = pbHashObject;
+ ctx->dwHashObject = dwHashObject;
+ ctx->cbHash = dwHash;
+
+ return 0;
+}
+
+int
+_libssh2_wincng_hash_update(_libssh2_wincng_hash_ctx *ctx,
+ const void *data, ULONG datalen)
+{
+ int ret;
+
+ ret = BCryptHashData(ctx->hHash, (PUCHAR)data, datalen, 0);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_hash_final(_libssh2_wincng_hash_ctx *ctx,
+ unsigned char *hash)
+{
+ int ret;
+
+ ret = BCryptFinishHash(ctx->hHash, hash, ctx->cbHash, 0);
+
+ BCryptDestroyHash(ctx->hHash);
+ ctx->hHash = NULL;
+
+ _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject);
+ ctx->pbHashObject = NULL;
+ ctx->dwHashObject = 0;
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_hash(const unsigned char *data, ULONG datalen,
+ BCRYPT_ALG_HANDLE hAlg,
+ unsigned char *hash, ULONG hashlen)
+{
+ _libssh2_wincng_hash_ctx ctx;
+ int ret;
+
+ ret = _libssh2_wincng_hash_init(&ctx, hAlg, hashlen, NULL, 0);
+ if(!ret) {
+ ret = _libssh2_wincng_hash_update(&ctx, data, datalen);
+ ret |= _libssh2_wincng_hash_final(&ctx, hash);
+ }
+
+ return ret;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: HMAC functions
+ */
+
+int _libssh2_hmac_ctx_init(libssh2_hmac_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ return 1;
+}
+
+#if LIBSSH2_MD5
+int _libssh2_hmac_md5_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacMD5,
+ MD5_DIGEST_LENGTH,
+ key, (ULONG) keylen);
+
+ return ret == 0 ? 1 : 0;
+}
+#endif
+
+int _libssh2_hmac_sha1_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA1,
+ SHA_DIGEST_LENGTH,
+ key, (ULONG) keylen);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int _libssh2_hmac_sha256_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA256,
+ SHA256_DIGEST_LENGTH,
+ key, (ULONG) keylen);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int _libssh2_hmac_sha512_init(libssh2_hmac_ctx *ctx,
+ void *key, size_t keylen)
+{
+ int ret = _libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHmacSHA512,
+ SHA512_DIGEST_LENGTH,
+ key, (ULONG) keylen);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int _libssh2_hmac_update(libssh2_hmac_ctx *ctx,
+ const void *data, size_t datalen)
+{
+ int ret = _libssh2_wincng_hash_update(ctx, data, (ULONG) datalen);
+
+ return ret == 0 ? 1 : 0;
+}
+
+int _libssh2_hmac_final(libssh2_hmac_ctx *ctx, void *data)
+{
+ int ret = BCryptFinishHash(ctx->hHash, data, ctx->cbHash, 0);
+
+ return BCRYPT_SUCCESS(ret) ? 1 : 0;
+}
+
+void _libssh2_hmac_cleanup(libssh2_hmac_ctx *ctx)
+{
+ BCryptDestroyHash(ctx->hHash);
+ ctx->hHash = NULL;
+
+ _libssh2_wincng_safe_free(ctx->pbHashObject, ctx->dwHashObject);
+ ctx->pbHashObject = NULL;
+ ctx->dwHashObject = 0;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Key functions
+ */
+
+static int
+_libssh2_wincng_key_sha_verify(_libssh2_wincng_key_ctx *ctx,
+ ULONG hashlen,
+ const unsigned char *sig,
+ ULONG sig_len,
+ const unsigned char *m,
+ ULONG m_len,
+ ULONG flags)
+{
+ BCRYPT_PKCS1_PADDING_INFO paddingInfoPKCS1;
+ BCRYPT_ALG_HANDLE hAlgHash;
+ void *pPaddingInfo;
+ unsigned char *data, *hash;
+ ULONG datalen;
+ int ret;
+
+ if(hashlen == SHA_DIGEST_LENGTH) {
+ hAlgHash = _libssh2_wincng.hAlgHashSHA1;
+ paddingInfoPKCS1.pszAlgId = BCRYPT_SHA1_ALGORITHM;
+ }
+ else if(hashlen == SHA256_DIGEST_LENGTH) {
+ hAlgHash = _libssh2_wincng.hAlgHashSHA256;
+ paddingInfoPKCS1.pszAlgId = BCRYPT_SHA256_ALGORITHM;
+ }
+ else if(hashlen == SHA384_DIGEST_LENGTH) {
+ hAlgHash = _libssh2_wincng.hAlgHashSHA384;
+ paddingInfoPKCS1.pszAlgId = BCRYPT_SHA384_ALGORITHM;
+ }
+ else if(hashlen == SHA512_DIGEST_LENGTH) {
+ hAlgHash = _libssh2_wincng.hAlgHashSHA512;
+ paddingInfoPKCS1.pszAlgId = BCRYPT_SHA512_ALGORITHM;
+ }
+ else {
+ return -1;
+ }
+
+ datalen = m_len;
+ data = malloc(datalen);
+ if(!data) {
+ return -1;
+ }
+
+ hash = malloc(hashlen);
+ if(!hash) {
+ free(data);
+ return -1;
+ }
+ memcpy(data, m, datalen);
+
+ ret = _libssh2_wincng_hash(data, datalen,
+ hAlgHash,
+ hash, hashlen);
+ _libssh2_wincng_safe_free(data, datalen);
+
+ if(ret) {
+ _libssh2_wincng_safe_free(hash, hashlen);
+ return -1;
+ }
+
+ datalen = sig_len;
+ data = malloc(datalen);
+ if(!data) {
+ _libssh2_wincng_safe_free(hash, hashlen);
+ return -1;
+ }
+
+ if(flags & BCRYPT_PAD_PKCS1) {
+ pPaddingInfo = &paddingInfoPKCS1;
+ }
+ else
+ pPaddingInfo = NULL;
+
+ memcpy(data, sig, datalen);
+
+ ret = BCryptVerifySignature(ctx->hKey, pPaddingInfo,
+ hash, hashlen, data, datalen, flags);
+
+ _libssh2_wincng_safe_free(hash, hashlen);
+ _libssh2_wincng_safe_free(data, datalen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+#ifdef HAVE_LIBCRYPT32
+static int
+_libssh2_wincng_load_pem(LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase,
+ const char *headerbegin,
+ const char *headerend,
+ unsigned char **data,
+ size_t *datalen)
+{
+ FILE *fp;
+ int ret;
+
+ fp = fopen(filename, FOPEN_READTEXT);
+ if(!fp) {
+ return -1;
+ }
+
+ ret = _libssh2_pem_parse(session, headerbegin, headerend,
+ passphrase,
+ fp, data, datalen);
+
+ fclose(fp);
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_load_private(LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase,
+ unsigned char **ppbEncoded,
+ size_t *pcbEncoded,
+ int tryLoadRSA, int tryLoadDSA)
+{
+ unsigned char *data = NULL;
+ size_t datalen = 0;
+ int ret = -1;
+
+ if(ret && tryLoadRSA) {
+ ret = _libssh2_wincng_load_pem(session, filename, passphrase,
+ PEM_RSA_HEADER, PEM_RSA_FOOTER,
+ &data, &datalen);
+ }
+
+ if(ret && tryLoadDSA) {
+ ret = _libssh2_wincng_load_pem(session, filename, passphrase,
+ PEM_DSA_HEADER, PEM_DSA_FOOTER,
+ &data, &datalen);
+ }
+
+ if(!ret) {
+ *ppbEncoded = data;
+ *pcbEncoded = datalen;
+ }
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_load_private_memory(LIBSSH2_SESSION *session,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const unsigned char *passphrase,
+ unsigned char **ppbEncoded,
+ size_t *pcbEncoded,
+ int tryLoadRSA, int tryLoadDSA)
+{
+ unsigned char *data = NULL;
+ size_t datalen = 0;
+ int ret = -1;
+
+ (void)passphrase;
+
+ if(ret && tryLoadRSA) {
+ ret = _libssh2_pem_parse_memory(session,
+ PEM_RSA_HEADER, PEM_RSA_FOOTER,
+ privatekeydata, privatekeydata_len,
+ &data, &datalen);
+ }
+
+ if(ret && tryLoadDSA) {
+ ret = _libssh2_pem_parse_memory(session,
+ PEM_DSA_HEADER, PEM_DSA_FOOTER,
+ privatekeydata, privatekeydata_len,
+ &data, &datalen);
+ }
+
+ if(!ret) {
+ *ppbEncoded = data;
+ *pcbEncoded = datalen;
+ }
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_asn_decode(unsigned char *pbEncoded,
+ DWORD cbEncoded,
+ LPCSTR lpszStructType,
+ unsigned char **ppbDecoded,
+ DWORD *pcbDecoded)
+{
+ unsigned char *pbDecoded = NULL;
+ DWORD cbDecoded = 0;
+ int ret;
+
+ ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ lpszStructType,
+ pbEncoded, cbEncoded, 0, NULL,
+ NULL, &cbDecoded);
+ if(!ret) {
+ return -1;
+ }
+
+ pbDecoded = malloc(cbDecoded);
+ if(!pbDecoded) {
+ return -1;
+ }
+
+ ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ lpszStructType,
+ pbEncoded, cbEncoded, 0, NULL,
+ pbDecoded, &cbDecoded);
+ if(!ret) {
+ _libssh2_wincng_safe_free(pbDecoded, cbDecoded);
+ return -1;
+ }
+
+
+ *ppbDecoded = pbDecoded;
+ *pcbDecoded = cbDecoded;
+
+ return 0;
+}
+
+static int
+_libssh2_wincng_bn_ltob(unsigned char *pbInput,
+ DWORD cbInput,
+ unsigned char **ppbOutput,
+ DWORD *pcbOutput)
+{
+ unsigned char *pbOutput;
+ DWORD cbOutput, index, offset, length;
+
+ if(cbInput < 1) {
+ return 0;
+ }
+
+ offset = 0;
+ length = cbInput - 1;
+ cbOutput = cbInput;
+ if(pbInput[length] & (1 << 7)) {
+ offset++;
+ cbOutput += offset;
+ }
+
+ pbOutput = (unsigned char *)malloc(cbOutput);
+ if(!pbOutput) {
+ return -1;
+ }
+
+ pbOutput[0] = 0;
+ for(index = 0; ((index + offset) < cbOutput)
+ && (index < cbInput); index++) {
+ pbOutput[index + offset] = pbInput[length - index];
+ }
+
+
+ *ppbOutput = pbOutput;
+ *pcbOutput = cbOutput;
+
+ return 0;
+}
+
+static int
+_libssh2_wincng_asn_decode_bn(unsigned char *pbEncoded,
+ DWORD cbEncoded,
+ unsigned char **ppbDecoded,
+ DWORD *pcbDecoded)
+{
+ unsigned char *pbDecoded = NULL;
+ PCRYPT_DATA_BLOB pbInteger;
+ DWORD cbDecoded = 0, cbInteger;
+ int ret;
+
+ ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded,
+ X509_MULTI_BYTE_UINT,
+ (void *)&pbInteger, &cbInteger);
+ if(!ret) {
+ ret = _libssh2_wincng_bn_ltob(pbInteger->pbData,
+ pbInteger->cbData,
+ &pbDecoded, &cbDecoded);
+ if(!ret) {
+ *ppbDecoded = pbDecoded;
+ *pcbDecoded = cbDecoded;
+ }
+ _libssh2_wincng_safe_free(pbInteger, cbInteger);
+ }
+
+ return ret;
+}
+
+static int
+_libssh2_wincng_asn_decode_bns(unsigned char *pbEncoded,
+ DWORD cbEncoded,
+ unsigned char ***prpbDecoded,
+ DWORD **prcbDecoded,
+ DWORD *pcbCount)
+{
+ PCRYPT_DER_BLOB pBlob;
+ unsigned char **rpbDecoded;
+ PCRYPT_SEQUENCE_OF_ANY pbDecoded;
+ DWORD cbDecoded, *rcbDecoded, index, length;
+ int ret;
+
+ ret = _libssh2_wincng_asn_decode(pbEncoded, cbEncoded,
+ X509_SEQUENCE_OF_ANY,
+ (void *)&pbDecoded, &cbDecoded);
+ if(!ret) {
+ length = pbDecoded->cValue;
+
+ rpbDecoded = malloc(sizeof(PBYTE) * length);
+ if(rpbDecoded) {
+ rcbDecoded = malloc(sizeof(DWORD) * length);
+ if(rcbDecoded) {
+ for(index = 0; index < length; index++) {
+ pBlob = &pbDecoded->rgValue[index];
+ ret = _libssh2_wincng_asn_decode_bn(pBlob->pbData,
+ pBlob->cbData,
+ &rpbDecoded[index],
+ &rcbDecoded[index]);
+ if(ret)
+ break;
+ }
+
+ if(!ret) {
+ *prpbDecoded = rpbDecoded;
+ *prcbDecoded = rcbDecoded;
+ *pcbCount = length;
+ }
+ else {
+ for(length = 0; length < index; length++) {
+ _libssh2_wincng_safe_free(rpbDecoded[length],
+ rcbDecoded[length]);
+ rpbDecoded[length] = NULL;
+ rcbDecoded[length] = 0;
+ }
+ free(rpbDecoded);
+ free(rcbDecoded);
+ }
+ }
+ else {
+ free(rpbDecoded);
+ ret = -1;
+ }
+ }
+ else {
+ ret = -1;
+ }
+
+ _libssh2_wincng_safe_free(pbDecoded, cbDecoded);
+ }
+
+ return ret;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+#if LIBSSH2_RSA || LIBSSH2_DSA
+static ULONG
+_libssh2_wincng_bn_size(const unsigned char *bignum, ULONG length)
+{
+ ULONG offset;
+
+ if(!bignum)
+ return 0;
+
+ length--;
+
+ offset = 0;
+ while(!(*(bignum + offset)) && (offset < length))
+ offset++;
+
+ length++;
+
+ return length - offset;
+}
+#endif
+
+
+#if LIBSSH2_RSA
+/*******************************************************************/
+/*
+ * Windows CNG backend: RSA functions
+ */
+
+int
+_libssh2_wincng_rsa_new(libssh2_rsa_ctx **rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata,
+ unsigned long coefflen)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_RSAKEY_BLOB *rsakey;
+ LPCWSTR lpszBlobType;
+ ULONG keylen, offset, mlen, p1len = 0, p2len = 0;
+ int ret;
+
+ mlen = max(_libssh2_wincng_bn_size(ndata, nlen),
+ _libssh2_wincng_bn_size(ddata, dlen));
+ offset = sizeof(BCRYPT_RSAKEY_BLOB);
+ keylen = offset + elen + mlen;
+ if(ddata && dlen > 0) {
+ p1len = max(_libssh2_wincng_bn_size(pdata, plen),
+ _libssh2_wincng_bn_size(e1data, e1len));
+ p2len = max(_libssh2_wincng_bn_size(qdata, qlen),
+ _libssh2_wincng_bn_size(e2data, e2len));
+ keylen += p1len * 3 + p2len * 2 + mlen;
+ }
+
+ rsakey = (BCRYPT_RSAKEY_BLOB *)malloc(keylen);
+ if(!rsakey) {
+ return -1;
+ }
+
+ memset(rsakey, 0, keylen);
+
+
+ /* https://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */
+ rsakey->BitLength = mlen * 8;
+ rsakey->cbPublicExp = elen;
+ rsakey->cbModulus = mlen;
+
+ memcpy((unsigned char *)rsakey + offset, edata, elen);
+ offset += elen;
+
+ if(nlen < mlen)
+ memcpy((unsigned char *)rsakey + offset + mlen - nlen, ndata, nlen);
+ else
+ memcpy((unsigned char *)rsakey + offset, ndata + nlen - mlen, mlen);
+
+ if(ddata && dlen > 0) {
+ offset += mlen;
+
+ if(plen < p1len)
+ memcpy((unsigned char *)rsakey + offset + p1len - plen,
+ pdata, plen);
+ else
+ memcpy((unsigned char *)rsakey + offset,
+ pdata + plen - p1len, p1len);
+ offset += p1len;
+
+ if(qlen < p2len)
+ memcpy((unsigned char *)rsakey + offset + p2len - qlen,
+ qdata, qlen);
+ else
+ memcpy((unsigned char *)rsakey + offset,
+ qdata + qlen - p2len, p2len);
+ offset += p2len;
+
+ if(e1len < p1len)
+ memcpy((unsigned char *)rsakey + offset + p1len - e1len,
+ e1data, e1len);
+ else
+ memcpy((unsigned char *)rsakey + offset,
+ e1data + e1len - p1len, p1len);
+ offset += p1len;
+
+ if(e2len < p2len)
+ memcpy((unsigned char *)rsakey + offset + p2len - e2len,
+ e2data, e2len);
+ else
+ memcpy((unsigned char *)rsakey + offset,
+ e2data + e2len - p2len, p2len);
+ offset += p2len;
+
+ if(coefflen < p1len)
+ memcpy((unsigned char *)rsakey + offset + p1len - coefflen,
+ coeffdata, coefflen);
+ else
+ memcpy((unsigned char *)rsakey + offset,
+ coeffdata + coefflen - p1len, p1len);
+ offset += p1len;
+
+ if(dlen < mlen)
+ memcpy((unsigned char *)rsakey + offset + mlen - dlen,
+ ddata, dlen);
+ else
+ memcpy((unsigned char *)rsakey + offset,
+ ddata + dlen - mlen, mlen);
+
+ lpszBlobType = BCRYPT_RSAFULLPRIVATE_BLOB;
+ rsakey->Magic = BCRYPT_RSAFULLPRIVATE_MAGIC;
+ rsakey->cbPrime1 = p1len;
+ rsakey->cbPrime2 = p2len;
+ }
+ else {
+ lpszBlobType = BCRYPT_RSAPUBLIC_BLOB;
+ rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC;
+ rsakey->cbPrime1 = 0;
+ rsakey->cbPrime2 = 0;
+ }
+
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL, lpszBlobType,
+ &hKey, (PUCHAR)rsakey, keylen, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(rsakey, keylen);
+ return -1;
+ }
+
+
+ *rsa = malloc(sizeof(libssh2_rsa_ctx));
+ if(!(*rsa)) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(rsakey, keylen);
+ return -1;
+ }
+
+ (*rsa)->hKey = hKey;
+ (*rsa)->pbKeyObject = rsakey;
+ (*rsa)->cbKeyObject = keylen;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBCRYPT32
+static int
+_libssh2_wincng_rsa_new_private_parse(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ unsigned char *pbEncoded,
+ size_t cbEncoded)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ unsigned char *pbStructInfo;
+ DWORD cbStructInfo;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_asn_decode(pbEncoded, (DWORD)cbEncoded,
+ PKCS_RSA_PRIVATE_KEY,
+ &pbStructInfo, &cbStructInfo);
+
+ _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
+
+ if(ret) {
+ return -1;
+ }
+
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL,
+ LEGACY_RSAPRIVATE_BLOB, &hKey,
+ pbStructInfo, cbStructInfo, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo);
+ return -1;
+ }
+
+
+ *rsa = malloc(sizeof(libssh2_rsa_ctx));
+ if(!(*rsa)) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(pbStructInfo, cbStructInfo);
+ return -1;
+ }
+
+ (*rsa)->hKey = hKey;
+ (*rsa)->pbKeyObject = pbStructInfo;
+ (*rsa)->cbKeyObject = cbStructInfo;
+
+ return 0;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+int
+_libssh2_wincng_rsa_new_private(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ size_t cbEncoded;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_load_private(session, filename, passphrase,
+ &pbEncoded, &cbEncoded, 1, 0);
+ if(ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_rsa_new_private_parse(rsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)rsa;
+ (void)filename;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to load RSA key from private key file: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_rsa_new_private_frommemory(libssh2_rsa_ctx **rsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ const unsigned char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ size_t cbEncoded;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len,
+ passphrase,
+ &pbEncoded, &cbEncoded, 1, 0);
+ if(ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_rsa_new_private_parse(rsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)rsa;
+ (void)filedata;
+ (void)filedata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract private key from memory: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+#if LIBSSH2_RSA_SHA1
+int
+_libssh2_wincng_rsa_sha1_verify(libssh2_rsa_ctx *rsa,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len)
+{
+ return _libssh2_wincng_key_sha_verify(rsa, SHA_DIGEST_LENGTH,
+ sig, (ULONG)sig_len,
+ m, (ULONG)m_len,
+ BCRYPT_PAD_PKCS1);
+}
+#endif
+
+#if LIBSSH2_RSA_SHA2
+int
+_libssh2_wincng_rsa_sha2_verify(libssh2_rsa_ctx *rsa,
+ size_t hash_len,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len)
+{
+ return _libssh2_wincng_key_sha_verify(rsa, (ULONG)hash_len,
+ sig, (ULONG)sig_len,
+ m, (ULONG)m_len,
+ BCRYPT_PAD_PKCS1);
+}
+#endif
+
+static int
+_libssh2_wincng_rsa_sha_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ BCRYPT_PKCS1_PADDING_INFO paddingInfo;
+ unsigned char *data, *sig;
+ ULONG cbData, datalen, siglen;
+ NTSTATUS ret;
+
+ if(hash_len == SHA_DIGEST_LENGTH)
+ paddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
+ else if(hash_len == SHA256_DIGEST_LENGTH)
+ paddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;
+ else if(hash_len == SHA384_DIGEST_LENGTH)
+ paddingInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM;
+ else if(hash_len == SHA512_DIGEST_LENGTH)
+ paddingInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM;
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unsupported hash digest length");
+ return -1;
+ }
+
+ datalen = (ULONG)hash_len;
+ data = malloc(datalen);
+ if(!data) {
+ return -1;
+ }
+ memcpy(data, hash, datalen);
+
+ ret = BCryptSignHash(rsa->hKey, &paddingInfo,
+ data, datalen, NULL, 0,
+ &cbData, BCRYPT_PAD_PKCS1);
+ if(BCRYPT_SUCCESS(ret)) {
+ siglen = cbData;
+ sig = LIBSSH2_ALLOC(session, siglen);
+ if(sig) {
+ ret = BCryptSignHash(rsa->hKey, &paddingInfo,
+ data, datalen, sig, siglen,
+ &cbData, BCRYPT_PAD_PKCS1);
+ if(BCRYPT_SUCCESS(ret)) {
+ *signature_len = siglen;
+ *signature = sig;
+ }
+ else {
+ LIBSSH2_FREE(session, sig);
+ }
+ }
+ else
+ ret = (NTSTATUS)STATUS_NO_MEMORY;
+ }
+
+ _libssh2_wincng_safe_free(data, datalen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_rsa_sha1_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ return _libssh2_wincng_rsa_sha_sign(session, rsa,
+ hash, hash_len,
+ signature, signature_len);
+}
+
+int
+_libssh2_wincng_rsa_sha2_sign(LIBSSH2_SESSION *session,
+ libssh2_rsa_ctx *rsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len)
+{
+ return _libssh2_wincng_rsa_sha_sign(session, rsa,
+ hash, hash_len,
+ signature, signature_len);
+}
+
+void
+_libssh2_wincng_rsa_free(libssh2_rsa_ctx *rsa)
+{
+ if(!rsa)
+ return;
+
+ BCryptDestroyKey(rsa->hKey);
+ rsa->hKey = NULL;
+
+ _libssh2_wincng_safe_free(rsa->pbKeyObject, rsa->cbKeyObject);
+ _libssh2_wincng_safe_free(rsa, sizeof(libssh2_rsa_ctx));
+}
+#endif
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: DSA functions
+ */
+
+#if LIBSSH2_DSA
+int
+_libssh2_wincng_dsa_new(libssh2_dsa_ctx **dsa,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *gdata,
+ unsigned long glen,
+ const unsigned char *ydata,
+ unsigned long ylen,
+ const unsigned char *xdata,
+ unsigned long xlen)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_DSA_KEY_BLOB *dsakey;
+ LPCWSTR lpszBlobType;
+ ULONG keylen, offset, length;
+ int ret;
+
+ length = max(max(_libssh2_wincng_bn_size(pdata, plen),
+ _libssh2_wincng_bn_size(gdata, glen)),
+ _libssh2_wincng_bn_size(ydata, ylen));
+ offset = sizeof(BCRYPT_DSA_KEY_BLOB);
+ keylen = offset + length * 3;
+ if(xdata && xlen > 0)
+ keylen += 20;
+
+ dsakey = (BCRYPT_DSA_KEY_BLOB *)malloc(keylen);
+ if(!dsakey) {
+ return -1;
+ }
+
+ memset(dsakey, 0, keylen);
+
+
+ /* https://msdn.microsoft.com/library/windows/desktop/aa833126.aspx */
+ dsakey->cbKey = length;
+
+ memset(dsakey->Count, -1, sizeof(dsakey->Count));
+ memset(dsakey->Seed, -1, sizeof(dsakey->Seed));
+
+ if(qlen < 20)
+ memcpy(dsakey->q + 20 - qlen, qdata, qlen);
+ else
+ memcpy(dsakey->q, qdata + qlen - 20, 20);
+
+ if(plen < length)
+ memcpy((unsigned char *)dsakey + offset + length - plen,
+ pdata, plen);
+ else
+ memcpy((unsigned char *)dsakey + offset,
+ pdata + plen - length, length);
+ offset += length;
+
+ if(glen < length)
+ memcpy((unsigned char *)dsakey + offset + length - glen,
+ gdata, glen);
+ else
+ memcpy((unsigned char *)dsakey + offset,
+ gdata + glen - length, length);
+ offset += length;
+
+ if(ylen < length)
+ memcpy((unsigned char *)dsakey + offset + length - ylen,
+ ydata, ylen);
+ else
+ memcpy((unsigned char *)dsakey + offset,
+ ydata + ylen - length, length);
+
+ if(xdata && xlen > 0) {
+ offset += length;
+
+ if(xlen < 20)
+ memcpy((unsigned char *)dsakey + offset + 20 - xlen, xdata, xlen);
+ else
+ memcpy((unsigned char *)dsakey + offset, xdata + xlen - 20, 20);
+
+ lpszBlobType = BCRYPT_DSA_PRIVATE_BLOB;
+ dsakey->dwMagic = BCRYPT_DSA_PRIVATE_MAGIC;
+ }
+ else {
+ lpszBlobType = BCRYPT_DSA_PUBLIC_BLOB;
+ dsakey->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC;
+ }
+
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgDSA, NULL, lpszBlobType,
+ &hKey, (PUCHAR)dsakey, keylen, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(dsakey, keylen);
+ return -1;
+ }
+
+
+ *dsa = malloc(sizeof(libssh2_dsa_ctx));
+ if(!(*dsa)) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(dsakey, keylen);
+ return -1;
+ }
+
+ (*dsa)->hKey = hKey;
+ (*dsa)->pbKeyObject = dsakey;
+ (*dsa)->cbKeyObject = keylen;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBCRYPT32
+static int
+_libssh2_wincng_dsa_new_private_parse(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ unsigned char *pbEncoded,
+ size_t cbEncoded)
+{
+ unsigned char **rpbDecoded;
+ DWORD *rcbDecoded, index, length;
+ int ret;
+
+ (void)session;
+
+ ret = _libssh2_wincng_asn_decode_bns(pbEncoded, (DWORD)cbEncoded,
+ &rpbDecoded, &rcbDecoded, &length);
+
+ _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
+
+ if(ret) {
+ return -1;
+ }
+
+
+ if(length == 6) {
+ ret = _libssh2_wincng_dsa_new(dsa,
+ rpbDecoded[1], rcbDecoded[1],
+ rpbDecoded[2], rcbDecoded[2],
+ rpbDecoded[3], rcbDecoded[3],
+ rpbDecoded[4], rcbDecoded[4],
+ rpbDecoded[5], rcbDecoded[5]);
+ }
+ else {
+ ret = -1;
+ }
+
+ for(index = 0; index < length; index++) {
+ _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]);
+ rpbDecoded[index] = NULL;
+ rcbDecoded[index] = 0;
+ }
+
+ free(rpbDecoded);
+ free(rcbDecoded);
+
+ return ret;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+int
+_libssh2_wincng_dsa_new_private(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ const char *filename,
+ const unsigned char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ size_t cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private(session, filename, passphrase,
+ &pbEncoded, &cbEncoded, 0, 1);
+ if(ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_dsa_new_private_parse(dsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)dsa;
+ (void)filename;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to load DSA key from private key file: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_dsa_new_private_frommemory(libssh2_dsa_ctx **dsa,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ const unsigned char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ size_t cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private_memory(session, filedata, filedata_len,
+ passphrase,
+ &pbEncoded, &cbEncoded, 0, 1);
+ if(ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_dsa_new_private_parse(dsa, session,
+ pbEncoded, cbEncoded);
+#else
+ (void)dsa;
+ (void)filedata;
+ (void)filedata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract private key from memory: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_dsa_sha1_verify(libssh2_dsa_ctx *dsa,
+ const unsigned char *sig_fixed,
+ const unsigned char *m,
+ size_t m_len)
+{
+ return _libssh2_wincng_key_sha_verify(dsa, SHA_DIGEST_LENGTH, sig_fixed,
+ 40, m, (ULONG)m_len, 0);
+}
+
+int
+_libssh2_wincng_dsa_sha1_sign(libssh2_dsa_ctx *dsa,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char *sig_fixed)
+{
+ unsigned char *data, *sig;
+ ULONG cbData, datalen, siglen;
+ NTSTATUS ret;
+
+ datalen = (ULONG)hash_len;
+ data = malloc(datalen);
+ if(!data) {
+ return -1;
+ }
+
+ memcpy(data, hash, datalen);
+
+ ret = BCryptSignHash(dsa->hKey, NULL, data, datalen,
+ NULL, 0, &cbData, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ siglen = cbData;
+ if(siglen == 40) {
+ sig = malloc(siglen);
+ if(sig) {
+ ret = BCryptSignHash(dsa->hKey, NULL, data, datalen,
+ sig, siglen, &cbData, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ memcpy(sig_fixed, sig, siglen);
+ }
+
+ _libssh2_wincng_safe_free(sig, siglen);
+ }
+ else
+ ret = (NTSTATUS)STATUS_NO_MEMORY;
+ }
+ else
+ ret = (NTSTATUS)STATUS_NO_MEMORY;
+ }
+
+ _libssh2_wincng_safe_free(data, datalen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void
+_libssh2_wincng_dsa_free(libssh2_dsa_ctx *dsa)
+{
+ if(!dsa)
+ return;
+
+ BCryptDestroyKey(dsa->hKey);
+ dsa->hKey = NULL;
+
+ _libssh2_wincng_safe_free(dsa->pbKeyObject, dsa->cbKeyObject);
+ _libssh2_wincng_safe_free(dsa, sizeof(libssh2_dsa_ctx));
+}
+#endif
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: ECDSA helper functions
+ */
+
+#if LIBSSH2_ECDSA
+
+/*
+ * Decode an uncompressed point.
+ */
+static int
+_libssh2_wincng_ecdsa_decode_uncompressed_point(
+ IN const unsigned char *encoded_point,
+ IN size_t encoded_point_len,
+ OUT _libssh2_ecdsa_point *point)
+{
+ unsigned int curve;
+
+ if(!point) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ /* Verify that the point uses uncompressed format */
+ if(encoded_point_len == 0 || encoded_point[0] != 4) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) {
+ if(_wincng_ecdsa_algorithms[curve].point_length ==
+ (encoded_point_len - 1) / 2) {
+
+ point->curve = curve;
+
+ point->x = encoded_point + 1;
+ point->x_len = _wincng_ecdsa_algorithms[curve].point_length;
+
+ point->y = point->x + point->x_len;
+ point->y_len = _wincng_ecdsa_algorithms[curve].point_length;
+
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+
+ return LIBSSH2_ERROR_INVAL;
+}
+
+/*
+ * Create a IEEE P-1363 signature from a point.
+ *
+ * The IEEE P-1363 format is defined as r || s,
+ * where r and s are of the same length.
+ */
+static int
+_libssh2_wincng_p1363signature_from_point(IN const unsigned char *r,
+ IN size_t r_len,
+ IN const unsigned char *s,
+ IN size_t s_len,
+ IN libssh2_curve_type curve,
+ OUT PUCHAR *signature,
+ OUT size_t *signature_length)
+{
+ const unsigned char *r_trimmed;
+ const unsigned char *s_trimmed;
+ size_t r_trimmed_len;
+ size_t s_trimmed_len;
+
+ /* Validate parameters */
+ if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *signature = NULL;
+ *signature_length = (size_t)
+ _wincng_ecdsa_algorithms[curve].point_length * 2;
+
+ /* Trim leading zero, if any */
+ r_trimmed = r;
+ r_trimmed_len = r_len;
+ if(r_len > 0 && r[0] == '\0') {
+ r_trimmed++;
+ r_trimmed_len--;
+ }
+
+ s_trimmed = s;
+ s_trimmed_len = s_len;
+ if(s_len > 0 && s[0] == '\0') {
+ s_trimmed++;
+ s_trimmed_len--;
+ }
+
+ /* Concatenate into zero-filled buffer and zero-pad if necessary */
+ *signature = calloc(1, *signature_length);
+ if(!*signature) {
+ return LIBSSH2_ERROR_ALLOC;
+ }
+
+ memcpy(
+ *signature + (*signature_length / 2) - r_trimmed_len,
+ r_trimmed,
+ r_trimmed_len);
+ memcpy(
+ *signature + (*signature_length) - s_trimmed_len,
+ s_trimmed,
+ s_trimmed_len);
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * Create a CNG public key from an ECC point.
+ */
+static int
+_libssh2_wincng_publickey_from_point(IN _libssh2_wincng_ecc_keytype keytype,
+ IN _libssh2_ecdsa_point *point,
+ OUT BCRYPT_KEY_HANDLE *key)
+{
+
+ int result = LIBSSH2_ERROR_NONE;
+ NTSTATUS status;
+
+ PBCRYPT_ECCKEY_BLOB ecc_blob;
+ size_t ecc_blob_len;
+
+ /* Validate parameters */
+ if(!key) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ if(point->x_len != point->y_len) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *key = NULL;
+
+ /* Initialize a blob to import */
+ ecc_blob_len = sizeof(BCRYPT_ECCKEY_BLOB) + point->x_len + point->y_len;
+ ecc_blob = malloc(ecc_blob_len);
+ if(!ecc_blob) {
+ return LIBSSH2_ERROR_ALLOC;
+ }
+
+ ecc_blob->cbKey = point->x_len;
+ ecc_blob->dwMagic =
+ _wincng_ecdsa_algorithms[point->curve].public_import_magic[keytype];
+
+ /** Copy x, y */
+ memcpy(
+ (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB),
+ point->x,
+ point->x_len);
+ memcpy(
+ (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB) + point->x_len,
+ point->y,
+ point->y_len);
+
+ status = BCryptImportKeyPair(
+ keytype == WINCNG_ECC_KEYTYPE_ECDSA
+ ? _libssh2_wincng.hAlgECDSA[point->curve]
+ : _libssh2_wincng.hAlgECDH[point->curve],
+ NULL,
+ BCRYPT_ECCPUBLIC_BLOB,
+ key,
+ (PUCHAR)ecc_blob,
+ (ULONG)ecc_blob_len,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ result = LIBSSH2_ERROR_NONE;
+
+cleanup:
+ free(ecc_blob);
+ return result;
+}
+
+/*
+ * Create a CNG private key from an ECC point.
+ */
+static int
+_libssh2_wincng_privatekey_from_point(IN _libssh2_wincng_ecc_keytype keytype,
+ IN _libssh2_ecdsa_point *q,
+ IN unsigned char *d,
+ IN size_t d_len,
+ OUT BCRYPT_KEY_HANDLE *key)
+{
+ int result = LIBSSH2_ERROR_NONE;
+ NTSTATUS status;
+
+ PBCRYPT_ECCKEY_BLOB ecc_blob;
+ size_t ecc_blob_len;
+
+ /* Validate parameters */
+ if(!key) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ if(q->x_len != q->y_len) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *key = NULL;
+
+ /* Initialize a blob to import */
+ ecc_blob_len =
+ sizeof(BCRYPT_ECCPRIVATE_BLOB) + q->x_len + q->y_len + d_len;
+ ecc_blob = malloc(ecc_blob_len);
+ if(!ecc_blob) {
+ return LIBSSH2_ERROR_ALLOC;
+ }
+
+ ecc_blob->cbKey = q->x_len;
+ ecc_blob->dwMagic =
+ _wincng_ecdsa_algorithms[q->curve].private_import_magic[keytype];
+
+ /* Copy x, y, d */
+ memcpy(
+ (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB),
+ q->x,
+ q->x_len);
+ memcpy(
+ (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB) + q->x_len,
+ q->y,
+ q->y_len);
+ memcpy(
+ (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB) + q->x_len + q->y_len,
+ d,
+ d_len);
+
+ status = BCryptImportKeyPair(
+ keytype == WINCNG_ECC_KEYTYPE_ECDSA
+ ? _libssh2_wincng.hAlgECDSA[q->curve]
+ : _libssh2_wincng.hAlgECDH[q->curve],
+ NULL,
+ BCRYPT_ECCPRIVATE_BLOB,
+ key,
+ (PUCHAR)ecc_blob,
+ (ULONG)ecc_blob_len,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ result = LIBSSH2_ERROR_NONE;
+
+cleanup:
+ free(ecc_blob);
+ return result;
+}
+
+/*
+ * Get the uncompressed point encoding for a CNG key.
+ */
+static int
+_libssh2_wincng_uncompressed_point_from_publickey(
+ IN LIBSSH2_SESSION *session,
+ IN libssh2_curve_type curve,
+ IN BCRYPT_KEY_HANDLE key,
+ OUT PUCHAR *encoded_point,
+ OUT size_t *encoded_point_len)
+{
+ int result = LIBSSH2_ERROR_NONE;
+ NTSTATUS status;
+
+ PBCRYPT_ECCKEY_BLOB ecc_blob = NULL;
+ ULONG ecc_blob_len;
+ PUCHAR point_x;
+ PUCHAR point_y;
+
+ /* Validate parameters */
+ if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ if(!encoded_point || !encoded_point_len) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *encoded_point = NULL;
+ *encoded_point_len = 0;
+
+ /*
+ * Export point as BCRYPT_ECCKEY_BLOB, a dynamically-sized structure.
+ */
+ status = BCryptExportKey(key,
+ NULL,
+ BCRYPT_ECCPUBLIC_BLOB,
+ NULL,
+ 0,
+ &ecc_blob_len,
+ 0);
+ if(BCRYPT_SUCCESS(status) && ecc_blob_len > 0) {
+ ecc_blob = LIBSSH2_ALLOC(session, ecc_blob_len);
+ if(!ecc_blob) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ status = BCryptExportKey(key,
+ NULL,
+ BCRYPT_ECCPUBLIC_BLOB,
+ (PUCHAR)ecc_blob,
+ ecc_blob_len,
+ &ecc_blob_len,
+ 0);
+ }
+
+ if(!BCRYPT_SUCCESS(status)) {
+ result = _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Decoding the ECC public key failed");
+ goto cleanup;
+ }
+
+ point_x = (PUCHAR)ecc_blob + sizeof(BCRYPT_ECCKEY_BLOB);
+ point_y = (PUCHAR)ecc_blob + ecc_blob->cbKey + sizeof(BCRYPT_ECCKEY_BLOB);
+
+ /*
+ * Create uncompressed point, which needs to look like the following:
+ *
+ * struct uncompressed_point {
+ * UCHAR tag = 4; // uncompressed
+ * PUCHAR[size] x;
+ * PUCHAR[size] y;
+ * }
+ */
+
+ *encoded_point_len = (size_t)ecc_blob->cbKey * 2 + 1;
+ *encoded_point = LIBSSH2_ALLOC(session, *encoded_point_len);
+ if(!*encoded_point) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ **encoded_point = 4; /* Uncompressed tag */
+ memcpy((*encoded_point) + 1, point_x, ecc_blob->cbKey);
+ memcpy((*encoded_point) + 1 + ecc_blob->cbKey, point_y, ecc_blob->cbKey);
+
+cleanup:
+ if(ecc_blob) {
+ LIBSSH2_FREE(session, ecc_blob);
+ }
+
+ return result;
+}
+
+static void
+_libssh_wincng_reverse_bytes(IN PUCHAR buffer,
+ IN size_t buffer_len)
+{
+ PUCHAR start = buffer;
+ PUCHAR end = buffer + buffer_len - 1;
+ while(start < end) {
+ unsigned char tmp = *end;
+ *end = *start;
+ *start = tmp;
+ start++;
+ end--;
+ }
+}
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: ECDSA functions
+ */
+
+void
+_libssh2_wincng_ecdsa_free(IN _libssh2_wincng_ecdsa_key *key)
+{
+ if(!key) {
+ return;
+ }
+
+ (void)BCryptDestroyKey(key->handle);
+ free(key);
+}
+
+
+/*
+ * _libssh2_ecdsa_create_key
+ *
+ * Creates a local private ECDH key based on input curve
+ * and returns the public key in uncompressed point encoding.
+ */
+
+int
+_libssh2_wincng_ecdh_create_key(IN LIBSSH2_SESSION *session,
+ OUT _libssh2_wincng_ecdsa_key **privatekey,
+ OUT unsigned char **encoded_publickey,
+ OUT size_t *encoded_publickey_len,
+ IN libssh2_curve_type curve)
+{
+ int result = LIBSSH2_ERROR_NONE;
+ NTSTATUS status;
+
+ BCRYPT_KEY_HANDLE key_handle = NULL;
+
+ /* Validate parameters */
+ if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ if(!_libssh2_wincng.hAlgECDH[curve]) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ if(!privatekey || !encoded_publickey || !encoded_publickey_len) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *privatekey = NULL;
+ *encoded_publickey = NULL;
+ *encoded_publickey_len = 0;
+
+ /* Create an ECDH key pair using the requested curve */
+ status = BCryptGenerateKeyPair(
+ _libssh2_wincng.hAlgECDH[curve],
+ &key_handle,
+ _wincng_ecdsa_algorithms[curve].key_length,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = _libssh2_error(
+ session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Creating ECC key pair failed");
+ goto cleanup;
+ }
+
+ status = BCryptFinalizeKeyPair(key_handle, 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = _libssh2_error(
+ session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Creating ECDH key pair failed");
+ goto cleanup;
+ }
+
+ result = _libssh2_wincng_uncompressed_point_from_publickey(
+ session,
+ curve,
+ key_handle,
+ encoded_publickey,
+ encoded_publickey_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ result = _libssh2_error(
+ session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Exporting ECDH key pair failed");
+ }
+
+ *privatekey = malloc(sizeof(_libssh2_wincng_ecdsa_key));
+ if(!*privatekey) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ (*privatekey)->curve = curve;
+ (*privatekey)->handle = key_handle;
+
+cleanup:
+ if(result != LIBSSH2_ERROR_NONE && key_handle) {
+ (void)BCryptDestroyKey(key_handle);
+ }
+
+ if(result != LIBSSH2_ERROR_NONE && *privatekey) {
+ free(*privatekey);
+ }
+
+ return result;
+}
+
+/*
+ * _libssh2_ecdsa_curve_name_with_octal_new
+ *
+ * Creates an ECDSA public key from an uncompressed point.
+ */
+
+int
+_libssh2_wincng_ecdsa_curve_name_with_octal_new(
+ OUT _libssh2_wincng_ecdsa_key **key,
+ IN const unsigned char *publickey_encoded,
+ IN size_t publickey_encoded_len,
+ IN libssh2_curve_type curve)
+{
+ int result = LIBSSH2_ERROR_NONE;
+
+ BCRYPT_KEY_HANDLE publickey_handle;
+ _libssh2_ecdsa_point publickey;
+
+ /* Validate parameters */
+ if(curve >= ARRAY_SIZE(_wincng_ecdsa_algorithms)) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ if(!key) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *key = NULL;
+
+ result = _libssh2_wincng_ecdsa_decode_uncompressed_point(
+ publickey_encoded,
+ publickey_encoded_len,
+ &publickey);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ result = _libssh2_wincng_publickey_from_point(
+ WINCNG_ECC_KEYTYPE_ECDSA,
+ &publickey,
+ &publickey_handle);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ *key = malloc(sizeof(_libssh2_wincng_ecdsa_key));
+ if(!*key) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ (*key)->handle = publickey_handle;
+ (*key)->curve = curve;
+
+cleanup:
+
+ return result;
+}
+
+/*
+ * _libssh2_ecdh_gen_k
+ *
+ * Computes the shared secret K given a local private key,
+ * remote public key and length
+ */
+
+int
+_libssh2_wincng_ecdh_gen_k(OUT _libssh2_bn **secret,
+ IN _libssh2_wincng_ecdsa_key *privatekey,
+ IN const unsigned char *server_publickey_encoded,
+ IN size_t server_publickey_encoded_len)
+{
+ int result = LIBSSH2_ERROR_NONE;
+ NTSTATUS status;
+
+ BCRYPT_KEY_HANDLE publickey_handle;
+ BCRYPT_SECRET_HANDLE agreed_secret_handle = NULL;
+ ULONG secret_len;
+ _libssh2_ecdsa_point server_publickey;
+
+ /* Validate parameters */
+ if(!secret) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *secret = NULL;
+
+ /* Decode the public key */
+ result = _libssh2_wincng_ecdsa_decode_uncompressed_point(
+ server_publickey_encoded,
+ server_publickey_encoded_len,
+ &server_publickey);
+ if(result != LIBSSH2_ERROR_NONE) {
+ return result;
+ }
+
+ result = _libssh2_wincng_publickey_from_point(
+ WINCNG_ECC_KEYTYPE_ECDH,
+ &server_publickey,
+ &publickey_handle);
+ if(result != LIBSSH2_ERROR_NONE) {
+ return result;
+ }
+
+ /* Establish the shared secret between ourselves and the peer */
+ status = BCryptSecretAgreement(
+ privatekey->handle,
+ publickey_handle,
+ &agreed_secret_handle,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ /* Compute the size of the buffer that is needed to hold the derived
+ * shared secret.
+ *
+ * NB. The use of BCRYPT_KDF_RAW_SECRET requires Windows 10 or newer.
+ * On older versions, the BCryptDeriveKey returns STATUS_NOT_SUPPORTED.
+ */
+ status = BCryptDeriveKey(
+ agreed_secret_handle,
+ BCRYPT_KDF_RAW_SECRET,
+ NULL,
+ NULL,
+ 0,
+ &secret_len,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ /* Allocate a secret bignum to be ready to receive the derived secret */
+ *secret = _libssh2_wincng_bignum_init();
+ if(!*secret) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ if(_libssh2_wincng_bignum_resize(*secret, secret_len)) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ /* And populate the secret bignum */
+ status = BCryptDeriveKey(
+ agreed_secret_handle,
+ BCRYPT_KDF_RAW_SECRET,
+ NULL,
+ (*secret)->bignum,
+ secret_len,
+ &secret_len,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ /* BCRYPT_KDF_RAW_SECRET returns the little-endian representation of the
+ * raw secret, so we need to swap it to big endian order.
+ */
+
+ _libssh_wincng_reverse_bytes((*secret)->bignum, secret_len);
+
+ result = LIBSSH2_ERROR_NONE;
+
+cleanup:
+ if(result != LIBSSH2_ERROR_NONE && agreed_secret_handle) {
+ _libssh2_wincng_bignum_free(*secret);
+ }
+
+ if(result != LIBSSH2_ERROR_NONE && agreed_secret_handle) {
+ BCryptDestroySecret(agreed_secret_handle);
+ }
+
+ return result;
+}
+
+/*
+ * _libssh2_ecdsa_curve_type_from_name
+ *
+ */
+int
+_libssh2_wincng_ecdsa_curve_type_from_name(IN const char *name,
+ OUT libssh2_curve_type *out_curve)
+{
+ unsigned int curve;
+
+ /* Validate parameters */
+ if(!out_curve) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ for(curve = 0; curve < ARRAY_SIZE(_wincng_ecdsa_algorithms); curve++) {
+ if(strcmp(name, _wincng_ecdsa_algorithms[curve].name) == 0) {
+ *out_curve = curve;
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+
+ return LIBSSH2_ERROR_INVAL;
+}
+
+/*
+ * _libssh2_ecdsa_verify
+ *
+ * Verifies the ECDSA signature of a hashed message
+ *
+ */
+
+int
+_libssh2_wincng_ecdsa_verify(IN _libssh2_wincng_ecdsa_key *key,
+ IN const unsigned char *r,
+ IN size_t r_len,
+ IN const unsigned char *s,
+ IN size_t s_len,
+ IN const unsigned char *m,
+ IN size_t m_len)
+{
+ int result = LIBSSH2_ERROR_NONE;
+ NTSTATUS status;
+
+ PUCHAR signature_p1363 = NULL;
+ size_t signature_p1363_len;
+ ULONG hash_len;
+ PUCHAR hash = NULL;
+ BCRYPT_ALG_HANDLE hash_alg;
+
+ /* CNG expects signatures in IEEE P-1363 format. */
+ result = _libssh2_wincng_p1363signature_from_point(
+ r,
+ r_len,
+ s,
+ s_len,
+ _libssh2_wincng_ecdsa_get_curve_type(key),
+ &signature_p1363,
+ &signature_p1363_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Create hash over m */
+ switch(_libssh2_wincng_ecdsa_get_curve_type(key)) {
+ case LIBSSH2_EC_CURVE_NISTP256:
+ hash_len = 256/8;
+ hash_alg = _libssh2_wincng.hAlgHashSHA256;
+ break;
+
+ case LIBSSH2_EC_CURVE_NISTP384:
+ hash_len = 384/8;
+ hash_alg = _libssh2_wincng.hAlgHashSHA384;
+ break;
+
+ case LIBSSH2_EC_CURVE_NISTP521:
+ hash_len = 512/8;
+ hash_alg = _libssh2_wincng.hAlgHashSHA512;
+ break;
+
+ default:
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ hash = malloc(hash_len);
+ result = _libssh2_wincng_hash(
+ m,
+ (ULONG)m_len,
+ hash_alg,
+ hash,
+ hash_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Verify signature over hash */
+ status = BCryptVerifySignature(
+ key->handle,
+ NULL,
+ hash,
+ hash_len,
+ signature_p1363,
+ (ULONG)signature_p1363_len,
+ 0);
+
+ if(status == STATUS_INVALID_SIGNATURE) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+ else if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ result = LIBSSH2_ERROR_NONE;
+
+cleanup:
+ if(hash) {
+ free(hash);
+ }
+
+ if(signature_p1363) {
+ free(signature_p1363);
+ }
+
+ return result;
+}
+
+/*
+ *_libssh2_ecdsa_new_private
+ *
+ * Creates a new private key given a file path and password
+ *
+ */
+
+int
+_libssh2_wincng_ecdsa_new_private(OUT _libssh2_wincng_ecdsa_key **key,
+ IN LIBSSH2_SESSION *session,
+ IN const char *filename,
+ IN const unsigned char *passphrase)
+{
+ int result;
+
+ FILE *file_handle = NULL;
+ unsigned char *data = NULL;
+ size_t datalen = 0;
+
+ /* Validate parameters */
+ if(!key || !session || !filename) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *key = NULL;
+
+ if(passphrase && strlen((const char *)passphrase) > 0) {
+ return _libssh2_error(
+ session,
+ LIBSSH2_ERROR_INVAL,
+ "Passphrase-protected ECDSA private key files are unsupported");
+ }
+
+ file_handle = fopen(filename, FOPEN_READTEXT);
+ if(!file_handle) {
+ result = _libssh2_error(
+ session,
+ LIBSSH2_ERROR_INVAL,
+ "Opening the private key file failed");
+ goto cleanup;
+ }
+
+ result = _libssh2_pem_parse(session,
+ PEM_ECDSA_HEADER,
+ PEM_ECDSA_FOOTER,
+ passphrase,
+ file_handle,
+ &data,
+ &datalen);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ result = _libssh2_wincng_ecdsa_new_private_frommemory(
+ key,
+ session,
+ (const char *)data,
+ datalen,
+ passphrase);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+cleanup:
+ if(file_handle) {
+ fclose(file_handle);
+ }
+
+ if(data) {
+ LIBSSH2_FREE(session, data);
+ }
+
+ return result;
+}
+
+int
+_libssh2_wincng_parse_ecdsa_privatekey(OUT _libssh2_wincng_ecdsa_key **key,
+ IN unsigned char *privatekey,
+ IN size_t privatekey_len)
+{
+ char *keytype = NULL;
+ size_t keytype_len;
+
+ unsigned char *ignore;
+ size_t ignore_len;
+
+ unsigned char *publickey;
+ size_t publickey_len;
+
+ libssh2_curve_type curve_type;
+ int result;
+ uint32_t check1, check2;
+ struct string_buf data_buffer;
+
+ _libssh2_ecdsa_point q;
+ unsigned char *d;
+ size_t d_len;
+
+ BCRYPT_KEY_HANDLE key_handle = NULL;
+
+ *key = NULL;
+
+ data_buffer.data = privatekey;
+ data_buffer.dataptr = privatekey;
+ data_buffer.len = privatekey_len;
+
+ /* Read the 2 checkints and check that they match */
+ result = _libssh2_get_u32(&data_buffer, &check1);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ result = _libssh2_get_u32(&data_buffer, &check2);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ if(check1 != check2) {
+ result = LIBSSH2_ERROR_FILE;
+ goto cleanup;
+ }
+
+ /* What follows is a key as defined in */
+ /* draft-miller-ssh-agent, section-3.2.2 */
+
+ /* Read the key type */
+ result = _libssh2_get_string(&data_buffer,
+ (unsigned char **)&keytype,
+ &keytype_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ result = _libssh2_wincng_ecdsa_curve_type_from_name(keytype, &curve_type);
+ if(result < 0) {
+ goto cleanup;
+ }
+
+ /* Read the curve */
+ result = _libssh2_get_string(&data_buffer, &ignore, &ignore_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Read Q */
+ result = _libssh2_get_string(&data_buffer, &publickey, &publickey_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ result = _libssh2_wincng_ecdsa_decode_uncompressed_point(
+ publickey,
+ publickey_len,
+ &q);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Read d */
+ result = _libssh2_get_bignum_bytes(&data_buffer, &d, &d_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Ignore the rest (comment, etc) */
+
+ /* Use Q and d to create a key handle */
+ result = _libssh2_wincng_privatekey_from_point(
+ WINCNG_ECC_KEYTYPE_ECDSA,
+ &q,
+ d,
+ d_len,
+ &key_handle);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ *key = malloc(sizeof(_libssh2_wincng_ecdsa_key));
+ if(!*key) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ (*key)->curve = q.curve;
+ (*key)->handle = key_handle;
+
+ result = LIBSSH2_ERROR_NONE;
+
+cleanup:
+ if(result != LIBSSH2_ERROR_NONE && key_handle) {
+ (void)BCryptDestroyKey(key_handle);
+ }
+
+ return result;
+}
+
+/*
+ * _libssh2_ecdsa_new_private
+ *
+ * Creates a new private key given a file data and password.
+ * ECDSA private key files use the decoding defined in PROTOCOL.key
+ * in the OpenSSL source tree.
+ */
+int
+_libssh2_wincng_ecdsa_new_private_frommemory(
+ OUT _libssh2_wincng_ecdsa_key **key,
+ IN LIBSSH2_SESSION *session,
+ IN const char *data,
+ IN size_t data_len,
+ IN const unsigned char *passphrase)
+{
+ int result;
+
+ struct string_buf data_buffer;
+ uint32_t index;
+ uint32_t key_count;
+ unsigned char *privatekey;
+ size_t privatekey_len;
+
+ /* Validate parameters */
+ if(!key || !session || !data) {
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ *key = NULL;
+
+ if(passphrase && strlen((const char *)passphrase) > 0) {
+ return _libssh2_error(
+ session,
+ LIBSSH2_ERROR_INVAL,
+ "Passphrase-protected ECDSA private key files are unsupported");
+ }
+
+ /* Read OPENSSL_PRIVATEKEY_AUTH_MAGIC */
+ if(strncmp(data, OPENSSL_PRIVATEKEY_AUTH_MAGIC, data_len) != 0) {
+ result = -1;
+ goto cleanup;
+ }
+
+ data_buffer.len = data_len;
+ data_buffer.data = (unsigned char *)data;
+ data_buffer.dataptr =
+ (unsigned char *)data + strlen(OPENSSL_PRIVATEKEY_AUTH_MAGIC) + 1;
+
+ /* Read ciphername, should be 'none' as we don't support passphrases */
+ result = _libssh2_match_string(&data_buffer, "none");
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Read kdfname, should be 'none' as we don't support passphrases */
+ result = _libssh2_match_string(&data_buffer, "none");
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Read kdfoptions, should be empty */
+ result = _libssh2_match_string(&data_buffer, "");
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ /* Read number of keys N */
+ result = _libssh2_get_u32(&data_buffer, &key_count);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ if(key_count == 0) {
+ result = LIBSSH2_ERROR_FILE;
+ goto cleanup;
+ }
+
+ /* Skip all public keys */
+ for(index = 0; index < key_count; index++) {
+ unsigned char *publickey;
+ size_t publickey_len;
+
+ result = _libssh2_get_string(&data_buffer, &publickey, &publickey_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+ }
+
+ /* Read first private key */
+ result = _libssh2_get_string(&data_buffer, &privatekey, &privatekey_len);
+ if(result != LIBSSH2_ERROR_NONE) {
+ goto cleanup;
+ }
+
+ result = _libssh2_wincng_parse_ecdsa_privatekey(
+ key,
+ privatekey,
+ privatekey_len);
+
+cleanup:
+ if(result != LIBSSH2_ERROR_NONE) {
+ return _libssh2_error(
+ session,
+ result,
+ "The key is malformed");
+ }
+
+ return result;
+}
+
+/*
+ * _libssh2_ecdsa_sign
+ *
+ * Computes the ECDSA signature of a previously-hashed message
+ *
+ */
+
+int
+_libssh2_wincng_ecdsa_sign(IN LIBSSH2_SESSION *session,
+ IN _libssh2_wincng_ecdsa_key *key,
+ IN const unsigned char *hash,
+ IN size_t hash_len,
+ OUT unsigned char **signature,
+ OUT size_t *signature_len)
+{
+ NTSTATUS status;
+ int result = LIBSSH2_ERROR_NONE;
+
+ unsigned char *hash_buffer;
+
+ unsigned char *cng_signature = NULL;
+ ULONG cng_signature_len;
+
+ ULONG signature_maxlen;
+ unsigned char *signature_ptr;
+
+ *signature = NULL;
+ *signature_len = 0;
+
+ /* CNG expects a mutable buffer */
+ hash_buffer = malloc(hash_len);
+ if(!hash_buffer) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ memcpy(hash_buffer, hash, hash_len);
+
+ status = BCryptSignHash(
+ key->handle,
+ NULL,
+ hash_buffer,
+ (ULONG)hash_len,
+ NULL,
+ 0,
+ &cng_signature_len,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ cng_signature = malloc(cng_signature_len);
+ if(!cng_signature) {
+ result = LIBSSH2_ERROR_ALLOC;
+ goto cleanup;
+ }
+
+ status = BCryptSignHash(
+ key->handle,
+ NULL,
+ hash_buffer,
+ (ULONG)hash_len,
+ cng_signature,
+ cng_signature_len,
+ &cng_signature_len,
+ 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ result = LIBSSH2_ERROR_PUBLICKEY_PROTOCOL;
+ goto cleanup;
+ }
+
+ /*
+ cng_signature is in IEEE P-1163 format: r || s.
+ Convert to ecdsa_signature_blob: mpint(r) || mpint(s)
+ */
+
+ signature_maxlen =
+ cng_signature_len / 2 + 5 + /* mpint(r) */
+ cng_signature_len / 2 + 5; /* mpint(s) */
+
+ *signature = LIBSSH2_ALLOC(session, signature_maxlen);
+ signature_ptr = *signature;
+
+ _libssh2_store_bignum2_bytes(
+ &signature_ptr,
+ cng_signature,
+ cng_signature_len / 2);
+
+ _libssh2_store_bignum2_bytes(
+ &signature_ptr,
+ cng_signature + (cng_signature_len / 2),
+ cng_signature_len / 2);
+
+ *signature_len = signature_ptr - *signature;
+
+cleanup:
+ if(cng_signature) {
+ free(cng_signature);
+ }
+
+ if(hash_buffer) {
+ free(hash_buffer);
+ }
+
+ return result;
+}
+
+/*
+ * _libssh2_ecdsa_get_curve_type
+ *
+ * returns key curve type that maps to libssh2_curve_type
+ *
+ */
+
+libssh2_curve_type
+_libssh2_wincng_ecdsa_get_curve_type(IN _libssh2_wincng_ecdsa_key *key)
+{
+ return key->curve;
+}
+
+#endif
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Key functions
+ */
+
+#ifdef HAVE_LIBCRYPT32
+static DWORD
+_libssh2_wincng_pub_priv_write(unsigned char *key,
+ DWORD offset,
+ const unsigned char *bignum,
+ const DWORD length)
+{
+ _libssh2_htonu32(key + offset, length);
+ offset += 4;
+
+ memcpy(key + offset, bignum, length);
+ offset += length;
+
+ return offset;
+}
+
+static int
+_libssh2_wincng_pub_priv_keyfile_parse(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ unsigned char *pbEncoded,
+ size_t cbEncoded)
+{
+ unsigned char **rpbDecoded = NULL;
+ DWORD *rcbDecoded = NULL;
+ unsigned char *key = NULL, *mth = NULL;
+ DWORD keylen = 0, mthlen = 0;
+ DWORD index, offset, length = 0;
+ int ret;
+
+ ret = _libssh2_wincng_asn_decode_bns(pbEncoded, (DWORD)cbEncoded,
+ &rpbDecoded, &rcbDecoded, &length);
+
+ _libssh2_wincng_safe_free(pbEncoded, cbEncoded);
+
+ if(ret) {
+ return -1;
+ }
+
+
+ if(length == 9) { /* private RSA key */
+ mthlen = 7;
+ mth = LIBSSH2_ALLOC(session, mthlen);
+ if(mth) {
+ memcpy(mth, "ssh-rsa", mthlen);
+ }
+ else {
+ ret = -1;
+ }
+
+
+ keylen = 4 + mthlen + 4 + rcbDecoded[2] + 4 + rcbDecoded[1];
+ key = LIBSSH2_ALLOC(session, keylen);
+ if(key) {
+ offset = _libssh2_wincng_pub_priv_write(key, 0, mth, mthlen);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[2],
+ rcbDecoded[2]);
+
+ _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[1],
+ rcbDecoded[1]);
+ }
+ else {
+ ret = -1;
+ }
+
+ }
+ else if(length == 6) { /* private DSA key */
+ mthlen = 7;
+ mth = LIBSSH2_ALLOC(session, mthlen);
+ if(mth) {
+ memcpy(mth, "ssh-dss", mthlen);
+ }
+ else {
+ ret = -1;
+ }
+
+ keylen = 4 + mthlen + 4 + rcbDecoded[1] + 4 + rcbDecoded[2]
+ + 4 + rcbDecoded[3] + 4 + rcbDecoded[4];
+ key = LIBSSH2_ALLOC(session, keylen);
+ if(key) {
+ offset = _libssh2_wincng_pub_priv_write(key, 0, mth, mthlen);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[1],
+ rcbDecoded[1]);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[2],
+ rcbDecoded[2]);
+
+ offset = _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[3],
+ rcbDecoded[3]);
+
+ _libssh2_wincng_pub_priv_write(key, offset,
+ rpbDecoded[4],
+ rcbDecoded[4]);
+ }
+ else {
+ ret = -1;
+ }
+
+ }
+ else {
+ ret = -1;
+ }
+
+
+ for(index = 0; index < length; index++) {
+ _libssh2_wincng_safe_free(rpbDecoded[index], rcbDecoded[index]);
+ rpbDecoded[index] = NULL;
+ rcbDecoded[index] = 0;
+ }
+
+ free(rpbDecoded);
+ free(rcbDecoded);
+
+
+ if(ret) {
+ if(mth)
+ LIBSSH2_FREE(session, mth);
+ if(key)
+ LIBSSH2_FREE(session, key);
+ }
+ else {
+ *method = mth;
+ *method_len = mthlen;
+ *pubkeydata = key;
+ *pubkeydata_len = keylen;
+ }
+
+ return ret;
+}
+#endif /* HAVE_LIBCRYPT32 */
+
+int
+_libssh2_wincng_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ size_t cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private(session, privatekey,
+ (const unsigned char *)passphrase,
+ &pbEncoded, &cbEncoded, 1, 1);
+ if(ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_pub_priv_keyfile_parse(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pbEncoded, cbEncoded);
+#else
+ (void)method;
+ (void)method_len;
+ (void)pubkeydata;
+ (void)pubkeydata_len;
+ (void)privatekey;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to load public key from private key file: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+#ifdef HAVE_LIBCRYPT32
+ unsigned char *pbEncoded;
+ size_t cbEncoded;
+ int ret;
+
+ ret = _libssh2_wincng_load_private_memory(session, privatekeydata,
+ privatekeydata_len,
+ (const unsigned char *)
+ passphrase,
+ &pbEncoded, &cbEncoded, 1, 1);
+ if(ret) {
+ return -1;
+ }
+
+ return _libssh2_wincng_pub_priv_keyfile_parse(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pbEncoded, cbEncoded);
+#else
+ (void)method;
+ (void)method_len;
+ (void)pubkeydata_len;
+ (void)pubkeydata;
+ (void)privatekeydata;
+ (void)privatekeydata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unable to extract public key from private key in memory: "
+ "Method unsupported in Windows CNG backend");
+#endif /* HAVE_LIBCRYPT32 */
+}
+
+int
+_libssh2_wincng_sk_pub_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ int *algorithm,
+ unsigned char *flags,
+ const char **application,
+ const unsigned char **key_handle,
+ size_t *handle_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ (void)method;
+ (void)method_len;
+ (void)pubkeydata;
+ (void)pubkeydata_len;
+ (void)algorithm;
+ (void)flags;
+ (void)application;
+ (void)key_handle;
+ (void)handle_len;
+ (void)privatekeydata;
+ (void)privatekeydata_len;
+ (void)passphrase;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public SK key from private key file: "
+ "Method unimplemented in Windows CNG backend");
+}
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Cipher functions
+ */
+
+int
+_libssh2_wincng_cipher_init(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ unsigned char *iv,
+ unsigned char *secret,
+ int encrypt)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_KEY_DATA_BLOB_HEADER *header;
+ unsigned char *pbKeyObject, *pbIV, *pbCtr, *pbIVCopy;
+ ULONG dwKeyObject, dwIV, dwCtrLength, dwBlockLength, cbData, keylen;
+ int ret;
+
+ (void)encrypt;
+
+ ret = BCryptGetProperty(*type.phAlg, BCRYPT_OBJECT_LENGTH,
+ (unsigned char *)&dwKeyObject,
+ sizeof(dwKeyObject),
+ &cbData, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ return -1;
+ }
+
+ ret = BCryptGetProperty(*type.phAlg, BCRYPT_BLOCK_LENGTH,
+ (unsigned char *)&dwBlockLength,
+ sizeof(dwBlockLength),
+ &cbData, 0);
+ if(!BCRYPT_SUCCESS(ret)) {
+ return -1;
+ }
+
+ pbKeyObject = malloc(dwKeyObject);
+ if(!pbKeyObject) {
+ return -1;
+ }
+
+
+ keylen = (ULONG)sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) +
+ type.dwKeyLength;
+ header = (BCRYPT_KEY_DATA_BLOB_HEADER *)malloc(keylen);
+ if(!header) {
+ free(pbKeyObject);
+ return -1;
+ }
+
+
+ header->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
+ header->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
+ header->cbKeyData = type.dwKeyLength;
+
+ memcpy((unsigned char *)header + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER),
+ secret, type.dwKeyLength);
+
+ ret = BCryptImportKey(*type.phAlg, NULL, BCRYPT_KEY_DATA_BLOB, &hKey,
+ pbKeyObject, dwKeyObject,
+ (PUCHAR)header, keylen, 0);
+
+ _libssh2_wincng_safe_free(header, keylen);
+
+ if(!BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject);
+ return -1;
+ }
+
+ pbIV = NULL;
+ pbCtr = NULL;
+ dwIV = 0;
+ dwCtrLength = 0;
+
+ if(type.useIV || type.ctrMode) {
+ pbIVCopy = malloc(dwBlockLength);
+ if(!pbIVCopy) {
+ BCryptDestroyKey(hKey);
+ _libssh2_wincng_safe_free(pbKeyObject, dwKeyObject);
+ return -1;
+ }
+ memcpy(pbIVCopy, iv, dwBlockLength);
+
+ if(type.ctrMode) {
+ pbCtr = pbIVCopy;
+ dwCtrLength = dwBlockLength;
+ }
+ else if(type.useIV) {
+ pbIV = pbIVCopy;
+ dwIV = dwBlockLength;
+ }
+ }
+
+ ctx->hKey = hKey;
+ ctx->pbKeyObject = pbKeyObject;
+ ctx->pbIV = pbIV;
+ ctx->pbCtr = pbCtr;
+ ctx->dwKeyObject = dwKeyObject;
+ ctx->dwIV = dwIV;
+ ctx->dwBlockLength = dwBlockLength;
+ ctx->dwCtrLength = dwCtrLength;
+
+ return 0;
+}
+
+int
+_libssh2_wincng_cipher_crypt(_libssh2_cipher_ctx *ctx,
+ _libssh2_cipher_type(type),
+ int encrypt,
+ unsigned char *block,
+ size_t blocklen, int firstlast)
+{
+ unsigned char *pbOutput, *pbInput;
+ ULONG cbOutput, cbInput;
+ NTSTATUS ret;
+
+ (void)type;
+ (void)firstlast;
+
+ cbInput = (ULONG)blocklen;
+
+ if(type.ctrMode) {
+ pbInput = ctx->pbCtr;
+ }
+ else {
+ pbInput = block;
+ }
+
+ if(encrypt || type.ctrMode) {
+ ret = BCryptEncrypt(ctx->hKey, pbInput, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0);
+ }
+ else {
+ ret = BCryptDecrypt(ctx->hKey, pbInput, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV, NULL, 0, &cbOutput, 0);
+ }
+ if(BCRYPT_SUCCESS(ret)) {
+ pbOutput = malloc(cbOutput);
+ if(pbOutput) {
+ if(encrypt || type.ctrMode) {
+ ret = BCryptEncrypt(ctx->hKey, pbInput, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV,
+ pbOutput, cbOutput, &cbOutput, 0);
+ }
+ else {
+ ret = BCryptDecrypt(ctx->hKey, pbInput, cbInput, NULL,
+ ctx->pbIV, ctx->dwIV,
+ pbOutput, cbOutput, &cbOutput, 0);
+ }
+ if(BCRYPT_SUCCESS(ret)) {
+ if(type.ctrMode) {
+ _libssh2_xor_data(block, block, pbOutput, blocklen);
+ _libssh2_aes_ctr_increment(ctx->pbCtr, ctx->dwCtrLength);
+ }
+ else {
+ memcpy(block, pbOutput, cbOutput);
+ }
+ }
+
+ _libssh2_wincng_safe_free(pbOutput, cbOutput);
+ }
+ else
+ ret = (NTSTATUS)STATUS_NO_MEMORY;
+ }
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void
+_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx)
+{
+ BCryptDestroyKey(ctx->hKey);
+ ctx->hKey = NULL;
+
+ _libssh2_wincng_safe_free(ctx->pbKeyObject, ctx->dwKeyObject);
+ ctx->pbKeyObject = NULL;
+ ctx->dwKeyObject = 0;
+
+ _libssh2_wincng_safe_free(ctx->pbIV, ctx->dwBlockLength);
+ ctx->pbIV = NULL;
+ ctx->dwBlockLength = 0;
+
+ _libssh2_wincng_safe_free(ctx->pbCtr, ctx->dwCtrLength);
+ ctx->pbCtr = NULL;
+ ctx->dwCtrLength = 0;
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: BigNumber functions
+ */
+
+_libssh2_bn *
+_libssh2_wincng_bignum_init(void)
+{
+ _libssh2_bn *bignum;
+
+ bignum = (_libssh2_bn *)malloc(sizeof(_libssh2_bn));
+ if(bignum) {
+ bignum->bignum = NULL;
+ bignum->length = 0;
+ }
+
+ return bignum;
+}
+
+static int
+_libssh2_wincng_bignum_resize(_libssh2_bn *bn, ULONG length)
+{
+ unsigned char *bignum;
+
+ if(!bn)
+ return -1;
+
+ if(length == bn->length)
+ return 0;
+
+ if(bn->bignum && bn->length > 0 && length < bn->length) {
+ _libssh2_explicit_zero(bn->bignum + length, bn->length - length);
+ }
+
+ bignum = realloc(bn->bignum, length);
+ if(!bignum)
+ return -1;
+
+ bn->bignum = bignum;
+ bn->length = length;
+
+ return 0;
+}
+
+static int
+_libssh2_wincng_bignum_rand(_libssh2_bn *rnd, int bits, int top, int bottom)
+{
+ unsigned char *bignum;
+ ULONG length;
+
+ if(!rnd)
+ return -1;
+
+ length = (ULONG) (ceil(((double)bits) / 8.0) * sizeof(unsigned char));
+ if(_libssh2_wincng_bignum_resize(rnd, length))
+ return -1;
+
+ bignum = rnd->bignum;
+
+ if(_libssh2_wincng_random(bignum, length))
+ return -1;
+
+ /* calculate significant bits in most significant byte */
+ bits %= 8;
+ if(bits == 0)
+ bits = 8;
+
+ /* fill most significant byte with zero padding */
+ bignum[0] &= (unsigned char)((1 << bits) - 1);
+
+ /* set most significant bits in most significant byte */
+ if(top == 0)
+ bignum[0] |= (unsigned char)(1 << (bits - 1));
+ else if(top == 1)
+ bignum[0] |= (unsigned char)(3 << (bits - 2));
+
+ /* make odd by setting first bit in least significant byte */
+ if(bottom)
+ bignum[length - 1] |= 1;
+
+ return 0;
+}
+
+static int
+_libssh2_wincng_bignum_mod_exp(_libssh2_bn *r,
+ _libssh2_bn *a,
+ _libssh2_bn *p,
+ _libssh2_bn *m)
+{
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_RSAKEY_BLOB *rsakey;
+ unsigned char *bignum;
+ ULONG keylen, offset, length;
+ NTSTATUS ret;
+
+ if(!r || !a || !p || !m)
+ return -1;
+
+ offset = sizeof(BCRYPT_RSAKEY_BLOB);
+ keylen = offset + p->length + m->length;
+
+ rsakey = (BCRYPT_RSAKEY_BLOB *)malloc(keylen);
+ if(!rsakey)
+ return -1;
+
+
+ /* https://msdn.microsoft.com/library/windows/desktop/aa375531.aspx */
+ rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC;
+ rsakey->BitLength = m->length * 8;
+ rsakey->cbPublicExp = p->length;
+ rsakey->cbModulus = m->length;
+ rsakey->cbPrime1 = 0;
+ rsakey->cbPrime2 = 0;
+
+ memcpy((unsigned char *)rsakey + offset, p->bignum, p->length);
+ offset += p->length;
+
+ memcpy((unsigned char *)rsakey + offset, m->bignum, m->length);
+ offset = 0;
+
+ ret = BCryptImportKeyPair(_libssh2_wincng.hAlgRSA, NULL,
+ BCRYPT_RSAPUBLIC_BLOB, &hKey,
+ (PUCHAR)rsakey, keylen, 0);
+ if(BCRYPT_SUCCESS(ret)) {
+ ret = BCryptEncrypt(hKey, a->bignum, a->length, NULL, NULL, 0,
+ NULL, 0, &length, BCRYPT_PAD_NONE);
+ if(BCRYPT_SUCCESS(ret)) {
+ if(!_libssh2_wincng_bignum_resize(r, length)) {
+ length = max(a->length, length);
+ bignum = malloc(length);
+ if(bignum) {
+ memcpy_with_be_padding(bignum, length,
+ a->bignum, a->length);
+
+ ret = BCryptEncrypt(hKey, bignum, length, NULL, NULL, 0,
+ r->bignum, r->length, &offset,
+ BCRYPT_PAD_NONE);
+
+ _libssh2_wincng_safe_free(bignum, length);
+
+ if(BCRYPT_SUCCESS(ret)) {
+ _libssh2_wincng_bignum_resize(r, offset);
+ }
+ }
+ else
+ ret = (NTSTATUS)STATUS_NO_MEMORY;
+ }
+ else
+ ret = (NTSTATUS)STATUS_NO_MEMORY;
+ }
+
+ BCryptDestroyKey(hKey);
+ }
+
+ _libssh2_wincng_safe_free(rsakey, keylen);
+
+ return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+int
+_libssh2_wincng_bignum_set_word(_libssh2_bn *bn, ULONG word)
+{
+ ULONG offset, number, bits, length;
+
+ if(!bn)
+ return -1;
+
+ bits = 0;
+ number = word;
+ while(number >>= 1)
+ bits++;
+ bits++;
+
+ length = (ULONG) (ceil(((double)bits) / 8.0) * sizeof(unsigned char));
+ if(_libssh2_wincng_bignum_resize(bn, length))
+ return -1;
+
+ for(offset = 0; offset < length; offset++)
+ bn->bignum[offset] = (word >> (offset * 8)) & 0xff;
+
+ return 0;
+}
+
+ULONG
+_libssh2_wincng_bignum_bits(const _libssh2_bn *bn)
+{
+ unsigned char number;
+ ULONG offset, length, bits;
+
+ if(!bn || !bn->bignum || !bn->length)
+ return 0;
+
+ offset = 0;
+ length = bn->length - 1;
+ while(!bn->bignum[offset] && offset < length)
+ offset++;
+
+ bits = (length - offset) * 8;
+ number = bn->bignum[offset];
+ while(number >>= 1)
+ bits++;
+ bits++;
+
+ return bits;
+}
+
+int
+_libssh2_wincng_bignum_from_bin(_libssh2_bn *bn, ULONG len,
+ const unsigned char *bin)
+{
+ unsigned char *bignum;
+ ULONG offset, length, bits;
+
+ if(!bn || !bin || !len)
+ return -1;
+
+ if(_libssh2_wincng_bignum_resize(bn, len))
+ return -1;
+
+ memcpy(bn->bignum, bin, len);
+
+ bits = _libssh2_wincng_bignum_bits(bn);
+ length = (ULONG) (ceil(((double)bits) / 8.0) * sizeof(unsigned char));
+
+ offset = bn->length - length;
+ if(offset > 0) {
+ memmove(bn->bignum, bn->bignum + offset, length);
+
+ _libssh2_explicit_zero(bn->bignum + length, offset);
+
+ bignum = realloc(bn->bignum, length);
+ if(bignum) {
+ bn->bignum = bignum;
+ bn->length = length;
+ }
+ else {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+_libssh2_wincng_bignum_to_bin(const _libssh2_bn *bn, unsigned char *bin)
+{
+ if(bin && bn && bn->bignum && bn->length > 0) {
+ memcpy(bin, bn->bignum, bn->length);
+ return 0;
+ }
+
+ return -1;
+}
+
+void
+_libssh2_wincng_bignum_free(_libssh2_bn *bn)
+{
+ if(bn) {
+ if(bn->bignum) {
+ _libssh2_wincng_safe_free(bn->bignum, bn->length);
+ bn->bignum = NULL;
+ }
+ bn->length = 0;
+ _libssh2_wincng_safe_free(bn, sizeof(_libssh2_bn));
+ }
+}
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Diffie-Hellman support.
+ */
+
+void
+_libssh2_dh_init(_libssh2_dh_ctx *dhctx)
+{
+ /* Random from client */
+ dhctx->dh_handle = NULL;
+ dhctx->dh_params = NULL;
+ dhctx->dh_privbn = NULL;
+}
+
+void
+_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx)
+{
+ if(dhctx->dh_handle) {
+ BCryptDestroyKey(dhctx->dh_handle);
+ dhctx->dh_handle = NULL;
+ }
+ if(dhctx->dh_params) {
+ /* Since public dh_params are shared in clear text,
+ * we don't need to securely zero them out here */
+ free(dhctx->dh_params);
+ dhctx->dh_params = NULL;
+ }
+ if(dhctx->dh_privbn) {
+ _libssh2_wincng_bignum_free(dhctx->dh_privbn);
+ dhctx->dh_privbn = NULL;
+ }
+}
+
+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)
+{
+ const int hasAlgDHwithKDF = _libssh2_wincng.hasAlgDHwithKDF;
+
+ if(group_order < 0)
+ return -1;
+
+ while(_libssh2_wincng.hAlgDH && hasAlgDHwithKDF != -1) {
+ BCRYPT_DH_PARAMETER_HEADER *dh_params;
+ ULONG dh_params_len;
+ int status;
+ /* 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 */
+ ULONG key_length_bytes = max((ULONG)round_down(group_order, 8),
+ max(g->length, p->length));
+ BCRYPT_DH_KEY_BLOB *dh_key_blob;
+ LPCWSTR key_type;
+
+ /* 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 = (ULONG)sizeof(*dh_params) +
+ 2 * key_length_bytes;
+ dh_params = (BCRYPT_DH_PARAMETER_HEADER *)malloc(dh_params_len);
+ if(!dh_params) {
+ return -1;
+ }
+
+ /* Populate DH parameters blob; after the header follows the `p`
+ * value and the `g` value. */
+ dh_params->cbLength = dh_params_len;
+ dh_params->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC;
+ dh_params->cbKeyLength = key_length_bytes;
+ memcpy_with_be_padding((unsigned char *)dh_params +
+ sizeof(*dh_params),
+ key_length_bytes, p->bignum, p->length);
+ memcpy_with_be_padding((unsigned char *)dh_params +
+ sizeof(*dh_params) + key_length_bytes,
+ key_length_bytes, g->bignum, g->length);
+
+ status = BCryptSetProperty(dhctx->dh_handle, BCRYPT_DH_PARAMETERS,
+ (PUCHAR)dh_params, dh_params_len, 0);
+ if(hasAlgDHwithKDF == -1) {
+ /* We know that the raw KDF is not supported, so discard this. */
+ free(dh_params);
+ }
+ else {
+ /* 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;
+ }
+ dh_params = NULL;
+
+ if(!BCRYPT_SUCCESS(status)) {
+ return -1;
+ }
+
+ status = BCryptFinalizeKeyPair(dhctx->dh_handle, 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ return -1;
+ }
+
+ key_length_bytes = 0;
+ if(hasAlgDHwithKDF == 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. */
+ key_type = BCRYPT_DH_PUBLIC_BLOB;
+ }
+ else {
+ /* We also need to extract the private portion of the key to
+ * set it in the `*dhctx' bignum if the raw KDF is not supported.
+ * First measure up the size of the required buffer. */
+ key_type = BCRYPT_DH_PRIVATE_BLOB;
+ }
+ status = BCryptExportKey(dhctx->dh_handle, NULL, key_type,
+ NULL, 0, &key_length_bytes, 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ return -1;
+ }
+
+ dh_key_blob = (BCRYPT_DH_KEY_BLOB *)malloc(key_length_bytes);
+ if(!dh_key_blob) {
+ return -1;
+ }
+
+ status = BCryptExportKey(dhctx->dh_handle, NULL, key_type,
+ (PUCHAR)dh_key_blob, key_length_bytes,
+ &key_length_bytes, 0);
+ if(!BCRYPT_SUCCESS(status)) {
+ if(hasAlgDHwithKDF == 1) {
+ /* We have no private data, because raw KDF is supported */
+ free(dh_key_blob);
+ }
+ else { /* we may have potentially private data, use secure free */
+ _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
+ }
+ return -1;
+ }
+
+ if(hasAlgDHwithKDF == -1) {
+ /* We know that the raw KDF is not supported, so discard this */
+ BCryptDestroyKey(dhctx->dh_handle);
+ dhctx->dh_handle = NULL;
+ }
+
+ /* 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. */
+ if(_libssh2_wincng_bignum_resize(public, dh_key_blob->cbKey)) {
+ if(hasAlgDHwithKDF == 1) {
+ /* We have no private data, because raw KDF is supported */
+ free(dh_key_blob);
+ }
+ else { /* we may have potentially private data, use secure free */
+ _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
+ }
+ return -1;
+ }
+
+ /* Copy the public key data into the public bignum data buffer */
+ memcpy(public->bignum, (unsigned char *)dh_key_blob +
+ sizeof(*dh_key_blob) +
+ 2 * dh_key_blob->cbKey,
+ dh_key_blob->cbKey);
+
+ if(dh_key_blob->dwMagic == BCRYPT_DH_PRIVATE_MAGIC) {
+ /* BCRYPT_DH_PRIVATE_BLOB additionally contains the Private data */
+ dhctx->dh_privbn = _libssh2_wincng_bignum_init();
+ if(!dhctx->dh_privbn) {
+ _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
+ return -1;
+ }
+ if(_libssh2_wincng_bignum_resize(dhctx->dh_privbn,
+ dh_key_blob->cbKey)) {
+ _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
+ return -1;
+ }
+
+ /* Copy the private key data into the dhctx bignum data buffer */
+ memcpy(dhctx->dh_privbn->bignum, (unsigned char *)dh_key_blob +
+ sizeof(*dh_key_blob) +
+ 3 * dh_key_blob->cbKey,
+ dh_key_blob->cbKey);
+
+ /* Make sure the private key is an odd number, because only
+ * odd primes can be used with the RSA-based fallback while
+ * DH itself does not seem to care about it being odd or not. */
+ if(!(dhctx->dh_privbn->bignum[dhctx->dh_privbn->length-1] % 2)) {
+ _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
+ /* discard everything first, then try again */
+ _libssh2_dh_dtor(dhctx);
+ _libssh2_dh_init(dhctx);
+ continue;
+ }
+ }
+
+ _libssh2_wincng_safe_free(dh_key_blob, key_length_bytes);
+
+ return 0;
+ }
+
+ /* Generate x and e */
+ dhctx->dh_privbn = _libssh2_wincng_bignum_init();
+ if(!dhctx->dh_privbn)
+ return -1;
+ if(_libssh2_wincng_bignum_rand(dhctx->dh_privbn, (group_order*8)-1, 0, -1))
+ return -1;
+ if(_libssh2_wincng_bignum_mod_exp(public, g, dhctx->dh_privbn, 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_wincng.hAlgDH && _libssh2_wincng.hasAlgDHwithKDF != -1 &&
+ dhctx->dh_handle && dhctx->dh_params && f) {
+ BCRYPT_KEY_HANDLE peer_public = NULL;
+ BCRYPT_SECRET_HANDLE agreement = NULL;
+ ULONG secret_len_bytes = 0;
+ NTSTATUS status;
+ unsigned char *start, *end;
+ BCRYPT_DH_KEY_BLOB *public_blob;
+ ULONG key_length_bytes = max(f->length, dhctx->dh_params->cbKeyLength);
+ ULONG public_blob_len = (ULONG)(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;
+
+ public_blob = (BCRYPT_DH_KEY_BLOB *)malloc(public_blob_len);
+ if(!public_blob) {
+ return -1;
+ }
+ 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,
+ (PUCHAR)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)) {
+ if(status == STATUS_NOT_SUPPORTED) {
+ _libssh2_wincng.hasAlgDHwithKDF = -1;
+ }
+ goto out;
+ }
+
+ /* Expand the secret bignum to be ready to receive the derived secret
+ * */
+ if(_libssh2_wincng_bignum_resize(secret, secret_len_bytes)) {
+ status = (NTSTATUS)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)) {
+ if(status == STATUS_NOT_SUPPORTED) {
+ _libssh2_wincng.hasAlgDHwithKDF = -1;
+ }
+ 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;
+ _libssh2_wincng.hasAlgDHwithKDF = 1;
+
+out:
+ if(peer_public) {
+ BCryptDestroyKey(peer_public);
+ }
+ if(agreement) {
+ BCryptDestroySecret(agreement);
+ }
+
+ free(public_blob);
+
+ if(status == STATUS_NOT_SUPPORTED &&
+ _libssh2_wincng.hasAlgDHwithKDF == -1) {
+ goto fb; /* fallback to RSA-based implementation */
+ }
+ return BCRYPT_SUCCESS(status) ? 0 : -1;
+ }
+
+fb:
+ /* Compute the shared secret */
+ return _libssh2_wincng_bignum_mod_exp(secret, f, dhctx->dh_privbn, p);
+}
+
+/* _libssh2_supported_key_sign_algorithms
+ *
+ * Return supported key hash algo upgrades, see crypto.h
+ *
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len)
+{
+ (void)session;
+
+#if LIBSSH2_RSA_SHA2
+ if(key_method_len == 7 &&
+ memcmp(key_method, "ssh-rsa", key_method_len) == 0) {
+ return "rsa-sha2-512,rsa-sha2-256"
+#if LIBSSH2_RSA_SHA1
+ ",ssh-rsa"
+#endif
+ ;
+ }
+#else
+ (void)key_method;
+ (void)key_method_len;
+#endif
+
+ return NULL;
+}
+
+#endif /* LIBSSH2_CRYPTO_C */
diff --git a/libs/libssh2/src/wincng.h b/libs/libssh2/src/wincng.h
new file mode 100644
index 0000000000..a8d0d80575
--- /dev/null
+++ b/libs/libssh2/src/wincng.h
@@ -0,0 +1,561 @@
+#ifndef LIBSSH2_WINCNG_H
+#define LIBSSH2_WINCNG_H
+/*
+ * Copyright (C) Marc Hoersken <info@marc-hoersken.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define LIBSSH2_CRYPTO_ENGINE libssh2_wincng
+
+/* required for cross-compilation against the w64 mingw-runtime package */
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600)
+#undef _WIN32_WINNT
+#endif
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include <windows.h>
+#include <bcrypt.h>
+
+#define LIBSSH2_MD5 1
+
+#define LIBSSH2_HMAC_RIPEMD 0
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#define LIBSSH2_AES_CBC 1
+#define LIBSSH2_AES_CTR 1
+#define LIBSSH2_AES_GCM 0
+#define LIBSSH2_BLOWFISH 0
+#define LIBSSH2_RC4 1
+#define LIBSSH2_CAST 0
+#define LIBSSH2_3DES 1
+
+#define LIBSSH2_RSA 1
+#define LIBSSH2_RSA_SHA1 1
+#define LIBSSH2_RSA_SHA2 1
+#define LIBSSH2_DSA 1
+#define LIBSSH2_ED25519 0
+
+/*
+ * Conditionally enable ECDSA support.
+ *
+ * ECDSA support requires the use of
+ *
+ * BCryptDeriveKey(..., BCRYPT_KDF_RAW_SECRET, ... )
+ *
+ * This functionality is only available as of Windows 10. To maintain
+ * backward compatibility, ECDSA support is therefore disabled
+ * by default and needs to be explicitly enabled using a build
+ * flag.
+ */
+#ifdef LIBSSH2_ECDSA_WINCNG
+#define LIBSSH2_ECDSA 1
+#else
+#define LIBSSH2_ECDSA 0
+#endif
+
+#include "crypto_config.h"
+
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+#define MD5_DIGEST_LENGTH 16
+#endif
+#define SHA_DIGEST_LENGTH 20
+#define SHA256_DIGEST_LENGTH 32
+#define SHA384_DIGEST_LENGTH 48
+#define SHA512_DIGEST_LENGTH 64
+
+#define EC_MAX_POINT_LEN ((528 * 2 / 8) + 1)
+
+#if LIBSSH2_ECDSA
+#else
+#define _libssh2_ec_key void
+#endif
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Global context handles
+ */
+
+struct _libssh2_wincng_ctx {
+ BCRYPT_ALG_HANDLE hAlgRNG;
+ BCRYPT_ALG_HANDLE hAlgHashMD5;
+ BCRYPT_ALG_HANDLE hAlgHashSHA1;
+ BCRYPT_ALG_HANDLE hAlgHashSHA256;
+ BCRYPT_ALG_HANDLE hAlgHashSHA384;
+ BCRYPT_ALG_HANDLE hAlgHashSHA512;
+ BCRYPT_ALG_HANDLE hAlgHmacMD5;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA1;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA256;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA384;
+ BCRYPT_ALG_HANDLE hAlgHmacSHA512;
+ BCRYPT_ALG_HANDLE hAlgRSA;
+ BCRYPT_ALG_HANDLE hAlgDSA;
+ BCRYPT_ALG_HANDLE hAlgAES_CBC;
+ BCRYPT_ALG_HANDLE hAlgAES_ECB;
+ BCRYPT_ALG_HANDLE hAlgRC4_NA;
+ BCRYPT_ALG_HANDLE hAlg3DES_CBC;
+ BCRYPT_ALG_HANDLE hAlgDH;
+ BCRYPT_ALG_HANDLE hAlgChacha20;
+#if LIBSSH2_ECDSA
+ BCRYPT_ALG_HANDLE hAlgECDH[3]; /* indexed by libssh2_curve_type */
+ BCRYPT_ALG_HANDLE hAlgECDSA[3]; /* indexed by libssh2_curve_type */
+#endif
+ volatile int hasAlgDHwithKDF; /* -1=no, 0=maybe, 1=yes */
+};
+
+extern struct _libssh2_wincng_ctx _libssh2_wincng;
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Generic functions
+ */
+
+#define libssh2_crypto_init() \
+ _libssh2_wincng_init()
+#define libssh2_crypto_exit() \
+ _libssh2_wincng_free()
+
+#define _libssh2_random(buf, len) \
+ _libssh2_wincng_random(buf, len)
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Hash structure
+ */
+
+typedef struct __libssh2_wincng_hash_ctx {
+ BCRYPT_HASH_HANDLE hHash;
+ unsigned char *pbHashObject;
+ ULONG dwHashObject;
+ ULONG cbHash;
+} _libssh2_wincng_hash_ctx;
+
+/*
+ * Windows CNG backend: Hash functions
+ */
+
+#define libssh2_sha1_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha1_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA1, \
+ SHA_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha1_update(ctx, data, datalen) \
+ (_libssh2_wincng_hash_update(&ctx, data, (ULONG) datalen) == 0)
+#define libssh2_sha1_final(ctx, hash) \
+ (_libssh2_wincng_hash_final(&ctx, hash) == 0)
+#define libssh2_sha1(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA1, \
+ hash, SHA_DIGEST_LENGTH)
+
+#define libssh2_sha256_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha256_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA256, \
+ SHA256_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha256_update(ctx, data, datalen) \
+ (_libssh2_wincng_hash_update(&ctx, data, (ULONG) datalen) == 0)
+#define libssh2_sha256_final(ctx, hash) \
+ (_libssh2_wincng_hash_final(&ctx, hash) == 0)
+#define libssh2_sha256(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA256, \
+ hash, SHA256_DIGEST_LENGTH)
+
+#define libssh2_sha384_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha384_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA384, \
+ SHA384_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha384_update(ctx, data, datalen) \
+ (_libssh2_wincng_hash_update(&ctx, data, (ULONG) datalen) == 0)
+#define libssh2_sha384_final(ctx, hash) \
+ (_libssh2_wincng_hash_final(&ctx, hash) == 0)
+#define libssh2_sha384(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA384, \
+ hash, SHA384_DIGEST_LENGTH)
+
+#define libssh2_sha512_ctx _libssh2_wincng_hash_ctx
+#define libssh2_sha512_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashSHA512, \
+ SHA512_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_sha512_update(ctx, data, datalen) \
+ (_libssh2_wincng_hash_update(&ctx, data, (ULONG) datalen) == 0)
+#define libssh2_sha512_final(ctx, hash) \
+ (_libssh2_wincng_hash_final(&ctx, hash) == 0)
+#define libssh2_sha512(data, datalen, hash) \
+ _libssh2_wincng_hash(data, datalen, _libssh2_wincng.hAlgHashSHA512, \
+ hash, SHA512_DIGEST_LENGTH)
+
+#if LIBSSH2_MD5 || LIBSSH2_MD5_PEM
+#define libssh2_md5_ctx _libssh2_wincng_hash_ctx
+#define libssh2_md5_init(ctx) \
+ (_libssh2_wincng_hash_init(ctx, _libssh2_wincng.hAlgHashMD5, \
+ MD5_DIGEST_LENGTH, NULL, 0) == 0)
+#define libssh2_md5_update(ctx, data, datalen) \
+ (_libssh2_wincng_hash_update(&ctx, data, (ULONG) datalen) == 0)
+#define libssh2_md5_final(ctx, hash) \
+ (_libssh2_wincng_hash_final(&ctx, hash) == 0)
+#endif
+
+/*
+ * Windows CNG backend: HMAC functions
+ */
+
+#define libssh2_hmac_ctx _libssh2_wincng_hash_ctx
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Key Context structure
+ */
+
+typedef struct __libssh2_wincng_key_ctx {
+ BCRYPT_KEY_HANDLE hKey;
+ void *pbKeyObject;
+ DWORD cbKeyObject;
+} _libssh2_wincng_key_ctx;
+
+
+/*
+ * Windows CNG backend: RSA functions
+ */
+
+#define libssh2_rsa_ctx _libssh2_wincng_key_ctx
+#define _libssh2_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len) \
+ _libssh2_wincng_rsa_new(rsactx, e, e_len, n, n_len, \
+ d, d_len, p, p_len, q, q_len, \
+ e1, e1_len, e2, e2_len, c, c_len)
+#define _libssh2_rsa_new_private(rsactx, s, filename, passphrase) \
+ _libssh2_wincng_rsa_new_private(rsactx, s, filename, passphrase)
+#define _libssh2_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_wincng_rsa_new_private_frommemory(rsactx, s, filedata, \
+ filedata_len, passphrase)
+#define _libssh2_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len) \
+ _libssh2_wincng_rsa_sha1_sign(s, rsactx, hash, hash_len, sig, sig_len)
+#define _libssh2_rsa_sha2_sign(s, rsactx, hash, hash_len, sig, sig_len) \
+ _libssh2_wincng_rsa_sha2_sign(s, rsactx, hash, hash_len, sig, sig_len)
+#define _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len) \
+ _libssh2_wincng_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len)
+#define _libssh2_rsa_sha2_verify(rsactx, hash_len, sig, sig_len, m, m_len) \
+ _libssh2_wincng_rsa_sha2_verify(rsactx, hash_len, sig, sig_len, m, m_len)
+#define _libssh2_rsa_free(rsactx) \
+ _libssh2_wincng_rsa_free(rsactx)
+
+/*
+ * Windows CNG backend: DSA functions
+ */
+
+#define libssh2_dsa_ctx _libssh2_wincng_key_ctx
+#define _libssh2_dsa_new(dsactx, p, p_len, q, q_len, \
+ g, g_len, y, y_len, x, x_len) \
+ _libssh2_wincng_dsa_new(dsactx, p, p_len, q, q_len, \
+ g, g_len, y, y_len, x, x_len)
+#define _libssh2_dsa_new_private(dsactx, s, filename, passphrase) \
+ _libssh2_wincng_dsa_new_private(dsactx, s, filename, passphrase)
+#define _libssh2_dsa_new_private_frommemory(dsactx, s, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_wincng_dsa_new_private_frommemory(dsactx, s, filedata, \
+ filedata_len, passphrase)
+#define _libssh2_dsa_sha1_sign(dsactx, hash, hash_len, sig) \
+ _libssh2_wincng_dsa_sha1_sign(dsactx, hash, hash_len, sig)
+#define _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len) \
+ _libssh2_wincng_dsa_sha1_verify(dsactx, sig, m, m_len)
+#define _libssh2_dsa_free(dsactx) \
+ _libssh2_wincng_dsa_free(dsactx)
+
+
+/*
+ * Windows CNG backend: ECDSA functions
+ */
+
+typedef enum {
+ LIBSSH2_EC_CURVE_NISTP256 = 0,
+ LIBSSH2_EC_CURVE_NISTP384 = 1,
+ LIBSSH2_EC_CURVE_NISTP521 = 2,
+} libssh2_curve_type;
+
+typedef struct __libssh2_wincng_ecdsa_ctx {
+ BCRYPT_KEY_HANDLE handle;
+ libssh2_curve_type curve;
+} _libssh2_wincng_ecdsa_key;
+
+#define libssh2_ecdsa_ctx _libssh2_wincng_ecdsa_key
+
+#if LIBSSH2_ECDSA
+#define _libssh2_ec_key _libssh2_wincng_ecdsa_key
+#endif
+
+void
+_libssh2_wincng_ecdsa_free(libssh2_ecdsa_ctx* ctx);
+
+#define _libssh2_ecdsa_create_key(session, privkey, pubkey_octal, \
+ pubkey_octal_len, curve) \
+ _libssh2_wincng_ecdh_create_key(session, privkey, pubkey_octal, \
+ pubkey_octal_len, curve)
+
+#define _libssh2_ecdsa_curve_name_with_octal_new(ctx, k, k_len, curve) \
+ _libssh2_wincng_ecdsa_curve_name_with_octal_new(ctx, k, k_len, curve)
+
+#define _libssh2_ecdh_gen_k(k, privkey, server_pubkey, server_pubkey_len) \
+ _libssh2_wincng_ecdh_gen_k(k, privkey, server_pubkey, server_pubkey_len)
+
+#define _libssh2_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len) \
+ _libssh2_wincng_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len)
+
+#define _libssh2_ecdsa_new_private(ctx, session, filename, passphrase) \
+ _libssh2_wincng_ecdsa_new_private(ctx, session, filename, passphrase)
+
+#define _libssh2_ecdsa_new_private_frommemory(ctx, session, filedata, \
+ filedata_len, passphrase) \
+ _libssh2_wincng_ecdsa_new_private_frommemory(ctx, session, filedata, \
+ filedata_len, passphrase)
+
+#define _libssh2_ecdsa_sign(session, ctx, hash, hash_len, sign, sign_len) \
+ _libssh2_wincng_ecdsa_sign(session, ctx, hash, hash_len, sign, sign_len)
+
+#define _libssh2_ecdsa_get_curve_type(ctx) \
+ _libssh2_wincng_ecdsa_get_curve_type(ctx)
+
+#define _libssh2_ecdsa_free(ecdsactx) \
+ _libssh2_wincng_ecdsa_free(ecdsactx)
+
+
+/*
+ * Windows CNG backend: Key functions
+ */
+
+#define _libssh2_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw) \
+ _libssh2_wincng_pub_priv_keyfile(s, m, m_len, p, p_len, pk, pw)
+#define _libssh2_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw) \
+ _libssh2_wincng_pub_priv_keyfilememory(s, m, m_len, p, p_len, \
+ pk, pk_len, pw)
+#define _libssh2_sk_pub_keyfilememory(s, m, m_len, p, p_len, alg, app, \
+ f, kh, kh_len, pk, pk_len, pw) \
+ _libssh2_wincng_sk_pub_keyfilememory(s, m, m_len, p, p_len, alg, app, \
+ f, kh, kh_len, pk, pk_len, pw)
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: Cipher Context structure
+ */
+
+struct _libssh2_wincng_cipher_ctx {
+ BCRYPT_KEY_HANDLE hKey;
+ unsigned char *pbKeyObject;
+ unsigned char *pbIV;
+ unsigned char *pbCtr;
+ ULONG dwKeyObject;
+ ULONG dwIV;
+ ULONG dwBlockLength;
+ ULONG dwCtrLength;
+};
+
+#define _libssh2_cipher_ctx struct _libssh2_wincng_cipher_ctx
+
+/*
+ * Windows CNG backend: Cipher Type structure
+ */
+
+struct _libssh2_wincng_cipher_type {
+ BCRYPT_ALG_HANDLE *phAlg;
+ ULONG dwKeyLength;
+ int useIV; /* TODO: Convert to bool when a C89 compatible bool type
+ is defined */
+ int ctrMode;
+};
+
+#define _libssh2_cipher_type(type) struct _libssh2_wincng_cipher_type type
+
+#define _libssh2_cipher_aes256ctr { &_libssh2_wincng.hAlgAES_ECB, 32, 0, 1 }
+#define _libssh2_cipher_aes192ctr { &_libssh2_wincng.hAlgAES_ECB, 24, 0, 1 }
+#define _libssh2_cipher_aes128ctr { &_libssh2_wincng.hAlgAES_ECB, 16, 0, 1 }
+#define _libssh2_cipher_aes256 { &_libssh2_wincng.hAlgAES_CBC, 32, 1, 0 }
+#define _libssh2_cipher_aes192 { &_libssh2_wincng.hAlgAES_CBC, 24, 1, 0 }
+#define _libssh2_cipher_aes128 { &_libssh2_wincng.hAlgAES_CBC, 16, 1, 0 }
+#define _libssh2_cipher_arcfour { &_libssh2_wincng.hAlgRC4_NA, 16, 0, 0 }
+#define _libssh2_cipher_3des { &_libssh2_wincng.hAlg3DES_CBC, 24, 1, 0 }
+#define _libssh2_cipher_chacha20 { &_libssh2_wincng.hAlgChacha20, 24, 1, 0 }
+
+/*
+ * Windows CNG backend: Cipher functions
+ */
+
+#define _libssh2_cipher_init(ctx, type, iv, secret, encrypt) \
+ _libssh2_wincng_cipher_init(ctx, type, iv, secret, encrypt)
+#define _libssh2_cipher_crypt(ctx, type, encrypt, block, blocklen, fl) \
+ _libssh2_wincng_cipher_crypt(ctx, type, encrypt, block, blocklen, fl)
+#define _libssh2_cipher_dtor(ctx) \
+ _libssh2_wincng_cipher_dtor(ctx)
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: BigNumber Context
+ */
+
+#define _libssh2_bn_ctx int /* not used */
+#define _libssh2_bn_ctx_new() 0 /* not used */
+#define _libssh2_bn_ctx_free(bnctx) ((void)0) /* not used */
+
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: BigNumber structure
+ */
+
+struct _libssh2_wincng_bignum {
+ unsigned char *bignum;
+ ULONG length;
+};
+
+#define _libssh2_bn struct _libssh2_wincng_bignum
+
+/*
+ * Windows CNG backend: BigNumber functions
+ */
+
+#define _libssh2_bn_init() \
+ _libssh2_wincng_bignum_init()
+#define _libssh2_bn_init_from_bin() \
+ _libssh2_bn_init()
+#define _libssh2_bn_set_word(bn, word) \
+ _libssh2_wincng_bignum_set_word(bn, word)
+#define _libssh2_bn_from_bin(bn, len, bin) \
+ _libssh2_wincng_bignum_from_bin(bn, (ULONG) len, bin)
+#define _libssh2_bn_to_bin(bn, bin) \
+ _libssh2_wincng_bignum_to_bin(bn, bin)
+#define _libssh2_bn_bytes(bn) bn->length
+#define _libssh2_bn_bits(bn) \
+ _libssh2_wincng_bignum_bits(bn)
+#define _libssh2_bn_free(bn) \
+ _libssh2_wincng_bignum_free(bn)
+
+/*
+ * Windows CNG backend: Diffie-Hellman support
+ */
+
+/* Default generate and safe prime sizes for
+ diffie-hellman-group-exchange-sha1 */
+#define LIBSSH2_DH_GEX_MINGROUP 2048
+#define LIBSSH2_DH_GEX_OPTGROUP 4096
+#define LIBSSH2_DH_GEX_MAXGROUP 4096
+
+#define LIBSSH2_DH_MAX_MODULUS_BITS 16384
+
+typedef struct {
+ /* holds our private and 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;
+ /* records the parsed out private key component for
+ * fallback if the DH API raw KDF is not supported */
+ struct _libssh2_wincng_bignum *dh_privbn;
+} _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)
+#define libssh2_dh_secret(dhctx, secret, f, p, bnctx) \
+ _libssh2_dh_secret(dhctx, secret, f, p)
+#define libssh2_dh_dtor(dhctx) _libssh2_dh_dtor(dhctx)
+
+/*******************************************************************/
+/*
+ * Windows CNG backend: forward declarations
+ */
+void _libssh2_wincng_init(void);
+void _libssh2_wincng_free(void);
+int _libssh2_wincng_random(void *buf, size_t len);
+
+int
+_libssh2_wincng_hash_init(_libssh2_wincng_hash_ctx *ctx,
+ BCRYPT_ALG_HANDLE hAlg, ULONG hashlen,
+ unsigned char *key, ULONG keylen);
+int
+_libssh2_wincng_hash_update(_libssh2_wincng_hash_ctx *ctx,
+ const void *data, ULONG datalen);
+int
+_libssh2_wincng_hash_final(_libssh2_wincng_hash_ctx *ctx,
+ unsigned char *hash);
+int
+_libssh2_wincng_hash(const unsigned char *data, ULONG datalen,
+ BCRYPT_ALG_HANDLE hAlg,
+ unsigned char *hash, ULONG hashlen);
+
+void
+_libssh2_wincng_rsa_free(libssh2_rsa_ctx *rsa);
+
+#if LIBSSH2_DSA
+void
+_libssh2_wincng_dsa_free(libssh2_dsa_ctx *dsa);
+#endif
+
+void
+_libssh2_wincng_cipher_dtor(_libssh2_cipher_ctx *ctx);
+
+_libssh2_bn *
+_libssh2_wincng_bignum_init(void);
+int
+_libssh2_wincng_bignum_set_word(_libssh2_bn *bn, ULONG word);
+ULONG
+_libssh2_wincng_bignum_bits(const _libssh2_bn *bn);
+int
+_libssh2_wincng_bignum_from_bin(_libssh2_bn *bn, ULONG len,
+ const unsigned char *bin);
+int
+_libssh2_wincng_bignum_to_bin(const _libssh2_bn *bn, unsigned char *bin);
+void
+_libssh2_wincng_bignum_free(_libssh2_bn *bn);
+extern void
+_libssh2_dh_init(_libssh2_dh_ctx *dhctx);
+extern int
+_libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public,
+ _libssh2_bn *g, _libssh2_bn *p, int group_order);
+extern int
+_libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret,
+ _libssh2_bn *f, _libssh2_bn *p);
+extern void
+_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx);
+
+#endif /* LIBSSH2_WINCNG_H */