diff options
author | George Hazan <george.hazan@gmail.com> | 2024-10-20 17:59:03 +0300 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2024-10-20 17:59:07 +0300 |
commit | 4da5235106afefe68a0a37cdd083241ecda9344b (patch) | |
tree | 8ebd7ef73d0ee68ebd4896f3917a0a8fce6035dc /libs/libssh2/src | |
parent | 91d9a431ee26ef580715a72b48d61634e0e4dec7 (diff) |
fixes #4752 (Update libssh2 to 1.11.1)
Diffstat (limited to 'libs/libssh2/src')
35 files changed, 9028 insertions, 458 deletions
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 */ |