Selaa lähdekoodia

Merge branch 'master' into dev

# Conflicts:
#	source/forms/ScpCommander.cpp

#	source/windows/WinConfiguration.h

Source commit: 6303fe6c4c5929ade1127007af6321e256f6940d
Martin Prikryl 3 vuotta sitten
vanhempi
sitoutus
d284d93a83
100 muutettua tiedostoa jossa 2578 lisäystä ja 1866 poistoa
  1. 6 6
      libs/expat/CMake.README
  2. 3 3
      libs/expat/CMakeLists.txt
  3. 95 0
      libs/expat/Changes
  4. 1 0
      libs/expat/Makefile.in
  5. 1 1
      libs/expat/README.md
  6. 2 2
      libs/expat/cmake/autotools/expat-noconfig__windows.cmake.in
  7. 25 12
      libs/expat/configure
  8. 10 2
      libs/expat/configure.ac
  9. 1 2
      libs/expat/doc/Makefile.am
  10. 2 2
      libs/expat/doc/Makefile.in
  11. 1 1
      libs/expat/doc/reference.html
  12. BIN
      libs/expat/doc/valid-xhtml10.png
  13. 1 1
      libs/expat/doc/xmlwf.1
  14. 2 2
      libs/expat/doc/xmlwf.xml
  15. 1 0
      libs/expat/examples/Makefile.in
  16. 1 1
      libs/expat/examples/elements.c
  17. 1 1
      libs/expat/examples/outline.c
  18. 3 3
      libs/expat/expat_config.h
  19. 1 0
      libs/expat/lib/Makefile.in
  20. 1 1
      libs/expat/lib/expat.h
  21. 145 54
      libs/expat/lib/xmlparse.c
  22. 1 1
      libs/expat/lib/xmlrole.c
  23. 2 7
      libs/expat/lib/xmltok.c
  24. 12 8
      libs/expat/lib/xmltok_impl.c
  25. 1 0
      libs/expat/tests/Makefile.in
  26. 1 0
      libs/expat/tests/benchmark/Makefile.in
  27. 1 1
      libs/expat/tests/benchmark/benchmark.c
  28. 247 4
      libs/expat/tests/runtests.c
  29. 7 3
      libs/expat/win32/expat.iss
  30. 1 0
      libs/expat/xmlwf/Makefile.in
  31. 1 1
      libs/expat/xmlwf/xmlfile.c
  32. 4 4
      libs/expat/xmlwf/xmlwf.c
  33. 8 5
      libs/puttyvs/PuTTYVS.vcxproj
  34. 334 183
      source/Putty.cbproj
  35. 1 0
      source/components/UnixDirView.cpp
  36. 15 1
      source/components/UnixDriveView.cpp
  37. 2 0
      source/components/UnixDriveView.h
  38. 0 5
      source/core/Configuration.cpp
  39. 0 1
      source/core/Configuration.h
  40. 3 5
      source/core/FtpFileSystem.cpp
  41. 2 1
      source/core/Interface.h
  42. 54 27
      source/core/PuttyIntf.cpp
  43. 1 1
      source/core/PuttyIntf.h
  44. 11 3
      source/core/RemoteFiles.cpp
  45. 1 1
      source/core/RemoteFiles.h
  46. 82 40
      source/core/SecureShell.cpp
  47. 3 1
      source/core/SecureShell.h
  48. 1 0
      source/core/WebDAVFileSystem.cpp
  49. 1 0
      source/filezilla/FileZillaIntf.cpp
  50. 1 0
      source/filezilla/FileZillaIntf.h
  51. 1 0
      source/filezilla/FtpControlSocket.cpp
  52. 41 35
      source/filezilla/FtpListResult.cpp
  53. 1 0
      source/filezilla/FtpListResult.h
  54. 1 1
      source/filezilla/structures.cpp
  55. 1 0
      source/filezilla/structures.h
  56. 1 1
      source/forms/Cleanup.dfm
  57. 1 1
      source/forms/Copy.dfm
  58. 1 1
      source/forms/Custom.cpp
  59. 1 1
      source/forms/FileSystemInfo.dfm
  60. 1 1
      source/forms/GenerateUrl.dfm
  61. 1 1
      source/forms/ImportSessions.dfm
  62. 3 1
      source/forms/NonVisual.cpp
  63. 8 8
      source/forms/Preferences.dfm
  64. 19 9
      source/forms/ScpCommander.cpp
  65. 1 1
      source/forms/SiteAdvanced.cpp
  66. 3 3
      source/forms/SiteAdvanced.dfm
  67. 6 0
      source/packages/filemng/DirView.pas
  68. 240 68
      source/packages/filemng/DriveView.pas
  69. 2 2
      source/packages/filemng/IEDriveInfo.pas
  70. 118 0
      source/putty/be_list.c
  71. 0 18
      source/putty/be_ssh.c
  72. 0 179
      source/putty/cproxy.c
  73. 14 0
      source/putty/crypto/aes-common.c
  74. 316 0
      source/putty/crypto/aes-ni.c
  75. 99 0
      source/putty/crypto/aes-select.c
  76. 29 927
      source/putty/crypto/aes-sw.c
  77. 126 0
      source/putty/crypto/aes.h
  78. 0 0
      source/putty/crypto/aesold.c
  79. 0 0
      source/putty/crypto/arcfour.c
  80. 1 1
      source/putty/crypto/argon2.c
  81. 5 6
      source/putty/crypto/bcrypt.c
  82. 0 0
      source/putty/crypto/blake2.c
  83. 1 1
      source/putty/crypto/blowfish.c
  84. 1 1
      source/putty/crypto/blowfish.h
  85. 0 0
      source/putty/crypto/chacha20-poly1305.c
  86. 1 1
      source/putty/crypto/des.c
  87. 11 22
      source/putty/crypto/diffie-hellman.c
  88. 129 129
      source/putty/crypto/dsa.c
  89. 4 0
      source/putty/crypto/ecc-arithmetic.c
  90. 6 14
      source/putty/crypto/ecc-ssh.c
  91. 2 2
      source/putty/crypto/ecc.h
  92. 13 0
      source/putty/crypto/hash_simple.c
  93. 6 6
      source/putty/crypto/hmac.c
  94. 0 0
      source/putty/crypto/mac.c
  95. 16 0
      source/putty/crypto/mac_simple.c
  96. 0 0
      source/putty/crypto/md5.c
  97. 212 26
      source/putty/crypto/mpint.c
  98. 1 1
      source/putty/crypto/mpint_i.h
  99. 1 1
      source/putty/crypto/prng.c
  100. 32 0
      source/putty/crypto/pubkey-pem.c

+ 6 - 6
libs/expat/CMake.README

@@ -3,25 +3,25 @@
 The cmake based buildsystem for expat works on Windows (cygwin, mingw, Visual
 Studio) and should work on all other platform cmake supports.
 
-Assuming ~/expat-2.4.3 is the source directory of expat, add a subdirectory
+Assuming ~/expat-2.4.6 is the source directory of expat, add a subdirectory
 build and change into that directory:
-~/expat-2.4.3$ mkdir build && cd build
-~/expat-2.4.3/build$
+~/expat-2.4.6$ mkdir build && cd build
+~/expat-2.4.6/build$
 
 From that directory, call cmake first, then call make, make test and
 make install in the usual way:
-~/expat-2.4.3/build$ cmake ..
+~/expat-2.4.6/build$ cmake ..
 -- The C compiler identification is GNU
 -- The CXX compiler identification is GNU
 ....
 -- Configuring done
 -- Generating done
--- Build files have been written to: /home/patrick/expat-2.4.3/build
+-- Build files have been written to: /home/patrick/expat-2.4.6/build
 
 If you want to specify the install location for your files, append
 -DCMAKE_INSTALL_PREFIX=/your/install/path to the cmake call.
 
-~/expat-2.4.3/build$ make && make test && make install
+~/expat-2.4.6/build$ make && make test && make install
 Scanning dependencies of target expat
 [  5%] Building C object CMakeFiles/expat.dir/lib/xmlparse.c.o
 [ 11%] Building C object CMakeFiles/expat.dir/lib/xmlrole.c.o

+ 3 - 3
libs/expat/CMakeLists.txt

@@ -64,7 +64,7 @@ endif()
 
 project(expat
     VERSION
-        2.4.3
+        2.4.6
     LANGUAGES
         C
 )
@@ -408,7 +408,7 @@ if(EXPAT_WITH_LIBBSD)
 endif()
 
 set(LIBCURRENT 9)   # sync
-set(LIBREVISION 3)  # with
+set(LIBREVISION 6)  # with
 set(LIBAGE 8)       # configure.ac!
 math(EXPR LIBCURRENT_MINUS_AGE "${LIBCURRENT} - ${LIBAGE}")
 
@@ -422,7 +422,7 @@ if(WIN32 AND NOT MINGW)
     #       on Windows by resorting to filename libexpat.dll since Expat 1.95.3.
     #       Everything but MSVC is already adding prefix "lib", automatically.
     # NOTE: "set_property(TARGET expat PROPERTY PREFIX lib)" would only affect *.dll
-    #       files but not *.lib files, so we have to rely on propert OUTPUT_NAME, instead.
+    #       files but not *.lib files, so we have to rely on property OUTPUT_NAME, instead.
     #       Property CMAKE_*_POSTFIX still applies.
     set_property(TARGET expat PROPERTY OUTPUT_NAME libexpat)
 endif()

+ 95 - 0
libs/expat/Changes

@@ -2,6 +2,101 @@ NOTE: We are looking for help with a few things:
       https://github.com/libexpat/libexpat/labels/help%20wanted
       If you can help, please get in touch.  Thanks!
 
+Release 2.4.6 Sun February 20 2022
+        Bug fixes:
+            #566  Fix a regression introduced by the fix for CVE-2022-25313
+                    in release 2.4.5 that affects applications that (1)
+                    call function XML_SetElementDeclHandler and (2) are
+                    parsing XML that contains nested element declarations
+                    (e.g. "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>").
+
+        Other changes:
+       #567 #568  Version info bumped from 9:5:8 to 9:6:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Matt Sergeant
+            Samanta Navarro
+            Sergei Trofimovich
+                 and
+            NixOS
+            Perl XML::Parser
+
+Release 2.4.5 Fri February 18 2022
+        Security fixes:
+            #562  CVE-2022-25235 -- Passing malformed 2- and 3-byte UTF-8
+                    sequences (e.g. from start tag names) to the XML
+                    processing application on top of Expat can cause
+                    arbitrary damage (e.g. code execution) depending
+                    on how invalid UTF-8 is handled inside the XML
+                    processor; validation was not their job but Expat's.
+                    Exploits with code execution are known to exist.
+            #561  CVE-2022-25236 -- Passing (one or more) namespace separator
+                    characters in "xmlns[:prefix]" attribute values
+                    made Expat send malformed tag names to the XML
+                    processor on top of Expat which can cause
+                    arbitrary damage (e.g. code execution) depending
+                    on such unexpectable cases are handled inside the XML
+                    processor; validation was not their job but Expat's.
+                    Exploits with code execution are known to exist.
+            #558  CVE-2022-25313 -- Fix stack exhaustion in doctype parsing
+                    that could be triggered by e.g. a 2 megabytes
+                    file with a large number of opening braces.
+                    Expected impact is denial of service or potentially
+                    arbitrary code execution.
+            #560  CVE-2022-25314 -- Fix integer overflow in function copyString;
+                    only affects the encoding name parameter at parser creation
+                    time which is often hardcoded (rather than user input),
+                    takes a value in the gigabytes to trigger, and a 64-bit
+                    machine.  Expected impact is denial of service.
+            #559  CVE-2022-25315 -- Fix integer overflow in function storeRawNames;
+                    needs input in the gigabytes and a 64-bit machine.
+                    Expected impact is denial of service or potentially
+                    arbitrary code execution.
+
+        Other changes:
+       #557 #564  Version info bumped from 9:4:8 to 9:5:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Ivan Fratric
+            Samanta Navarro
+                 and
+            Google Project Zero
+            JetBrains
+
+Release 2.4.4 Sun January 30 2022
+        Security fixes:
+            #550  CVE-2022-23852 -- Fix signed integer overflow
+                    (undefined behavior) in function XML_GetBuffer
+                    (that is also called by function XML_Parse internally)
+                    for when XML_CONTEXT_BYTES is defined to >0 (which is both
+                    common and default).
+                    Impact is denial of service or more.
+            #551  CVE-2022-23990 -- Fix unsigned integer overflow in function
+                    doProlog triggered by large content in element type
+                    declarations when there is an element declaration handler
+                    present (from a prior call to XML_SetElementDeclHandler).
+                    Impact is denial of service or more.
+
+        Bug fixes:
+       #544 #545  xmlwf: Fix a memory leak on output file opening error
+
+        Other changes:
+            #546  Autotools: Fix broken CMake support under Cygwin
+            #554  Windows: Add missing files to the installer to fix
+                    compilation with CMake from installed sources
+       #552 #554  Version info bumped from 9:3:8 to 9:4:8;
+                    see https://verbump.de/ for what these numbers do
+
+        Special thanks to:
+            Carlo Bramini
+            hwt0415
+            Roland Illig
+            Samanta Navarro
+                 and
+            Clang LeakSan and the Clang team
+
 Release 2.4.3 Sun January 16 2022
         Security fixes:
        #531 #534  CVE-2021-45960 -- Fix issues with left shifts by >=29 places

+ 1 - 0
libs/expat/Makefile.in

@@ -306,6 +306,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/README.md

@@ -5,7 +5,7 @@
 [![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
 
 
-# Expat, Release 2.4.3
+# Expat, Release 2.4.6
 
 This is Expat, a C library for parsing XML, started by
 [James Clark](https://en.wikipedia.org/wiki/James_Clark_%28programmer%29) in 1997.

+ 2 - 2
libs/expat/cmake/autotools/expat-noconfig__windows.cmake.in

@@ -9,11 +9,11 @@ set(CMAKE_IMPORT_FILE_VERSION 1)
 set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
 set_target_properties(expat::expat PROPERTIES
   IMPORTED_IMPLIB_NOCONFIG "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a"
-  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/bin/libexpat-@[email protected]"
+  IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/bin/@CMAKE_SHARED_LIBRARY_PREFIX@expat-@[email protected]"
   )
 
 list(APPEND _IMPORT_CHECK_TARGETS expat::expat )
-list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a" "${_IMPORT_PREFIX}/bin/libexpat-@[email protected]" )
+list(APPEND _IMPORT_CHECK_FILES_FOR_expat::expat "${_IMPORT_PREFIX}/@LIBDIR_BASENAME@/libexpat.dll.a" "${_IMPORT_PREFIX}/bin/@CMAKE_SHARED_LIBRARY_PREFIX@expat-@[email protected]" )
 
 # Commands beyond this point should not need to know the version.
 set(CMAKE_IMPORT_FILE_VERSION)

+ 25 - 12
libs/expat/configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for expat 2.4.3.
+# Generated by GNU Autoconf 2.71 for expat 2.4.6.
 #
 # Report bugs to <[email protected]>.
 #
@@ -621,8 +621,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='expat'
 PACKAGE_TARNAME='expat'
-PACKAGE_VERSION='2.4.3'
-PACKAGE_STRING='expat 2.4.3'
+PACKAGE_VERSION='2.4.6'
+PACKAGE_STRING='expat 2.4.6'
 PACKAGE_BUGREPORT='[email protected]'
 PACKAGE_URL=''
 
@@ -664,6 +664,7 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
+CMAKE_SHARED_LIBRARY_PREFIX
 AM_LDFLAGS
 AM_CXXFLAGS
 AM_CFLAGS
@@ -1413,7 +1414,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures expat 2.4.3 to adapt to many kinds of systems.
+\`configure' configures expat 2.4.6 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1484,7 +1485,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of expat 2.4.3:";;
+     short | recursive ) echo "Configuration of expat 2.4.6:";;
    esac
   cat <<\_ACEOF
 
@@ -1618,7 +1619,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-expat configure 2.4.3
+expat configure 2.4.6
 generated by GNU Autoconf 2.71
 
 Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2249,7 +2250,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by expat $as_me 2.4.3, which was
+It was created by expat $as_me 2.4.6, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -3816,7 +3817,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='expat'
- VERSION='2.4.3'
+ VERSION='2.4.6'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -3923,7 +3924,7 @@ fi
 
 
 LIBCURRENT=9   # sync
-LIBREVISION=3  # with
+LIBREVISION=6  # with
 LIBAGE=8       # CMakeLists.txt!
 
 ac_config_headers="$ac_config_headers expat_config.h"
@@ -19638,10 +19639,22 @@ printf "%s\n" "#define SIZEOF_VOID_P $ac_cv_sizeof_void_p" >>confdefs.h
 
 
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shared library name prefix" >&5
+printf %s "checking for shared library name prefix... " >&6; }
+case "${host_os}" in #(
+  cygwin*) :
+    CMAKE_SHARED_LIBRARY_PREFIX=cyg ;; #(
+  *) :
+    CMAKE_SHARED_LIBRARY_PREFIX=lib ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${CMAKE_SHARED_LIBRARY_PREFIX}" >&5
+printf "%s\n" "${CMAKE_SHARED_LIBRARY_PREFIX}" >&6; }
+
+
 case "${host_os}" in #(
   darwin*) :
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in ;; #(
-  mingw*) :
+  mingw*|cygwin*) :
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in ;; #(
   *) :
     CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in ;;
@@ -20214,7 +20227,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by expat $as_me 2.4.3, which was
+This file was extended by expat $as_me 2.4.6, which was
 generated by GNU Autoconf 2.71.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -20282,7 +20295,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-expat config.status 2.4.3
+expat config.status 2.4.6
 configured by $0, generated by GNU Autoconf 2.71,
   with options \\"\$ac_cs_config\\"
 

+ 10 - 2
libs/expat/configure.ac

@@ -82,7 +82,7 @@ dnl If the API changes incompatibly set LIBAGE back to 0
 dnl
 
 LIBCURRENT=9   # sync
-LIBREVISION=3  # with
+LIBREVISION=6  # with
 LIBAGE=8       # CMakeLists.txt!
 
 AC_CONFIG_HEADERS([expat_config.h])
@@ -395,9 +395,17 @@ AC_SUBST([AM_CFLAGS])
 AC_SUBST([AM_CXXFLAGS])
 AC_SUBST([AM_LDFLAGS])
 
+dnl Emulate the use of CMAKE_SHARED_LIBRARY_PREFIX under CMake
+AC_MSG_CHECKING([for shared library name prefix])
+AS_CASE("${host_os}",
+  [cygwin*], [CMAKE_SHARED_LIBRARY_PREFIX=cyg],
+  [CMAKE_SHARED_LIBRARY_PREFIX=lib])
+AC_MSG_RESULT([${CMAKE_SHARED_LIBRARY_PREFIX}])
+AC_SUBST([CMAKE_SHARED_LIBRARY_PREFIX])
+
 AS_CASE("${host_os}",
   [darwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in],
-  [mingw*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in],
+  [mingw*|cygwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in],
   [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in])
 AC_CONFIG_FILES([Makefile]
   [expat.pc]

+ 1 - 2
libs/expat/doc/Makefile.am

@@ -6,7 +6,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <[email protected]>
+# Copyright (c) 2017-2022 Sebastian Pipping <[email protected]>
 # Copyright (c) 2017      Stephen Groat <[email protected]>
 # Copyright (c) 2017      Joe Orton <[email protected]>
 # Licensed under the MIT license:
@@ -57,5 +57,4 @@ EXTRA_DIST = \
     ok.min.css \
     reference.html \
     style.css \
-    valid-xhtml10.png \
     xmlwf.xml

+ 2 - 2
libs/expat/doc/Makefile.in

@@ -22,7 +22,7 @@
 #                      \___/_/\_\ .__/ \__,_|\__|
 #                               |_| XML parser
 #
-# Copyright (c) 2017-2021 Sebastian Pipping <[email protected]>
+# Copyright (c) 2017-2022 Sebastian Pipping <[email protected]>
 # Copyright (c) 2017      Stephen Groat <[email protected]>
 # Copyright (c) 2017      Joe Orton <[email protected]>
 # Licensed under the MIT license:
@@ -209,6 +209,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@
@@ -344,7 +345,6 @@ EXTRA_DIST = \
     ok.min.css \
     reference.html \
     style.css \
-    valid-xhtml10.png \
     xmlwf.xml
 
 all: all-am

+ 1 - 1
libs/expat/doc/reference.html

@@ -49,7 +49,7 @@
   <div>
     <h1>
       The Expat XML Parser
-      <small>Release 2.4.3</small>
+      <small>Release 2.4.6</small>
     </h1>
   </div>
 <div class="content">

BIN
libs/expat/doc/valid-xhtml10.png


+ 1 - 1
libs/expat/doc/xmlwf.1

@@ -5,7 +5,7 @@
 \\$2 \(la\\$1\(ra\\$3
 ..
 .if \n(.g .mso www.tmac
-.TH XMLWF 1 "January 16, 2022" "" ""
+.TH XMLWF 1 "February 20, 2022" "" ""
 .SH NAME
 xmlwf \- Determines if an XML document is well-formed
 .SH SYNOPSIS

+ 2 - 2
libs/expat/doc/xmlwf.xml

@@ -21,8 +21,8 @@
           "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
   <!ENTITY dhfirstname "<firstname>Scott</firstname>">
   <!ENTITY dhsurname   "<surname>Bronson</surname>">
-  <!-- Please adjust the date whenever revising the manpage. -->
-  <!ENTITY dhdate      "<date>January 16, 2022</date>">
+  <!ENTITY dhdate      "<date>February 20, 2022</date>">
+  <!-- Please adjust this^^ date whenever cutting a new release. -->
   <!ENTITY dhsection   "<manvolnum>1</manvolnum>">
   <!ENTITY dhemail     "<email>[email protected]</email>">
   <!ENTITY dhusername  "Scott Bronson">

+ 1 - 0
libs/expat/examples/Makefile.in

@@ -230,6 +230,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/examples/elements.c

@@ -13,7 +13,7 @@
    Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2004-2006 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2016-2019 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      Zhongyuan Zhou <[email protected]>

+ 1 - 1
libs/expat/examples/outline.c

@@ -10,7 +10,7 @@
 
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2005-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2016-2019 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>

+ 3 - 3
libs/expat/expat_config.h

@@ -77,7 +77,7 @@
 #define PACKAGE_NAME "expat"
 
 /* Define to the full name and version of this package. */
-#define PACKAGE_STRING "expat 2.4.3"
+#define PACKAGE_STRING "expat 2.4.6"
 
 /* Define to the one symbol short name of this package. */
 #define PACKAGE_TARNAME "expat"
@@ -86,7 +86,7 @@
 #define PACKAGE_URL ""
 
 /* Define to the version of this package. */
-#define PACKAGE_VERSION "2.4.3"
+#define PACKAGE_VERSION "2.4.6"
 
 /* Define to 1 if all of the C90 standard headers exist (not just the ones
    required in a freestanding environment). This macro is provided for
@@ -94,7 +94,7 @@
 #define STDC_HEADERS 1
 
 /* Version number of package */
-#define VERSION "2.4.3"
+#define VERSION "2.4.6"
 
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */

+ 1 - 0
libs/expat/lib/Makefile.in

@@ -268,6 +268,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/lib/expat.h

@@ -1041,7 +1041,7 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
 */
 #define XML_MAJOR_VERSION 2
 #define XML_MINOR_VERSION 4
-#define XML_MICRO_VERSION 3
+#define XML_MICRO_VERSION 6
 
 #ifdef __cplusplus
 }

+ 145 - 54
libs/expat/lib/xmlparse.c

@@ -1,4 +1,4 @@
-/* 9ca2a2fedc35bcb13ba9a134ba5e173020bc2ff5f5a311abf742cec7da1ff26a (2.4.3+)
+/* a30d2613dcfdef81475a9d1a349134d2d42722172fdaa7d5bb12ed2aa74b9596 (2.4.6+)
                             __  __            _
                          ___\ \/ /_ __   __ _| |_
                         / _ \\  /| '_ \ / _` | __|
@@ -11,7 +11,7 @@
    Copyright (c) 2000-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2001-2002 Greg Stein <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
    Copyright (c) 2016      Eric Rahm <[email protected]>
    Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Gaurav <[email protected]>
@@ -33,6 +33,7 @@
    Copyright (c) 2019-2020 Ben Wagner <[email protected]>
    Copyright (c) 2019      Vadim Zeitlin <[email protected]>
    Copyright (c) 2021      Dong-hee Na <[email protected]>
+   Copyright (c) 2022      Samanta Navarro <[email protected]>
    Licensed under the MIT license:
 
    Permission is  hereby granted,  free of charge,  to any  person obtaining
@@ -717,8 +718,10 @@ XML_ParserCreate(const XML_Char *encodingName) {
 
 XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
+  // WINSCP
   XML_Char tmp[2];
-  *tmp = nsSep;
+  tmp[0] = nsSep;
+  tmp[1] = 0;
   return XML_ParserCreate_MM(encodingName, NULL, tmp);
 }
 
@@ -978,7 +981,7 @@ parserCreate(const XML_Char *encodingName,
 
   if (memsuite) {
     XML_Memory_Handling_Suite *mtemp;
-    parser = (XML_Parser)memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
+    parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
     if (parser != NULL) {
       mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
       mtemp->malloc_fcn = memsuite->malloc_fcn;
@@ -1347,8 +1350,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
      would be otherwise.
   */
   if (parser->m_ns) {
+    // WINSCP
     XML_Char tmp[2];
-    *tmp = parser->m_namespaceSeparator;
+    tmp[0] = parser->m_namespaceSeparator;
+    tmp[1] = 0;
     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
   } else {
     parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
@@ -2071,6 +2076,11 @@ XML_GetBuffer(XML_Parser parser, int len) {
     keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer);
     if (keep > XML_CONTEXT_BYTES)
       keep = XML_CONTEXT_BYTES;
+    /* Detect and prevent integer overflow */
+    if (keep > INT_MAX - neededSize) {
+      parser->m_errorCode = XML_ERROR_NO_MEMORY;
+      return NULL;
+    }
     neededSize += keep;
 #endif /* defined XML_CONTEXT_BYTES */
     if (neededSize
@@ -2561,6 +2571,7 @@ storeRawNames(XML_Parser parser) {
   while (tag) {
     int bufSize;
     int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+    size_t rawNameLen;
     char *rawNameBuf = tag->buf + nameLen;
     /* Stop if already stored.  Since m_tagStack is a stack, we can stop
        at the first entry that has already been copied; everything
@@ -2572,7 +2583,11 @@ storeRawNames(XML_Parser parser) {
     /* For re-use purposes we need to ensure that the
        size of tag->buf is a multiple of sizeof(XML_Char).
     */
-    bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char));
+    /* Detect and prevent integer overflow. */
+    if (rawNameLen > (size_t)INT_MAX - nameLen)
+      return XML_FALSE;
+    bufSize = nameLen + (int)rawNameLen;
     if (bufSize > tag->bufEnd - tag->buf) {
       char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
       if (temp == NULL)
@@ -3756,6 +3771,17 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
     if (! mustBeXML && isXMLNS
         && (len > xmlnsLen || uri[len] != xmlnsNamespace[len]))
       isXMLNS = XML_FALSE;
+
+    // NOTE: While Expat does not validate namespace URIs against RFC 3986,
+    //       we have to at least make sure that the XML processor on top of
+    //       Expat (that is splitting tag names by namespace separator into
+    //       2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused
+    //       by an attacker putting additional namespace separator characters
+    //       into namespace declarations.  That would be ambiguous and not to
+    //       be expected.
+    if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) {
+      return XML_ERROR_SYNTAX;
+    }
   }
   isXML = isXML && len == xmlLen;
   isXMLNS = isXMLNS && len == xmlnsLen;
@@ -4100,7 +4126,7 @@ initializeEncoding(XML_Parser parser) {
   const char *s;
 #ifdef XML_UNICODE
   char encodingBuf[128];
-  /* See comments abount `protoclEncodingName` in parserInit() */
+  /* See comments about `protocolEncodingName` in parserInit() */
   if (! parser->m_protocolEncodingName)
     s = NULL;
   else {
@@ -5380,7 +5406,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
       if (dtd->in_eldecl) {
         ELEMENT_TYPE *el;
         const XML_Char *name;
-        int nameLen;
+        size_t nameLen;
         const char *nxt
             = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar);
         int myindex = nextScaffoldPart(parser);
@@ -5396,7 +5422,13 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
         nameLen = 0;
         for (; name[nameLen++];)
           ;
-        dtd->contentStringLen += nameLen;
+
+        /* Detect and prevent integer overflow */
+        if (nameLen > UINT_MAX - dtd->contentStringLen) {
+          return XML_ERROR_NO_MEMORY;
+        }
+
+        dtd->contentStringLen += (unsigned)nameLen;
         if (parser->m_elementDeclHandler)
           handleDefault = XML_FALSE;
       }
@@ -6552,7 +6584,7 @@ normalizePublicId(XML_Char *publicId) {
 
 static DTD *
 dtdCreate(const XML_Memory_Handling_Suite *ms) {
-  DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD));
+  DTD *p = ms->malloc_fcn(sizeof(DTD));
   if (p == NULL)
     return p;
   poolInit(&(p->pool), ms);
@@ -6725,8 +6757,8 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
     if (! newE)
       return 0;
     if (oldE->nDefaultAtts) {
-      newE->defaultAtts = (DEFAULT_ATTRIBUTE *)ms->malloc_fcn(
-          oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+      newE->defaultAtts
+          = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
       if (! newE->defaultAtts) {
         return 0;
       }
@@ -6888,7 +6920,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
     /* table->size is a power of 2 */
     table->size = (size_t)1 << INIT_POWER;
     tsize = table->size * sizeof(NAMED *);
-    table->v = (NAMED **)table->mem->malloc_fcn(tsize);
+    table->v = table->mem->malloc_fcn(tsize);
     if (! table->v) {
       table->size = 0;
       return NULL;
@@ -6930,7 +6962,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
 
       { // WINSCP
       size_t tsize = newSize * sizeof(NAMED *);
-      NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize);
+      NAMED **newV = table->mem->malloc_fcn(tsize);
       if (! newV)
         return NULL;
       memset(newV, 0, tsize);
@@ -6961,7 +6993,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
       } // WINSCP
     }
   }
-  table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize);
+  table->v[i] = table->mem->malloc_fcn(createSize);
   if (! table->v[i])
     return NULL;
   memset(table->v[i], 0, createSize);
@@ -7249,7 +7281,7 @@ poolGrow(STRING_POOL *pool) {
     if (bytesToAllocate == 0)
       return XML_FALSE;
 
-    tem = (BLOCK *)pool->mem->malloc_fcn(bytesToAllocate);
+    tem = pool->mem->malloc_fcn(bytesToAllocate);
     if (! tem)
       return XML_FALSE;
     tem->size = blockSize;
@@ -7325,44 +7357,15 @@ nextScaffoldPart(XML_Parser parser) {
   return next;
 }
 
-static void
-build_node(XML_Parser parser, int src_node, XML_Content *dest,
-           XML_Content **contpos, XML_Char **strpos) {
-  DTD *const dtd = parser->m_dtd; /* save one level of indirection */
-  dest->type = dtd->scaffold[src_node].type;
-  dest->quant = dtd->scaffold[src_node].quant;
-  if (dest->type == XML_CTYPE_NAME) {
-    const XML_Char *src;
-    dest->name = *strpos;
-    src = dtd->scaffold[src_node].name;
-    for (;;) {
-      *(*strpos)++ = *src;
-      if (! *src)
-        break;
-      src++;
-    }
-    dest->numchildren = 0;
-    dest->children = NULL;
-  } else {
-    unsigned int i;
-    int cn;
-    dest->numchildren = dtd->scaffold[src_node].childcnt;
-    dest->children = *contpos;
-    *contpos += dest->numchildren;
-    for (i = 0, cn = dtd->scaffold[src_node].firstchild; i < dest->numchildren;
-         i++, cn = dtd->scaffold[cn].nextsib) {
-      build_node(parser, cn, &(dest->children[i]), contpos, strpos);
-    }
-    dest->name = NULL;
-  }
-}
-
 static XML_Content *
 build_model(XML_Parser parser) {
+  /* Function build_model transforms the existing parser->m_dtd->scaffold
+   * array of CONTENT_SCAFFOLD tree nodes into a new array of
+   * XML_Content tree nodes followed by a gapless list of zero-terminated
+   * strings. */
   DTD *const dtd = parser->m_dtd; /* save one level of indirection */
   XML_Content *ret;
-  XML_Content *cpos;
-  XML_Char *str;
+  XML_Char *str; /* the current string writing location */
 
   /* Detect and prevent integer overflow.
    * The preprocessor guard addresses the "always false" warning
@@ -7389,12 +7392,100 @@ build_model(XML_Parser parser) {
   if (! ret)
     return NULL;
 
-  str = (XML_Char *)(&ret[dtd->scaffCount]);
-  cpos = &ret[1];
+  /* What follows is an iterative implementation (of what was previously done
+   * recursively in a dedicated function called "build_node".  The old recursive
+   * build_node could be forced into stack exhaustion from input as small as a
+   * few megabyte, and so that was a security issue.  Hence, a function call
+   * stack is avoided now by resolving recursion.)
+   *
+   * The iterative approach works as follows:
+   *
+   * - We have two writing pointers, both walking up the result array; one does
+   *   the work, the other creates "jobs" for its colleague to do, and leads
+   *   the way:
+   *
+   *   - The faster one, pointer jobDest, always leads and writes "what job
+   *     to do" by the other, once they reach that place in the
+   *     array: leader "jobDest" stores the source node array index (relative
+   *     to array dtd->scaffold) in field "numchildren".
+   *
+   *   - The slower one, pointer dest, looks at the value stored in the
+   *     "numchildren" field (which actually holds a source node array index
+   *     at that time) and puts the real data from dtd->scaffold in.
+   *
+   * - Before the loop starts, jobDest writes source array index 0
+   *   (where the root node is located) so that dest will have something to do
+   *   when it starts operation.
+   *
+   * - Whenever nodes with children are encountered, jobDest appends
+   *   them as new jobs, in order.  As a result, tree node siblings are
+   *   adjacent in the resulting array, for example:
+   *
+   *     [0] root, has two children
+   *       [1] first child of 0, has three children
+   *         [3] first child of 1, does not have children
+   *         [4] second child of 1, does not have children
+   *         [5] third child of 1, does not have children
+   *       [2] second child of 0, does not have children
+   *
+   *   Or (the same data) presented in flat array view:
+   *
+   *     [0] root, has two children
+   *
+   *     [1] first child of 0, has three children
+   *     [2] second child of 0, does not have children
+   *
+   *     [3] first child of 1, does not have children
+   *     [4] second child of 1, does not have children
+   *     [5] third child of 1, does not have children
+   *
+   * - The algorithm repeats until all target array indices have been processed.
+   */
+  { // WINSCP
+  XML_Content *dest = ret; /* tree node writing location, moves upwards */
+  XML_Content *const destLimit = &ret[dtd->scaffCount];
+  XML_Content *jobDest = ret; /* next free writing location in target array */
+  str = (XML_Char *)&ret[dtd->scaffCount];
+
+  /* Add the starting job, the root node (index 0) of the source tree  */
+  (jobDest++)->numchildren = 0;
+
+  for (; dest < destLimit; dest++) {
+    /* Retrieve source tree array index from job storage */
+    const int src_node = (int)dest->numchildren;
+
+    /* Convert item */
+    dest->type = dtd->scaffold[src_node].type;
+    dest->quant = dtd->scaffold[src_node].quant;
+    if (dest->type == XML_CTYPE_NAME) {
+      const XML_Char *src;
+      dest->name = str;
+      src = dtd->scaffold[src_node].name;
+      for (;;) {
+        *str++ = *src;
+        if (! *src)
+          break;
+        src++;
+      }
+      dest->numchildren = 0;
+      dest->children = NULL;
+    } else {
+      unsigned int i;
+      int cn;
+      dest->name = NULL;
+      dest->numchildren = dtd->scaffold[src_node].childcnt;
+      dest->children = jobDest;
+
+      /* Append scaffold indices of children to array */
+      for (i = 0, cn = dtd->scaffold[src_node].firstchild;
+           i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib)
+        (jobDest++)->numchildren = (unsigned int)cn;
+    }
+  }
 
-  build_node(parser, 0, ret, &cpos, &str);
   return ret;
   } // WINSCP
+  } // WINSCP
 }
 
 static ELEMENT_TYPE *
@@ -7422,7 +7513,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
 
 static XML_Char *
 copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
-  int charsRequired = 0;
+  size_t charsRequired = 0;
   XML_Char *result;
 
   /* First determine how long the string is */

+ 1 - 1
libs/expat/lib/xmlrole.c

@@ -11,7 +11,7 @@
    Copyright (c) 2002      Greg Stein <[email protected]>
    Copyright (c) 2002-2006 Karl Waclawek <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
-   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
    Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>

+ 2 - 7
libs/expat/lib/xmltok.c

@@ -11,8 +11,8 @@
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002      Greg Stein <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
-   Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
+   Copyright (c) 2005-2009 Steven Solie <s[email protected]>
+   Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2016      Pascal Cuoq <[email protected]>
    Copyright (c) 2016      Don Lewis <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
@@ -98,11 +98,6 @@
         + ((((byte)[1]) & 3) << 1) + ((((byte)[2]) >> 5) & 1)]                 \
    & (1u << (((byte)[2]) & 0x1F)))
 
-#define UTF8_GET_NAMING(pages, p, n)                                           \
-  ((n) == 2                                                                    \
-       ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p))                   \
-       : ((n) == 3 ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) : 0))
-
 /* Detection of invalid UTF-8 sequences is based on Table 3.1B
    of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/
    with the additional restriction of not allowing the Unicode

+ 12 - 8
libs/expat/lib/xmltok_impl.c

@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2002      Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2002-2016 Karl Waclawek <[email protected]>
-   Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
+   Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2018      Benjamin Peterson <[email protected]>
    Copyright (c) 2018      Anton Maklakov <[email protected]>
@@ -69,7 +69,7 @@
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
-    if (! IS_NAME_CHAR(enc, ptr, n)) {                                         \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NAME_CHAR(enc, ptr, n)) {         \
       *nextTokPtr = ptr;                                                       \
       return XML_TOK_INVALID;                                                  \
     }                                                                          \
@@ -98,7 +98,7 @@
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
-    if (! IS_NMSTRT_CHAR(enc, ptr, n)) {                                       \
+    if (IS_INVALID_CHAR(enc, ptr, n) || ! IS_NMSTRT_CHAR(enc, ptr, n)) {       \
       *nextTokPtr = ptr;                                                       \
       return XML_TOK_INVALID;                                                  \
     }                                                                          \
@@ -1142,6 +1142,10 @@ PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
   case BT_LEAD##n:                                                             \
     if (end - ptr < n)                                                         \
       return XML_TOK_PARTIAL_CHAR;                                             \
+    if (IS_INVALID_CHAR(enc, ptr, n)) {                                        \
+      *nextTokPtr = ptr;                                                       \
+      return XML_TOK_INVALID;                                                  \
+    }                                                                          \
     if (IS_NMSTRT_CHAR(enc, ptr, n)) {                                         \
       ptr += n;                                                                \
       tok = XML_TOK_NAME;                                                      \
@@ -1270,7 +1274,7 @@ PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end,
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
       LEAD_CASE(2)
       LEAD_CASE(3)
@@ -1339,7 +1343,7 @@ PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end,
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
       LEAD_CASE(2)
       LEAD_CASE(3)
@@ -1518,7 +1522,7 @@ PREFIX(getAtts)(const ENCODING *enc, const char *ptr, int attsMax,
       state = inName;                                                          \
     }
 #  define LEAD_CASE(n)                                                         \
-  case BT_LEAD##n:                                                             \
+  case BT_LEAD##n: /* NOTE: The encoding has already been validated. */        \
     START_NAME ptr += (n - MINBPC(enc));                                       \
     break;
       LEAD_CASE(2)
@@ -1730,7 +1734,7 @@ PREFIX(nameLength)(const ENCODING *enc, const char *ptr) {
     switch (BYTE_TYPE(enc, ptr)) {
 #  define LEAD_CASE(n)                                                         \
   case BT_LEAD##n:                                                             \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     break;
       LEAD_CASE(2)
       LEAD_CASE(3)
@@ -1779,7 +1783,7 @@ PREFIX(updatePosition)(const ENCODING *enc, const char *ptr, const char *end,
       if (end - ptr < n) { \
         return; \
       } \
-    ptr += n;                                                                  \
+    ptr += n; /* NOTE: The encoding has already been validated. */             \
     pos->columnNumber++;                                                       \
     break;
       LEAD_CASE(2)

+ 1 - 0
libs/expat/tests/Makefile.in

@@ -516,6 +516,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@

+ 1 - 0
libs/expat/tests/benchmark/Makefile.in

@@ -227,6 +227,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/tests/benchmark/benchmark.c

@@ -7,7 +7,7 @@
                                  |_| XML parser
 
    Copyright (c) 2003-2006 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2017      Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Licensed under the MIT license:

+ 247 - 4
libs/expat/tests/runtests.c

@@ -8,7 +8,7 @@
 
    Copyright (c) 2001-2006 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2003      Greg Stein <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2005-2012 Karl Waclawek <[email protected]>
    Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2017-2018 Rhodri James <[email protected]>
@@ -2664,6 +2664,82 @@ START_TEST(test_dtd_elements) {
 }
 END_TEST
 
+static void XMLCALL
+element_decl_check_model(void *userData, const XML_Char *name,
+                         XML_Content *model) {
+  UNUSED_P(userData);
+  uint32_t errorFlags = 0;
+
+  /* Expected model array structure is this:
+   * [0] (type 6, quant 0)
+   *   [1] (type 5, quant 0)
+   *     [3] (type 4, quant 0, name "bar")
+   *     [4] (type 4, quant 0, name "foo")
+   *     [5] (type 4, quant 3, name "xyz")
+   *   [2] (type 4, quant 2, name "zebra")
+   */
+  errorFlags |= ((xcstrcmp(name, XCS("junk")) == 0) ? 0 : (1u << 0));
+  errorFlags |= ((model != NULL) ? 0 : (1u << 1));
+
+  errorFlags |= ((model[0].type == XML_CTYPE_SEQ) ? 0 : (1u << 2));
+  errorFlags |= ((model[0].quant == XML_CQUANT_NONE) ? 0 : (1u << 3));
+  errorFlags |= ((model[0].numchildren == 2) ? 0 : (1u << 4));
+  errorFlags |= ((model[0].children == &model[1]) ? 0 : (1u << 5));
+  errorFlags |= ((model[0].name == NULL) ? 0 : (1u << 6));
+
+  errorFlags |= ((model[1].type == XML_CTYPE_CHOICE) ? 0 : (1u << 7));
+  errorFlags |= ((model[1].quant == XML_CQUANT_NONE) ? 0 : (1u << 8));
+  errorFlags |= ((model[1].numchildren == 3) ? 0 : (1u << 9));
+  errorFlags |= ((model[1].children == &model[3]) ? 0 : (1u << 10));
+  errorFlags |= ((model[1].name == NULL) ? 0 : (1u << 11));
+
+  errorFlags |= ((model[2].type == XML_CTYPE_NAME) ? 0 : (1u << 12));
+  errorFlags |= ((model[2].quant == XML_CQUANT_REP) ? 0 : (1u << 13));
+  errorFlags |= ((model[2].numchildren == 0) ? 0 : (1u << 14));
+  errorFlags |= ((model[2].children == NULL) ? 0 : (1u << 15));
+  errorFlags |= ((xcstrcmp(model[2].name, XCS("zebra")) == 0) ? 0 : (1u << 16));
+
+  errorFlags |= ((model[3].type == XML_CTYPE_NAME) ? 0 : (1u << 17));
+  errorFlags |= ((model[3].quant == XML_CQUANT_NONE) ? 0 : (1u << 18));
+  errorFlags |= ((model[3].numchildren == 0) ? 0 : (1u << 19));
+  errorFlags |= ((model[3].children == NULL) ? 0 : (1u << 20));
+  errorFlags |= ((xcstrcmp(model[3].name, XCS("bar")) == 0) ? 0 : (1u << 21));
+
+  errorFlags |= ((model[4].type == XML_CTYPE_NAME) ? 0 : (1u << 22));
+  errorFlags |= ((model[4].quant == XML_CQUANT_NONE) ? 0 : (1u << 23));
+  errorFlags |= ((model[4].numchildren == 0) ? 0 : (1u << 24));
+  errorFlags |= ((model[4].children == NULL) ? 0 : (1u << 25));
+  errorFlags |= ((xcstrcmp(model[4].name, XCS("foo")) == 0) ? 0 : (1u << 26));
+
+  errorFlags |= ((model[5].type == XML_CTYPE_NAME) ? 0 : (1u << 27));
+  errorFlags |= ((model[5].quant == XML_CQUANT_PLUS) ? 0 : (1u << 28));
+  errorFlags |= ((model[5].numchildren == 0) ? 0 : (1u << 29));
+  errorFlags |= ((model[5].children == NULL) ? 0 : (1u << 30));
+  errorFlags |= ((xcstrcmp(model[5].name, XCS("xyz")) == 0) ? 0 : (1u << 31));
+
+  XML_SetUserData(g_parser, (void *)(uintptr_t)errorFlags);
+  XML_FreeContentModel(g_parser, model);
+}
+
+START_TEST(test_dtd_elements_nesting) {
+  // Payload inspired by a test in Perl's XML::Parser
+  const char *text = "<!DOCTYPE foo [\n"
+                     "<!ELEMENT junk ((bar|foo|xyz+), zebra*)>\n"
+                     "]>\n"
+                     "<foo/>";
+
+  XML_SetUserData(g_parser, (void *)(uintptr_t)-1);
+
+  XML_SetElementDeclHandler(g_parser, element_decl_check_model);
+  if (XML_Parse(g_parser, text, (int)strlen(text), XML_TRUE)
+      == XML_STATUS_ERROR)
+    xml_failure(g_parser);
+
+  if ((uint32_t)(uintptr_t)XML_GetUserData(g_parser) != 0)
+    fail("Element declaration model regression detected");
+}
+END_TEST
+
 /* Test foreign DTD handling */
 START_TEST(test_set_foreign_dtd) {
   const char *text1 = "<?xml version='1.0' encoding='us-ascii'?>\n";
@@ -3847,6 +3923,30 @@ START_TEST(test_get_buffer_2) {
 }
 END_TEST
 
+/* Test for signed integer overflow CVE-2022-23852 */
+#if defined(XML_CONTEXT_BYTES)
+START_TEST(test_get_buffer_3_overflow) {
+  XML_Parser parser = XML_ParserCreate(NULL);
+  assert(parser != NULL);
+
+  const char *const text = "\n";
+  const int expectedKeepValue = (int)strlen(text);
+
+  // After this call, variable "keep" in XML_GetBuffer will
+  // have value expectedKeepValue
+  if (XML_Parse(parser, text, (int)strlen(text), XML_FALSE /* isFinal */)
+      == XML_STATUS_ERROR)
+    xml_failure(parser);
+
+  assert(expectedKeepValue > 0);
+  if (XML_GetBuffer(parser, INT_MAX - expectedKeepValue + 1) != NULL)
+    fail("enlarging buffer not failed");
+
+  XML_ParserFree(parser);
+}
+END_TEST
+#endif // defined(XML_CONTEXT_BYTES)
+
 /* Test position information macros */
 START_TEST(test_byte_info_at_end) {
   const char *text = "<doc></doc>";
@@ -5974,6 +6074,105 @@ START_TEST(test_utf8_in_cdata_section_2) {
 }
 END_TEST
 
+START_TEST(test_utf8_in_start_tags) {
+  struct test_case {
+    bool goodName;
+    bool goodNameStart;
+    const char *tagName;
+  };
+
+  // The idea with the tests below is this:
+  // We want to cover 1-, 2- and 3-byte sequences, 4-byte sequences
+  // go to isNever and are hence not a concern.
+  //
+  // We start with a character that is a valid name character
+  // (or even name-start character, see XML 1.0r4 spec) and then we flip
+  // single bits at places where (1) the result leaves the UTF-8 encoding space
+  // and (2) we stay in the same n-byte sequence family.
+  //
+  // The flipped bits are highlighted in angle brackets in comments,
+  // e.g. "[<1>011 1001]" means we had [0011 1001] but we now flipped
+  // the most significant bit to 1 to leave UTF-8 encoding space.
+  struct test_case cases[] = {
+      // 1-byte UTF-8: [0xxx xxxx]
+      {true, true, "\x3A"},   // [0011 1010] = ASCII colon ':'
+      {false, false, "\xBA"}, // [<1>011 1010]
+      {true, false, "\x39"},  // [0011 1001] = ASCII nine '9'
+      {false, false, "\xB9"}, // [<1>011 1001]
+
+      // 2-byte UTF-8: [110x xxxx] [10xx xxxx]
+      {true, true, "\xDB\xA5"},   // [1101 1011] [1010 0101] =
+                                  // Arabic small waw U+06E5
+      {false, false, "\x9B\xA5"}, // [1<0>01 1011] [1010 0101]
+      {false, false, "\xDB\x25"}, // [1101 1011] [<0>010 0101]
+      {false, false, "\xDB\xE5"}, // [1101 1011] [1<1>10 0101]
+      {true, false, "\xCC\x81"},  // [1100 1100] [1000 0001] =
+                                  // combining char U+0301
+      {false, false, "\x8C\x81"}, // [1<0>00 1100] [1000 0001]
+      {false, false, "\xCC\x01"}, // [1100 1100] [<0>000 0001]
+      {false, false, "\xCC\xC1"}, // [1100 1100] [1<1>00 0001]
+
+      // 3-byte UTF-8: [1110 xxxx] [10xx xxxx] [10xxxxxx]
+      {true, true, "\xE0\xA4\x85"},   // [1110 0000] [1010 0100] [1000 0101] =
+                                      // Devanagari Letter A U+0905
+      {false, false, "\xA0\xA4\x85"}, // [1<0>10 0000] [1010 0100] [1000 0101]
+      {false, false, "\xE0\x24\x85"}, // [1110 0000] [<0>010 0100] [1000 0101]
+      {false, false, "\xE0\xE4\x85"}, // [1110 0000] [1<1>10 0100] [1000 0101]
+      {false, false, "\xE0\xA4\x05"}, // [1110 0000] [1010 0100] [<0>000 0101]
+      {false, false, "\xE0\xA4\xC5"}, // [1110 0000] [1010 0100] [1<1>00 0101]
+      {true, false, "\xE0\xA4\x81"},  // [1110 0000] [1010 0100] [1000 0001] =
+                                      // combining char U+0901
+      {false, false, "\xA0\xA4\x81"}, // [1<0>10 0000] [1010 0100] [1000 0001]
+      {false, false, "\xE0\x24\x81"}, // [1110 0000] [<0>010 0100] [1000 0001]
+      {false, false, "\xE0\xE4\x81"}, // [1110 0000] [1<1>10 0100] [1000 0001]
+      {false, false, "\xE0\xA4\x01"}, // [1110 0000] [1010 0100] [<0>000 0001]
+      {false, false, "\xE0\xA4\xC1"}, // [1110 0000] [1010 0100] [1<1>00 0001]
+  };
+  const bool atNameStart[] = {true, false};
+
+  size_t i = 0;
+  char doc[1024];
+  size_t failCount = 0;
+
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    size_t j = 0;
+    for (; j < sizeof(atNameStart) / sizeof(atNameStart[0]); j++) {
+      const bool expectedSuccess
+          = atNameStart[j] ? cases[i].goodNameStart : cases[i].goodName;
+      sprintf(doc, "<%s%s><!--", atNameStart[j] ? "" : "a", cases[i].tagName);
+      XML_Parser parser = XML_ParserCreate(NULL);
+
+      const enum XML_Status status
+          = XML_Parse(parser, doc, (int)strlen(doc), /*isFinal=*/XML_FALSE);
+
+      bool success = true;
+      if ((status == XML_STATUS_OK) != expectedSuccess) {
+        success = false;
+      }
+      if ((status == XML_STATUS_ERROR)
+          && (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN)) {
+        success = false;
+      }
+
+      if (! success) {
+        fprintf(
+            stderr,
+            "FAIL case %2u (%sat name start, %u-byte sequence, error code %d)\n",
+            (unsigned)i + 1u, atNameStart[j] ? "    " : "not ",
+            (unsigned)strlen(cases[i].tagName), XML_GetErrorCode(parser));
+        failCount++;
+      }
+
+      XML_ParserFree(parser);
+    }
+  }
+
+  if (failCount > 0) {
+    fail("UTF-8 regression detected");
+  }
+}
+END_TEST
+
 /* Test trailing spaces in elements are accepted */
 static void XMLCALL
 record_element_end_handler(void *userData, const XML_Char *name) {
@@ -6151,6 +6350,14 @@ START_TEST(test_bad_doctype) {
 }
 END_TEST
 
+START_TEST(test_bad_doctype_utf8) {
+  const char *text = "<!DOCTYPE \xDB\x25"
+                     "doc><doc/>"; // [1101 1011] [<0>010 0101]
+  expect_failure(text, XML_ERROR_INVALID_TOKEN,
+                 "Invalid UTF-8 in DOCTYPE not faulted");
+}
+END_TEST
+
 START_TEST(test_bad_doctype_utf16) {
   const char text[] =
       /* <!DOCTYPE doc [ \x06f2 ]><doc/>
@@ -7196,6 +7403,35 @@ START_TEST(test_ns_double_colon_doctype) {
 }
 END_TEST
 
+START_TEST(test_ns_separator_in_uri) {
+  struct test_case {
+    enum XML_Status expectedStatus;
+    const char *doc;
+  };
+  struct test_case cases[] = {
+      {XML_STATUS_OK, "<doc xmlns='one_two' />"},
+      {XML_STATUS_ERROR, "<doc xmlns='one&#x0A;two' />"},
+  };
+
+  size_t i = 0;
+  size_t failCount = 0;
+  for (; i < sizeof(cases) / sizeof(cases[0]); i++) {
+    XML_Parser parser = XML_ParserCreateNS(NULL, '\n');
+    XML_SetElementHandler(parser, dummy_start_element, dummy_end_element);
+    if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc),
+                  /*isFinal*/ XML_TRUE)
+        != cases[i].expectedStatus) {
+      failCount++;
+    }
+    XML_ParserFree(parser);
+  }
+
+  if (failCount) {
+    fail("Namespace separator handling is broken");
+  }
+}
+END_TEST
+
 /* Control variable; the number of times duff_allocator() will successfully
  * allocate */
 #define ALLOC_ALWAYS_SUCCEED (-1)
@@ -7352,7 +7588,7 @@ START_TEST(test_misc_version) {
     fail("Version mismatch");
 
 #if ! defined(XML_UNICODE) || defined(XML_UNICODE_WCHAR_T)
-  if (xcstrcmp(version_text, XCS("expat_2.4.3"))) /* needs bump on releases */
+  if (xcstrcmp(version_text, XCS("expat_2.4.6"))) /* needs bump on releases */
     fail("XML_*_VERSION in expat.h out of sync?\n");
 #else
   /* If we have XML_UNICODE defined but not XML_UNICODE_WCHAR_T
@@ -11286,7 +11522,7 @@ START_TEST(test_accounting_precision) {
       {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
        filled_later},
       {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
-       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+       sizeof(XML_Char) * 5 /* number of predefined entities */, filled_later},
       {"<e1 xmlns='https://example.org/'>\n"
        "  <e2 xmlns=''/>\n"
        "</e1>",
@@ -11296,7 +11532,7 @@ START_TEST(test_accounting_precision) {
       {"<e>text</e>", NULL, NULL, 0, filled_later},
       {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
       {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
-       sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+       sizeof(XML_Char) * 5 /* number of predefined entities */, filled_later},
       {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
 
       /* Prolog */
@@ -11703,6 +11939,7 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_memory_allocation);
   tcase_add_test(tc_basic, test_default_current);
   tcase_add_test(tc_basic, test_dtd_elements);
+  tcase_add_test(tc_basic, test_dtd_elements_nesting);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_set_foreign_dtd);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_foreign_dtd_not_standalone);
   tcase_add_test__ifdef_xml_dtd(tc_basic, test_invalid_foreign_dtd);
@@ -11731,6 +11968,9 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_empty_parse);
   tcase_add_test(tc_basic, test_get_buffer_1);
   tcase_add_test(tc_basic, test_get_buffer_2);
+#if defined(XML_CONTEXT_BYTES)
+  tcase_add_test(tc_basic, test_get_buffer_3_overflow);
+#endif
   tcase_add_test(tc_basic, test_byte_info_at_end);
   tcase_add_test(tc_basic, test_byte_info_at_error);
   tcase_add_test(tc_basic, test_byte_info_at_cdata);
@@ -11814,6 +12054,7 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_ext_entity_utf8_non_bom);
   tcase_add_test(tc_basic, test_utf8_in_cdata_section);
   tcase_add_test(tc_basic, test_utf8_in_cdata_section_2);
+  tcase_add_test(tc_basic, test_utf8_in_start_tags);
   tcase_add_test(tc_basic, test_trailing_spaces_in_elements);
   tcase_add_test(tc_basic, test_utf16_attribute);
   tcase_add_test(tc_basic, test_utf16_second_attr);
@@ -11822,6 +12063,7 @@ make_suite(void) {
   tcase_add_test(tc_basic, test_bad_attr_desc_keyword);
   tcase_add_test(tc_basic, test_bad_attr_desc_keyword_utf16);
   tcase_add_test(tc_basic, test_bad_doctype);
+  tcase_add_test(tc_basic, test_bad_doctype_utf8);
   tcase_add_test(tc_basic, test_bad_doctype_utf16);
   tcase_add_test(tc_basic, test_bad_doctype_plus);
   tcase_add_test(tc_basic, test_bad_doctype_star);
@@ -11878,6 +12120,7 @@ make_suite(void) {
   tcase_add_test(tc_namespace, test_ns_utf16_doctype);
   tcase_add_test(tc_namespace, test_ns_invalid_doctype);
   tcase_add_test(tc_namespace, test_ns_double_colon_doctype);
+  tcase_add_test(tc_namespace, test_ns_separator_in_uri);
 
   suite_add_tcase(s, tc_misc);
   tcase_add_checked_fixture(tc_misc, NULL, basic_teardown);

+ 7 - 3
libs/expat/win32/expat.iss

@@ -36,14 +36,14 @@
 ; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 ; USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-#define expatVer "2.4.3"
+#define expatVer "2.4.6"
 
 [Setup]
 AppName=Expat
 AppId=expat
 AppVersion={#expatVer}
 AppVerName=Expat {#expatVer}
-AppCopyright=Copyright © 1997-2021 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
+AppCopyright=Copyright © 1997-2022 Thai Open Source Software Center, Clark Cooper, and the Expat maintainers
 AppPublisher=The Expat Developers
 AppPublisherURL=https://libexpat.github.io/
 AppSupportURL=https://libexpat.github.io/
@@ -73,16 +73,20 @@ Flags: ignoreversion; Source: COPYING;                      DestDir: "{app}"; De
 Flags: ignoreversion; Source: README.md;                    DestDir: "{app}"; DestName: README.txt
 Flags: ignoreversion; Source: doc\*.html;                   DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: doc\*.css;                    DestDir: "{app}\Doc"
-Flags: ignoreversion; Source: doc\*.png;                    DestDir: "{app}\Doc"
+Flags: ignoreversion; Source: doc\*.xml;                    DestDir: "{app}\Doc"
 Flags: ignoreversion; Source: win32\bin\Release\*.dll;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\bin\Release\*.lib;      DestDir: "{app}\Bin"
 Flags: ignoreversion; Source: win32\README.txt;             DestDir: "{app}\Source"
+Flags: ignoreversion; Source: AUTHORS;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: Changes;                      DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMake.README;                 DestDir: "{app}\Source"
 Flags: ignoreversion; Source: CMakeLists.txt;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: ConfigureChecks.cmake;        DestDir: "{app}\Source"
+Flags: ignoreversion; Source: expat.pc.cmake;               DestDir: "{app}\Source"
 Flags: ignoreversion; Source: expat_config.h.cmake;         DestDir: "{app}\Source"
+Flags: ignoreversion; Source: run.sh.in;                    DestDir: "{app}\Source"
 Flags: ignoreversion; Source: cmake\expat-config.cmake.in;  DestDir: "{app}\Source\cmake"
+Flags: ignoreversion; Source: fuzz\*.c;                     DestDir: "{app}\Source\fuzz"
 Flags: ignoreversion; Source: lib\*.c;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.h;                      DestDir: "{app}\Source\lib"
 Flags: ignoreversion; Source: lib\*.def;                    DestDir: "{app}\Source\lib"

+ 1 - 0
libs/expat/xmlwf/Makefile.in

@@ -235,6 +235,7 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CMAKE_SHARED_LIBRARY_PREFIX = @CMAKE_SHARED_LIBRARY_PREFIX@
 CPPFLAGS = @CPPFLAGS@
 CSCOPE = @CSCOPE@
 CTAGS = @CTAGS@

+ 1 - 1
libs/expat/xmlwf/xmlfile.c

@@ -10,7 +10,7 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2002-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2004-2006 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
    Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>

+ 4 - 4
libs/expat/xmlwf/xmlwf.c

@@ -10,8 +10,8 @@
    Copyright (c) 2000      Clark Cooper <[email protected]>
    Copyright (c) 2001-2003 Fred L. Drake, Jr. <[email protected]>
    Copyright (c) 2004-2009 Karl Waclawek <[email protected]>
-   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
-   Copyright (c) 2016-2021 Sebastian Pipping <[email protected]>
+   Copyright (c) 2005-2007 Steven Solie <s[email protected]>
+   Copyright (c) 2016-2022 Sebastian Pipping <[email protected]>
    Copyright (c) 2017      Rhodri James <[email protected]>
    Copyright (c) 2019      David Loffredo <[email protected]>
    Copyright (c) 2020      Joe Orton <[email protected]>
@@ -1175,9 +1175,9 @@ tmain(int argc, XML_Char **argv) {
       if (! userData.fp) {
         tperror(outName);
         exitCode = XMLWF_EXIT_OUTPUT_ERROR;
+        free(outName);
+        XML_ParserFree(parser);
         if (continueOnError) {
-          free(outName);
-          cleanupUserData(&userData);
           continue;
         } else {
           break;

+ 8 - 5
libs/puttyvs/PuTTYVS.vcxproj

@@ -48,9 +48,10 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WINSCP;WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+      <AdditionalIncludeDirectories>..\..\source\putty</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -65,10 +66,11 @@
       <Optimization>MaxSpeed</Optimization>
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WINSCP;WIN32;NDEBUG;_LIB;SECURITY_WIN32;WINSCP_VS;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <BufferSecurityCheck>false</BufferSecurityCheck>
       <ExceptionHandling>false</ExceptionHandling>
       <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+      <AdditionalIncludeDirectories>..\..\source\putty</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -78,9 +80,10 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="..\..\source\putty\sshaes.c" />
-    <ClCompile Include="..\..\source\putty\sshargon2.c" />
-    <ClCompile Include="..\..\source\putty\sshsh256.c" />
+    <ClCompile Include="..\..\source\putty\crypto\aes-ni.c" />
+    <ClCompile Include="..\..\source\putty\crypto\aes-sw.c" />
+    <ClCompile Include="..\..\source\putty\crypto\argon2.c" />
+    <ClCompile Include="..\..\source\putty\crypto\sha256-sw.c" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 334 - 183
source/Putty.cbproj

@@ -59,14 +59,14 @@
 		<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;$(DCC_Namespace)</DCC_Namespace>
 		<Defines>SECURITY_WIN32;WINSCP;MPEXT;PLATFORM_HAS_SMEMCLR;_WINDOWS;$(Defines)</Defines>
 		<FinalOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</FinalOutputDir>
-		<IncludePath>putty\;putty\windows\;$(BDS)\include;$(IncludePath)</IncludePath>
+		<IncludePath>putty\windows\utils\;putty\utils\;putty\stubs\;putty\ssh\;putty\proxy\;putty\crypto\;putty\;putty\windows\;$(BDS)\include;$(IncludePath)</IncludePath>
 		<IntermediateOutputDir>$(INTERM_PATH)\$(Platform)\$(Config)</IntermediateOutputDir>
 		<Manifest_File>None</Manifest_File>
 		<Multithreaded>true</Multithreaded>
 		<OutputExt>lib</OutputExt>
 		<ProjectType>CppStaticLibrary</ProjectType>
 		<SanitizedProjectName>Putty</SanitizedProjectName>
-		<TLIB_PageSize>128</TLIB_PageSize>
+		<TLIB_PageSize>256</TLIB_PageSize>
 		<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
 		<VerInfo_Locale>1033</VerInfo_Locale>
 	</PropertyGroup>
@@ -94,289 +94,440 @@
 		<TASM_Debugging>None</TASM_Debugging>
 	</PropertyGroup>
 	<ItemGroup>
-		<CppCompile Include="putty\agentf.c">
-			<BuildOrder>60</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\be_misc.c">
-			<BuildOrder>47</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\be_ssh.c">
-			<BuildOrder>84</BuildOrder>
+		<CppCompile Include="putty\be_list.c">
+			<BuildOrder>0</BuildOrder>
 		</CppCompile>
 		<CppCompile Include="putty\callback.c">
-			<BuildOrder>5</BuildOrder>
-			<BuildOrder>25</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\conf.c">
-			<BuildOrder>5</BuildOrder>
-			<BuildOrder>25</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\cproxy.c">
-			<BuildOrder>5</BuildOrder>
-			<BuildOrder>25</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\ecc.c">
-			<BuildOrder>73</BuildOrder>
+			<BuildOrder>1</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\errsock.c">
-			<BuildOrder>44</BuildOrder>
+		<CppCompile Include="putty\crypto\aes-common.c">
+			<BuildOrder>9</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\import.c">
-			<BuildOrder>46</BuildOrder>
+		<CppCompile Include="putty\crypto\aesold.c">
+			<BuildOrder>150</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\logging.c">
-			<BuildOrder>27</BuildOrder>
+		<CppCompile Include="putty\crypto\aes-select.c">
 			<BuildOrder>11</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\mainchan.c">
-			<BuildOrder>69</BuildOrder>
-		</CppCompile>
-		<CppCompile Include="putty\marshal.c">
-			<BuildOrder>55</BuildOrder>
+		<CppCompile Include="putty\crypto\aes-sw.c">
+			<BuildOrder>12</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\memory.c">
-			<BuildOrder>73</BuildOrder>
+		<CppCompile Include="putty\crypto\arcfour.c">
+			<BuildOrder>13</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\misc.c">
-			<BuildOrder>26</BuildOrder>
+		<CppCompile Include="putty\crypto\argon2.c">
 			<BuildOrder>14</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\miscucs.c">
-			<BuildOrder>51</BuildOrder>
+		<CppCompile Include="putty\crypto\bcrypt.c">
+			<BuildOrder>15</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\mpint.c">
-			<BuildOrder>74</BuildOrder>
+		<CppCompile Include="putty\crypto\blake2.c">
+			<BuildOrder>16</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\noshare.c">
-			<BuildOrder>41</BuildOrder>
+		<CppCompile Include="putty\crypto\blowfish.c">
+			<BuildOrder>17</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\nullplug.c">
-			<BuildOrder>56</BuildOrder>
+		<CppCompile Include="putty\crypto\chacha20-poly1305.c">
+			<BuildOrder>24</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\pgssapi.c">
-			<BuildOrder>37</BuildOrder>
+		<CppCompile Include="putty\crypto\des.c">
+			<BuildOrder>18</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\portfwd.c">
-			<BuildOrder>23</BuildOrder>
-			<BuildOrder>17</BuildOrder>
+		<CppCompile Include="putty\crypto\diffie-hellman.c">
+			<BuildOrder>19</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\proxy.c">
+		<CppCompile Include="putty\crypto\dsa.c">
 			<BuildOrder>20</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\settings.c">
-			<BuildOrder>82</BuildOrder>
+		<CppCompile Include="putty\crypto\ecc-arithmetic.c">
+			<BuildOrder>21</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\crypto\ecc-ssh.c">
+			<BuildOrder>22</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\crypto\hash_simple.c">
+			<BuildOrder>150</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh.c">
+		<CppCompile Include="putty\crypto\hmac.c">
 			<BuildOrder>23</BuildOrder>
-			<BuildOrder>19</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2bpp.c">
-			<BuildOrder>54</BuildOrder>
+		<CppCompile Include="putty\crypto\mac.c">
+			<BuildOrder>25</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2bpp-bare.c">
-			<BuildOrder>59</BuildOrder>
+		<CppCompile Include="putty\crypto\mac_simple.c">
+			<BuildOrder>26</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2censor.c">
-			<BuildOrder>58</BuildOrder>
+		<CppCompile Include="putty\crypto\md5.c">
+			<BuildOrder>27</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2connection.c">
-			<BuildOrder>66</BuildOrder>
+		<CppCompile Include="putty\crypto\mpint.c">
+			<BuildOrder>28</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2connection-client.c">
-			<BuildOrder>72</BuildOrder>
+		<CppCompile Include="putty\crypto\prng.c">
+			<BuildOrder>29</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2kex-client.c">
-			<BuildOrder>73</BuildOrder>
+		<CppCompile Include="putty\crypto\pubkey-pem.c">
+			<BuildOrder>30</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2transhk.c">
-			<BuildOrder>70</BuildOrder>
+		<CppCompile Include="putty\crypto\pubkey-ppk.c">
+			<BuildOrder>31</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2transport.c">
-			<BuildOrder>67</BuildOrder>
+		<CppCompile Include="putty\crypto\rsa.c">
+			<BuildOrder>32</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssh2userauth.c">
-			<BuildOrder>68</BuildOrder>
+		<CppCompile Include="putty\crypto\sha1-common.c">
+			<BuildOrder>33</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshaes.c">
-			<BuildOrder>26</BuildOrder>
-			<BuildOrder>22</BuildOrder>
+		<CppCompile Include="putty\crypto\sha1-select.c">
+			<BuildOrder>34</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshaesold.c">
-			<BuildOrder>77</BuildOrder>
+		<CppCompile Include="putty\crypto\sha1-sw.c">
+			<BuildOrder>35</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\ssharcf.c">
-			<BuildOrder>29</BuildOrder>
-			<BuildOrder>21</BuildOrder>
+		<CppCompile Include="putty\crypto\sha256-common.c">
+			<BuildOrder>37</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshargon2.c">
-			<BuildOrder>29</BuildOrder>
+		<CppCompile Include="putty\crypto\sha256-select.c">
+			<BuildOrder>38</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshauxcrypt.c">
-			<BuildOrder>78</BuildOrder>
+		<CppCompile Include="putty\crypto\sha256-sw.c">
+			<BuildOrder>39</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshbcrypt.c">
-			<BuildOrder>50</BuildOrder>
+		<CppCompile Include="putty\crypto\sha3.c">
+			<BuildOrder>36</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshblake2.c">
-			<BuildOrder>50</BuildOrder>
+		<CppCompile Include="putty\crypto\sha512-common.c">
+			<BuildOrder>40</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshblowf.c">
-			<BuildOrder>34</BuildOrder>
-			<BuildOrder>32</BuildOrder>
+		<CppCompile Include="putty\crypto\sha512-select.c">
+			<BuildOrder>41</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshccp.c">
-			<BuildOrder>49</BuildOrder>
+		<CppCompile Include="putty\crypto\sha512-sw.c">
+			<BuildOrder>42</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshcommon.c">
-			<BuildOrder>62</BuildOrder>
+		<CppCompile Include="putty\errsock.c">
+			<BuildOrder>2</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshdes.c">
+		<CppCompile Include="putty\import.c">
+			<BuildOrder>3</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\logging.c">
+			<BuildOrder>4</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\proxy\cproxy.c">
 			<BuildOrder>44</BuildOrder>
-			<BuildOrder>32</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshdh.c">
-			<BuildOrder>47</BuildOrder>
-			<BuildOrder>29</BuildOrder>
+		<CppCompile Include="putty\proxy\http_p.c">
+			<BuildOrder>45</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshdss.c">
-			<BuildOrder>50</BuildOrder>
-			<BuildOrder>28</BuildOrder>
+		<CppCompile Include="putty\proxy\interactor.c">
+			<BuildOrder>46</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\proxy\local.c">
+			<BuildOrder>47</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshecc.c">
+		<CppCompile Include="putty\proxy\nosshproxy.c">
 			<BuildOrder>48</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshgssc.c">
-			<BuildOrder>38</BuildOrder>
+		<CppCompile Include="putty\proxy\proxy.c">
+			<BuildOrder>50</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshhmac.c">
-			<BuildOrder>79</BuildOrder>
+		<CppCompile Include="putty\proxy\socks4.c">
+			<BuildOrder>51</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshmac.c">
-			<BuildOrder>61</BuildOrder>
+		<CppCompile Include="putty\proxy\socks5.c">
+			<BuildOrder>52</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshmd5.c">
-			<BuildOrder>53</BuildOrder>
-			<BuildOrder>31</BuildOrder>
+		<CppCompile Include="putty\proxy\telnet.c">
+			<BuildOrder>54</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshprng.c">
-			<BuildOrder>80</BuildOrder>
+		<CppCompile Include="putty\settings.c">
+			<BuildOrder>5</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshpubk.c">
+		<CppCompile Include="putty\ssh\agentf.c">
+			<BuildOrder>55</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\bpp2.c">
 			<BuildOrder>56</BuildOrder>
-			<BuildOrder>30</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshrand.c">
+		<CppCompile Include="putty\ssh\bpp-bare.c">
+			<BuildOrder>57</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\censor2.c">
+			<BuildOrder>58</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\common_p.c">
 			<BuildOrder>59</BuildOrder>
-			<BuildOrder>18</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshrsa.c">
+		<CppCompile Include="putty\ssh\connection2.c">
+			<BuildOrder>60</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\connection2-client.c">
+			<BuildOrder>61</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\gssc.c">
 			<BuildOrder>62</BuildOrder>
-			<BuildOrder>6</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshsh256.c">
+		<CppCompile Include="putty\ssh\kex2-client.c">
+			<BuildOrder>63</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\mainchan.c">
+			<BuildOrder>64</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\nosharing.c">
 			<BuildOrder>65</BuildOrder>
-			<BuildOrder>5</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshsh512.c">
-			<BuildOrder>8</BuildOrder>
+		<CppCompile Include="putty\ssh\pgssapi.c">
+			<BuildOrder>66</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\portfwd.c">
+			<BuildOrder>67</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\sharing.c">
 			<BuildOrder>68</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshsha.c">
+		<CppCompile Include="putty\ssh\ssh.c">
+			<BuildOrder>69</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\transient-hostkey-cache.c">
+			<BuildOrder>70</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\transport2.c">
 			<BuildOrder>71</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\userauth2-client.c">
+			<BuildOrder>72</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\verstring.c">
+			<BuildOrder>73</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\ssh\zlib.c">
+			<BuildOrder>75</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\sshpubk.c">
 			<BuildOrder>7</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshsha3.c">
-			<BuildOrder>42</BuildOrder>
+		<CppCompile Include="putty\sshrand.c">
+			<BuildOrder>8</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshshare.c">
-			<BuildOrder>42</BuildOrder>
+		<CppCompile Include="putty\stubs\nullplug.c">
+			<BuildOrder>76</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshutils.c">
-			<BuildOrder>63</BuildOrder>
+		<CppCompile Include="putty\utils\antispoof.c">
+			<BuildOrder>77</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshverstring.c">
-			<BuildOrder>63</BuildOrder>
+		<CppCompile Include="putty\utils\backend_socket_log.c">
+			<BuildOrder>78</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\sshzlib.c">
-			<BuildOrder>74</BuildOrder>
-			<BuildOrder>4</BuildOrder>
+		<CppCompile Include="putty\utils\base64_decode_atom.c">
+			<BuildOrder>79</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\base64_encode_atom.c">
+			<BuildOrder>80</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\stripctrl.c">
+		<CppCompile Include="putty\utils\bufchain.c">
 			<BuildOrder>81</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\tree234.c">
-			<BuildOrder>77</BuildOrder>
-			<BuildOrder>1</BuildOrder>
+		<CppCompile Include="putty\utils\burnstr.c">
+			<BuildOrder>151</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\utils.c">
-			<BuildOrder>75</BuildOrder>
+		<CppCompile Include="putty\utils\conf.c">
+			<BuildOrder>82</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\wildcard.c">
+		<CppCompile Include="putty\utils\conf_launchable.c">
 			<BuildOrder>83</BuildOrder>
-			<BuildOrder>3</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\wincapi.c">
-			<BuildOrder>83</BuildOrder>
+		<CppCompile Include="putty\utils\ctrlparse.c">
+			<BuildOrder>84</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\windefs.c">
-			<BuildOrder>83</BuildOrder>
+		<CppCompile Include="putty\utils\default_description.c">
+			<BuildOrder>85</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\wingss.c">
+		<CppCompile Include="putty\utils\dup_mb_to_wc.c">
 			<BuildOrder>86</BuildOrder>
-			<BuildOrder>2</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winhandl.c">
-			<BuildOrder>43</BuildOrder>
+		<CppCompile Include="putty\utils\dupcat.c">
+			<BuildOrder>148</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winhsock.c">
-			<BuildOrder>45</BuildOrder>
+		<CppCompile Include="putty\utils\dupprintf.c">
+			<BuildOrder>87</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\dupstr.c">
+			<BuildOrder>152</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winmisc.c">
+		<CppCompile Include="putty\utils\host_strchr.c">
 			<BuildOrder>92</BuildOrder>
-			<BuildOrder>14</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winmiscs.c">
-			<BuildOrder>76</BuildOrder>
+		<CppCompile Include="putty\utils\host_strchr_internal.c">
+			<BuildOrder>93</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\host_strcspn.c">
+			<BuildOrder>90</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\host_strduptrim.c">
+			<BuildOrder>91</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnet.c">
+		<CppCompile Include="putty\utils\host_strrchr.c">
+			<BuildOrder>94</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\log_proxy_stderr.c">
 			<BuildOrder>95</BuildOrder>
-			<BuildOrder>17</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnoise.c">
+		<CppCompile Include="putty\utils\make_spr_sw_abort_static.c">
+			<BuildOrder>153</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\marshal.c">
+			<BuildOrder>96</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\memory.c">
+			<BuildOrder>97</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\memxor.c">
 			<BuildOrder>98</BuildOrder>
-			<BuildOrder>16</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnojmp.c">
-			<BuildOrder>39</BuildOrder>
+		<CppCompile Include="putty\utils\null_lp.c">
+			<BuildOrder>99</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winnpc.c">
-			<BuildOrder>39</BuildOrder>
+		<CppCompile Include="putty\utils\nullseat.c">
+			<BuildOrder>100</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winpgntc.c">
-			<BuildOrder>13</BuildOrder>
+		<CppCompile Include="putty\utils\nullstrcmp.c">
 			<BuildOrder>101</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winproxy.c">
+		<CppCompile Include="putty\utils\out_of_memory.c">
+			<BuildOrder>102</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\parse_blocksize.c">
+			<BuildOrder>103</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\prompts.c">
 			<BuildOrder>104</BuildOrder>
-			<BuildOrder>10</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winsecur.c">
-			<BuildOrder>43</BuildOrder>
+		<CppCompile Include="putty\utils\ptrlen.c">
+			<BuildOrder>105</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winstore.c">
-			<BuildOrder>9</BuildOrder>
+		<CppCompile Include="putty\utils\seat_connection_fatal.c">
 			<BuildOrder>107</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\wintime.c">
-			<BuildOrder>12</BuildOrder>
+		<CppCompile Include="putty\utils\sk_free_peer_info.c">
+			<BuildOrder>108</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\smemclr.c">
+			<BuildOrder>109</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\smemeq.c">
 			<BuildOrder>110</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\windows\winucs.c">
-			<BuildOrder>52</BuildOrder>
+		<CppCompile Include="putty\utils\spr_get_error_message.c">
+			<BuildOrder>111</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\ssh2_pick_fingerprint.c">
+			<BuildOrder>112</BuildOrder>
 		</CppCompile>
-		<CppCompile Include="putty\x11fwd.c">
+		<CppCompile Include="putty\utils\sshutils.c">
 			<BuildOrder>113</BuildOrder>
-			<BuildOrder>11</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\strbuf.c">
+			<BuildOrder>114</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\string_length_for_printf.c">
+			<BuildOrder>154</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\stripctrl.c">
+			<BuildOrder>115</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\tempseat.c">
+			<BuildOrder>116</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\tree234.c">
+			<BuildOrder>117</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\version.c">
+			<BuildOrder>119</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\wildcard.c">
+			<BuildOrder>120</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\utils\write_c_string_literal.c">
+			<BuildOrder>121</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\agent-client.c">
+			<BuildOrder>127</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\gss.c">
+			<BuildOrder>128</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\handle-io.c">
+			<BuildOrder>129</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\handle-socket.c">
+			<BuildOrder>130</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\handle-wait.c">
+			<BuildOrder>151</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\local-proxy.c">
+			<BuildOrder>131</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\named-pipe-client.c">
+			<BuildOrder>132</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\network.c">
+			<BuildOrder>133</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\noise.c">
+			<BuildOrder>134</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\no-jump-list.c">
+			<BuildOrder>135</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\storage.c">
+			<BuildOrder>136</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\unicode.c">
+			<BuildOrder>137</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\agent_named_pipe_name.c">
+			<BuildOrder>138</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\cryptoapi.c">
+			<BuildOrder>139</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\defaults.c">
+			<BuildOrder>140</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\dll_hijacking_protection.c">
+			<BuildOrder>141</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\escape_registry_key.c">
+			<BuildOrder>142</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\filename.c">
+			<BuildOrder>143</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\fontspec.c">
+			<BuildOrder>144</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\get_system_dir.c">
+			<BuildOrder>147</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\get_username.c">
+			<BuildOrder>145</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\load_system32_dll.c">
+			<BuildOrder>154</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\ltime.c">
+			<BuildOrder>147</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\open_for_write_would_lose_data.c">
+			<BuildOrder>149</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\security_p.c">
+			<BuildOrder>150</BuildOrder>
+		</CppCompile>
+		<CppCompile Include="putty\windows\utils\win_strerror.c">
+			<BuildOrder>152</BuildOrder>
 		</CppCompile>
 		<BuildConfiguration Include="Base">
 			<Key>Base</Key>

+ 1 - 0
source/components/UnixDirView.cpp

@@ -1021,6 +1021,7 @@ TDateTime __fastcall TUnixDirView::ItemFileTime(TListItem * Item,
       break;
 
     case mfMDHM:
+    case mfYMDHM:
       Precision = tpMinute;
       break;
 

+ 15 - 1
source/components/UnixDriveView.cpp

@@ -52,6 +52,7 @@ __fastcall TCustomUnixDriveView::TCustomUnixDriveView(TComponent* Owner) :
   FDummyDragFile = NULL;
   FPendingDelete = new TList();
   FDragDropFilesEx->PreferCopy = true;
+  FChangingDirectory = false;
 }
 //---------------------------------------------------------------------------
 __fastcall TCustomUnixDriveView::~TCustomUnixDriveView()
@@ -473,6 +474,13 @@ void __fastcall TCustomUnixDriveView::Delete(TTreeNode * Node)
   }
 }
 //---------------------------------------------------------------------------
+bool __fastcall TCustomUnixDriveView::CanChange(TTreeNode * Node)
+{
+  return
+    TCustomDriveView::CanChange(Node) &&
+    !FChangingDirectory;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomUnixDriveView::Change(TTreeNode * Node)
 {
   #ifndef DESIGN_ONLY
@@ -512,7 +520,13 @@ void __fastcall TCustomUnixDriveView::Change(TTreeNode * Node)
         }
         try
         {
-          Terminal->ChangeDirectory(NodePathName(Node));
+          {
+            // Prevent curther changes while loading the folder.
+            // Particularly prevent user from trying to proceed with incremental search.
+            TValueRestorer<bool> ChangingDirectoryRestorer(FChangingDirectory);
+            FChangingDirectory = true;
+            Terminal->ChangeDirectory(NodePathName(Node));
+          }
           TCustomDriveView::Change(Node);
         }
         __finally

+ 2 - 0
source/components/UnixDriveView.h

@@ -33,6 +33,7 @@ protected:
   DYNAMIC void __fastcall Change(TTreeNode * Node);
   virtual void __fastcall CreateWnd();
   virtual void __fastcall DestroyWnd();
+  DYNAMIC bool __fastcall CanChange(TTreeNode * Node);
 
   void __fastcall LoadDirectory();
   TTreeNode * __fastcall LoadPath(UnicodeString Path);
@@ -100,6 +101,7 @@ private:
   bool FShowInaccesibleDirectories;
   TRemoteFile * FDummyDragFile;
   TList * FPendingDelete;
+  bool FChangingDirectory;
 
   bool __fastcall IsRootNameStored();
   void __fastcall SetShowInaccesibleDirectories(bool value);

+ 0 - 5
source/core/Configuration.cpp

@@ -1169,11 +1169,6 @@ void __fastcall TConfiguration::SetNulStorage()
   FStorage = stNul;
 }
 //---------------------------------------------------------------------------
-void __fastcall TConfiguration::SetDefaultStorage()
-{
-  FStorage = stDetect;
-}
-//---------------------------------------------------------------------------
 void __fastcall TConfiguration::SetExplicitIniFileStorageName(const UnicodeString & FileName)
 {
   FIniFileStorageName = FileName;

+ 0 - 1
source/core/Configuration.h

@@ -225,7 +225,6 @@ public:
   void __fastcall ScheduleCustomIniFileStorageUse(const UnicodeString & ACustomIniFileStorageName);
   void __fastcall SetExplicitIniFileStorageName(const UnicodeString & FileName);
   void __fastcall SetNulStorage();
-  void __fastcall SetDefaultStorage();
   UnicodeString __fastcall GetAutomaticIniFileStorageName(bool ReadingOnly);
   UnicodeString __fastcall GetDefaultIniFileExportPath();
   UnicodeString __fastcall GetIniFileParamValue();

+ 3 - 5
source/core/FtpFileSystem.cpp

@@ -4344,9 +4344,7 @@ void __fastcall TFTPFileSystem::RemoteFileTimeToDateTimeAndPrecision(const TRemo
       DateTime = DateTime +
         EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
           (unsigned short)Source.Second, 0);
-      // not exact as we got year as well, but it is most probably
-      // guessed by FZAPI anyway
-      ModificationFmt = Source.HasSeconds ? mfFull : mfMDHM;
+      ModificationFmt = Source.HasSeconds ? mfFull : (Source.HasYear ? mfYMDHM : mfMDHM);
 
       // With IIS, the Utc should be false only for MDTM
       if (FWindowsServer && !Source.Utc)
@@ -4469,11 +4467,11 @@ bool __fastcall TFTPFileSystem::HandleListData(const wchar_t * Path,
       catch (Exception & E)
       {
         UnicodeString EntryData =
-          FORMAT(L"%s/%s/%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
+          FORMAT(L"%s/%s/%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
             (Entry->Name, Entry->Permissions, Entry->HumanPerm, Entry->Owner, Entry->Group, Entry->OwnerGroup, IntToStr(Entry->Size),
              int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
              Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
-             int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
+             int(Entry->Time.HasYear), int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
         throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
       }
 

+ 2 - 1
source/core/Interface.h

@@ -141,7 +141,8 @@ enum TPromptKind
   pkCryptoCard,
   pkKeybInteractive,
   pkPassword,
-  pkNewPassword
+  pkNewPassword,
+  pkProxyAuth
 };
 
 enum TPromptUserParam { pupEcho = 0x01, pupRemember = 0x02 };

+ 54 - 27
source/core/PuttyIntf.cpp

@@ -25,7 +25,7 @@ THierarchicalStorage * PuttyStorage = NULL;
 //---------------------------------------------------------------------------
 extern "C"
 {
-#include <winstuff.h>
+#include <windows\platform.h>
 }
 const UnicodeString OriginalPuttyRegistryStorageKey(_T(PUTTY_REG_POS));
 const UnicodeString KittyRegistryStorageKey(L"Software\\9bis.com\\KiTTY");
@@ -92,6 +92,18 @@ bool RandomSeedExists()
     FileExists(ApiPath(Configuration->RandomSeedFileName));
 }
 //---------------------------------------------------------------------------
+TSecureShell * GetSeatSecureShell(Seat * seat)
+{
+  DebugAssert(seat != NULL);
+  if (is_tempseat(seat))
+  {
+    seat = tempseat_get_real(seat);
+  }
+
+  TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
+  return SecureShell;
+}
+//---------------------------------------------------------------------------
 TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
 {
   if (!is_ssh(plug) && !is_pfwd(plug))
@@ -113,9 +125,7 @@ TSecureShell * GetSecureShell(Plug * plug, bool & pfwd)
     seat = get_ssh_seat(plug);
   }
   DebugAssert(seat != NULL);
-
-  TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
-  return SecureShell;
+  return GetSeatSecureShell(seat);
 }
 //---------------------------------------------------------------------------
 struct callback_set * get_callback_set(Plug * plug)
@@ -127,7 +137,7 @@ struct callback_set * get_callback_set(Plug * plug)
 //---------------------------------------------------------------------------
 struct callback_set * get_seat_callback_set(Seat * seat)
 {
-  TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
+  TSecureShell * SecureShell = GetSeatSecureShell(seat);
   return SecureShell->GetCallbackSet();
 }
 //---------------------------------------------------------------------------
@@ -147,14 +157,14 @@ extern "C" const char * do_select(Plug * plug, SOCKET skt, bool enable)
   return NULL;
 }
 //---------------------------------------------------------------------------
-static size_t output(Seat * seat, bool is_stderr, const void * data, size_t len)
+static size_t output(Seat * seat, SeatOutputType type, const void * data, size_t len)
 {
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
-  if (static_cast<int>(static_cast<char>(is_stderr)) == -1)
+  if (static_cast<int>(static_cast<char>(type)) == -1)
   {
     SecureShell->CWrite(reinterpret_cast<const char *>(data), len);
   }
-  else if (!is_stderr)
+  else if (type != SEAT_OUTPUT_STDERR)
   {
     SecureShell->FromBackend(reinterpret_cast<const unsigned char *>(data), len);
   }
@@ -170,13 +180,13 @@ static bool eof(Seat *)
   return false;
 }
 //---------------------------------------------------------------------------
-static int get_userpass_input(Seat * seat, prompts_t * p, bufchain * DebugUsedArg(input))
+static SeatPromptResult get_userpass_input(Seat * seat, prompts_t * p)
 {
   DebugAssert(p != NULL);
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   DebugAssert(SecureShell != NULL);
 
-  int Result;
+  SeatPromptResult Result;
   TStrings * Prompts = new TStringList();
   TStrings * Results = new TStringList();
   try
@@ -223,11 +233,11 @@ static int get_userpass_input(Seat * seat, prompts_t * p, bufchain * DebugUsedAr
         }
         prompt_set_result(Prompt, S.c_str());
       }
-      Result = 1;
+      Result = SPR_OK;
     }
     else
     {
-      Result = 0;
+      Result = SPR_USER_ABORT;
     }
   }
   __finally
@@ -246,9 +256,9 @@ static void connection_fatal(Seat * seat, const char * message)
   SecureShell->PuttyFatalError(UnicodeString(AnsiString(message)));
 }
 //---------------------------------------------------------------------------
-int verify_ssh_host_key(Seat * seat, const char * host, int port, const char * keytype,
-  char * keystr, const char * DebugUsedArg(keydisp), char ** key_fingerprints,
-  void (*DebugUsedArg(callback))(void *ctx, int result), void * DebugUsedArg(ctx))
+SeatPromptResult confirm_ssh_host_key(Seat * seat, const char * host, int port, const char * keytype,
+  char * keystr, const char * DebugUsedArg(keydisp), char ** key_fingerprints, bool DebugUsedArg(mismatch),
+  void (*DebugUsedArg(callback))(void *ctx, SeatPromptResult result), void * DebugUsedArg(ctx))
 {
   UnicodeString FingerprintSHA256, FingerprintMD5;
   if (key_fingerprints[SSH_FPTYPE_SHA256] != NULL)
@@ -263,7 +273,7 @@ int verify_ssh_host_key(Seat * seat, const char * host, int port, const char * k
   SecureShell->VerifyHostKey(host, port, keytype, keystr, FingerprintSHA256, FingerprintMD5);
 
   // We should return 0 when key was not confirmed, we throw exception instead.
-  return 1;
+  return SPR_OK;
 }
 //---------------------------------------------------------------------------
 bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
@@ -273,20 +283,20 @@ bool have_ssh_host_key(Seat * seat, const char * hostname, int port,
   return SecureShell->HaveHostKey(hostname, port, keytype) ? 1 : 0;
 }
 //---------------------------------------------------------------------------
-int confirm_weak_crypto_primitive(Seat * seat, const char * algtype, const char * algname,
-  void (*/*callback*/)(void * ctx, int result), void * /*ctx*/)
+SeatPromptResult confirm_weak_crypto_primitive(Seat * seat, const char * algtype, const char * algname,
+  void (*/*callback*/)(void * ctx, SeatPromptResult result), void * /*ctx*/)
 {
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
   SecureShell->AskAlg(algtype, algname);
 
   // We should return 0 when alg was not confirmed, we throw exception instead.
-  return 1;
+  return SPR_OK;
 }
 //---------------------------------------------------------------------------
-int confirm_weak_cached_hostkey(Seat *, const char * /*algname*/, const char * /*betteralgs*/,
-  void (*/*callback*/)(void *ctx, int result), void * /*ctx*/)
+SeatPromptResult confirm_weak_cached_hostkey(Seat *, const char * /*algname*/, const char * /*betteralgs*/,
+  void (*/*callback*/)(void *ctx, SeatPromptResult result), void * /*ctx*/)
 {
-  return 1;
+  return SPR_OK;
 }
 //---------------------------------------------------------------------------
 void old_keyfile_warning(void)
@@ -294,11 +304,22 @@ void old_keyfile_warning(void)
   // no reference to TSecureShell instance available
 }
 //---------------------------------------------------------------------------
-void display_banner(Seat * seat, const char * banner, int size)
+size_t banner(Seat * seat, const void * data, size_t len)
 {
   TSecureShell * SecureShell = static_cast<ScpSeat *>(seat)->SecureShell;
-  UnicodeString Banner(UTF8String(banner, size));
+  UnicodeString Banner(UTF8String(static_cast<const char *>(data), len));
   SecureShell->DisplayBanner(Banner);
+  return 0; // PuTTY never uses the value
+}
+//---------------------------------------------------------------------------
+uintmax_t strtoumax(const char *nptr, char **endptr, int base)
+{
+  if (DebugAlwaysFalse(endptr != NULL) ||
+      DebugAlwaysFalse(base != 10))
+  {
+    Abort();
+  }
+  return StrToInt64(UnicodeString(AnsiString(nptr)));
 }
 //---------------------------------------------------------------------------
 static void SSHFatalError(const char * Format, va_list Param)
@@ -384,13 +405,17 @@ static const SeatVtable ScpSeatVtable =
   {
     output,
     eof,
+    nullseat_sent,
+    banner,
     get_userpass_input,
+    nullseat_notify_session_started,
     nullseat_notify_remote_exit,
+    nullseat_notify_remote_disconnect,
     connection_fatal,
     nullseat_update_specials_menu,
     nullseat_get_ttymode,
     nullseat_set_busy_status,
-    verify_ssh_host_key,
+    confirm_ssh_host_key,
     confirm_weak_crypto_primitive,
     confirm_weak_cached_hostkey,
     nullseat_is_always_utf8,
@@ -399,7 +424,9 @@ static const SeatVtable ScpSeatVtable =
     nullseat_get_windowid,
     nullseat_get_window_pixel_size,
     nullseat_stripctrl_new,
-    nullseat_set_trust_status_vacuously,
+    nullseat_set_trust_status,
+    nullseat_can_set_trust_status_yes,
+    nullseat_has_mixed_input_stream_yes,
     nullseat_verbose_yes,
     nullseat_interactive_no,
     nullseat_get_cursor_position,
@@ -989,7 +1016,7 @@ UnicodeString __fastcall ParseOpenSshPubLine(const UnicodeString & Line, const s
 UnicodeString __fastcall GetKeyTypeHuman(const UnicodeString & KeyType)
 {
   UnicodeString Result;
-  if (KeyType == ssh_dss.cache_id)
+  if (KeyType == ssh_dsa.cache_id)
   {
     Result = L"DSA";
   }

+ 1 - 1
source/core/PuttyIntf.h

@@ -18,7 +18,7 @@ extern "C"
 #include <ssh.h>
 #undef new
 #include <puttyexp.h>
-#include <proxy.h>
+#include <proxy\proxy.h>
 #include <storage.h>
 // Defined in misc.h - Conflicts with std::min/max
 #undef min

+ 11 - 3
source/core/RemoteFiles.cpp

@@ -407,6 +407,7 @@ TDateTime __fastcall ReduceDateTimePrecision(TDateTime DateTime,
     DecodeTime(DateTime, H, N, S, MS);
     switch (Precision)
     {
+      case mfYMDHM:
       case mfMDHM:
         S = 0;
         MS = 0;
@@ -443,6 +444,7 @@ UnicodeString __fastcall UserModificationStr(TDateTime DateTime,
       return L"";
     case mfMDY:
       return FormatDateTime(L"ddddd", DateTime);
+    case mfYMDHM:
     case mfMDHM:
       return FormatDateTime(L"ddddd t", DateTime);
     case mfFull:
@@ -469,6 +471,10 @@ UnicodeString __fastcall ModificationStr(TDateTime DateTime,
       return FORMAT(L"%3s %2d %2d:%2.2d",
         (EngShortMonthNames[Month-1], Day, Hour, Min));
 
+    case mfYMDHM:
+      return FORMAT(L"%3s %2d %2d:%2.2d %4d",
+        (EngShortMonthNames[Month-1], Day, Hour, Min, Year));
+
     default:
       DebugFail();
       // fall thru
@@ -1024,7 +1030,7 @@ bool __fastcall TRemoteFile::IsTimeShiftingApplicable()
 //---------------------------------------------------------------------------
 bool __fastcall TRemoteFile::IsTimeShiftingApplicable(TModificationFmt ModificationFmt)
 {
-  return (ModificationFmt == mfMDHM) || (ModificationFmt == mfFull);
+  return (ModificationFmt == mfMDHM) || (ModificationFmt == mfYMDHM) || (ModificationFmt == mfFull);
 }
 //---------------------------------------------------------------------------
 void __fastcall TRemoteFile::ShiftTimeInSeconds(__int64 Seconds)
@@ -1296,7 +1302,7 @@ void __fastcall TRemoteFile::SetListingStr(UnicodeString value)
             {
               Year = (Word)StrToInt(Col);
               if (Year > 10000) Abort();
-              // When we don't got time we assume midnight
+              // When we didn't get time we assume midnight
               Hour = 0; Min = 0; Sec = 0;
               FModificationFmt = mfMDY;
             }
@@ -1306,7 +1312,9 @@ void __fastcall TRemoteFile::SetListingStr(UnicodeString value)
         FModification = EncodeDateVerbose(Year, Month, Day) + EncodeTimeVerbose(Hour, Min, Sec, 0);
         // adjust only when time is known,
         // adjusting default "midnight" time makes no sense
-        if ((FModificationFmt == mfMDHM) || (FModificationFmt == mfFull))
+        if ((FModificationFmt == mfMDHM) ||
+             DebugAlwaysFalse(FModificationFmt == mfYMDHM) ||
+             (FModificationFmt == mfFull))
         {
           DebugAssert(Terminal != NULL);
           FModification = AdjustDateTimeFromUnix(FModification,

+ 1 - 1
source/core/RemoteFiles.h

@@ -5,7 +5,7 @@
 #include <vector>
 #include <map>
 //---------------------------------------------------------------------------
-enum TModificationFmt { mfNone, mfMDHM, mfMDY, mfFull };
+enum TModificationFmt { mfNone, mfMDHM, mfYMDHM, mfMDY, mfFull };
 //---------------------------------------------------------------------------
 #define SYMLINKSTR L" -> "
 #define ROOTDIRECTORY L"/"

+ 82 - 40
source/core/SecureShell.cpp

@@ -77,15 +77,16 @@ __fastcall TSecureShell::TSecureShell(TSessionUI* UI,
   FWaitingForData = 0;
   FCallbackSet.reset(new callback_set());
   memset(FCallbackSet.get(), 0, sizeof(callback_set));
-  FCallbackSet->handles_by_evtomain = new_handles_by_evtomain();
+  FCallbackSet->ready_event = INVALID_HANDLE_VALUE;
 }
 //---------------------------------------------------------------------------
 __fastcall TSecureShell::~TSecureShell()
 {
-  freetree234(FCallbackSet->handles_by_evtomain);
-  FCallbackSet->handles_by_evtomain = NULL;
   DebugAssert(FWaiting == 0);
-  Active = false;
+  if (Active)
+  {
+    Close();
+  }
   ResetConnection();
   CloseHandle(FSocketEvent);
 }
@@ -115,6 +116,7 @@ void __fastcall TSecureShell::ResetConnection()
   }
   FLogCtx = NULL;
   FClosed = false;
+  FLoggedKnownHostKeys.clear();
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::ResetSessionInfo()
@@ -410,7 +412,7 @@ void __fastcall TSecureShell::Open()
   // do not use UTF-8 until decided otherwise (see TSCPFileSystem::DetectUtf())
   FUtfStrings = false;
 
-  Active = false;
+  FActive = false;
 
   FAuthenticationLog = L"";
   FNoConnectionResponse = false;
@@ -446,6 +448,10 @@ void __fastcall TSecureShell::Open()
       PuttyFatalError(InitError);
     }
     FUI->Information(LoadStr(STATUS_CONNECT), true);
+    if (!Active && DebugAlwaysTrue(HasLocalProxy()))
+    {
+      FActive = true;
+    }
     Init();
 
     CheckConnection(CONNECTION_FAILED);
@@ -702,6 +708,7 @@ TPromptKind __fastcall TSecureShell::IdentifyPromptKind(UnicodeString & Name)
     { L"SSH server authentication", SERVER_PROMPT_TITLE },
     { L"SSH password", PASSWORD_TITLE },
     { L"New SSH password", NEW_PASSWORD_TITLE },
+    { L"SOCKS proxy authentication", PROXY_AUTH_TITLE },
   };
 
   int Index = TranslatePuttyMessage(NameTranslation, LENOF(NameTranslation), Name);
@@ -735,6 +742,10 @@ TPromptKind __fastcall TSecureShell::IdentifyPromptKind(UnicodeString & Name)
   {
     PromptKind = pkNewPassword;
   }
+  else if (Index == 8)
+  {
+    PromptKind = pkProxyAuth;
+  }
   else
   {
     PromptKind = pkPrompt;
@@ -839,6 +850,16 @@ bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
     PromptTranslationCount = LENOF(NewPasswordPromptTranslation);
     PromptDesc = L"new password";
   }
+  else if (PromptKind == pkProxyAuth)
+  {
+    static const TPuttyTranslation ProxyAuthPromptTranslation[] = {
+      { L"Proxy username: ", PROXY_AUTH_USERNAME_PROMPT },
+      { L"Proxy password: ", PROXY_AUTH_PASSWORD_PROMPT },
+    };
+    PromptTranslation = ProxyAuthPromptTranslation;
+    PromptTranslationCount = LENOF(ProxyAuthPromptTranslation);
+    PromptDesc = L"proxy authentication";
+  }
   else
   {
     PromptDesc = L"unknown";
@@ -849,7 +870,7 @@ bool __fastcall TSecureShell::PromptUser(bool /*ToServer*/,
     (Instructions.IsEmpty() ? UnicodeString(L"<no instructions>") : FORMAT(L"\"%s\"", (Instructions)));
   UnicodeString PromptsLog =
     (Prompts->Count > 0 ? FORMAT(L"\"%s\"", (Prompts->Strings[0])) : UnicodeString(L"<no prompt>")) +
-    (Prompts->Count > 1 ? FORMAT(L"%d more", (Prompts->Count - 1)) : UnicodeString());
+    (Prompts->Count > 1 ? FORMAT(L" + %d more", (Prompts->Count - 1)) : UnicodeString());
   LogEvent(FORMAT(L"Prompt (%s, \"%s\", %s, %s)", (PromptDesc, AName, InstructionsLog, PromptsLog)));
 
   Name = Name.Trim();
@@ -1339,7 +1360,8 @@ void __fastcall TSecureShell::DispatchSendBuffer(int BufSize)
 void __fastcall TSecureShell::Send(const unsigned char * Buf, Integer Len)
 {
   CheckConnection();
-  int BufSize = backend_send(FBackendHandle, const_cast<char *>(reinterpret_cast<const char *>(Buf)), Len);
+  backend_send(FBackendHandle, const_cast<char *>(reinterpret_cast<const char *>(Buf)), Len);
+  int BufSize = backend_sendbuffer(FBackendHandle);
   if (Configuration->ActualLogProtocol >= 1)
   {
     LogEvent(FORMAT(L"Sent %u bytes", (static_cast<int>(Len))));
@@ -1596,6 +1618,11 @@ void __fastcall TSecureShell::SocketEventSelect(SOCKET Socket, HANDLE Event, boo
   }
 }
 //---------------------------------------------------------------------------
+bool TSecureShell::HasLocalProxy()
+{
+  return (FSessionData->ProxyMethod == pmCmd);
+}
+//---------------------------------------------------------------------------
 void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Enable)
 {
   if (!FActive && !Enable)
@@ -1619,7 +1646,7 @@ void __fastcall TSecureShell::UpdateSocket(SOCKET value, bool Enable)
     }
     else
     {
-      DebugAssert(FSessionData->ProxyMethod == pmCmd);
+      DebugAssert(HasLocalProxy());
     }
 
     if (Enable)
@@ -1654,21 +1681,6 @@ void __fastcall TSecureShell::UpdatePortFwdSocket(SOCKET value, bool Enable)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TSecureShell::SetActive(bool value)
-{
-  if (FActive != value)
-  {
-    if (value)
-    {
-      Open();
-    }
-    else
-    {
-      Close();
-    }
-  }
-}
-//---------------------------------------------------------------------------
 void __fastcall TSecureShell::FreeBackend()
 {
   if (FBackendHandle != NULL)
@@ -1697,6 +1709,25 @@ void __fastcall TSecureShell::FreeBackend()
       sfree(FCallbackSet->pktin_freeq_head);
       FCallbackSet->pktin_freeq_head = NULL;
     }
+
+    if (FCallbackSet->handlewaits_tree_real != NULL)
+    {
+      DebugAssert(count234(FCallbackSet->handlewaits_tree_real) <= 1);
+      while (count234(FCallbackSet->handlewaits_tree_real) > 0)
+      {
+        HandleWait * AHandleWait = static_cast<HandleWait *>(index234(FCallbackSet->handlewaits_tree_real, 0));
+        delete_handle_wait(FCallbackSet.get(), AHandleWait);
+      }
+
+      freetree234(FCallbackSet->handlewaits_tree_real);
+      FCallbackSet->handlewaits_tree_real = NULL;
+    }
+
+    if (FCallbackSet->ready_event != INVALID_HANDLE_VALUE)
+    {
+      CloseHandle(FCallbackSet->ready_event);
+      FCallbackSet->ready_event = INVALID_HANDLE_VALUE;
+    }
   }
 }
 //---------------------------------------------------------------------------
@@ -1971,8 +2002,6 @@ bool __fastcall TSecureShell::ProcessNetworkEvents(SOCKET Socket)
 bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventRequired,
   WSANETWORKEVENTS * Events)
 {
-  CheckConnection();
-
   bool Result = false;
 
   do
@@ -1982,8 +2011,8 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
       LogEvent(L"Looking for network events");
     }
     unsigned int TicksBefore = GetTickCount();
-    int HandleCount;
-    HANDLE * Handles = NULL;
+    HandleWaitList * WaitList = NULL;
+
     try
     {
       unsigned int Timeout = MSec;
@@ -1998,15 +2027,15 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
           TimeoutStep = 0;
         }
         Timeout -= TimeoutStep;
-        if (Handles != NULL)
+        if (WaitList != NULL)
         {
-          sfree(Handles);
+          handle_wait_list_free(WaitList);
         }
         // It returns only busy handles, so the set can change with every call to run_toplevel_callbacks.
-        Handles = handle_get_events(FCallbackSet->handles_by_evtomain, &HandleCount);
-        Handles = sresize(Handles, HandleCount + 1, HANDLE);
-        Handles[HandleCount] = FSocketEvent;
-        WaitResult = WaitForMultipleObjects(HandleCount + 1, Handles, FALSE, TimeoutStep);
+        WaitList = get_handle_wait_list(FCallbackSet.get());
+        DebugAssert(WaitList->nhandles < MAXIMUM_WAIT_OBJECTS);
+        WaitList->handles[WaitList->nhandles] = FSocketEvent;
+        WaitResult = WaitForMultipleObjects(WaitList->nhandles + 1, WaitList->handles, FALSE, TimeoutStep);
         FUI->ProcessGUI();
         // run_toplevel_callbacks can cause processing of pending raw data, so:
         // 1) Check for changes in our pending buffer - wait criteria in Receive()
@@ -2022,20 +2051,21 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
         }
       } while ((WaitResult == WAIT_TIMEOUT) && (Timeout > 0) && !Result);
 
-      if (WaitResult < WAIT_OBJECT_0 + HandleCount)
+      if (WaitResult < WAIT_OBJECT_0 + WaitList->nhandles)
       {
-        if (handle_got_event(FCallbackSet->handles_by_evtomain, Handles[WaitResult - WAIT_OBJECT_0]))
+        if (handle_wait_activate(FCallbackSet.get(), WaitList, WaitResult - WAIT_OBJECT_0))
         {
           Result = true;
         }
       }
-      else if (WaitResult == WAIT_OBJECT_0 + HandleCount)
+      else if (WaitResult == WAIT_OBJECT_0 + WaitList->nhandles)
       {
         if (Configuration->ActualLogProtocol >= 1)
         {
           LogEvent(L"Detected network event");
         }
 
+        DebugAssert(FSocket != INVALID_SOCKET);
         if (Events == NULL)
         {
           if (ProcessNetworkEvents(FSocket))
@@ -2081,7 +2111,10 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
     }
     __finally
     {
-      sfree(Handles);
+      if (WaitList != NULL)
+      {
+        handle_wait_list_free(WaitList);
+      }
     }
 
 
@@ -2100,7 +2133,8 @@ bool __fastcall TSecureShell::EventSelectLoop(unsigned int MSec, bool ReadEventR
       }
     }
 
-    if ((FSendBuf > 0) && (TicksAfter - FLastSendBufferUpdate >= 1000))
+    if ((FSocket != INVALID_SOCKET) &&
+        (FSendBuf > 0) && (TicksAfter - FLastSendBufferUpdate >= 1000))
     {
       DWORD BufferLen = 0;
       DWORD OutLen = 0;
@@ -2658,9 +2692,13 @@ bool __fastcall TSecureShell::HaveHostKey(UnicodeString Host, int Port, const Un
     }
   }
 
-  if (Result)
+  if (Result &&
+      (FLoggedKnownHostKeys.find(KeyType) == FLoggedKnownHostKeys.end()))
   {
     LogEvent(FORMAT(L"Have a known host key of type %s", (KeyType)));
+    // This is called multiple times for the same cached key since PuTTY 0.76 (support for rsa-sha2*?).
+    // Avoid repeated log entries.
+    FLoggedKnownHostKeys.insert(KeyType);
   }
 
   return Result;
@@ -2690,7 +2728,11 @@ void __fastcall TSecureShell::AskAlg(UnicodeString AlgType, UnicodeString AlgNam
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::DisplayBanner(const UnicodeString & Banner)
 {
-  FUI->DisplayBanner(Banner);
+  // Since 0.77 PuTTY calls this again with CRLF if the actual banner does not end with one.
+  if (!Banner.Trim().IsEmpty())
+  {
+    FUI->DisplayBanner(Banner);
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::OldKeyfileWarning()

+ 3 - 1
source/core/SecureShell.h

@@ -76,6 +76,7 @@ private:
   ScpLogPolicy * FLogPolicy;
   ScpSeat * FSeat;
   LogContext * FLogCtx;
+  std::set<UnicodeString> FLoggedKnownHostKeys;
 
   void __fastcall Init();
   void __fastcall SetActive(bool value);
@@ -110,6 +111,7 @@ private:
     const UnicodeString & StoredKeys, const UnicodeString & KeyStr, const UnicodeString & FingerprintMD5, const UnicodeString & FingerprintSHA256);
   UnicodeString StoreHostKey(
     const UnicodeString & Host, int Port, const UnicodeString & KeyType, const UnicodeString & KeyStr);
+  bool HasLocalProxy();
 
 protected:
   TCaptureOutputEvent FOnCaptureOutput;
@@ -178,7 +180,7 @@ public:
   UnicodeString __fastcall ConvertFromPutty(const char * Str, int Length);
   struct callback_set * GetCallbackSet();
 
-  __property bool Active = { read = FActive, write = SetActive };
+  __property bool Active = { read = FActive };
   __property bool Ready = { read = GetReady };
   __property TCaptureOutputEvent OnCaptureOutput = { read = FOnCaptureOutput, write = FOnCaptureOutput };
   __property TDateTime LastDataSent = { read = FLastDataSent };

+ 1 - 0
source/core/WebDAVFileSystem.cpp

@@ -892,6 +892,7 @@ void __fastcall TWebDAVFileSystem::ParsePropResultSet(TRemoteFile * File,
           EncodeDateVerbose((unsigned short)Year, (unsigned short)Month, (unsigned short)Day) +
           EncodeTimeVerbose((unsigned short)Hour, (unsigned short)Min, (unsigned short)Sec, 0);
         File->Modification = ConvertTimestampFromUTC(Modification);
+        // Should use mfYMDHM or mfMDY when appropriate according to Filled
         File->ModificationFmt = mfFull;
       }
       else

+ 1 - 0
source/filezilla/FileZillaIntf.cpp

@@ -293,6 +293,7 @@ void __fastcall CopyFileTime(TRemoteFileTime & Dest, const t_directory::t_dirent
   Dest.Second = Source.second;
   Dest.HasTime = Source.hastime;
   Dest.HasDate = Source.hasdate;
+  Dest.HasYear = Source.hasyear;
   Dest.HasSeconds = Source.hasseconds;
   Dest.Utc = Source.utc;
 }

+ 1 - 0
source/filezilla/FileZillaIntf.h

@@ -20,6 +20,7 @@ struct TRemoteFileTime
   int Minute;
   int Second;
   bool HasTime;
+  bool HasYear;
   bool HasSeconds;
   bool HasDate;
   bool Utc;

+ 1 - 0
source/filezilla/FtpControlSocket.cpp

@@ -4416,6 +4416,7 @@ bool CFtpControlSocket::HandleMdtm(int code, t_directory::t_direntry::t_date & d
             date.minute = m;
             date.second = s;
             date.hastime = true;
+            date.hasyear = true;
             date.hasseconds = hasseconds;
             date.hasdate = true;
             date.utc = true;

+ 41 - 35
source/filezilla/FtpListResult.cpp

@@ -674,6 +674,7 @@ bool CFtpListResult::ParseShortDate(const char *str, int len, t_directory::t_dir
   }
 
   date.hasdate = TRUE;
+  date.hasyear = TRUE;
   return true;
 }
 
@@ -803,6 +804,7 @@ BOOL CFtpListResult::parseAsVMS(const char *line, const int linelen, t_directory
   p++;
 
   dir.date.year = static_cast<int>(strntoi64(p, tokenlen - (p - str)));
+  dir.date.hasyear = TRUE;
 
   //Get time
   str = GetNextToken(line, linelen, tokenlen, pos, 0);
@@ -1169,6 +1171,7 @@ bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_diren
   if (result)
   {
     date.year = Year;
+    date.hasyear = TRUE;
     date.month = Month;
     date.day = Day;
     date.hour = Hours;
@@ -1179,6 +1182,32 @@ bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_diren
   return result;
 }
 
+void CFtpListResult::GuessYearIfUnknown(t_directory::t_direntry::t_date & Date)
+{
+  // Problem: Some servers use times only for files newer than 6 months,
+  // others use one year as limit. IIS shows time for files from the current year (jan-dec).
+  // So there is no support for files with time
+  // dated in the near future. Under normal conditions there should not be such files.
+  if (!Date.year) // might use direntry.date.hasyear now?
+  {
+    CTime curtime = CTime::GetCurrentTime();
+    int curday = curtime.GetDay();
+    int curmonth = curtime.GetMonth();
+    int curyear = curtime.GetYear();
+    int now = curmonth * 31 + curday;
+    int file = Date.month * 31 + Date.day;
+    if ((now + 1) >= file)
+    {
+      Date.year = curyear;
+    }
+    else
+    {
+      Date.year = curyear - 1;
+    }
+    // year is guessed, not setting hasyear
+  }
+}
+
 BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_directory::t_direntry &direntry)
 {
   int pos = 0;
@@ -1467,6 +1496,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth == 4) //2002-10-14
     {
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
+      direntry.date.hasyear = TRUE;
       sday = pos2 + 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1488,6 +1518,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth) //14-10-2002 or 01-jun-99
     {
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
+      direntry.date.hasyear = TRUE;
       sday = smonth;
       sdaylen = p - smonth;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1529,6 +1560,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth==4)
     {
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
+      direntry.date.hasyear = TRUE;
       sday = pos2 + 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1550,6 +1582,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth==2)
     {
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
+      direntry.date.hasyear = TRUE;
       sday = smonth;
       sdaylen = p - smonth;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1652,6 +1685,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
   {
     gotYear = true;
     direntry.date.year = year;
+    direntry.date.hasyear = TRUE;
   }
   else
   {
@@ -1729,23 +1763,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     }
     direntry.date.hastime = TRUE;
 
-    //Problem: Some servers use times only for files newer than 6 months,
-    //others use one year as limit. IIS shows time for files from the current year (jan-dec).
-    //So there is no support for files with time
-    //dated in the near future. Under normal conditions there should not be such files.
-    if (!direntry.date.year)
-    {
-      CTime curtime = CTime::GetCurrentTime();
-      int curday = curtime.GetDay();
-      int curmonth = curtime.GetMonth();
-      int curyear = curtime.GetYear();
-      int now = curmonth*31+curday;
-      int file = direntry.date.month*31+direntry.date.day;
-      if ((now+1)>=file)
-        direntry.date.year = curyear;
-      else
-        direntry.date.year = curyear-1;
-    }
+    GuessYearIfUnknown(direntry.date);
     bCouldBeVShell = FALSE;
   }
   else
@@ -1758,12 +1776,13 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
         return false;
       }
     }
-    else if (!direntry.date.year)
+    else if (!direntry.date.year) // might use direntry.date.hasyear now?
     {
       //No delimiters -> year
 
       direntry.date.hastime = FALSE;
       direntry.date.year = static_cast<int>(strntoi64(stimeyear, stimeyearlen));
+      direntry.date.hasyear = TRUE;
     }
     else
     {
@@ -1772,7 +1791,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     }
   }
 
-  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid!
+  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid! might use direntry.date.hasyear now?
   {
     return FALSE;
   }
@@ -1918,6 +1937,7 @@ void CFtpListResult::TimeTToDate(time_t TimeT, t_directory::t_direntry::t_date &
 {
   tm * sTime = gmtime(&TimeT);
   Date.year = sTime->tm_year + 1900;
+  Date.hasyear = TRUE;
   Date.month = sTime->tm_mon+1;
   Date.day = sTime->tm_mday;
   Date.hour = sTime->tm_hour;
@@ -2044,22 +2064,7 @@ BOOL CFtpListResult::parseAsOther(const char *line, const int linelen, t_directo
         direntry.date.minute = static_cast<int>(strntoi64(strpos+1, tokenlen - (strpos - str) - 1));
         direntry.date.hastime = TRUE;
 
-        //Problem: Some servers use times only for files newer than 6 months,
-        //others use one year as limit. So there is no support for files with time
-        //dated in the near future. Under normal conditions there should not be such files
-        if (!direntry.date.year)
-        {
-          CTime curtime = CTime::GetCurrentTime();
-          int curday = curtime.GetDay();
-          int curmonth = curtime.GetMonth();
-          int curyear = curtime.GetYear();
-          int now = curmonth*31+curday;
-          int file = direntry.date.month*31+direntry.date.day;
-          if ((now+1)>=file)
-            direntry.date.year = curyear;
-          else
-            direntry.date.year = curyear-1;
-        }
+        GuessYearIfUnknown(direntry.date);
       }
 
       str = GetNextToken(line, linelen, tokenlen, pos, 1);
@@ -2111,6 +2116,7 @@ BOOL CFtpListResult::parseAsOther(const char *line, const int linelen, t_directo
         return FALSE;
 
       direntry.date.year = static_cast<int>(strntoi64(str, tokenlen));
+      direntry.date.hasyear = TRUE;
       if (direntry.date.year < 50)
         direntry.date.year += 2000;
       else if (direntry.date.year < 1000)

+ 1 - 0
source/filezilla/FtpListResult.h

@@ -59,6 +59,7 @@ private:
   bool parseTime(const char * str, int len, t_directory::t_direntry::t_date & date) const;
   bool ParseSize(const char * str, int len, __int64 & size) const;
   void TimeTToDate(time_t TimeT, t_directory::t_direntry::t_date & date) const;
+  static void GuessYearIfUnknown(t_directory::t_direntry::t_date & Date);
 
   bool parseMlsdDateTime(const CString value, t_directory::t_direntry::t_date & date) const;
 

+ 1 - 1
source/filezilla/structures.cpp

@@ -47,5 +47,5 @@ t_directory::t_direntry::t_direntry()
 t_directory::t_direntry::t_date::t_date()
 {
   year=month=day=hour=minute=second=0;
-  hasdate=hastime=hasseconds=utc=FALSE;
+  hasdate=hastime=hasyear=hasseconds=utc=FALSE;
 }

+ 1 - 0
source/filezilla/structures.h

@@ -33,6 +33,7 @@ public:
       t_date();
       int year,month,day,hour,minute,second;
       bool hastime;
+      bool hasyear; // ignored and assumed true when hasseconds
       bool hasseconds;
       bool hasdate;
       bool utc;

+ 1 - 1
source/forms/Cleanup.dfm

@@ -89,7 +89,7 @@ object CleanupDialog: TCleanupDialog
   object CheckAllButton: TButton
     Left = 8
     Top = 266
-    Width = 108
+    Width = 113
     Height = 25
     Anchors = [akLeft, akBottom]
     Caption = 'Un/check &all'

+ 1 - 1
source/forms/Copy.dfm

@@ -93,7 +93,7 @@ object CopyDialog: TCopyDialog
   object QueueCheck2: TCheckBox
     Left = 12
     Top = 112
-    Width = 297
+    Width = 317
     Height = 17
     Caption = 'Transfer on background (add to transfer &queue) X'
     TabOrder = 4

+ 1 - 1
source/forms/Custom.cpp

@@ -1397,7 +1397,7 @@ __fastcall TUsageStatisticsDialog::TUsageStatisticsDialog() :
 
   ClipboardButton = new TButton(this);
   ClipboardButton->Caption = LoadStr(USAGE_COPY);
-  ClipboardButton->Width = ScaleByTextHeight(this, 121);
+  ClipboardButton->Width = ScaleByTextHeight(this, 161);
   ClipboardButton->OnClick = ClipboardButtonClick;
   AddDialogButton(ClipboardButton);
 

+ 1 - 1
source/forms/FileSystemInfo.dfm

@@ -356,7 +356,7 @@ object FileSystemInfoDialog: TFileSystemInfoDialog
   object ClipboardButton: TButton
     Left = 8
     Top = 364
-    Width = 121
+    Width = 161
     Height = 25
     Anchors = [akRight, akBottom]
     Caption = '&Copy to Clipboard'

+ 1 - 1
source/forms/GenerateUrl.dfm

@@ -222,7 +222,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
   object ClipboardButton: TButton
     Left = 8
     Top = 305
-    Width = 145
+    Width = 165
     Height = 25
     Anchors = [akLeft, akBottom]
     Caption = '&Copy to Clipboard'

+ 1 - 1
source/forms/ImportSessions.dfm

@@ -76,7 +76,7 @@ object ImportSessionsDialog: TImportSessionsDialog
   object CheckAllButton: TButton
     Left = 8
     Top = 242
-    Width = 108
+    Width = 113
     Height = 25
     Anchors = [akLeft, akBottom]
     Caption = 'Un/check &all'

+ 3 - 1
source/forms/NonVisual.cpp

@@ -1149,7 +1149,9 @@ static void GiveItemPriority(TTBCustomItem * Item)
 {
   DebugAssert(Item->GetTopComponent() != NULL);
   TTBCustomToolbar * ToolbarComponent = dynamic_cast<TTBCustomToolbar *>(Item->GetTopComponent());
-  if (ToolbarComponent != NULL)
+  if ((ToolbarComponent != NULL) &&
+      // Only for top-level buttons on custom command toolbar, not for submenus of the custom commands menu in the main menu
+      (Item->Parent == ToolbarComponent->Items))
   {
     TTBItemViewer * Viewer = ToolbarComponent->View->Find(Item);
     ToolbarComponent->View->GivePriority(Viewer);

+ 8 - 8
source/forms/Preferences.dfm

@@ -736,9 +736,9 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = ControlChange
           end
           object PanelSearchCombo: TComboBox
-            Left = 216
+            Left = 196
             Top = 191
-            Width = 156
+            Width = 176
             Height = 21
             Style = csDropDownList
             Anchors = [akTop, akRight]
@@ -1291,7 +1291,7 @@ object PreferencesDialog: TPreferencesDialog
           object AddCommandButton: TButton
             Left = 16
             Top = 313
-            Width = 83
+            Width = 98
             Height = 25
             Anchors = [akLeft, akBottom]
             Caption = '&Add...'
@@ -1303,7 +1303,7 @@ object PreferencesDialog: TPreferencesDialog
           object RemoveCommandButton: TButton
             Left = 16
             Top = 344
-            Width = 83
+            Width = 98
             Height = 25
             Anchors = [akLeft, akBottom]
             Caption = '&Remove'
@@ -1331,9 +1331,9 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = UpDownCommandButtonClick
           end
           object EditCommandButton: TButton
-            Left = 112
+            Left = 127
             Top = 313
-            Width = 83
+            Width = 98
             Height = 25
             Anchors = [akLeft, akBottom]
             Caption = '&Edit...'
@@ -1341,9 +1341,9 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = EditCommandButtonClick
           end
           object ConfigureCommandButton: TButton
-            Left = 112
+            Left = 127
             Top = 313
-            Width = 83
+            Width = 98
             Height = 25
             Anchors = [akLeft, akBottom]
             Caption = '&Configure...'

+ 19 - 9
source/forms/ScpCommander.cpp

@@ -2325,23 +2325,33 @@ void __fastcall TScpCommanderForm::DoLocalPathComboBoxItemClick(TDirView * ADirV
   DebugAssert((PathComboBox->ItemIndex >= 0) && (PathComboBox->ItemIndex < FLocalPathComboBoxPaths->Count));
 
   UnicodeString Path = FLocalPathComboBoxPaths->Strings[PathComboBox->ItemIndex];
-  if (PathComboBox->ItemIndex >= FLocalSpecialPaths)
+  try
   {
-    UnicodeString Drive = DriveInfo->GetDriveKey(Path);
-    TDirView * CurrentDirView = dynamic_cast<TDirView *>(DirView(osCurrent));
-    if (IsLocalBrowserMode() && (ADirView != CurrentDirView) &&
-        SamePaths(DriveInfo->GetDriveKey(CurrentDirView->PathName), Drive))
+    if (PathComboBox->ItemIndex >= FLocalSpecialPaths)
     {
-      ADirView->Path = CurrentDirView->PathName;
+      UnicodeString Drive = DriveInfo->GetDriveKey(Path);
+      TDirView * CurrentDirView = dynamic_cast<TDirView *>(DirView(osCurrent));
+      if (IsLocalBrowserMode() && (ADirView != CurrentDirView) &&
+        SamePaths(DriveInfo->GetDriveKey(CurrentDirView->PathName), Drive))
+      {
+        ADirView->Path = CurrentDirView->PathName;
+      }
+      else
+      {
+        ADirView->ExecuteDrive(Drive);
+      }
     }
     else
     {
-      ADirView->ExecuteDrive(Drive);
+      ADirView->Path = Path;
     }
   }
-  else
+  catch (...)
   {
-    ADirView->Path = Path;
+    // Changing the path failed, reset the combo box back.
+    // Does not recurse, so infinite recursion should not happen.
+    DoLocalPathComboBoxItemClick(ADirView, PathComboBox);
+    throw;
   }
 }
 //---------------------------------------------------------------------------

+ 1 - 1
source/forms/SiteAdvanced.cpp

@@ -836,7 +836,7 @@ void __fastcall TSiteAdvancedDialog::UpdateControls()
       FtpPasvModeCheck->Checked = true;
       MessageDialog(MainInstructions(LoadStr(FTP_PASV_MODE_REQUIRED)), qtInformation, qaOK);
     }
-    EnableControl(BufferSizeCheck, SshProtocol || FtpProtocol);
+    EnableControl(BufferSizeCheck, SshProtocol || FtpProtocol || S3Protocol);
     PingGroup->Visible = !FtpProtocol;
     EnableControl(PingGroup, SshProtocol);
     EnableControl(PingIntervalSecEdit, PingGroup->Enabled && !PingOffButton->Checked);

+ 3 - 3
source/forms/SiteAdvanced.dfm

@@ -527,7 +527,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
           object ShowEncryptionKeyCheck: TCheckBox
             Left = 12
             Top = 61
-            Width = 97
+            Width = 117
             Height = 17
             Caption = '&Show key'
             TabOrder = 2
@@ -2327,7 +2327,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
             OnChange = DataChange
           end
           object PrivateKeyToolsButton: TButton
-            Left = 151
+            Left = 201
             Top = 86
             Width = 101
             Height = 25
@@ -2338,7 +2338,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
           object PrivateKeyViewButton: TButton
             Left = 12
             Top = 86
-            Width = 133
+            Width = 183
             Height = 25
             Caption = '&Display Public Key'
             TabOrder = 2

+ 6 - 0
source/packages/filemng/DirView.pas

@@ -2793,6 +2793,7 @@ end;
 procedure TDirView.ExecuteDrive(Drive: string);
 var
   APath: string;
+  DriveRoot: string;
 begin
   if Assigned(FLastPath) and FLastPath.ContainsKey(Drive) then
   begin
@@ -2810,6 +2811,11 @@ begin
     if DriveInfo.IsRealDrive(Drive) then
     begin
       GetDir(Integer(Drive[1]) - Integer('A') + 1, APath);
+      DriveRoot := DriveInfo.GetDriveRoot(Drive);
+      // When the drive is not valid, the GetDir returns the current drive working directory, detect that,
+      // and let it fail later when trying to open root of the invalid drive.
+      if not StartsText(DriveRoot, APath) then
+        APath := DriveRoot;
       APath := ExcludeTrailingPathDelimiter(APath);
     end
       else

+ 240 - 68
source/packages/filemng/DriveView.pas

@@ -56,6 +56,8 @@ const
   dvdsFloppy          = 8;  {Include floppy drives}
   dvdsRereadAllways   = 16; {Refresh drivestatus in any case}
 
+  WM_USER_SHCHANGENOTIFY = WM_USER + $2000 + 13;
+
 type
   EInvalidDirName  = class(Exception);
   ENodeNotAssigned = class(Exception);
@@ -68,8 +70,12 @@ type
     DiscMonitor: TDiscMonitor; {Monitor thread}
     ChangeTimer: TTimer;       {Change timer for the monitor thread}
     DefaultDir: string;        {Current directory}
+    DriveHandle: THandle;
+    NotificationHandle: HDEVNOTIFY;
   end;
 
+  TDriveStatusPair = TPair<string, TDriveStatus>;
+
   TScanDirInfo = record
     SearchNewDirs: Boolean;
     StartNode: TTreeNode;
@@ -130,6 +136,7 @@ type
     FRenameNode: TTreeNode;
     FLastRenameName: string;
     FInternalWindowHandle: HWND;
+    FChangeNotify: ULONG;
     FPrevSelected: TTreeNode;
     FPrevSelectedIndex: Integer;
     FChangeTimerSuspended: Integer;
@@ -183,6 +190,11 @@ type
     function WatchThreadActive: Boolean; overload;
     function WatchThreadActive(Drive: string): Boolean; overload;
     procedure InternalWndProc(var Msg: TMessage);
+    procedure UpdateDriveNotifications(Drive: string);
+    procedure DriveRemoved(Drive: string);
+    procedure DriveRemoving(Drive: string);
+    procedure CancelDriveRefresh;
+    procedure ScheduleDriveRefresh;
 
     function DirAttrMask: Integer;
     function CreateDriveStatus: TDriveStatus;
@@ -459,6 +471,8 @@ end;
 constructor TDriveView.Create(AOwner: TComponent);
 var
   Drive: TRealDrive;
+  ChangeNotifyEntry: TSHChangeNotifyEntry;
+  Dummy: string;
 begin
   inherited;
 
@@ -495,6 +509,19 @@ begin
 
   FInternalWindowHandle := Classes.AllocateHWnd(InternalWndProc);
 
+  // Source: petr.solin 2022-02-25
+  FChangeNotify := 0;
+  if SpecialFolderLocation(CSIDL_DESKTOP, Dummy, ChangeNotifyEntry.pidl) then
+  begin
+    ChangeNotifyEntry.fRecursive := False;
+
+    FChangeNotify :=
+      SHChangeNotifyRegister(
+        FInternalWindowHandle, SHCNRF_ShellLevel or SHCNRF_NewDelivery,
+        SHCNE_RENAMEFOLDER or SHCNE_MEDIAINSERTED or SHCNE_MEDIAREMOVED,
+        WM_USER_SHCHANGENOTIFY, 1, ChangeNotifyEntry);
+  end;
+
   with FDragDropFilesEx do
   begin
     ShellExtensions.DragDropHandler := True;
@@ -503,19 +530,21 @@ end; {Create}
 
 destructor TDriveView.Destroy;
 var
-  DriveStatus: TDriveStatus;
+  DriveStatusPair: TDriveStatusPair;
 begin
+  if FChangeNotify <> 0 then SHChangeNotifyDeregister(FChangeNotify);
   Classes.DeallocateHWnd(FInternalWindowHandle);
 
-  for DriveStatus in FDriveStatus.Values do
+  for DriveStatusPair in FDriveStatus do
   begin
-    with DriveStatus do
+    with DriveStatusPair.Value do
     begin
       if Assigned(DiscMonitor) then
-        DiscMonitor.Free;
+        FreeAndNil(DiscMonitor);
       if Assigned(ChangeTimer) then
-        ChangeTimer.Free;
+        FreeAndNil(ChangeTimer);
     end;
+    UpdateDriveNotifications(DriveStatusPair.Key);
   end;
   FDriveStatus.Free;
 
@@ -541,9 +570,17 @@ begin
     ChangeTimer.Interval := 0;
     ChangeTimer.Enabled := False;
     ChangeTimer.OnTimer := ChangeTimerOnTimer;
+    DriveHandle := INVALID_HANDLE_VALUE;
+    NotificationHandle := nil;
   end;
 end;
 
+procedure TDriveView.DriveRemoving(Drive: string);
+begin
+  DriveRemoved(Drive);
+  TerminateWatchThread(Drive);
+end;
+
 type
   PDevBroadcastHdr = ^TDevBroadcastHdr;
   TDevBroadcastHdr = record
@@ -561,37 +598,79 @@ type
     dbcv_flags: WORD;
   end;
 
+  PDEV_BROADCAST_HANDLE = ^DEV_BROADCAST_HANDLE;
+  DEV_BROADCAST_HANDLE = record
+    dbch_size       : DWORD;
+    dbch_devicetype : DWORD;
+    dbch_reserved   : DWORD;
+    dbch_handle     : THandle;
+    dbch_hdevnotify : HDEVNOTIFY  ;
+    dbch_eventguid  : TGUID;
+    dbch_nameoffset : LongInt;
+    dbch_data       : Byte;
+  end;
+
+  PPItemIDList = ^PItemIDList;
+
 const
+  DBT_DEVTYP_HANDLE = $00000006;
   DBT_CONFIGCHANGED = $0018;
   DBT_DEVICEARRIVAL = $8000;
+  DBT_DEVICEQUERYREMOVE = $8001;
   DBT_DEVICEREMOVEPENDING = $8003;
   DBT_DEVICEREMOVECOMPLETE = $8004;
   DBT_DEVTYP_VOLUME = $00000002;
 
+// WORKAROUND Declaration in Winapi.ShlObj.pas is wrong
+function SHChangeNotification_Lock(hChange: THandle; dwProcId: DWORD;
+  var PPidls: PPItemIDList; var plEvent: Longint): THANDLE; stdcall;
+external 'shell32.dll' name 'SHChangeNotification_Lock';
+
 procedure TDriveView.InternalWndProc(var Msg: TMessage);
 var
+  DeviceType: DWORD;
   UnitMask: DWORD;
+  DeviceHandle: THandle;
   Drive: Char;
+  DriveStatusPair: TDriveStatusPair;
+  PPIDL: PPItemIDList;
+  Event: LONG;
+  Lock: THandle;
 begin
   with Msg do
   begin
+    if Msg = WM_USER_SHCHANGENOTIFY then
+    begin
+      Lock := SHChangeNotification_Lock(wParam, lParam, PPIDL, Event);
+      try
+        if (Event = SHCNE_RENAMEFOLDER) or // = drive rename
+           (Event = SHCNE_MEDIAINSERTED) or // also bitlocker drive unlock (also sends SHCNE_UPDATEDIR)
+           (Event = SHCNE_MEDIAREMOVED) then
+        begin
+          ScheduleDriveRefresh;
+        end;
+      finally
+        SHChangeNotification_Unlock(Lock);
+      end;
+    end
+      else
     if Msg = WM_DEVICECHANGE then
     begin
-       if (wParam = DBT_CONFIGCHANGED) or
-          (wParam = DBT_DEVICEARRIVAL) or
-          (wParam = DBT_DEVICEREMOVECOMPLETE) then
+      if (wParam = DBT_CONFIGCHANGED) or
+         (wParam = DBT_DEVICEARRIVAL) or
+         (wParam = DBT_DEVICEREMOVECOMPLETE) then
       begin
-        // Delay refreshing drives for a sec.
-        // Particularly with CD/DVD drives, if we query display name
-        // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
-        // Actually one sec does not help usually, but we do not want to wait any longer,
-        // because we want to add USB drives asap.
-        SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
+        ScheduleDriveRefresh;
       end
         else
-      if wParam = DBT_DEVICEREMOVEPENDING then
+      if (wParam = DBT_DEVICEQUERYREMOVE) or
+         (wParam = DBT_DEVICEREMOVEPENDING) then
       begin
-        if PDevBroadcastHdr(lParam)^.dbch_devicetype = DBT_DEVTYP_VOLUME then
+        DeviceType := PDevBroadcastHdr(lParam)^.dbch_devicetype;
+        // This is specifically for VeraCrypt.
+        // For normal drives, see DBT_DEVTYP_HANDLE below
+        // (and maybe now that we have generic implementation, this specific code for VeraCrypt might not be needed anymore)
+        if DeviceType = DBT_DEVTYP_VOLUME then
         begin
           UnitMask := PDevBroadcastVolume(lParam)^.dbcv_unitmask;
           Drive := FirstDrive;
@@ -599,30 +678,28 @@ begin
           begin
             if UnitMask and $01 <> 0 then
             begin
-              // Disable disk monitor to release the handle to the drive.
-              // It may happen that the dirve is not removed in the end. In this case we do not currently resume the
-              // monitoring. We can watch for DBT_DEVICEQUERYREMOVEFAILED to resume the monitoring.
-              // But currently we implement this for VeraCrypt, which does not send this notification.
-              with GetDriveStatus(Drive) do
-              begin
-                if Assigned(DiscMonitor) then
-                begin
-                  DiscMonitor.Enabled := False;
-                  DiscMonitor.Free;
-                  DiscMonitor := nil;
-                end;
-              end;
+              DriveRemoving(Drive);
             end;
             UnitMask := UnitMask shr 1;
             Drive := Chr(Ord(Drive) + 1);
           end;
+        end
+          else
+        if DeviceType = DBT_DEVTYP_HANDLE then
+        begin
+          DeviceHandle := PDEV_BROADCAST_HANDLE(lParam)^.dbch_handle;
+          for DriveStatusPair in FDriveStatus do
+            if DriveStatusPair.Value.DriveHandle = DeviceHandle then
+            begin
+              DriveRemoving(DriveStatusPair.Key);
+            end;
         end;
       end;
     end
       else
     if Msg = WM_TIMER then
     begin
-      KillTimer(FInternalWindowHandle, 1);
+      CancelDriveRefresh;
       try
         //DriveInfo.Load;
         RefreshRootNodes(dsAll or dvdsRereadAllways);
@@ -637,6 +714,23 @@ begin
   end;
 end;
 
+procedure TDriveView.CancelDriveRefresh;
+begin
+  KillTimer(FInternalWindowHandle, 1);
+end;
+
+procedure TDriveView.ScheduleDriveRefresh;
+begin
+  CancelDriveRefresh;
+  // Delay refreshing drives for a sec.
+  // Particularly with CD/DVD drives, if we query display name
+  // immediately after receiving DBT_DEVICEARRIVAL, we do not get media label.
+  // Actually one sec does not help usually, but we do not want to wait any longer,
+  // because we want to add USB drives asap.
+  // And this problem might be solved now by SHChangeNotifyRegister/SHCNE_RENAMEFOLDER.
+  SetTimer(FInternalWindowHandle, 1, MSecsPerSec, nil);
+end;
+
 procedure TDriveView.CreateWnd;
 var
   DriveStatus: TDriveStatus;
@@ -1276,7 +1370,7 @@ end;
 
 function TDriveView.GetDrives: TStrings;
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
   Drives: TStringList;
 begin
   Drives := TStringList.Create;
@@ -1289,6 +1383,41 @@ begin
   Result := Drives;
 end;
 
+procedure TDriveView.DriveRemoved(Drive: string);
+var
+  NewDrive: Char;
+begin
+  if (Directory <> '') and (Directory[1] = Drive) then
+  begin
+    if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
+      else NewDrive := SystemDrive;
+
+    repeat
+      if NewDrive < SystemDrive then NewDrive := SystemDrive
+        else
+      if NewDrive = SystemDrive then NewDrive := LastDrive
+        else Dec(NewDrive);
+      DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
+
+      if NewDrive = Drive then
+      begin
+        Break;
+      end;
+
+      if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
+      begin
+        Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
+        break;
+      end;
+    until False;
+
+    if not Assigned(Selected) then
+    begin
+      Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
+    end;
+  end;
+end;
+
 procedure TDriveView.RefreshRootNodes(dsFlags: Integer);
 var
   Drives: TStrings;
@@ -1296,7 +1425,6 @@ var
   SaveCursor: TCursor;
   WasValid: Boolean;
   NodeData: TNodeData;
-  NewDrive: Char;
   DriveStatus: TDriveStatus;
   NextDriveNode: TTreeNode;
   Index: Integer;
@@ -1373,35 +1501,7 @@ begin
           if WasValid then
           {Drive has been removed => delete rootnode:}
           begin
-            if (Directory <> '') and (Directory[1] = Drive) then
-            begin
-              if DriveInfo.IsRealDrive(Drive) then NewDrive := Drive[1]
-                else NewDrive := SystemDrive;
-
-              repeat
-                if NewDrive < SystemDrive then NewDrive := SystemDrive
-                  else
-                if NewDrive = SystemDrive then NewDrive := LastDrive
-                  else Dec(NewDrive);
-                DriveInfo.ReadDriveStatus(NewDrive, dsSize or dsImageIndex);
-
-                if NewDrive = Drive then
-                begin
-                  Break;
-                end;
-
-                if DriveInfo.Get(NewDrive).Valid and DriveInfo.Get(NewDrive).DriveReady and Assigned(GetDriveStatus(NewDrive).RootNode) then
-                begin
-                  Directory := NodePathName(GetDriveStatus(NewDrive).RootNode);
-                  break;
-                end;
-              until False;
-
-              if not Assigned(Selected) then
-              begin
-                Directory := NodePathName(GetDriveStatus(SystemDrive).RootNode);
-              end;
-            end;
+            DriveRemoved(Drive);
             Scanned := False;
             Verified := False;
             RootNode.Delete;
@@ -1962,6 +2062,7 @@ begin
       DiscMonitor.SetDirectory(DriveInfo.GetDriveRoot(Drive));
       DiscMonitor.Open;
     end;
+    UpdateDriveNotifications(Drive); // probably noop, as the monitor is not enabled yet
   end;
 end; {CreateWatchThread}
 
@@ -1999,13 +2100,14 @@ end; {NodeWatched}
 procedure TDriveView.ChangeInvalid(Sender: TObject; const Directory: string;
   const ErrorStr: string);
 var
-  Dir: string;
+  Drive: string;
 begin
-  Dir := (Sender as TDiscMonitor).Directories[0];
-  with GetDriveStatus(DriveInfo.GetDriveKey(Dir)) do
+  Drive := DriveInfo.GetDriveKey((Sender as TDiscMonitor).Directories[0]);
+  with GetDriveStatus(Drive) do
   begin
     DiscMonitor.Close;
   end;
+  UpdateDriveNotifications(Drive);
 end; {DirWatchChangeInvalid}
 
 procedure TDriveView.ChangeDetected(Sender: TObject; const Directory: string;
@@ -2030,7 +2132,7 @@ end; {DirWatchChangeDetected}
 
 procedure TDriveView.ChangeTimerOnTimer(Sender: TObject);
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
 begin
   if (FChangeTimerSuspended = 0) and (Sender is TTimer) then
   begin
@@ -2062,6 +2164,60 @@ begin
   end;
 end; {ChangeTimerOnTimer}
 
+procedure TDriveView.UpdateDriveNotifications(Drive: string);
+var
+  NeedNotifications: Boolean;
+  Path: string;
+  DevBroadcastHandle: DEV_BROADCAST_HANDLE;
+  Size: Integer;
+begin
+  if DriveInfo.IsFixedDrive(Drive) then
+  begin
+    with GetDriveStatus(Drive) do
+    begin
+      NeedNotifications :=
+        WatchThreadActive(Drive) and
+        (DriveInfo.Get(Drive).DriveType <> DRIVE_REMOTE) and
+        DriveInfo.Get(Drive).DriveReady;
+
+      if NeedNotifications <> (DriveHandle <> INVALID_HANDLE_VALUE) then
+      begin
+        if NeedNotifications then
+        begin
+          Path := DriveInfo.GetDriveRoot(Drive);
+          DriveHandle :=
+            CreateFile(PChar(Path), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
+            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL, 0);
+          if DriveHandle <> INVALID_HANDLE_VALUE then
+          begin
+            Size := SizeOf(DevBroadcastHandle);
+            ZeroMemory(@DevBroadcastHandle, Size);
+            DevBroadcastHandle.dbch_size := Size;
+            DevBroadcastHandle.dbch_devicetype := DBT_DEVTYP_HANDLE;
+            DevBroadcastHandle.dbch_handle := DriveHandle;
+
+            NotificationHandle :=
+              RegisterDeviceNotification(FInternalWindowHandle, @DevBroadcastHandle, DEVICE_NOTIFY_WINDOW_HANDLE);
+            if NotificationHandle = nil then
+            begin
+              CloseHandle(DriveHandle);
+              DriveHandle := INVALID_HANDLE_VALUE;
+            end;
+          end;
+        end
+          else
+        begin
+          UnregisterDeviceNotification(NotificationHandle);
+          NotificationHandle := nil;
+
+          CloseHandle(DriveHandle);
+          DriveHandle := INVALID_HANDLE_VALUE;
+        end;
+      end;
+    end;
+  end;
+end;
+
 procedure TDriveView.StartWatchThread;
 var
   Drive: string;
@@ -2079,14 +2235,22 @@ begin
     if Assigned(DiscMonitor) and not DiscMonitor.Enabled then
       DiscMonitor.Enabled := True;
   end;
+  UpdateDriveNotifications(Drive);
 end; {StartWatchThread}
 
 procedure TDriveView.StopWatchThread;
+var
+  Drive: string;
 begin
   if Assigned(Selected) then
-    with GetDriveStatus(GetDriveToNode(Selected)) do
+  begin
+    Drive := GetDriveToNode(Selected);
+    with GetDriveStatus(Drive) do
       if Assigned(DiscMonitor) then
         DiscMonitor.Enabled := False;
+
+    UpdateDriveNotifications(Drive);
+  end;
 end; {StopWatchThread}
 
 procedure TDriveView.SuspendChangeTimer;
@@ -2108,11 +2272,13 @@ begin
       DiscMonitor.Free;
       DiscMonitor := nil;
     end;
+
+  UpdateDriveNotifications(Drive);
 end; {StopWatchThread}
 
 procedure TDriveView.StartAllWatchThreads;
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
   Drive: string;
 begin
   if (csDesigning in ComponentState) or (not FWatchDirectory) then
@@ -2125,7 +2291,10 @@ begin
         if not Assigned(DiscMonitor) then
           CreateWatchThread(DriveStatusPair.Key);
         if Assigned(DiscMonitor) and (not DiscMonitor.Active) then
+        begin
           DiscMonitor.Open;
+          UpdateDriveNotifications(Drive);
+        end;
       end;
 
   if Assigned(Selected) then
@@ -2140,7 +2309,7 @@ end; {StartAllWatchThreads}
 
 procedure TDriveView.StopAllWatchThreads;
 var
-  DriveStatusPair: TPair<string, TDriveStatus>;
+  DriveStatusPair: TDriveStatusPair;
 begin
   if (csDesigning in ComponentState) or (not FWatchDirectory) then
      Exit;
@@ -2149,7 +2318,10 @@ begin
     with DriveStatusPair.Value do
     begin
       if Assigned(DiscMonitor) then
+      begin
         DiscMonitor.Close;
+        UpdateDriveNotifications(DriveStatusPair.Key);
+      end;
     end;
 end; {StopAllWatchThreads}
 

+ 2 - 2
source/packages/filemng/IEDriveInfo.pas

@@ -552,7 +552,7 @@ begin
       end;
 
       {DisplayName:}
-      if (Flags and dsDisplayName <> 0) then
+      if (Flags and dsDisplayName) <> 0 then
       begin
         {Fetch drives displayname:}
         SimpleName := GetSimpleName(Drive);
@@ -589,7 +589,7 @@ begin
       end;
 
       {ImageIndex:}
-      if ((Flags and dsImageIndex) <> 0) and (ImageIndex < 5) then
+      if (Flags and dsImageIndex) <> 0 then
       begin
         if Assigned(PIDL) then
         begin

+ 118 - 0
source/putty/be_list.c

@@ -0,0 +1,118 @@
+/*
+ * Source file that is rebuilt per application, and provides the list
+ * of backends, the default protocol, and the application name.
+ *
+ * This file expects the build system to provide some per-application
+ * definitions on the compiler command line. So you don't just add it
+ * directly to the sources list for an application. Instead you call
+ * the be_list() function defined in setup.cmake, e.g.
+ *
+ *    be_list(target-name AppName [SSH] [SERIAL] [OTHERBACKENDS])
+ *
+ * This translates into the following command-line macro definitions
+ * used by the code below:
+ *
+ *  - APPNAME should be defined to the name of the program, in
+ *    user-facing capitalisation (e.g. PuTTY rather than putty).
+ *    Unquoted: it's easier to stringify it in the preprocessor than
+ *    to persuade cmake to put the right quotes on the command line on
+ *    all build platforms.
+ *
+ *  - The following macros should each be defined to 1 if a given set
+ *    of backends should be added to the backends[] list, or 0 if they
+ *    should not be:
+ *
+ *     * SSH: the two SSH backends (SSH proper, and bare-ssh-connection)
+ *
+ *     * SERIAL: the serial port backend
+ *
+ *     * OTHERBACKENDS: the non-cryptographic network protocol backends
+ *       (Telnet, Rlogin, SUPDUP, Raw)
+ */
+
+#include <stdio.h>
+#include "putty.h"
+
+const char *const appname = STR(APPNAME);
+
+/*
+ * Define the default protocol for the application. This is always a
+ * network backend (serial ports come second behind network, in every
+ * case). Applications that don't have either (such as pterm) don't
+ * need this variable anyway, so just set it to -1.
+ */
+#if SSH
+const int be_default_protocol = PROT_SSH;
+#elif OTHERBACKENDS
+const int be_default_protocol = PROT_TELNET;
+#else
+const int be_default_protocol = -1;
+#endif
+
+/*
+ * List all the configured backends, in the order they should appear
+ * in the config box.
+ */
+const struct BackendVtable *const backends[] = {
+    /*
+     * Start with the most-preferred network-remote-login protocol.
+     * That's SSH if present, otherwise Telnet if present.
+     */
+#if SSH
+    &ssh_backend,
+#elif OTHERBACKENDS
+    &telnet_backend,         /* Telnet at the top if SSH is absent  */
+#endif
+
+    /*
+     * Second on the list is the serial-port backend, if available.
+     */
+#if SERIAL
+    &serial_backend,
+#endif
+
+    /*
+     * After that come the remaining network protocols: Telnet if it
+     * hasn't already appeared above, and Rlogin, SUPDUP and Raw.
+     */
+#if OTHERBACKENDS && SSH
+    &telnet_backend,         /* only if SSH displaced it at the top  */
+#endif
+#if OTHERBACKENDS
+    &rlogin_backend,
+    &supdup_backend,
+    &raw_backend,
+#endif
+
+    /*
+     * Bare ssh-connection / PSUSAN is a niche protocol and goes well
+     * down the list.
+     */
+#if SSH
+    &sshconn_backend,
+#endif
+
+    /*
+     * Done. Null pointer to mark the end of the list.
+     */
+    NULL
+};
+
+/*
+ * Number of backends at the start of the above list that should have
+ * radio buttons in the config UI.
+ *
+ * The rule is: the most-preferred network backend, and Serial, each
+ * get a radio button if present.
+ *
+ * The rest will be relegated to a dropdown list.
+ */
+const size_t n_ui_backends =
+    0
+#if SSH || OTHERBACKENDS
+    + 1
+#endif
+#if SERIAL
+    + 1
+#endif
+    ;

+ 0 - 18
source/putty/be_ssh.c

@@ -1,18 +0,0 @@
-/*
- * Linking module for programs that are restricted to only using
- * SSH-type protocols (pscp and psftp). These still have a choice of
- * two actual backends, because they can also speak PROT_SSHCONN.
- */
-
-#include <stdio.h>
-#include "putty.h"
-
-const int be_default_protocol = PROT_SSH;
-
-const struct BackendVtable *const backends[] = {
-    &ssh_backend,
-    &sshconn_backend,
-    NULL
-};
-
-const size_t n_ui_backends = 0;  /* not used in programs with a config UI */

+ 0 - 179
source/putty/cproxy.c

@@ -1,179 +0,0 @@
-/*
- * Routines to do cryptographic interaction with proxies in PuTTY.
- * This is in a separate module from proxy.c, so that it can be
- * conveniently removed in PuTTYtel by replacing this module with
- * the stub version nocproxy.c.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "putty.h"
-#include "ssh.h" /* For MD5 support */
-#include "network.h"
-#include "proxy.h"
-#include "marshal.h"
-
-static void hmacmd5_chap(const unsigned char *challenge, int challen,
-                         const char *passwd, unsigned char *response)
-{
-    mac_simple(&ssh_hmac_md5, ptrlen_from_asciz(passwd),
-               make_ptrlen(challenge, challen), response);
-}
-
-void proxy_socks5_offerencryptedauth(BinarySink *bs)
-{
-    put_byte(bs, 0x03);              /* CHAP */
-}
-
-int proxy_socks5_handlechap (ProxySocket *p)
-{
-
-    /* CHAP authentication reply format:
-     *  version number (1 bytes) = 1
-     *  number of commands (1 byte)
-     *
-     * For each command:
-     *  command identifier (1 byte)
-     *  data length (1 byte)
-     */
-    unsigned char data[260];
-    unsigned char outbuf[20];
-
-    while(p->chap_num_attributes == 0 ||
-          p->chap_num_attributes_processed < p->chap_num_attributes) {
-        if (p->chap_num_attributes == 0 ||
-            p->chap_current_attribute == -1) {
-            /* CHAP normally reads in two bytes, either at the
-             * beginning or for each attribute/value pair.  But if
-             * we're waiting for the value's data, we might not want
-             * to read 2 bytes.
-             */
-
-            if (bufchain_size(&p->pending_input_data) < 2)
-                return 1;              /* not got anything yet */
-
-            /* get the response */
-            bufchain_fetch(&p->pending_input_data, data, 2);
-            bufchain_consume(&p->pending_input_data, 2);
-        }
-
-        if (p->chap_num_attributes == 0) {
-            /* If there are no attributes, this is our first msg
-             * with the server, where we negotiate version and
-             * number of attributes
-             */
-            if (data[0] != 0x01) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy wants"
-                             " a different CHAP version",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-            if (data[1] == 0x00) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy won't"
-                             " negotiate CHAP with us",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-            p->chap_num_attributes = data[1];
-        } else {
-            if (p->chap_current_attribute == -1) {
-                /* We have to read in each attribute/value pair -
-                 * those we don't understand can be ignored, but
-                 * there are a few we'll need to handle.
-                 */
-                p->chap_current_attribute = data[0];
-                p->chap_current_datalen = data[1];
-            }
-            if (bufchain_size(&p->pending_input_data) <
-                p->chap_current_datalen)
-                return 1;              /* not got everything yet */
-
-            /* get the response */
-            bufchain_fetch(&p->pending_input_data, data,
-                           p->chap_current_datalen);
-
-            bufchain_consume(&p->pending_input_data,
-                             p->chap_current_datalen);
-
-            switch (p->chap_current_attribute) {
-              case 0x00:
-                /* Successful authentication */
-                if (data[0] == 0x00)
-                    p->state = 2;
-                else {
-                    plug_closing(p->plug, "Proxy error: SOCKS proxy"
-                                 " refused CHAP authentication",
-                                 PROXY_ERROR_GENERAL, 0);
-                    return 1;
-                }
-              break;
-              case 0x03:
-                outbuf[0] = 0x01; /* Version */
-                outbuf[1] = 0x01; /* One attribute */
-                outbuf[2] = 0x04; /* Response */
-                outbuf[3] = 0x10; /* Length */
-                hmacmd5_chap(data, p->chap_current_datalen,
-                             conf_get_str(p->conf, CONF_proxy_password),
-                             &outbuf[4]);
-                sk_write(p->sub_socket, outbuf, 20);
-              break;
-              case 0x11:
-                /* Chose a protocol */
-                if (data[0] != 0x85) {
-                    plug_closing(p->plug, "Proxy error: Server chose "
-                                 "CHAP of other than HMAC-MD5 but we "
-                                 "didn't offer it!",
-                                 PROXY_ERROR_GENERAL, 0);
-                    return 1;
-                }
-              break;
-            }
-            p->chap_current_attribute = -1;
-            p->chap_num_attributes_processed++;
-        }
-        if (p->state == 8 &&
-            p->chap_num_attributes_processed >= p->chap_num_attributes) {
-            p->chap_num_attributes = 0;
-            p->chap_num_attributes_processed = 0;
-            p->chap_current_datalen = 0;
-        }
-    }
-    return 0;
-}
-
-int proxy_socks5_selectchap(ProxySocket *p)
-{
-    char *username = conf_get_str(p->conf, CONF_proxy_username);
-    char *password = conf_get_str(p->conf, CONF_proxy_password);
-    if (username[0] || password[0]) {
-        char chapbuf[514];
-        int ulen;
-        chapbuf[0] = '\x01'; /* Version */
-        chapbuf[1] = '\x02'; /* Number of attributes sent */
-        chapbuf[2] = '\x11'; /* First attribute - algorithms list */
-        chapbuf[3] = '\x01'; /* Only one CHAP algorithm */
-        chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */
-        chapbuf[5] = '\x02'; /* Second attribute - username */
-
-        ulen = strlen(username);
-        if (ulen > 255) ulen = 255;
-        if (ulen < 1) ulen = 1;
-
-        chapbuf[6] = ulen;
-        memcpy(chapbuf+7, username, ulen);
-
-        sk_write(p->sub_socket, chapbuf, ulen + 7);
-        p->chap_num_attributes = 0;
-        p->chap_num_attributes_processed = 0;
-        p->chap_current_attribute = -1;
-        p->chap_current_datalen = 0;
-
-        p->state = 8;
-    } else
-        plug_closing(p->plug, "Proxy error: Server chose "
-                     "CHAP authentication but we didn't offer it!",
-                 PROXY_ERROR_GENERAL, 0);
-    return 1;
-}

+ 14 - 0
source/putty/crypto/aes-common.c

@@ -0,0 +1,14 @@
+/*
+ * Common variable definitions across all the AES implementations.
+ */
+
+#include "ssh.h"
+#include "aes.h"
+
+const uint8_t aes_key_setup_round_constants[10] = {
+    /* The first few powers of X in GF(2^8), used during key setup.
+     * This can safely be a lookup table without side channel risks,
+     * because key setup iterates through it once in a standard way
+     * regardless of the key. */
+    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
+};

+ 316 - 0
source/putty/crypto/aes-ni.c

@@ -0,0 +1,316 @@
+/*
+ * Hardware-accelerated implementation of AES using x86 AES-NI.
+ */
+
+#include "ssh.h"
+#include "aes.h"
+
+#ifndef WINSCP_VS
+bool aes_ni_available(void);
+ssh_cipher *aes_ni_new(const ssh_cipheralg *alg);
+void aes_ni_free(ssh_cipher *ciph);
+void aes_ni_setiv_cbc(ssh_cipher *ciph, const void *iv);
+void aes_ni_setkey(ssh_cipher *ciph, const void *vkey);
+void aes_ni_setiv_sdctr(ssh_cipher *ciph, const void *iv);
+#define NI_ENC_DEC_H(len)                                               \
+    void aes##len##_ni_cbc_encrypt(                                     \
+        ssh_cipher *ciph, void *vblk, int blklen);                      \
+    void aes##len##_ni_cbc_decrypt(                                     \
+        ssh_cipher *ciph, void *vblk, int blklen);                      \
+    void aes##len##_ni_sdctr(                                           \
+        ssh_cipher *ciph, void *vblk, int blklen);                      \
+
+NI_ENC_DEC_H(128)
+NI_ENC_DEC_H(192)
+NI_ENC_DEC_H(256)
+#else
+
+#include <wmmintrin.h>
+#include <smmintrin.h>
+
+#if defined(__clang__) || defined(__GNUC__)
+#include <cpuid.h>
+#define GET_CPU_ID(out) __cpuid(1, (out)[0], (out)[1], (out)[2], (out)[3])
+#else
+#define GET_CPU_ID(out) __cpuid(out, 1)
+#endif
+
+/*static WINSCP*/ bool aes_ni_available(void)
+{
+    /*
+     * Determine if AES is available on this CPU, by checking that
+     * both AES itself and SSE4.1 are supported.
+     */
+    unsigned int CPUInfo[4];
+    GET_CPU_ID(CPUInfo);
+    return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19));
+}
+
+/*
+ * Core AES-NI encrypt/decrypt functions, one per length and direction.
+ */
+
+#define NI_CIPHER(len, dir, dirlong, repmacro)                          \
+    static inline __m128i aes_ni_##len##_##dir(                         \
+        __m128i v, const __m128i *keysched)                             \
+    {                                                                   \
+        v = _mm_xor_si128(v, *keysched++);                              \
+        repmacro(v = _mm_aes##dirlong##_si128(v, *keysched++););        \
+        return _mm_aes##dirlong##last_si128(v, *keysched);              \
+    }
+
+NI_CIPHER(128, e, enc, REP9)
+NI_CIPHER(128, d, dec, REP9)
+NI_CIPHER(192, e, enc, REP11)
+NI_CIPHER(192, d, dec, REP11)
+NI_CIPHER(256, e, enc, REP13)
+NI_CIPHER(256, d, dec, REP13)
+
+/*
+ * The main key expansion.
+ */
+static void aes_ni_key_expand(
+    const unsigned char *key, size_t key_words,
+    __m128i *keysched_e, __m128i *keysched_d)
+{
+    size_t rounds = key_words + 6;
+    size_t sched_words = (rounds + 1) * 4;
+
+    /*
+     * Store the key schedule as 32-bit integers during expansion, so
+     * that it's easy to refer back to individual previous words. We
+     * collect them into the final __m128i form at the end.
+     */
+    uint32_t sched[MAXROUNDKEYS * 4];
+
+    unsigned rconpos = 0;
+
+    for (size_t i = 0; i < sched_words; i++) {
+        if (i < key_words) {
+            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
+        } else {
+            uint32_t temp = sched[i - 1];
+
+            bool rotate_and_round_constant = (i % key_words == 0);
+            bool only_sub = (key_words == 8 && i % 8 == 4);
+
+            if (rotate_and_round_constant) {
+                __m128i v = _mm_setr_epi32(0,temp,0,0);
+                v = _mm_aeskeygenassist_si128(v, 0);
+                temp = _mm_extract_epi32(v, 1);
+
+                assert(rconpos < lenof(aes_key_setup_round_constants));
+                temp ^= aes_key_setup_round_constants[rconpos++];
+            } else if (only_sub) {
+                __m128i v = _mm_setr_epi32(0,temp,0,0);
+                v = _mm_aeskeygenassist_si128(v, 0);
+                temp = _mm_extract_epi32(v, 0);
+            }
+
+            sched[i] = sched[i - key_words] ^ temp;
+        }
+    }
+
+    /*
+     * Combine the key schedule words into __m128i vectors and store
+     * them in the output context.
+     */
+    for (size_t round = 0; round <= rounds; round++)
+        keysched_e[round] = _mm_setr_epi32(
+            sched[4*round  ], sched[4*round+1],
+            sched[4*round+2], sched[4*round+3]);
+
+    smemclr(sched, sizeof(sched));
+
+    /*
+     * Now prepare the modified keys for the inverse cipher.
+     */
+    for (size_t eround = 0; eround <= rounds; eround++) {
+        size_t dround = rounds - eround;
+        __m128i rkey = keysched_e[eround];
+        if (eround && dround)      /* neither first nor last */
+            rkey = _mm_aesimc_si128(rkey);
+        keysched_d[dround] = rkey;
+    }
+}
+
+// WINSCP
+// WORKAROUND
+// Cannot use _mm_setr_epi* - it results in the constant being stored in .rdata segment.
+// objconv reports:
+// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
+// Despite that the code crashes.
+// This macro is based on:
+// Based on https://stackoverflow.com/q/35268036/850848
+#define _MM_SETR_EPI8(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \
+    { (char)a0, (char)a1, (char)a2, (char)a3, (char)a4, (char)a5, (char)a6, (char)a7, \
+      (char)a8, (char)a9, (char)aa, (char)ab, (char)ac, (char)ad, (char)ae, (char)af }
+
+/*
+ * Auxiliary routine to increment the 128-bit counter used in SDCTR
+ * mode.
+ */
+static inline __m128i aes_ni_sdctr_increment(__m128i v)
+{
+    const __m128i ONE = _MM_SETR_EPI8(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); // WINSCP
+    const __m128i ZERO = _mm_setzero_si128();
+
+    /* Increment the low-order 64 bits of v */
+    v  = _mm_add_epi64(v, ONE);
+    /* Check if they've become zero */
+    __m128i cmp = _mm_cmpeq_epi64(v, ZERO);
+    /* If so, the low half of cmp is all 1s. Pack that into the high
+     * half of addend with zero in the low half. */
+    __m128i addend = _mm_unpacklo_epi64(ZERO, cmp);
+    /* And subtract that from v, which increments the high 64 bits iff
+     * the low 64 wrapped round. */
+    v = _mm_sub_epi64(v, addend);
+
+    return v;
+}
+
+/*
+ * Auxiliary routine to reverse the byte order of a vector, so that
+ * the SDCTR IV can be made big-endian for feeding to the cipher.
+ */
+static inline __m128i aes_ni_sdctr_reverse(__m128i v)
+{
+    const __m128i R = _MM_SETR_EPI8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); // WINSCP
+    v = _mm_shuffle_epi8(
+        v, R); // WINSCP
+    return v;
+}
+
+/*
+ * The SSH interface and the cipher modes.
+ */
+
+typedef struct aes_ni_context aes_ni_context;
+struct aes_ni_context {
+    __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
+
+    void *pointer_to_free;
+    ssh_cipher ciph;
+};
+
+/*static WINSCP*/ ssh_cipher *aes_ni_new(const ssh_cipheralg *alg)
+{
+    const struct aes_extra *extra = (const struct aes_extra *)alg->extra;
+    if (!check_availability(extra))
+        return NULL;
+
+    /*
+     * The __m128i variables in the context structure need to be
+     * 16-byte aligned, but not all malloc implementations that this
+     * code has to work with will guarantee to return a 16-byte
+     * aligned pointer. So we over-allocate, manually realign the
+     * pointer ourselves, and store the original one inside the
+     * context so we know how to free it later.
+     */
+    void *allocation = smalloc(sizeof(aes_ni_context) + 15);
+    uintptr_t alloc_address = (uintptr_t)allocation;
+    uintptr_t aligned_address = (alloc_address + 15) & ~15;
+    aes_ni_context *ctx = (aes_ni_context *)aligned_address;
+
+    ctx->ciph.vt = alg;
+    ctx->pointer_to_free = allocation;
+    return &ctx->ciph;
+}
+
+/*static WINSCP*/ void aes_ni_free(ssh_cipher *ciph)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    void *allocation = ctx->pointer_to_free;
+    smemclr(ctx, sizeof(*ctx));
+    sfree(allocation);
+}
+
+/*static WINSCP*/ void aes_ni_setkey(ssh_cipher *ciph, const void *vkey)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    const unsigned char *key = (const unsigned char *)vkey;
+
+    aes_ni_key_expand(key, ctx->ciph.vt->real_keybits / 32,
+                      ctx->keysched_e, ctx->keysched_d);
+}
+
+/*static WINSCP*/ void aes_ni_setiv_cbc(ssh_cipher *ciph, const void *iv)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    ctx->iv = _mm_loadu_si128(iv);
+}
+
+/*static WINSCP*/ void aes_ni_setiv_sdctr(ssh_cipher *ciph, const void *iv)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+    __m128i counter = _mm_loadu_si128(iv);
+    ctx->iv = aes_ni_sdctr_reverse(counter);
+}
+
+typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched);
+
+static inline void aes_cbc_ni_encrypt(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        __m128i plaintext = _mm_loadu_si128((const __m128i *)blk);
+        __m128i cipher_input = _mm_xor_si128(plaintext, ctx->iv);
+        __m128i ciphertext = encrypt(cipher_input, ctx->keysched_e);
+        _mm_storeu_si128((__m128i *)blk, ciphertext);
+        ctx->iv = ciphertext;
+    }
+}
+
+static inline void aes_cbc_ni_decrypt(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        __m128i ciphertext = _mm_loadu_si128((const __m128i *)blk);
+        __m128i decrypted = decrypt(ciphertext, ctx->keysched_d);
+        __m128i plaintext = _mm_xor_si128(decrypted, ctx->iv);
+        _mm_storeu_si128((__m128i *)blk, plaintext);
+        ctx->iv = ciphertext;
+    }
+}
+
+static inline void aes_sdctr_ni(
+    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
+{
+    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
+
+    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
+         blk < finish; blk += 16) {
+        __m128i counter = aes_ni_sdctr_reverse(ctx->iv);
+        __m128i keystream = encrypt(counter, ctx->keysched_e);
+        __m128i input = _mm_loadu_si128((const __m128i *)blk);
+        __m128i output = _mm_xor_si128(input, keystream);
+        _mm_storeu_si128((__m128i *)blk, output);
+        ctx->iv = aes_ni_sdctr_increment(ctx->iv);
+    }
+}
+
+#define NI_ENC_DEC(len)                                                 \
+    /*static WINSCP*/ void aes##len##_ni_cbc_encrypt(                              \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); }       \
+    /*static WINSCP*/ void aes##len##_ni_cbc_decrypt(                              \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); }       \
+    /*static WINSCP*/ void aes##len##_ni_sdctr(                                    \
+        ssh_cipher *ciph, void *vblk, int blklen)                       \
+    { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); }             \
+
+NI_ENC_DEC(128)
+NI_ENC_DEC(192)
+NI_ENC_DEC(256)
+
+#endif // WINSCP_VS
+
+AES_EXTRA(_ni);
+AES_ALL_VTABLES(_ni, "AES-NI accelerated");

+ 99 - 0
source/putty/crypto/aes-select.c

@@ -0,0 +1,99 @@
+/*
+ * Top-level vtables to select an AES implementation.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "aes.h"
+
+static ssh_cipher *aes_select(const ssh_cipheralg *alg)
+{
+    const ssh_cipheralg *const *real_algs = (const ssh_cipheralg **)alg->extra;
+
+    { // WINSCP
+    size_t i;
+    for (i = 0; real_algs[i]; i++) {
+        const ssh_cipheralg *alg = real_algs[i];
+        const struct aes_extra *alg_extra =
+            (const struct aes_extra *)alg->extra;
+        if (check_availability(alg_extra))
+            return ssh_cipher_new(alg);
+    }
+
+    /* We should never reach the NULL at the end of the list, because
+     * the last non-NULL entry should be software-only AES, which is
+     * always available. */
+    unreachable("aes_select ran off the end of its list");
+    } // WINSCP
+}
+
+#if HAVE_AES_NI
+#define IF_NI(...) __VA_ARGS__
+#else
+#define IF_NI(...)
+#endif
+
+#if HAVE_NEON_CRYPTO
+#define IF_NEON(...) __VA_ARGS__
+#else
+#define IF_NEON(...)
+#endif
+
+#define AES_SELECTOR_VTABLE(mode_c, mode_protocol, mode_display, bits)  \
+    static const ssh_cipheralg *                                        \
+    ssh_aes ## bits ## _ ## mode_c ## _impls[] = {                      \
+        IF_NI(&ssh_aes ## bits ## _ ## mode_c ## _ni,)                  \
+        IF_NEON(&ssh_aes ## bits ## _ ## mode_c ## _neon,)              \
+        &ssh_aes ## bits ## _ ## mode_c ## _sw,                         \
+        NULL,                                                           \
+    };                                                                  \
+    const ssh_cipheralg ssh_aes ## bits ## _ ## mode_c = {              \
+        /* WINSCP */ \
+        /*.new =*/ aes_select,                                              \
+        NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+        /*.ssh2_id =*/ "aes" #bits "-" mode_protocol,                       \
+        /*.blksize =*/ 16,                                                  \
+        /*.real_keybits =*/ bits,                                           \
+        /*.padded_keybytes =*/ bits/8,                                      \
+        0, \
+        /*.text_name =*/ "AES-" #bits " " mode_display                      \
+        " (dummy selector vtable)",                                     \
+        NULL, \
+        /*.extra =*/ ssh_aes ## bits ## _ ## mode_c ## _impls,              \
+    }
+
+AES_SELECTOR_VTABLE(cbc, "cbc", "CBC", 128);
+AES_SELECTOR_VTABLE(cbc, "cbc", "CBC", 192);
+AES_SELECTOR_VTABLE(cbc, "cbc", "CBC", 256);
+AES_SELECTOR_VTABLE(sdctr, "ctr", "SDCTR", 128);
+AES_SELECTOR_VTABLE(sdctr, "ctr", "SDCTR", 192);
+AES_SELECTOR_VTABLE(sdctr, "ctr", "SDCTR", 256);
+
+static const ssh_cipheralg ssh_rijndael_lysator = {
+    /* Same as aes256_cbc, but with a different protocol ID */
+    /*.new =*/ aes_select,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    /*.ssh2_id =*/ "[email protected]",
+    /*.blksize =*/ 16,
+    /*.real_keybits =*/ 256,
+    /*.padded_keybytes =*/ 256/8,
+    0,
+    /*.text_name =*/ "AES-256 CBC (dummy selector vtable)",
+    NULL,
+    /*.extra =*/ ssh_aes256_cbc_impls,
+};
+
+static const ssh_cipheralg *const aes_list[] = {
+    &ssh_aes256_sdctr,
+    &ssh_aes256_cbc,
+    &ssh_rijndael_lysator,
+    &ssh_aes192_sdctr,
+    &ssh_aes192_cbc,
+    &ssh_aes128_sdctr,
+    &ssh_aes128_cbc,
+};
+
+const ssh2_ciphers ssh2_aes = { lenof(aes_list), aes_list };

+ 29 - 927
source/putty/sshaes.c → source/putty/crypto/aes-sw.c

@@ -1,265 +1,4 @@
 /*
- * sshaes.c - implementation of AES
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "ssh.h"
-#include "mpint_i.h"               /* we reuse the BignumInt system */
-
-/*
- * Start by deciding whether we can support hardware AES at all.
- */
-#define HW_AES_NONE 0
-#define HW_AES_NI 1
-#define HW_AES_NEON 2
-
-#ifdef _FORCE_AES_NI
-#   define HW_AES HW_AES_NI
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<wmmintrin.h>) &&       \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_AES HW_AES_NI
-#   endif
-#elif defined(__GNUC__)
-#    if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) && \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_AES HW_AES_NI
-#    endif
-#elif defined (_MSC_VER)
-#   if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729
-#      define HW_AES HW_AES_NI
-#   endif
-#endif
-
-#define HW_AES HW_AES_NI // WINSCP
-
-#ifdef _FORCE_AES_NEON
-#   define HW_AES HW_AES_NEON
-#elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-    /* Arm can potentially support both endiannesses, but this code
-     * hasn't been tested on anything but little. If anyone wants to
-     * run big-endian, they'll need to fix it first. */
-#elif defined __ARM_FEATURE_CRYPTO
-    /* If the Arm crypto extension is available already, we can
-     * support NEON AES without having to enable anything by hand */
-#   define HW_AES HW_AES_NEON
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<arm_neon.h>) &&       \
-    (defined(__aarch64__))
-        /* clang can enable the crypto extension in AArch64 using
-         * __attribute__((target)) */
-#       define HW_AES HW_AES_NEON
-#       define USE_CLANG_ATTR_TARGET_AARCH64
-#   endif
-#elif defined _MSC_VER
-#   if defined _M_ARM64
-#       define HW_AES HW_AES_NEON
-        /* 64-bit Visual Studio uses the header <arm64_neon.h> in place
-         * of the standard <arm_neon.h> */
-#       define USE_ARM64_NEON_H
-#   elif defined _M_ARM
-#       define HW_AES HW_AES_NEON
-        /* 32-bit Visual Studio uses the right header name, but requires
-         * this #define to enable a set of intrinsic definitions that
-         * do not omit one of the parameters for vaes[ed]q_u8 */
-#       define _ARM_USE_NEW_NEON_INTRINSICS
-#   endif
-#endif
-
-#if defined _FORCE_SOFTWARE_AES || !defined HW_AES
-#   undef HW_AES
-#   define HW_AES HW_AES_NONE
-#endif
-
-#if HW_AES == HW_AES_NI
-#define HW_NAME_SUFFIX " (AES-NI accelerated)"
-#elif HW_AES == HW_AES_NEON
-#define HW_NAME_SUFFIX " (NEON accelerated)"
-#else
-#define HW_NAME_SUFFIX " (!NONEXISTENT ACCELERATED VERSION!)"
-#endif
-
-/*
- * Vtable collection for AES. For each SSH-level cipher id (i.e.
- * combination of key length and cipher mode), we provide three
- * vtables: one for the pure software implementation, one using
- * hardware acceleration (if available), and a top-level one which is
- * never actually instantiated, and only contains a new() method whose
- * job is to decide which of the other two to return an actual
- * instance of.
- */
-
-static ssh_cipher *aes_select(const ssh_cipheralg *alg);
-static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg);
-static void aes_sw_free(ssh_cipher *);
-static void aes_sw_setiv_cbc(ssh_cipher *, const void *iv);
-static void aes_sw_setiv_sdctr(ssh_cipher *, const void *iv);
-static void aes_sw_setkey(ssh_cipher *, const void *key);
-/*WINSCP static*/ ssh_cipher *aes_hw_new(const ssh_cipheralg *alg);
-/*WINSCP static*/ void aes_hw_free(ssh_cipher *);
-/*WINSCP static*/ void aes_hw_setiv_cbc(ssh_cipher *, const void *iv);
-/*WINSCP static*/ void aes_hw_setiv_sdctr(ssh_cipher *, const void *iv);
-/*WINSCP static*/ void aes_hw_setkey(ssh_cipher *, const void *key);
-
-#ifndef WINSCP_VS
-struct aes_extra {
-    const ssh_cipheralg *sw, *hw;
-};
-
-#define VTABLES_INNER(cid, pid, bits, name, encsuffix,                  \
-                      decsuffix, setivsuffix, flagsval)                 \
-    /*WINSCP static*/ void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    /*WINSCP static*/ void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_sw = {                              \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_sw_new,                                              \
-        /*.free =*/ aes_sw_free,                                            \
-        /*.setiv =*/ aes_sw_##setivsuffix,                                  \
-        /*.setkey =*/ aes_sw_setkey,                                        \
-        /*.encrypt =*/ cid##_sw##encsuffix,                                 \
-        /*.decrypt =*/ cid##_sw##decsuffix,                                 \
-        NULL, NULL, /*WINSCP*/                                              \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name " (unaccelerated)",                           \
-        NULL, NULL, /*WINSCP*/                                              \
-    };                                                                  \
-                                                                        \
-    /*WINSCP static*/ void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    /*WINSCP static*/ void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_hw = {                              \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_hw_new,                                              \
-        /*.free =*/ aes_hw_free,                                            \
-        /*.setiv =*/ aes_hw_##setivsuffix,                                  \
-        /*.setkey =*/ aes_hw_setkey,                                        \
-        /*.encrypt =*/ cid##_hw##encsuffix,                                 \
-        /*.decrypt =*/ cid##_hw##decsuffix,                                 \
-        NULL, NULL, /*WINSCP*/                                              \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name HW_NAME_SUFFIX,                               \
-        NULL, NULL, /*WINSCP*/                                              \
-    };                                                                  \
-                                                                        \
-    static const struct aes_extra extra_##cid = {                       \
-        &ssh_##cid##_sw, &ssh_##cid##_hw };                             \
-                                                                        \
-    const ssh_cipheralg ssh_##cid = {                                   \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_select,                                              \
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL,                           \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name " (dummy selector vtable)",                   \
-        NULL,                                                               \
-        /*.extra =*/ &extra_##cid                                           \
-    };                                                                  \
-
-#define VTABLES(keylen)                                                 \
-    VTABLES_INNER(aes ## keylen ## _cbc, "aes" #keylen "-cbc",          \
-                  keylen, "AES-" #keylen " CBC", _encrypt, _decrypt,    \
-                  setiv_cbc, SSH_CIPHER_IS_CBC)                         \
-    VTABLES_INNER(aes ## keylen ## _sdctr, "aes" #keylen "-ctr",        \
-                  keylen, "AES-" #keylen " SDCTR",,, setiv_sdctr, 0)
-
-VTABLES(128)
-VTABLES(192)
-VTABLES(256)
-
-static const ssh_cipheralg ssh_rijndael_lysator = {
-    /* Same as aes256_cbc, but with a different protocol ID */
-    // WINSCP
-    /*.new =*/ aes_select,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "[email protected]",
-    /*.blksize =*/ 16,
-    /*.real_keybits =*/ 256,
-    /*.padded_keybytes =*/ 256/8,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "AES-256 CBC (dummy selector vtable)",
-    NULL, // WINSCP
-    /*.extra =*/ &extra_aes256_cbc,
-};
-
-static const ssh_cipheralg *const aes_list[] = {
-    &ssh_aes256_sdctr,
-    &ssh_aes256_cbc,
-    &ssh_rijndael_lysator,
-    &ssh_aes192_sdctr,
-    &ssh_aes192_cbc,
-    &ssh_aes128_sdctr,
-    &ssh_aes128_cbc,
-};
-
-const ssh2_ciphers ssh2_aes = { lenof(aes_list), aes_list };
-#endif
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-/*WINSCP static*/ bool aes_hw_available(void);
-
-/*
- * The top-level selection function, caching the results of
- * aes_hw_available() so it only has to run once.
- */
-static bool aes_hw_available_cached(void)
-{
-    static bool initialised = false;
-    static bool hw_available;
-    if (!initialised) {
-        hw_available = aes_hw_available();
-        initialised = true;
-    }
-    return hw_available;
-}
-
-#ifndef WINSCP_VS
-static ssh_cipher *aes_select(const ssh_cipheralg *alg)
-{
-    const struct aes_extra *extra = (const struct aes_extra *)alg->extra;
-    const ssh_cipheralg *real_alg =
-        aes_hw_available_cached() ? extra->hw : extra->sw;
-
-    return ssh_cipher_new(real_alg);
-}
-#endif
-
-/* ----------------------------------------------------------------------
- * Definitions likely to be helpful to multiple implementations.
- */
-
-#define REP2(x) x x
-#define REP4(x) REP2(REP2(x))
-#define REP8(x) REP2(REP4(x))
-#define REP9(x) REP8(x) x
-#define REP11(x) REP8(x) REP2(x) x
-#define REP13(x) REP8(x) REP4(x) x
-
-static const uint8_t key_setup_round_constants[] = {
-    /* The first few powers of X in GF(2^8), used during key setup.
-     * This can safely be a lookup table without side channel risks,
-     * because key setup iterates through it once in a standard way
-     * regardless of the key. */
-    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
-};
-
-#define MAXROUNDKEYS 15
-
-/* ----------------------------------------------------------------------
  * Software implementation of AES.
  *
  * This implementation uses a bit-sliced representation. Instead of
@@ -275,6 +14,16 @@ static const uint8_t key_setup_round_constants[] = {
  * ops you get 64 S-box lookups, not just one.
  */
 
+#include "ssh.h"
+#include "aes.h"
+#include "mpint_i.h"               /* we reuse the BignumInt system */
+
+static bool aes_sw_available(void)
+{
+    /* Software AES is always available */
+    return true;
+}
+
 #define SLICE_PARALLELISM (BIGNUM_INT_BYTES / 2)
 
 #ifdef WINSCP_VS
@@ -947,8 +696,8 @@ struct aes_sliced_key {
             }
 
             if (rotate_and_round_constant) {
-                assert(rconpos < lenof(key_setup_round_constants));
-                uint8_t rcon = key_setup_round_constants[rconpos++];
+                assert(rconpos < lenof(aes_key_setup_round_constants));
+                uint8_t rcon = aes_key_setup_round_constants[rconpos++];
                 for (size_t i = 0; i < 8; i++)
                     slices[i] ^= 1 & (rcon >> i);
             }
@@ -1291,684 +1040,37 @@ static inline void aes_sdctr_sw(
 }
 
 #define SW_ENC_DEC(len)                                 \
-    /*WINSCP static*/ void aes##len##_cbc_sw_encrypt(              \
+    /*WINSCP static*/ void aes##len##_sw_cbc_encrypt(              \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_encrypt(ciph, vblk, blklen); }         \
-    /*WINSCP static*/ void aes##len##_cbc_sw_decrypt(              \
+    /*WINSCP static*/ void aes##len##_sw_cbc_decrypt(              \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_decrypt(ciph, vblk, blklen); }         \
-    /*WINSCP static*/ void aes##len##_sdctr_sw(                    \
+    /*WINSCP static*/ void aes##len##_sw_sdctr(                    \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_sdctr_sw(ciph, vblk, blklen); }
 
-SW_ENC_DEC(128)
-SW_ENC_DEC(192)
-SW_ENC_DEC(256)
-
-#endif
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using x86 AES-NI.
- */
-
-#if HW_AES == HW_AES_NI
-
-#ifdef WINSCP_VS
-
-/*
- * Set target architecture for Clang and GCC
- */
-#if !defined(__clang__) && defined(__GNUC__)
-#    pragma GCC target("aes")
-#    pragma GCC target("sse4.1")
-#endif
-
-#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
-#    define FUNC_ISA __attribute__ ((target("sse4.1,aes")))
-#else
-#    define FUNC_ISA
-#endif
-
-#include <wmmintrin.h>
-#include <smmintrin.h>
-
-#if defined(__clang__) || defined(__GNUC__)
-#include <cpuid.h>
-#define GET_CPU_ID(out) __cpuid(1, (out)[0], (out)[1], (out)[2], (out)[3])
-#else
-#define GET_CPU_ID(out) __cpuid(out, 1)
-#endif
-
-bool aes_hw_available(void)
-{
-    /*
-     * Determine if AES is available on this CPU, by checking that
-     * both AES itself and SSE4.1 are supported.
-     */
-    unsigned int CPUInfo[4];
-    GET_CPU_ID(CPUInfo);
-    return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19));
-}
-
-/*
- * Core AES-NI encrypt/decrypt functions, one per length and direction.
- */
-
-#define NI_CIPHER(len, dir, dirlong, repmacro)                          \
-    static FUNC_ISA inline __m128i aes_ni_##len##_##dir(                \
-        __m128i v, const __m128i *keysched)                             \
-    {                                                                   \
-        v = _mm_xor_si128(v, *keysched++);                              \
-        repmacro(v = _mm_aes##dirlong##_si128(v, *keysched++););        \
-        return _mm_aes##dirlong##last_si128(v, *keysched);              \
-    }
-
-NI_CIPHER(128, e, enc, REP9)
-NI_CIPHER(128, d, dec, REP9)
-NI_CIPHER(192, e, enc, REP11)
-NI_CIPHER(192, d, dec, REP11)
-NI_CIPHER(256, e, enc, REP13)
-NI_CIPHER(256, d, dec, REP13)
-
-/*
- * The main key expansion.
- */
-static FUNC_ISA void aes_ni_key_expand(
-    const unsigned char *key, size_t key_words,
-    __m128i *keysched_e, __m128i *keysched_d)
-{
-    size_t rounds = key_words + 6;
-    size_t sched_words = (rounds + 1) * 4;
-
-    /*
-     * Store the key schedule as 32-bit integers during expansion, so
-     * that it's easy to refer back to individual previous words. We
-     * collect them into the final __m128i form at the end.
-     */
-    uint32_t sched[MAXROUNDKEYS * 4];
-
-    unsigned rconpos = 0;
-
-    for (size_t i = 0; i < sched_words; i++) {
-        if (i < key_words) {
-            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
-        } else {
-            uint32_t temp = sched[i - 1];
-
-            bool rotate_and_round_constant = (i % key_words == 0);
-            bool only_sub = (key_words == 8 && i % 8 == 4);
-
-            if (rotate_and_round_constant) {
-                __m128i v = _mm_setr_epi32(0,temp,0,0);
-                v = _mm_aeskeygenassist_si128(v, 0);
-                temp = _mm_extract_epi32(v, 1);
-
-                assert(rconpos < lenof(key_setup_round_constants));
-                temp ^= key_setup_round_constants[rconpos++];
-            } else if (only_sub) {
-                __m128i v = _mm_setr_epi32(0,temp,0,0);
-                v = _mm_aeskeygenassist_si128(v, 0);
-                temp = _mm_extract_epi32(v, 0);
-            }
-
-            sched[i] = sched[i - key_words] ^ temp;
-        }
-    }
-
-    /*
-     * Combine the key schedule words into __m128i vectors and store
-     * them in the output context.
-     */
-    for (size_t round = 0; round <= rounds; round++)
-        keysched_e[round] = _mm_setr_epi32(
-            sched[4*round  ], sched[4*round+1],
-            sched[4*round+2], sched[4*round+3]);
-
-    smemclr(sched, sizeof(sched));
-
-    /*
-     * Now prepare the modified keys for the inverse cipher.
-     */
-    for (size_t eround = 0; eround <= rounds; eround++) {
-        size_t dround = rounds - eround;
-        __m128i rkey = keysched_e[eround];
-        if (eround && dround)      /* neither first nor last */
-            rkey = _mm_aesimc_si128(rkey);
-        keysched_d[dround] = rkey;
-    }
-}
-
-// WINSCP
-// WORKAROUND
-// Cannot use _mm_setr_epi* - it results in the constant being stored in .rdata segment.
-// objconv reports:
-// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
-// Despite that the code crashes.
-// This macro is based on:
-// Based on https://stackoverflow.com/q/35268036/850848
-#define _MM_SETR_EPI8(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \
-    { (char)a0, (char)a1, (char)a2, (char)a3, (char)a4, (char)a5, (char)a6, (char)a7, \
-      (char)a8, (char)a9, (char)aa, (char)ab, (char)ac, (char)ad, (char)ae, (char)af }
-
-/*
- * Auxiliary routine to increment the 128-bit counter used in SDCTR
- * mode.
- */
-static FUNC_ISA inline __m128i aes_ni_sdctr_increment(__m128i v)
-{
-    const __m128i ONE = _MM_SETR_EPI8(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); // WINSCP
-    const __m128i ZERO = _mm_setzero_si128();
-
-    /* Increment the low-order 64 bits of v */
-    v  = _mm_add_epi64(v, ONE);
-    /* Check if they've become zero */
-    __m128i cmp = _mm_cmpeq_epi64(v, ZERO);
-    /* If so, the low half of cmp is all 1s. Pack that into the high
-     * half of addend with zero in the low half. */
-    __m128i addend = _mm_unpacklo_epi64(ZERO, cmp);
-    /* And subtract that from v, which increments the high 64 bits iff
-     * the low 64 wrapped round. */
-    v = _mm_sub_epi64(v, addend);
-
-    return v;
-}
-
-/*
- * Auxiliary routine to reverse the byte order of a vector, so that
- * the SDCTR IV can be made big-endian for feeding to the cipher.
- */
-static FUNC_ISA inline __m128i aes_ni_sdctr_reverse(__m128i v)
-{
-    const __m128i R = _MM_SETR_EPI8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); // WINSCP
-    v = _mm_shuffle_epi8(
-        v, R); // WINSCP
-    return v;
-}
-
-/*
- * The SSH interface and the cipher modes.
- */
-
-typedef struct aes_ni_context aes_ni_context;
-struct aes_ni_context {
-    __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
-
-    void *pointer_to_free;
-    ssh_cipher ciph;
-};
-
-/*static WINSCP*/ ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    if (!aes_hw_available_cached())
-        return NULL;
-
-    /*
-     * The __m128i variables in the context structure need to be
-     * 16-byte aligned, but not all malloc implementations that this
-     * code has to work with will guarantee to return a 16-byte
-     * aligned pointer. So we over-allocate, manually realign the
-     * pointer ourselves, and store the original one inside the
-     * context so we know how to free it later.
-     */
-    void *allocation = smalloc(sizeof(aes_ni_context) + 15);
-    uintptr_t alloc_address = (uintptr_t)allocation;
-    uintptr_t aligned_address = (alloc_address + 15) & ~15;
-    aes_ni_context *ctx = (aes_ni_context *)aligned_address;
-
-    ctx->ciph.vt = alg;
-    ctx->pointer_to_free = allocation;
-    return &ctx->ciph;
-}
-
-/*static WINSCP*/ void aes_hw_free(ssh_cipher *ciph)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    void *allocation = ctx->pointer_to_free;
-    smemclr(ctx, sizeof(*ctx));
-    sfree(allocation);
-}
-
-/*static WINSCP*/ void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    aes_ni_key_expand(key, ctx->ciph.vt->real_keybits / 32,
-                      ctx->keysched_e, ctx->keysched_d);
-}
-
-/*static WINSCP*/ FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    ctx->iv = _mm_loadu_si128(iv);
-}
-
-/*static WINSCP*/ FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    __m128i counter = _mm_loadu_si128(iv);
-    ctx->iv = aes_ni_sdctr_reverse(counter);
-}
-
-typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched);
-
-static FUNC_ISA inline void aes_cbc_ni_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i plaintext = _mm_loadu_si128((const __m128i *)blk);
-        __m128i cipher_input = _mm_xor_si128(plaintext, ctx->iv);
-        __m128i ciphertext = encrypt(cipher_input, ctx->keysched_e);
-        _mm_storeu_si128((__m128i *)blk, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
+#else // WINSCP_VS
 
-static FUNC_ISA inline void aes_cbc_ni_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i ciphertext = _mm_loadu_si128((const __m128i *)blk);
-        __m128i decrypted = decrypt(ciphertext, ctx->keysched_d);
-        __m128i plaintext = _mm_xor_si128(decrypted, ctx->iv);
-        _mm_storeu_si128((__m128i *)blk, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_sdctr_ni(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i counter = aes_ni_sdctr_reverse(ctx->iv);
-        __m128i keystream = encrypt(counter, ctx->keysched_e);
-        __m128i input = _mm_loadu_si128((const __m128i *)blk);
-        __m128i output = _mm_xor_si128(input, keystream);
-        _mm_storeu_si128((__m128i *)blk, output);
-        ctx->iv = aes_ni_sdctr_increment(ctx->iv);
-    }
-}
-
-#define NI_ENC_DEC(len)                                                 \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_cbc_hw_encrypt(          \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); }       \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_cbc_hw_decrypt(          \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); }       \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_sdctr_hw(                \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); }             \
-
-NI_ENC_DEC(128)
-NI_ENC_DEC(192)
-NI_ENC_DEC(256)
+#define SW_ENC_DEC(len)                                 \
+    void aes##len##_sw_cbc_encrypt(                     \
+        ssh_cipher *ciph, void *vblk, int blklen);      \
+    void aes##len##_sw_cbc_decrypt(                     \
+        ssh_cipher *ciph, void *vblk, int blklen);      \
+    void aes##len##_sw_sdctr(                           \
+        ssh_cipher *ciph, void *vblk, int blklen);      \
 
 #endif // WINSCP_VS
 
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using Arm NEON.
- */
-
-#elif HW_AES == HW_AES_NEON
-
-/*
- * Manually set the target architecture, if we decided above that we
- * need to.
- */
-#ifdef USE_CLANG_ATTR_TARGET_AARCH64
-/*
- * A spot of cheating: redefine some ACLE feature macros before
- * including arm_neon.h. Otherwise we won't get the AES intrinsics
- * defined by that header, because it will be looking at the settings
- * for the whole translation unit rather than the ones we're going to
- * put on some particular functions using __attribute__((target)).
- */
-#define __ARM_NEON 1
-#define __ARM_FEATURE_CRYPTO 1
-#define __ARM_FEATURE_AES 1
-#define FUNC_ISA __attribute__ ((target("neon,crypto")))
-#endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
-
-#ifndef FUNC_ISA
-#define FUNC_ISA
-#endif
-
-#ifdef USE_ARM64_NEON_H
-#include <arm64_neon.h>
-#else
-#include <arm_neon.h>
-#endif
-
-static bool aes_hw_available(void)
-{
-    /*
-     * For Arm, we delegate to a per-platform AES detection function,
-     * because it has to be implemented by asking the operating system
-     * rather than directly querying the CPU.
-     *
-     * That's because Arm systems commonly have multiple cores that
-     * are not all alike, so any method of querying whether NEON
-     * crypto instructions work on the _current_ CPU - even one as
-     * crude as just trying one and catching the SIGILL - wouldn't
-     * give an answer that you could still rely on the first time the
-     * OS migrated your process to another CPU.
-     */
-    return platform_aes_hw_available();
-}
-
-/*
- * Core NEON encrypt/decrypt functions, one per length and direction.
- */
-
-#define NEON_CIPHER(len, repmacro)                              \
-    static FUNC_ISA inline uint8x16_t aes_neon_##len##_e(       \
-        uint8x16_t v, const uint8x16_t *keysched)               \
-    {                                                           \
-        repmacro(v = vaesmcq_u8(vaeseq_u8(v, *keysched++)););   \
-        v = vaeseq_u8(v, *keysched++);                          \
-        return veorq_u8(v, *keysched);                          \
-    }                                                           \
-    static FUNC_ISA inline uint8x16_t aes_neon_##len##_d(       \
-        uint8x16_t v, const uint8x16_t *keysched)               \
-    {                                                           \
-        repmacro(v = vaesimcq_u8(vaesdq_u8(v, *keysched++)););  \
-        v = vaesdq_u8(v, *keysched++);                          \
-        return veorq_u8(v, *keysched);                          \
-    }
-
-NEON_CIPHER(128, REP9)
-NEON_CIPHER(192, REP11)
-NEON_CIPHER(256, REP13)
-
-/*
- * The main key expansion.
- */
-static FUNC_ISA void aes_neon_key_expand(
-    const unsigned char *key, size_t key_words,
-    uint8x16_t *keysched_e, uint8x16_t *keysched_d)
-{
-    size_t rounds = key_words + 6;
-    size_t sched_words = (rounds + 1) * 4;
-
-    /*
-     * Store the key schedule as 32-bit integers during expansion, so
-     * that it's easy to refer back to individual previous words. We
-     * collect them into the final uint8x16_t form at the end.
-     */
-    uint32_t sched[MAXROUNDKEYS * 4];
-
-    unsigned rconpos = 0;
-
-    for (size_t i = 0; i < sched_words; i++) {
-        if (i < key_words) {
-            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
-        } else {
-            uint32_t temp = sched[i - 1];
-
-            bool rotate_and_round_constant = (i % key_words == 0);
-            bool sub = rotate_and_round_constant ||
-                (key_words == 8 && i % 8 == 4);
-
-            if (rotate_and_round_constant)
-                temp = (temp << 24) | (temp >> 8);
-
-            if (sub) {
-                uint32x4_t v32 = vdupq_n_u32(temp);
-                uint8x16_t v8 = vreinterpretq_u8_u32(v32);
-                v8 = vaeseq_u8(v8, vdupq_n_u8(0));
-                v32 = vreinterpretq_u32_u8(v8);
-                temp = vget_lane_u32(vget_low_u32(v32), 0);
-            }
-
-            if (rotate_and_round_constant) {
-                assert(rconpos < lenof(key_setup_round_constants));
-                temp ^= key_setup_round_constants[rconpos++];
-            }
-
-            sched[i] = sched[i - key_words] ^ temp;
-        }
-    }
-
-    /*
-     * Combine the key schedule words into uint8x16_t vectors and
-     * store them in the output context.
-     */
-    for (size_t round = 0; round <= rounds; round++)
-        keysched_e[round] = vreinterpretq_u8_u32(vld1q_u32(sched + 4*round));
-
-    smemclr(sched, sizeof(sched));
-
-    /*
-     * Now prepare the modified keys for the inverse cipher.
-     */
-    for (size_t eround = 0; eround <= rounds; eround++) {
-        size_t dround = rounds - eround;
-        uint8x16_t rkey = keysched_e[eround];
-        if (eround && dround)      /* neither first nor last */
-            rkey = vaesimcq_u8(rkey);
-        keysched_d[dround] = rkey;
-    }
-}
-
-/*
- * Auxiliary routine to reverse the byte order of a vector, so that
- * the SDCTR IV can be made big-endian for feeding to the cipher.
- *
- * In fact we don't need to reverse the vector _all_ the way; we leave
- * the two lanes in MSW,LSW order, because that makes no difference to
- * the efficiency of the increment. That way we only have to reverse
- * bytes within each lane in this function.
- */
-static FUNC_ISA inline uint8x16_t aes_neon_sdctr_reverse(uint8x16_t v)
-{
-    return vrev64q_u8(v);
-}
-
-/*
- * Auxiliary routine to increment the 128-bit counter used in SDCTR
- * mode. There's no instruction to treat a 128-bit vector as a single
- * long integer, so instead we have to increment the bottom half
- * unconditionally, and the top half if the bottom half started off as
- * all 1s (in which case there was about to be a carry).
- */
-static FUNC_ISA inline uint8x16_t aes_neon_sdctr_increment(uint8x16_t in)
-{
-#ifdef __aarch64__
-    /* There will be a carry if the low 64 bits are all 1s. */
-    uint64x1_t all1 = vcreate_u64(0xFFFFFFFFFFFFFFFF);
-    uint64x1_t carry = vceq_u64(vget_high_u64(vreinterpretq_u64_u8(in)), all1);
-
-    /* Make a word whose bottom half is unconditionally all 1s, and
-     * the top half is 'carry', i.e. all 0s most of the time but all
-     * 1s if we need to increment the top half. Then that word is what
-     * we need to _subtract_ from the input counter. */
-    uint64x2_t subtrahend = vcombine_u64(carry, all1);
-#else
-    /* AArch32 doesn't have comparisons that operate on a 64-bit lane,
-     * so we start by comparing each 32-bit half of the low 64 bits
-     * _separately_ to all-1s. */
-    uint32x2_t all1 = vdup_n_u32(0xFFFFFFFF);
-    uint32x2_t carry = vceq_u32(
-        vget_high_u32(vreinterpretq_u32_u8(in)), all1);
-
-    /* Swap the 32-bit words of the compare output, and AND with the
-     * unswapped version. Now carry is all 1s iff the bottom half of
-     * the input counter was all 1s, and all 0s otherwise. */
-    carry = vand_u32(carry, vrev64_u32(carry));
-
-    /* Now make the vector to subtract in the same way as above. */
-    uint64x2_t subtrahend = vreinterpretq_u64_u32(vcombine_u32(carry, all1));
-#endif
-
-    return vreinterpretq_u8_u64(
-        vsubq_u64(vreinterpretq_u64_u8(in), subtrahend));
-}
-
-/*
- * The SSH interface and the cipher modes.
- */
-
-typedef struct aes_neon_context aes_neon_context;
-struct aes_neon_context {
-    uint8x16_t keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
-
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    if (!aes_hw_available_cached())
-        return NULL;
-
-    aes_neon_context *ctx = snew(aes_neon_context);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void aes_hw_free(ssh_cipher *ciph)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    aes_neon_key_expand(key, ctx->ciph.vt->real_keybits / 32,
-                      ctx->keysched_e, ctx->keysched_d);
-}
-
-static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    ctx->iv = vld1q_u8(iv);
-}
-
-static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    uint8x16_t counter = vld1q_u8(iv);
-    ctx->iv = aes_neon_sdctr_reverse(counter);
-}
-
-typedef uint8x16_t (*aes_neon_fn)(uint8x16_t v, const uint8x16_t *keysched);
-
-static FUNC_ISA inline void aes_cbc_neon_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t plaintext = vld1q_u8(blk);
-        uint8x16_t cipher_input = veorq_u8(plaintext, ctx->iv);
-        uint8x16_t ciphertext = encrypt(cipher_input, ctx->keysched_e);
-        vst1q_u8(blk, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_cbc_neon_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn decrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t ciphertext = vld1q_u8(blk);
-        uint8x16_t decrypted = decrypt(ciphertext, ctx->keysched_d);
-        uint8x16_t plaintext = veorq_u8(decrypted, ctx->iv);
-        vst1q_u8(blk, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_sdctr_neon(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t counter = aes_neon_sdctr_reverse(ctx->iv);
-        uint8x16_t keystream = encrypt(counter, ctx->keysched_e);
-        uint8x16_t input = vld1q_u8(blk);
-        uint8x16_t output = veorq_u8(input, keystream);
-        vst1q_u8(blk, output);
-        ctx->iv = aes_neon_sdctr_increment(ctx->iv);
-    }
-}
-
-#define NEON_ENC_DEC(len)                                               \
-    static FUNC_ISA void aes##len##_cbc_hw_encrypt(                     \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_neon_encrypt(ciph, vblk, blklen, aes_neon_##len##_e); }   \
-    static FUNC_ISA void aes##len##_cbc_hw_decrypt(                     \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_neon_decrypt(ciph, vblk, blklen, aes_neon_##len##_d); }   \
-    static FUNC_ISA void aes##len##_sdctr_hw(                           \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_sdctr_neon(ciph, vblk, blklen, aes_neon_##len##_e); }         \
-
-NEON_ENC_DEC(128)
-NEON_ENC_DEC(192)
-NEON_ENC_DEC(256)
-
-/* ----------------------------------------------------------------------
- * Stub functions if we have no hardware-accelerated AES. In this
- * case, aes_hw_new returns NULL (though it should also never be
- * selected by aes_select, so the only thing that should even be
- * _able_ to call it is testcrypt). As a result, the remaining vtable
- * functions should never be called at all.
- */
-
-#elif HW_AES == HW_AES_NONE
-
-bool aes_hw_available(void)
-{
-    return false;
-}
-
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    return NULL;
-}
-
-#define STUB_BODY { unreachable("Should never be called"); }
-
-static void aes_hw_free(ssh_cipher *ciph) STUB_BODY
-static void aes_hw_setkey(ssh_cipher *ciph, const void *key) STUB_BODY
-static void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) STUB_BODY
-static void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) STUB_BODY
-#define STUB_ENC_DEC(len)                                       \
-    static void aes##len##_cbc_hw_encrypt(                      \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
-    static void aes##len##_cbc_hw_decrypt(                      \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
-    static void aes##len##_sdctr_hw(                            \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY
-
-STUB_ENC_DEC(128)
-STUB_ENC_DEC(192)
-STUB_ENC_DEC(256)
-
-#endif /* HW_AES */
+SW_ENC_DEC(128)
+SW_ENC_DEC(192)
+SW_ENC_DEC(256)
 
 #ifndef WINSCP_VS
 
+AES_EXTRA(_sw);
+AES_ALL_VTABLES(_sw, "unaccelerated");
+
 #ifdef MPEXT
 
 #include "puttyexp.h"

+ 126 - 0
source/putty/crypto/aes.h

@@ -0,0 +1,126 @@
+/*
+ * Definitions likely to be helpful to multiple AES implementations.
+ */
+
+/*
+ * The 'extra' structure used by AES implementations is used to
+ * include information about how to check if a given implementation is
+ * available at run time, and whether we've already checked.
+ */
+struct aes_extra_mutable;
+struct aes_extra {
+    /* Function to check availability. Might be expensive, so we don't
+     * want to call it more than once. */
+    bool (*check_available)(void);
+
+    /* Point to a writable substructure. */
+    struct aes_extra_mutable *mut;
+};
+struct aes_extra_mutable {
+    bool checked_availability;
+    bool is_available;
+};
+static inline bool check_availability(const struct aes_extra *extra)
+{
+    if (!extra->mut->checked_availability) {
+        extra->mut->is_available = extra->check_available();
+        extra->mut->checked_availability = true;
+    }
+
+    return extra->mut->is_available;
+}
+
+/*
+ * Macros to define vtables for AES variants. There are a lot of
+ * these, because of the cross product between cipher modes, key
+ * sizes, and assorted HW/SW implementations, so it's worth spending
+ * some effort here to reduce the boilerplate in the sub-files.
+ */
+
+#define AES_EXTRA(impl_c)                                               \
+    static struct aes_extra_mutable aes ## impl_c ## _extra_mut;        \
+    static const struct aes_extra aes ## impl_c ## _extra = {           \
+        /* WINSCP */ \
+        /*.check_available =*/ aes ## impl_c ## _available,                 \
+        /*.mut =*/ &aes ## impl_c ## _extra_mut,                            \
+    }
+
+// WINSCP string constants are for avoiding 
+// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
+// in objconv
+
+#define AES_CBC_VTABLE(impl_c, impl_display, bits)                      \
+    const char ssh_aes ## bits ## _cbc ## impl_c ## ssh2_id[] = "aes" #bits "-cbc"; /*WINSCP*/ \
+    const char ssh_aes ## bits ## _cbc ## impl_c ## text_name[] = "AES-" #bits " CBC (" impl_display ")"; /*WINSCP*/ \
+    const ssh_cipheralg ssh_aes ## bits ## _cbc ## impl_c = {           \
+        /*WINSCP*/ \
+        /*.new =*/ aes ## impl_c ## _new,                                   \
+        /*.free =*/ aes ## impl_c ## _free,                                 \
+        /*.setiv =*/ aes ## impl_c ## _setiv_cbc,                           \
+        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
+        /*.encrypt =*/ aes ## bits ## impl_c ## _cbc_encrypt,               \
+        /*.decrypt =*/ aes ## bits ## impl_c ## _cbc_decrypt,               \
+        NULL, \
+        NULL, \
+        /*.ssh2_id =*/ ssh_aes ## bits ## _cbc ## impl_c ## ssh2_id, /*WINSCP*/ \
+        /*.blksize =*/ 16,                                                  \
+        /*.real_keybits =*/ bits,                                           \
+        /*.padded_keybytes =*/ bits/8,                                      \
+        /*.flags =*/ SSH_CIPHER_IS_CBC,                                     \
+        /*.text_name =*/ ssh_aes ## bits ## _cbc ## impl_c ## text_name, /*WINSCP*/ \
+        NULL, \
+        /*.extra =*/ &aes ## impl_c ## _extra,                              \
+    }
+
+#define AES_SDCTR_VTABLE(impl_c, impl_display, bits)                    \
+    const char ssh_aes ## bits ## _sdctr ## impl_c ## ssh2_id[] = "aes" #bits "-ctr"; /*WINSCP*/ \
+    const char ssh_aes ## bits ## _sdctr ## impl_c ## text_name[] = "AES-" #bits " SDCTR (" impl_display ")"; /*WINSCP*/ \
+    const ssh_cipheralg ssh_aes ## bits ## _sdctr ## impl_c = {         \
+        /*WINSCP*/ \
+        /*.new =*/ aes ## impl_c ## _new,                                   \
+        /*.free =*/ aes ## impl_c ## _free,                                 \
+        /*.setiv =*/ aes ## impl_c ## _setiv_sdctr,                         \
+        /*.setkey =*/ aes ## impl_c ## _setkey,                             \
+        /*.encrypt =*/ aes ## bits ## impl_c ## _sdctr,                     \
+        /*.decrypt =*/ aes ## bits ## impl_c ## _sdctr,                     \
+        NULL, \
+        NULL, \
+        /*.ssh2_id =*/ ssh_aes ## bits ## _sdctr ## impl_c ## ssh2_id, /*WINSCP*/ \
+        /*.blksize =*/ 16,                                                  \
+        /*.real_keybits =*/ bits,                                           \
+        /*.padded_keybytes =*/ bits/8,                                      \
+        /*.flags =*/ 0,                                                     \
+        /*.text_name =*/ ssh_aes ## bits ## _sdctr ## impl_c ## text_name, /*WINSCP*/ \
+        NULL, \
+        /*.extra =*/ &aes ## impl_c ## _extra,                              \
+    }
+
+#define AES_ALL_VTABLES(impl_c, impl_display)           \
+    AES_CBC_VTABLE(impl_c, impl_display, 128);          \
+    AES_CBC_VTABLE(impl_c, impl_display, 192);          \
+    AES_CBC_VTABLE(impl_c, impl_display, 256);          \
+    AES_SDCTR_VTABLE(impl_c, impl_display, 128);        \
+    AES_SDCTR_VTABLE(impl_c, impl_display, 192);        \
+    AES_SDCTR_VTABLE(impl_c, impl_display, 256)
+
+/*
+ * Macros to repeat a piece of code particular numbers of times that
+ * correspond to 1 fewer than the number of AES rounds. (Because the
+ * last round is different.)
+ */
+#define REP2(x) x x
+#define REP4(x) REP2(REP2(x))
+#define REP8(x) REP2(REP4(x))
+#define REP9(x) REP8(x) x
+#define REP11(x) REP8(x) REP2(x) x
+#define REP13(x) REP8(x) REP4(x) x
+
+/*
+ * The round constants used in key schedule expansion.
+ */
+extern const uint8_t aes_key_setup_round_constants[10];
+
+/*
+ * The largest number of round keys ever needed.
+ */
+#define MAXROUNDKEYS 15

+ 0 - 0
source/putty/sshaesold.c → source/putty/crypto/aesold.c


+ 0 - 0
source/putty/ssharcf.c → source/putty/crypto/arcfour.c


+ 1 - 1
source/putty/sshargon2.c → source/putty/crypto/argon2.c

@@ -289,7 +289,7 @@ static void argon2_internal(uint32_t p, uint32_t T, uint32_t m, uint32_t t,
      * that in the initial slice on the first pass, we've already written
      * values into the first two columns during the initial setup above. So
      * 'jstart' indicates the starting index in each segment we process; it
-     * starts off as 2 so that we don't overwrite the inital setup, and then
+     * starts off as 2 so that we don't overwrite the initial setup, and then
      * after the first slice is done, we set it to 0, and it stays there.
      *
      * d_mode indicates whether we're being data-dependent (true) or

+ 5 - 6
source/putty/sshbcrypt.c → source/putty/crypto/bcrypt.c

@@ -9,7 +9,7 @@
 #include <stddef.h>
 #include <string.h>
 #include "ssh.h"
-#include "sshblowf.h"
+#include "blowfish.h"
 
 BlowfishContext *bcrypt_setup(const unsigned char *key, int keybytes,
                               const unsigned char *salt, int saltbytes)
@@ -69,8 +69,7 @@ void bcrypt_genblock(int counter,
     smemclr(&hashed_salt, sizeof(hashed_salt));
 }
 
-void openssh_bcrypt(const char *passphrase,
-                    const unsigned char *salt, int saltbytes,
+void openssh_bcrypt(ptrlen passphrase, ptrlen salt,
                     int rounds, unsigned char *out, int outbytes)
 {
     unsigned char hashed_passphrase[64];
@@ -80,7 +79,7 @@ void openssh_bcrypt(const char *passphrase,
     int modulus, residue, i, j, round;
 
     /* Hash the passphrase to get the bcrypt key material */
-    hash_simple(&ssh_sha512, ptrlen_from_asciz(passphrase), hashed_passphrase);
+    hash_simple(&ssh_sha512, passphrase, hashed_passphrase);
 
     /* We output key bytes in a scattered fashion to meld all output
      * key blocks into all parts of the output. To do this, we pick a
@@ -97,8 +96,8 @@ void openssh_bcrypt(const char *passphrase,
          * by bcrypt in the following loop */
         memset(outblock, 0, sizeof(outblock));
 
-        thissalt = salt;
-        thissaltbytes = saltbytes;
+        thissalt = salt.ptr;
+        thissaltbytes = salt.len;
         for (round = 0; round < rounds; round++) {
             bcrypt_genblock(round == 0 ? residue+1 : 0,
                             hashed_passphrase,

+ 0 - 0
source/putty/sshblake2.c → source/putty/crypto/blake2.c


+ 1 - 1
source/putty/sshblowf.c → source/putty/crypto/blowfish.c

@@ -7,7 +7,7 @@
 #include <assert.h>
 #include <stdio.h>
 #include "ssh.h"
-#include "sshblowf.h"
+#include "blowfish.h"
 
 struct BlowfishContext {
     uint32_t S0[256], S1[256], S2[256], S3[256], P[18];

+ 1 - 1
source/putty/sshblowf.h → source/putty/crypto/blowfish.h

@@ -1,5 +1,5 @@
 /*
- * Header file shared between sshblowf.c and sshbcrypt.c. Exposes the
+ * Header file shared between blowfish.c and bcrypt.c. Exposes the
  * internal Blowfish routines needed by bcrypt.
  */
 

+ 0 - 0
source/putty/sshccp.c → source/putty/crypto/chacha20-poly1305.c


+ 1 - 1
source/putty/sshdes.c → source/putty/crypto/des.c

@@ -1,5 +1,5 @@
 /*
- * sshdes.c: implementation of DES.
+ * Implementation of DES.
  */
 
 /*

+ 11 - 22
source/putty/sshdh.c → source/putty/crypto/diffie-hellman.c

@@ -19,12 +19,18 @@ struct dh_extra {
 
 static void dh_group1_construct(dh_ctx *ctx)
 {
+    /* Command to recompute, from the expression in RFC 2412 section E.2:
+spigot -B16 '2^1024 - 2^960 - 1 + 2^64 * ( floor(2^894 pi) + 129093 )'
+     */
     ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF");
     ctx->g = mp_from_integer(2);
 }
 
 static void dh_group14_construct(dh_ctx *ctx)
 {
+    /* Command to recompute, from the expression in RFC 3526 section 3:
+spigot -B16 '2^2048 - 2^1984 - 1 + 2^64 * ( floor(2^1918 pi) + 124476 )'
+     */
     ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF");
     ctx->g = mp_from_integer(2);
 }
@@ -33,7 +39,7 @@ static const struct dh_extra extra_group1 = {
     false, dh_group1_construct,
 };
 
-static const ssh_kex ssh_diffiehellman_group1_sha1 = {
+const ssh_kex ssh_diffiehellman_group1_sha1 = {
     "diffie-hellman-group1-sha1", "group1",
     KEXTYPE_DH, &ssh_sha1, &extra_group1,
 };
@@ -48,12 +54,12 @@ static const struct dh_extra extra_group14 = {
     false, dh_group14_construct,
 };
 
-static const ssh_kex ssh_diffiehellman_group14_sha256 = {
+const ssh_kex ssh_diffiehellman_group14_sha256 = {
     "diffie-hellman-group14-sha256", "group14",
     KEXTYPE_DH, &ssh_sha256, &extra_group14,
 };
 
-static const ssh_kex ssh_diffiehellman_group14_sha1 = {
+const ssh_kex ssh_diffiehellman_group14_sha1 = {
     "diffie-hellman-group14-sha1", "group14",
     KEXTYPE_DH, &ssh_sha1, &extra_group14,
 };
@@ -96,7 +102,7 @@ const ssh_kexes ssh_diffiehellman_gex = { lenof(gex_list), gex_list };
  * Kerberos v5.
  *
  * (The same encoded OID, minus the two-byte DER header, is defined in
- * pgssapi.c as GSS_MECH_KRB5.)
+ * ssh/pgssapi.c as GSS_MECH_KRB5.)
  */
 #define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g=="
 
@@ -194,19 +200,8 @@ void dh_cleanup(dh_ctx *ctx)
 /*
  * DH stage 1: invent a number x between 1 and q, and compute e =
  * g^x mod p. Return e.
- *
- * If `nbits' is greater than zero, it is used as an upper limit
- * for the number of bits in x. This is safe provided that (a) you
- * use twice as many bits in x as the number of bits you expect to
- * use in your session key, and (b) the DH group is a safe prime
- * (which SSH demands that it must be).
- *
- * P. C. van Oorschot, M. J. Wiener
- * "On Diffie-Hellman Key Agreement with Short Exponents".
- * Advances in Cryptology: Proceedings of Eurocrypt '96
- * Springer-Verlag, May 1996.
  */
-mp_int *dh_create_e(dh_ctx *ctx, int nbits)
+mp_int *dh_create_e(dh_ctx *ctx)
 {
     /*
      * Lower limit is just 2.
@@ -218,12 +213,6 @@ mp_int *dh_create_e(dh_ctx *ctx, int nbits)
      */
     mp_int *hi = mp_copy(ctx->q);
     mp_sub_integer_into(hi, hi, 1);
-    if (nbits) {
-        mp_int *pow2 = mp_power_2(nbits+1);
-        mp_min_into(pow2, pow2, hi);
-        mp_free(hi);
-        hi = pow2;
-    }
 
     /*
      * Make a random number in that range.

+ 129 - 129
source/putty/sshdss.c → source/putty/crypto/dsa.c

@@ -1,5 +1,5 @@
 /*
- * Digital Signature Standard implementation for PuTTY.
+ * Digital Signature Algorithm implementation for PuTTY.
  */
 
 #include <stdio.h>
@@ -10,49 +10,49 @@
 #include "mpint.h"
 #include "misc.h"
 
-static void dss_freekey(ssh_key *key);    /* forward reference */
+static void dsa_freekey(ssh_key *key);    /* forward reference */
 
-static ssh_key *dss_new_pub(const ssh_keyalg *self, ptrlen data)
+static ssh_key *dsa_new_pub(const ssh_keyalg *self, ptrlen data)
 {
     BinarySource src[1];
-    struct dss_key *dss;
+    struct dsa_key *dsa;
 
     BinarySource_BARE_INIT_PL(src, data);
     if (!ptrlen_eq_string(get_string(src), "ssh-dss"))
         return NULL;
 
-    dss = snew(struct dss_key);
-    dss->sshk.vt = &ssh_dss;
-    dss->p = get_mp_ssh2(src);
-    dss->q = get_mp_ssh2(src);
-    dss->g = get_mp_ssh2(src);
-    dss->y = get_mp_ssh2(src);
-    dss->x = NULL;
+    dsa = snew(struct dsa_key);
+    dsa->sshk.vt = &ssh_dsa;
+    dsa->p = get_mp_ssh2(src);
+    dsa->q = get_mp_ssh2(src);
+    dsa->g = get_mp_ssh2(src);
+    dsa->y = get_mp_ssh2(src);
+    dsa->x = NULL;
 
     if (get_err(src) ||
-        mp_eq_integer(dss->p, 0) || mp_eq_integer(dss->q, 0)) {
+        mp_eq_integer(dsa->p, 0) || mp_eq_integer(dsa->q, 0)) {
         /* Invalid key. */
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
     }
 
-    return &dss->sshk;
+    return &dsa->sshk;
 }
 
-static void dss_freekey(ssh_key *key)
+static void dsa_freekey(ssh_key *key)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
-    if (dss->p)
-        mp_free(dss->p);
-    if (dss->q)
-        mp_free(dss->q);
-    if (dss->g)
-        mp_free(dss->g);
-    if (dss->y)
-        mp_free(dss->y);
-    if (dss->x)
-        mp_free(dss->x);
-    sfree(dss);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
+    if (dsa->p)
+        mp_free(dsa->p);
+    if (dsa->q)
+        mp_free(dsa->q);
+    if (dsa->g)
+        mp_free(dsa->g);
+    if (dsa->y)
+        mp_free(dsa->y);
+    if (dsa->x)
+        mp_free(dsa->x);
+    sfree(dsa);
 }
 
 static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
@@ -69,55 +69,55 @@ static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
     } // WINSCP
 }
 
-static char *dss_cache_str(ssh_key *key)
+static char *dsa_cache_str(ssh_key *key)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     strbuf *sb = strbuf_new();
 
-    if (!dss->p) {
+    if (!dsa->p) {
         strbuf_free(sb);
         return NULL;
     }
 
-    append_hex_to_strbuf(sb, dss->p);
-    append_hex_to_strbuf(sb, dss->q);
-    append_hex_to_strbuf(sb, dss->g);
-    append_hex_to_strbuf(sb, dss->y);
+    append_hex_to_strbuf(sb, dsa->p);
+    append_hex_to_strbuf(sb, dsa->q);
+    append_hex_to_strbuf(sb, dsa->g);
+    append_hex_to_strbuf(sb, dsa->y);
 
     return strbuf_to_str(sb);
 }
 
-static key_components *dss_components(ssh_key *key)
+static key_components *dsa_components(ssh_key *key)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     key_components *kc = key_components_new();
 
     key_components_add_text(kc, "key_type", "DSA");
-    assert(dss->p);
-    key_components_add_mp(kc, "p", dss->p);
-    key_components_add_mp(kc, "q", dss->q);
-    key_components_add_mp(kc, "g", dss->g);
-    key_components_add_mp(kc, "public_y", dss->y);
-    if (dss->x)
-        key_components_add_mp(kc, "private_x", dss->x);
+    assert(dsa->p);
+    key_components_add_mp(kc, "p", dsa->p);
+    key_components_add_mp(kc, "q", dsa->q);
+    key_components_add_mp(kc, "g", dsa->g);
+    key_components_add_mp(kc, "public_y", dsa->y);
+    if (dsa->x)
+        key_components_add_mp(kc, "private_x", dsa->x);
 
     return kc;
 }
 
-static char *dss_invalid(ssh_key *key, unsigned flags)
+static char *dsa_invalid(ssh_key *key, unsigned flags)
 {
     /* No validity criterion will stop us from using a DSA key at all */
     return NULL;
 }
 
-static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
+static bool dsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     BinarySource src[1];
     unsigned char hash[20];
     bool toret;
 
-    if (!dss->p)
+    if (!dsa->p)
         return false;
 
     BinarySource_BARE_INIT_PL(src, sig);
@@ -159,8 +159,8 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     unsigned invalid = 0;
     invalid |= mp_eq_integer(r, 0);
     invalid |= mp_eq_integer(s, 0);
-    invalid |= mp_cmp_hs(r, dss->q);
-    invalid |= mp_cmp_hs(s, dss->q);
+    invalid |= mp_cmp_hs(r, dsa->q);
+    invalid |= mp_cmp_hs(s, dsa->q);
     if (invalid) {
         mp_free(r);
         mp_free(s);
@@ -171,7 +171,7 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
      * Step 1. w <- s^-1 mod q.
      */
     { // WINSCP
-    mp_int *w = mp_invert(s, dss->q);
+    mp_int *w = mp_invert(s, dsa->q);
     if (!w) {
         mp_free(r);
         mp_free(s);
@@ -184,20 +184,20 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     hash_simple(&ssh_sha1, data, hash);
     { // WINSCP
     mp_int *sha = mp_from_bytes_be(make_ptrlen(hash, 20));
-    mp_int *u1 = mp_modmul(sha, w, dss->q);
+    mp_int *u1 = mp_modmul(sha, w, dsa->q);
 
     /*
      * Step 3. u2 <- r * w mod q.
      */
-    mp_int *u2 = mp_modmul(r, w, dss->q);
+    mp_int *u2 = mp_modmul(r, w, dsa->q);
 
     /*
      * Step 4. v <- (g^u1 * y^u2 mod p) mod q.
      */
-    mp_int *gu1p = mp_modpow(dss->g, u1, dss->p);
-    mp_int *yu2p = mp_modpow(dss->y, u2, dss->p);
-    mp_int *gu1yu2p = mp_modmul(gu1p, yu2p, dss->p);
-    mp_int *v = mp_mod(gu1yu2p, dss->q);
+    mp_int *gu1p = mp_modpow(dsa->g, u1, dsa->p);
+    mp_int *yu2p = mp_modpow(dsa->y, u2, dsa->p);
+    mp_int *gu1yu2p = mp_modmul(gu1p, yu2p, dsa->p);
+    mp_int *v = mp_mod(gu1yu2p, dsa->q);
 
     /*
      * Step 5. v should now be equal to r.
@@ -223,57 +223,57 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     return toret;
 }
 
-static void dss_public_blob(ssh_key *key, BinarySink *bs)
+static void dsa_public_blob(ssh_key *key, BinarySink *bs)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
 
     put_stringz(bs, "ssh-dss");
-    put_mp_ssh2(bs, dss->p);
-    put_mp_ssh2(bs, dss->q);
-    put_mp_ssh2(bs, dss->g);
-    put_mp_ssh2(bs, dss->y);
+    put_mp_ssh2(bs, dsa->p);
+    put_mp_ssh2(bs, dsa->q);
+    put_mp_ssh2(bs, dsa->g);
+    put_mp_ssh2(bs, dsa->y);
 }
 
-static void dss_private_blob(ssh_key *key, BinarySink *bs)
+static void dsa_private_blob(ssh_key *key, BinarySink *bs)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
 
-    put_mp_ssh2(bs, dss->x);
+    put_mp_ssh2(bs, dsa->x);
 }
 
-static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
+static ssh_key *dsa_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
 {
     BinarySource src[1];
     ssh_key *sshk;
-    struct dss_key *dss;
+    struct dsa_key *dsa;
     ptrlen hash;
     unsigned char digest[20];
     mp_int *ytest;
 
-    sshk = dss_new_pub(self, pub);
+    sshk = dsa_new_pub(self, pub);
     if (!sshk)
         return NULL;
 
-    dss = container_of(sshk, struct dss_key, sshk);
+    dsa = container_of(sshk, struct dsa_key, sshk);
     BinarySource_BARE_INIT_PL(src, priv);
-    dss->x = get_mp_ssh2(src);
+    dsa->x = get_mp_ssh2(src);
     if (get_err(src)) {
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
     }
 
     /*
-     * Check the obsolete hash in the old DSS key format.
+     * Check the obsolete hash in the old DSA key format.
      */
     hash = get_string(src);
     if (hash.len == 20) {
         ssh_hash *h = ssh_hash_new(&ssh_sha1);
-        put_mp_ssh2(h, dss->p);
-        put_mp_ssh2(h, dss->q);
-        put_mp_ssh2(h, dss->g);
+        put_mp_ssh2(h, dsa->p);
+        put_mp_ssh2(h, dsa->q);
+        put_mp_ssh2(h, dsa->g);
         ssh_hash_final(h, digest);
         if (!smemeq(hash.ptr, digest, 20)) {
-            dss_freekey(&dss->sshk);
+            dsa_freekey(&dsa->sshk);
             return NULL;
         }
     }
@@ -281,75 +281,75 @@ static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
     /*
      * Now ensure g^x mod p really is y.
      */
-    ytest = mp_modpow(dss->g, dss->x, dss->p);
-    if (!mp_cmp_eq(ytest, dss->y)) {
+    ytest = mp_modpow(dsa->g, dsa->x, dsa->p);
+    if (!mp_cmp_eq(ytest, dsa->y)) {
         mp_free(ytest);
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
     }
     mp_free(ytest);
 
-    return &dss->sshk;
+    return &dsa->sshk;
 }
 
-static ssh_key *dss_new_priv_openssh(const ssh_keyalg *self,
+static ssh_key *dsa_new_priv_openssh(const ssh_keyalg *self,
                                      BinarySource *src)
 {
-    struct dss_key *dss;
+    struct dsa_key *dsa;
 
-    dss = snew(struct dss_key);
-    dss->sshk.vt = &ssh_dss;
+    dsa = snew(struct dsa_key);
+    dsa->sshk.vt = &ssh_dsa;
 
-    dss->p = get_mp_ssh2(src);
-    dss->q = get_mp_ssh2(src);
-    dss->g = get_mp_ssh2(src);
-    dss->y = get_mp_ssh2(src);
-    dss->x = get_mp_ssh2(src);
+    dsa->p = get_mp_ssh2(src);
+    dsa->q = get_mp_ssh2(src);
+    dsa->g = get_mp_ssh2(src);
+    dsa->y = get_mp_ssh2(src);
+    dsa->x = get_mp_ssh2(src);
 
     if (get_err(src) ||
-        mp_eq_integer(dss->q, 0) || mp_eq_integer(dss->p, 0)) {
+        mp_eq_integer(dsa->q, 0) || mp_eq_integer(dsa->p, 0)) {
         /* Invalid key. */
-        dss_freekey(&dss->sshk);
+        dsa_freekey(&dsa->sshk);
         return NULL;
     }
 
-    return &dss->sshk;
+    return &dsa->sshk;
 }
 
-static void dss_openssh_blob(ssh_key *key, BinarySink *bs)
+static void dsa_openssh_blob(ssh_key *key, BinarySink *bs)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
 
-    put_mp_ssh2(bs, dss->p);
-    put_mp_ssh2(bs, dss->q);
-    put_mp_ssh2(bs, dss->g);
-    put_mp_ssh2(bs, dss->y);
-    put_mp_ssh2(bs, dss->x);
+    put_mp_ssh2(bs, dsa->p);
+    put_mp_ssh2(bs, dsa->q);
+    put_mp_ssh2(bs, dsa->g);
+    put_mp_ssh2(bs, dsa->y);
+    put_mp_ssh2(bs, dsa->x);
 }
 
-static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
+static int dsa_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
 {
     ssh_key *sshk;
-    struct dss_key *dss;
+    struct dsa_key *dsa;
     int ret;
 
-    sshk = dss_new_pub(self, pub);
+    sshk = dsa_new_pub(self, pub);
     if (!sshk)
         return -1;
 
-    dss = container_of(sshk, struct dss_key, sshk);
-    ret = mp_get_nbits(dss->p);
-    dss_freekey(&dss->sshk);
+    dsa = container_of(sshk, struct dsa_key, sshk);
+    ret = mp_get_nbits(dsa->p);
+    dsa_freekey(&dsa->sshk);
 
     return ret;
 }
 
-mp_int *dss_gen_k(const char *id_string, mp_int *modulus,
+mp_int *dsa_gen_k(const char *id_string, mp_int *modulus,
                      mp_int *private_key,
                      unsigned char *digest, int digest_len)
 {
     /*
-     * The basic DSS signing algorithm is:
+     * The basic DSA signing algorithm is:
      *
      *  - invent a random k between 1 and q-1 (exclusive).
      *  - Compute r = (g^k mod p) mod q.
@@ -459,32 +459,32 @@ mp_int *dss_gen_k(const char *id_string, mp_int *modulus,
     } // WINSCP
 }
 
-static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
+static void dsa_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
 {
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
+    struct dsa_key *dsa = container_of(key, struct dsa_key, sshk);
     unsigned char digest[20];
     int i;
 
     hash_simple(&ssh_sha1, data, digest);
 
     { // WINSCP
-    mp_int *k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x,
+    mp_int *k = dsa_gen_k("DSA deterministic k generator", dsa->q, dsa->x,
                           digest, sizeof(digest));
-    mp_int *kinv = mp_invert(k, dss->q);       /* k^-1 mod q */
+    mp_int *kinv = mp_invert(k, dsa->q);       /* k^-1 mod q */
 
     /*
      * Now we have k, so just go ahead and compute the signature.
      */
-    mp_int *gkp = mp_modpow(dss->g, k, dss->p); /* g^k mod p */
-    mp_int *r = mp_mod(gkp, dss->q);        /* r = (g^k mod p) mod q */
+    mp_int *gkp = mp_modpow(dsa->g, k, dsa->p); /* g^k mod p */
+    mp_int *r = mp_mod(gkp, dsa->q);        /* r = (g^k mod p) mod q */
     mp_free(gkp);
 
     { // WINSCP
     mp_int *hash = mp_from_bytes_be(make_ptrlen(digest, 20));
-    mp_int *xr = mp_mul(dss->x, r);
+    mp_int *xr = mp_mul(dsa->x, r);
     mp_int *hxr = mp_add(xr, hash);         /* hash + x*r */
     { // WINSCP
-    mp_int *s = mp_modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash+x*r) mod q */
+    mp_int *s = mp_modmul(kinv, hxr, dsa->q); /* s = k^-1 * (hash+x*r) mod q */
     mp_free(hxr);
     mp_free(xr);
     mp_free(kinv);
@@ -504,21 +504,21 @@ static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
     } // WINSCP
 }
 
-const ssh_keyalg ssh_dss = {
+const ssh_keyalg ssh_dsa = {
     // WINSCP
-    /*.new_pub =*/ dss_new_pub,
-    /*.new_priv =*/ dss_new_priv,
-    /*.new_priv_openssh =*/ dss_new_priv_openssh,
-    /*.freekey =*/ dss_freekey,
-    /*.invalid =*/ dss_invalid,
-    /*.sign =*/ dss_sign,
-    /*.verify =*/ dss_verify,
-    /*.public_blob =*/ dss_public_blob,
-    /*.private_blob =*/ dss_private_blob,
-    /*.openssh_blob =*/ dss_openssh_blob,
-    /*.cache_str =*/ dss_cache_str,
-    /*.components =*/ dss_components,
-    /*.pubkey_bits =*/ dss_pubkey_bits,
+    /*.new_pub =*/ dsa_new_pub,
+    /*.new_priv =*/ dsa_new_priv,
+    /*.new_priv_openssh =*/ dsa_new_priv_openssh,
+    /*.freekey =*/ dsa_freekey,
+    /*.invalid =*/ dsa_invalid,
+    /*.sign =*/ dsa_sign,
+    /*.verify =*/ dsa_verify,
+    /*.public_blob =*/ dsa_public_blob,
+    /*.private_blob =*/ dsa_private_blob,
+    /*.openssh_blob =*/ dsa_openssh_blob,
+    /*.cache_str =*/ dsa_cache_str,
+    /*.components =*/ dsa_components,
+    /*.pubkey_bits =*/ dsa_pubkey_bits,
     /*.ssh_id =*/ "ssh-dss",
     /*.cache_id =*/ "dss",
     NULL, NULL,

+ 4 - 0
source/putty/ecc.c → source/putty/crypto/ecc-arithmetic.c

@@ -1,3 +1,7 @@
+/*
+ * Basic arithmetic for elliptic curves, implementing ecc.h.
+ */
+
 #include <assert.h>
 
 #include "ssh.h"

+ 6 - 14
source/putty/sshecc.c → source/putty/crypto/ecc-ssh.c

@@ -1,13 +1,5 @@
 /*
- * Elliptic-curve crypto module for PuTTY
- * Implements the three required curves, no optional curves
- *
- * NOTE: Only curves on prime field are handled by the maths functions
- *       in Weierstrass form using Jacobian co-ordinates.
- *
- *       Montgomery form curves are supported for DH. (Curve25519)
- *
- *       Edwards form curves are supported for DSA. (Ed25519, Ed448)
+ * Elliptic-curve signing and key exchange for PuTTY's SSH layer.
  */
 
 /*
@@ -652,8 +644,8 @@ static EdwardsPoint *eddsa_decode(ptrlen encoded, const struct ec_curve *curve)
     mp_free(y);
 
     /* A point constructed in this way will always satisfy the curve
-     * equation, unless ecc.c wasn't able to construct one at all, in
-     * which case P is now NULL. Either way, return it. */
+     * equation, unless ecc-arithmetic.c wasn't able to construct one
+     * at all, in which case P is now NULL. Either way, return it. */
     return P;
     } // WINSCP
     } // WINSCP
@@ -798,12 +790,12 @@ static char *ecc_cache_str_shared(
     strbuf *sb = strbuf_new();
 
     if (curve_name)
-        strbuf_catf(sb, "%s,", curve_name);
+        put_fmt(sb, "%s,", curve_name);
 
     { // WINSCP
     char *hx = mp_get_hex(x);
     char *hy = mp_get_hex(y);
-    strbuf_catf(sb, "0x%s,0x%s", hx, hy);
+    put_fmt(sb, "0x%s,0x%s", hx, hy);
     sfree(hx);
     sfree(hy);
     } // WINSCP
@@ -1300,7 +1292,7 @@ static void ecdsa_sign(ssh_key *key, ptrlen data,
     {
         unsigned char digest[20];
         hash_simple(&ssh_sha1, data, digest);
-        k = dss_gen_k(
+        k = dsa_gen_k(
             "ECDSA deterministic k generator", ek->curve->w.G_order,
             ek->privateKey, digest, sizeof(digest));
     }

+ 2 - 2
source/putty/ecc.h → source/putty/crypto/ecc.h

@@ -63,7 +63,7 @@ WeierstrassPoint *ecc_weierstrass_point_new_from_x(
 /* Memory management: copy and free points. */
 void ecc_weierstrass_point_copy_into(
     WeierstrassPoint *dest, WeierstrassPoint *src);
-WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *wc);
+WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *orig);
 void ecc_weierstrass_point_free(WeierstrassPoint *point);
 
 /* Check whether a point is actually on the curve. */
@@ -223,7 +223,7 @@ EdwardsPoint *ecc_edwards_point_new_from_y(
 
 /* Copy and free points. */
 void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src);
-EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *ec);
+EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *orig);
 void ecc_edwards_point_free(EdwardsPoint *point);
 
 /*

+ 13 - 0
source/putty/crypto/hash_simple.c

@@ -0,0 +1,13 @@
+/*
+ * Convenience function to hash a single piece of data, wrapping up
+ * the faff of making and freeing an ssh_hash.
+ */
+
+#include "ssh.h"
+
+void hash_simple(const ssh_hashalg *alg, ptrlen data, void *output)
+{
+    ssh_hash *hash = ssh_hash_new(alg);
+    put_datapl(hash, data);
+    ssh_hash_final(hash, output);
+}

+ 6 - 6
source/putty/sshhmac.c → source/putty/crypto/hmac.c

@@ -42,21 +42,21 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
     ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
 
     ctx->text_name = strbuf_new();
-    strbuf_catf(ctx->text_name, "HMAC-%s%s",
-                ctx->hashalg->text_basename, extra->suffix);
+    put_fmt(ctx->text_name, "HMAC-%s%s",
+            ctx->hashalg->text_basename, extra->suffix);
     if (extra->annotation || ctx->hashalg->annotation) {
-        strbuf_catf(ctx->text_name, " (");
+        put_fmt(ctx->text_name, " (");
         { // WINSCP
         const char *sep = "";
         if (extra->annotation) {
-            strbuf_catf(ctx->text_name, "%s%s", sep, extra->annotation);
+            put_fmt(ctx->text_name, "%s%s", sep, extra->annotation);
             sep = ", ";
         }
         if (ctx->hashalg->annotation) {
-            strbuf_catf(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
+            put_fmt(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
             sep = ", ";
         }
-        strbuf_catf(ctx->text_name, ")");
+        put_fmt(ctx->text_name, ")");
         } // WINSCP
     }
 

+ 0 - 0
source/putty/sshmac.c → source/putty/crypto/mac.c


+ 16 - 0
source/putty/crypto/mac_simple.c

@@ -0,0 +1,16 @@
+/*
+ * Convenience function to MAC a single piece of data, wrapping up
+ * the faff of making and freeing an ssh_mac.
+ */
+
+#include "ssh.h"
+
+void mac_simple(const ssh2_macalg *alg, ptrlen key, ptrlen data, void *output)
+{
+    ssh2_mac *mac = ssh2_mac_new(alg, NULL);
+    ssh2_mac_setkey(mac, key);
+    ssh2_mac_start(mac);
+    put_datapl(mac, data);
+    ssh2_mac_genresult(mac, output);
+    ssh2_mac_free(mac);
+}

+ 0 - 0
source/putty/sshmd5.c → source/putty/crypto/md5.c


+ 212 - 26
source/putty/mpint.c → source/putty/crypto/mpint.c

@@ -1,3 +1,7 @@
+/*
+ * Multiprecision integer arithmetic, implementing mpint.h.
+ */
+
 #include <assert.h>
 #include <limits.h>
 #include <stdio.h>
@@ -1648,43 +1652,137 @@ mp_int *monty_export(MontyContext *mc, mp_int *x)
     return toret;
 }
 
-static void monty_reduce(MontyContext *mc, mp_int *x)
-{
-    mp_int reduced = monty_reduce_internal(mc, x, *mc->scratch);
-    mp_copy_into(x, &reduced);
-    mp_clear(mc->scratch);
-}
-
+#define MODPOW_LOG2_WINDOW_SIZE 5
+#define MODPOW_WINDOW_SIZE (1 << MODPOW_LOG2_WINDOW_SIZE)
 mp_int *monty_pow(MontyContext *mc, mp_int *base, mp_int *exponent)
 {
-    /* square builds up powers of the form base^{2^i}. */
-    mp_int *square = mp_copy(base);
-    size_t i = 0;
+    /*
+     * Modular exponentiation is done from the top down, using a
+     * fixed-window technique.
+     *
+     * We have a table storing every power of the base from base^0 up
+     * to base^{w-1}, where w is a small power of 2, say 2^k. (k is
+     * defined above as MODPOW_LOG2_WINDOW_SIZE, and w = 2^k is
+     * defined as MODPOW_WINDOW_SIZE.)
+     *
+     * We break the exponent up into k-bit chunks, from the bottom up,
+     * that is
+     *
+     *   exponent = c_0 + 2^k c_1 + 2^{2k} c_2 + ... + 2^{nk} c_n
+     *
+     * and we compute base^exponent by computing in turn
+     *
+     *   base^{c_n}
+     *   base^{2^k c_n + c_{n-1}}
+     *   base^{2^{2k} c_n + 2^k c_{n-1} + c_{n-2}}
+     *   ...
+     *
+     * where each line is obtained by raising the previous line to the
+     * power 2^k (i.e. squaring it k times) and then multiplying in
+     * a value base^{c_i}, which we can look up in our table.
+     *
+     * Side-channel considerations: the exponent is secret, so
+     * actually doing a single table lookup by using a chunk of
+     * exponent bits as an array index would be an obvious leak of
+     * secret information into the cache. So instead, in each
+     * iteration, we read _all_ the table entries, and do a sequence
+     * of mp_select operations to leave just the one we wanted in the
+     * variable that will go into the multiplication. In other
+     * contexts (like software AES) that technique is so prohibitively
+     * slow that it makes you choose a strategy that doesn't use table
+     * lookups at all (we do bitslicing in preference); but here, this
+     * iteration through 2^k table elements is replacing k-1 bignum
+     * _multiplications_ that you'd have to use instead if you did
+     * simple square-and-multiply, and that makes it still a win.
+     */
+
+    /* Table that holds base^0, ..., base^{w-1} */
+    mp_int *table[MODPOW_WINDOW_SIZE];
+    table[0] = mp_copy(monty_identity(mc));
+    { // WINSCP
+    size_t i;
+    for (i = 1; i < MODPOW_WINDOW_SIZE; i++)
+        table[i] = monty_mul(mc, table[i-1], base);
 
-    /* out accumulates the output value. Starts at 1 (in Montgomery
-     * representation) and we multiply in each base^{2^i}. */
-    mp_int *out = mp_copy(mc->powers_of_r_mod_m[0]);
+    /* out accumulates the output value */
+    { // WINSCP
+    mp_int *out = mp_make_sized(mc->rw);
+    mp_copy_into(out, monty_identity(mc));
+
+    /* table_entry will hold each value we get out of the table */
+    { // WINSCP
+    mp_int *table_entry = mp_make_sized(mc->rw);
+
+    /* Bit index of the chunk of bits we're working on. Start with the
+     * highest multiple of k strictly less than the size of our
+     * bignum, i.e. the highest-index chunk of bits that might
+     * conceivably contain any nonzero bit. */
+    { // WINSCP
+    size_t i = (exponent->nw * BIGNUM_INT_BITS) - 1;
+    i -= i % MODPOW_LOG2_WINDOW_SIZE;
 
-    /* tmp holds each product we compute and reduce. */
-    mp_int *tmp = mp_make_sized(mc->rw * 2);
+    { // WINSCP
+    bool first_iteration = true;
 
     while (true) {
-        mp_mul_into(tmp, out, square);
-        monty_reduce(mc, tmp);
-        mp_select_into(out, out, tmp, mp_get_bit(exponent, i));
+        /* Construct the table index */
+        unsigned table_index = 0;
+        { // WINSCP
+        size_t j;
+        for (j = 0; j < MODPOW_LOG2_WINDOW_SIZE; j++)
+            table_index |= mp_get_bit(exponent, i+j) << j;
+
+        /* Iterate through the table to do a side-channel-safe lookup,
+         * ending up with table_entry = table[table_index] */
+        mp_copy_into(table_entry, table[0]);
+        { // WINSCP
+        size_t j;
+        for (j = 1; j < MODPOW_WINDOW_SIZE; j++) {
+            unsigned not_this_one =
+                ((table_index ^ j) + MODPOW_WINDOW_SIZE - 1)
+                >> MODPOW_LOG2_WINDOW_SIZE;
+            mp_select_into(table_entry, table[j], table_entry, not_this_one);
+        }
 
-        if (++i >= exponent->nw * BIGNUM_INT_BITS)
+        if (!first_iteration) {
+            /* Multiply into the output */
+            monty_mul_into(mc, out, out, table_entry);
+        } else {
+            /* On the first iteration, we can save one multiplication
+             * by just copying */
+            mp_copy_into(out, table_entry);
+            first_iteration = false;
+        }
+
+        /* If that was the bottommost chunk of bits, we're done */
+        if (i == 0)
             break;
 
-        mp_mul_into(tmp, square, square);
-        monty_reduce(mc, tmp);
-        mp_copy_into(square, tmp);
+        /* Otherwise, square k times and go round again. */
+        { // WINSCP
+        size_t j;
+        for (j = 0; j < MODPOW_LOG2_WINDOW_SIZE; j++)
+            monty_mul_into(mc, out, out, out);
+
+        i-= MODPOW_LOG2_WINDOW_SIZE;
+        } // WINSCP
+        } // WINSCP
+        } // WINSCP
     }
 
-    mp_free(square);
-    mp_free(tmp);
+    { // WINSCP
+    size_t i;
+    for (i = 0; i < MODPOW_WINDOW_SIZE; i++)
+        mp_free(table[i]);
+    mp_free(table_entry);
     mp_clear(mc->scratch);
     return out;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus)
@@ -2281,9 +2379,9 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out)
      */
     { // WINSCP
     mp_int *two_R = mp_make_sized(rw);
+    BignumInt top_word = (BignumInt)1 << ((log2_R+1) % BIGNUM_INT_BITS);
     mp_add_integer_into_shifted_by_words(
-        two_R, two_R, (BignumInt)1 << ((log2_R+1) % BIGNUM_INT_BITS),
-        (log2_R+1) / BIGNUM_INT_BITS);
+        two_R, two_R, top_word, (log2_R+1) / BIGNUM_INT_BITS);
 
     /*
      * Scratch space.
@@ -2408,6 +2506,94 @@ mp_int *mp_mod(mp_int *n, mp_int *d)
     return r;
 }
 
+uint32_t mp_mod_known_integer(mp_int *x, uint32_t m)
+{
+    uint64_t reciprocal = ((uint64_t)1 << 48) / m;
+    uint64_t accumulator = 0;
+    { // WINSCP
+    size_t i;
+    for (i = mp_max_bytes(x); i-- > 0 ;) {
+        accumulator = 0x100 * accumulator + mp_get_byte(x, i);
+        /*
+         * Let A be the value in 'accumulator' at this point, and let
+         * R be the value it will have after we subtract quot*m below.
+         *
+         * Lemma 1: if A < 2^48, then R < 2m.
+         *
+         * Proof:
+         *
+         * By construction, we have 2^48/m - 1 < reciprocal <= 2^48/m.
+         * Multiplying that by the accumulator gives
+         *
+         *      A/m * 2^48 - A < unshifted_quot <= A/m * 2^48
+         * i.e. 0 <= (A/m * 2^48) - unshifted_quot < A
+         * i.e. 0 <= A/m - unshifted_quot/2^48 < A/2^48
+         *
+         * So when we shift this quotient right by 48 bits, i.e. take
+         * the floor of (unshifted_quot/2^48), the value we take the
+         * floor of is at most A/2^48 less than the true rational
+         * value A/m that we _wanted_ to take the floor of.
+         *
+         * Provided A < 2^48, this is less than 1. So the quotient
+         * 'quot' that we've just produced is either the true quotient
+         * floor(A/m), or one less than it. Hence, the output value R
+         * is less than 2m. []
+         *
+         * Lemma 2: if A < 2^16 m, then the multiplication of
+         * accumulator*reciprocal does not overflow.
+         *
+         * Proof: as above, we have reciprocal <= 2^48/m. Multiplying
+         * by A gives unshifted_quot <= 2^48 * A / m < 2^48 * 2^16 =
+         * 2^64. []
+         */
+        { // WINSCP
+        uint64_t unshifted_quot = accumulator * reciprocal;
+        uint64_t quot = unshifted_quot >> 48;
+        accumulator -= quot * m;
+        } // WINSCP
+    }
+
+    /*
+     * Theorem 1: accumulator < 2m at the end of every iteration of
+     * this loop.
+     *
+     * Proof: induction on the above loop.
+     *
+     * Base case: at the start of the first loop iteration, the
+     * accumulator is 0, which is certainly < 2m.
+     *
+     * Inductive step: in each loop iteration, we take a value at most
+     * 2m-1, multiply it by 2^8, and add another byte less than 2^8 to
+     * generate the input value A to the reduction process above. So
+     * we have A < 2m * 2^8 - 1. We know m < 2^32 (because it was
+     * passed in as a uint32_t), so A < 2^41, which is enough to allow
+     * us to apply Lemma 1, showing that the value of 'accumulator' at
+     * the end of the loop is still < 2m. []
+     *
+     * Corollary: we need at most one final subtraction of m to
+     * produce the canonical residue of x mod m, i.e. in the range
+     * [0,m).
+     *
+     * Theorem 2: no multiplication in the inner loop overflows.
+     *
+     * Proof: in Theorem 1 we established A < 2m * 2^8 - 1 in every
+     * iteration. That is less than m * 2^16, so Lemma 2 applies.
+     *
+     * The other multiplication, of quot * m, cannot overflow because
+     * quot is at most A/m, so quot*m <= A < 2^64. []
+     */
+
+    { // WINSCP
+    uint32_t result = accumulator;
+    uint32_t reduced = result - m;
+    uint32_t select = -(reduced >> 31);
+    result = reduced ^ ((result ^ reduced) & select);
+    assert(result < m);
+    return result;
+    } // WINSCP
+    } // WINSCP
+}
+
 mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder_out)
 {
     /*

+ 1 - 1
source/putty/mpint_i.h → source/putty/crypto/mpint_i.h

@@ -266,7 +266,7 @@
 #endif /* DEFINE_BIGNUMDBLINT */
 
 /* ----------------------------------------------------------------------
- * Data structures used inside bignum.c.
+ * Data structures used inside mpint.c.
  */
 
 struct mp_int {

+ 1 - 1
source/putty/sshprng.c → source/putty/crypto/prng.c

@@ -1,5 +1,5 @@
 /*
- * sshprng.c: PuTTY's cryptographic pseudorandom number generator.
+ * PuTTY's cryptographic pseudorandom number generator.
  *
  * This module just defines the PRNG object type and its methods. The
  * usual global instance of it is managed by sshrand.c.

+ 32 - 0
source/putty/crypto/pubkey-pem.c

@@ -0,0 +1,32 @@
+/*
+ * Convenience functions to encrypt and decrypt OpenSSH PEM format for
+ * SSH-2 private key files. This uses triple-DES in SSH-2 style (one
+ * CBC layer), with three distinct keys, and an IV also generated from
+ * the passphrase.
+ */
+
+#include "ssh.h"
+
+static ssh_cipher *des3_pubkey_ossh_cipher(const void *vkey, const void *viv)
+{
+    ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh2);
+    ssh_cipher_setkey(c, vkey);
+    ssh_cipher_setiv(c, viv);
+    return c;
+}
+
+void des3_decrypt_pubkey_ossh(const void *vkey, const void *viv,
+                              void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv);
+    ssh_cipher_decrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}
+
+void des3_encrypt_pubkey_ossh(const void *vkey, const void *viv,
+                              void *vblk, int len)
+{
+    ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv);
+    ssh_cipher_encrypt(c, vblk, len);
+    ssh_cipher_free(c);
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä