Răsfoiți Sursa

Merge branch 'thirdparty' into thirdparty_dev

Source commit: 521f92cad5a5cc50489d3218314918f64378c674
Martin Prikryl 3 ani în urmă
părinte
comite
a0eaa6f57b
100 a modificat fișierele cu 3120 adăugiri și 1903 ștergeri
  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. 139 56
      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. 0 285
      source/putty/WINDOWS/winmiscs.c
  34. 118 0
      source/putty/be_list.c
  35. 0 18
      source/putty/be_ssh.c
  36. 0 179
      source/putty/cproxy.c
  37. 14 0
      source/putty/crypto/aes-common.c
  38. 281 0
      source/putty/crypto/aes-ni.c
  39. 89 0
      source/putty/crypto/aes-select.c
  40. 17 890
      source/putty/crypto/aes-sw.c
  41. 109 0
      source/putty/crypto/aes.h
  42. 0 0
      source/putty/crypto/arcfour.c
  43. 1 1
      source/putty/crypto/argon2.c
  44. 5 6
      source/putty/crypto/bcrypt.c
  45. 0 0
      source/putty/crypto/blake2.c
  46. 1 1
      source/putty/crypto/blowfish.c
  47. 1 1
      source/putty/crypto/blowfish.h
  48. 0 0
      source/putty/crypto/chacha20-poly1305.c
  49. 1 1
      source/putty/crypto/des.c
  50. 11 22
      source/putty/crypto/diffie-hellman.c
  51. 129 129
      source/putty/crypto/dsa.c
  52. 4 0
      source/putty/crypto/ecc-arithmetic.c
  53. 6 14
      source/putty/crypto/ecc-ssh.c
  54. 2 2
      source/putty/crypto/ecc.h
  55. 13 0
      source/putty/crypto/hash_simple.c
  56. 6 6
      source/putty/crypto/hmac.c
  57. 0 0
      source/putty/crypto/mac.c
  58. 16 0
      source/putty/crypto/mac_simple.c
  59. 0 0
      source/putty/crypto/md5.c
  60. 182 26
      source/putty/crypto/mpint.c
  61. 1 1
      source/putty/crypto/mpint_i.h
  62. 1 1
      source/putty/crypto/prng.c
  63. 32 0
      source/putty/crypto/pubkey-pem.c
  64. 29 0
      source/putty/crypto/pubkey-ppk.c
  65. 3 3
      source/putty/crypto/rsa.c
  66. 10 0
      source/putty/crypto/sha1-common.c
  67. 44 0
      source/putty/crypto/sha1-select.c
  68. 155 0
      source/putty/crypto/sha1-sw.c
  69. 109 0
      source/putty/crypto/sha1.h
  70. 30 0
      source/putty/crypto/sha256-common.c
  71. 44 0
      source/putty/crypto/sha256-select.c
  72. 157 0
      source/putty/crypto/sha256-sw.c
  73. 105 0
      source/putty/crypto/sha256.h
  74. 0 0
      source/putty/crypto/sha3.c
  75. 71 0
      source/putty/crypto/sha512-common.c
  76. 61 0
      source/putty/crypto/sha512-select.c
  77. 168 0
      source/putty/crypto/sha512-sw.c
  78. 131 0
      source/putty/crypto/sha512.h
  79. 48 25
      source/putty/defs.h
  80. 0 80
      source/putty/doc/Makefile
  81. 0 1
      source/putty/doc/blurb.but
  82. 105 17
      source/putty/doc/config.but
  83. 0 5
      source/putty/doc/copy.but
  84. 3 3
      source/putty/doc/errors.but
  85. 2 1
      source/putty/doc/faq.but
  86. 3 2
      source/putty/doc/gs.but
  87. 9 2
      source/putty/doc/index.but
  88. 0 15
      source/putty/doc/licence.but
  89. 4 4
      source/putty/doc/man-pageant.but
  90. 9 4
      source/putty/doc/man-plink.but
  91. 9 4
      source/putty/doc/man-pscp.but
  92. 9 4
      source/putty/doc/man-psftp.but
  93. 4 4
      source/putty/doc/man-psocks.but
  94. 59 5
      source/putty/doc/man-psusan.but
  95. 1 1
      source/putty/doc/man-pterm.but
  96. 1 1
      source/putty/doc/man-putty.but
  97. 1 1
      source/putty/doc/man-puttytel.but
  98. 86 0
      source/putty/doc/pageant.but
  99. 34 12
      source/putty/doc/pgpkeys.but
  100. 1 1
      source/putty/doc/plink.but

+ 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
 }

+ 139 - 56
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,7 @@ XML_ParserCreate(const XML_Char *encodingName) {
 
 XML_Parser XMLCALL
 XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) {
-  XML_Char tmp[2];
-  *tmp = nsSep;
+  XML_Char tmp[2] = {nsSep, 0};
   return XML_ParserCreate_MM(encodingName, NULL, tmp);
 }
 
@@ -974,7 +974,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;
@@ -1343,8 +1343,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
      would be otherwise.
   */
   if (parser->m_ns) {
-    XML_Char tmp[2];
-    *tmp = parser->m_namespaceSeparator;
+    XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
     parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
   } else {
     parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
@@ -2067,6 +2066,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
@@ -2557,6 +2561,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
@@ -2568,7 +2573,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)
@@ -3750,6 +3759,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;
@@ -4092,7 +4112,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 {
@@ -5367,7 +5387,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);
@@ -5383,7 +5403,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;
       }
@@ -6536,7 +6562,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);
@@ -6709,8 +6735,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;
       }
@@ -6872,7 +6898,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;
@@ -6912,7 +6938,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
       }
 
       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);
@@ -6941,7 +6967,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
       }
     }
   }
-  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);
@@ -7229,7 +7255,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;
@@ -7305,44 +7331,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
@@ -7368,10 +7365,96 @@ 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.
+   */
+  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;
 }
 
@@ -7400,7 +7483,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)
@@ -1775,7 +1779,7 @@ PREFIX(updatePosition)(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. */             \
     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;

+ 0 - 285
source/putty/WINDOWS/winmiscs.c

@@ -1,285 +0,0 @@
-/*
- * winmiscs.c: Windows-specific standalone functions. Has the same
- * relationship to winmisc.c that utils.c does to misc.c, but the
- * corresponding name 'winutils.c' was already taken.
- */
-
-#include "putty.h"
-
-#ifndef NO_SECUREZEROMEMORY
-/*
- * Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
- */
-void smemclr(void *b, size_t n) {
-    if (b && n > 0)
-        SecureZeroMemory(b, n);
-}
-#endif
-
-#ifdef MINEFIELD
-/*
- * Minefield - a Windows equivalent for Electric Fence
- */
-
-#define PAGESIZE 4096
-
-/*
- * Design:
- *
- * We start by reserving as much virtual address space as Windows
- * will sensibly (or not sensibly) let us have. We flag it all as
- * invalid memory.
- *
- * Any allocation attempt is satisfied by committing one or more
- * pages, with an uncommitted page on either side. The returned
- * memory region is jammed up against the _end_ of the pages.
- *
- * Freeing anything causes instantaneous decommitment of the pages
- * involved, so stale pointers are caught as soon as possible.
- */
-
-static int minefield_initialised = 0;
-static void *minefield_region = NULL;
-static long minefield_size = 0;
-static long minefield_npages = 0;
-static long minefield_curpos = 0;
-static unsigned short *minefield_admin = NULL;
-static void *minefield_pages = NULL;
-
-static void minefield_admin_hide(int hide)
-{
-    int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
-    VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
-}
-
-static void minefield_init(void)
-{
-    int size;
-    int admin_size;
-    int i;
-
-    for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
-        minefield_region = VirtualAlloc(NULL, size,
-                                        MEM_RESERVE, PAGE_NOACCESS);
-        if (minefield_region)
-            break;
-    }
-    minefield_size = size;
-
-    /*
-     * Firstly, allocate a section of that to be the admin block.
-     * We'll need a two-byte field for each page.
-     */
-    minefield_admin = minefield_region;
-    minefield_npages = minefield_size / PAGESIZE;
-    admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
-    minefield_npages = (minefield_size - admin_size) / PAGESIZE;
-    minefield_pages = (char *) minefield_region + admin_size;
-
-    /*
-     * Commit the admin region.
-     */
-    VirtualAlloc(minefield_admin, minefield_npages * 2,
-                 MEM_COMMIT, PAGE_READWRITE);
-
-    /*
-     * Mark all pages as unused (0xFFFF).
-     */
-    for (i = 0; i < minefield_npages; i++)
-        minefield_admin[i] = 0xFFFF;
-
-    /*
-     * Hide the admin region.
-     */
-    minefield_admin_hide(1);
-
-    minefield_initialised = 1;
-}
-
-static void minefield_bomb(void)
-{
-    div(1, *(int *) minefield_pages);
-}
-
-static void *minefield_alloc(int size)
-{
-    int npages;
-    int pos, lim, region_end, region_start;
-    int start;
-    int i;
-
-    npages = (size + PAGESIZE - 1) / PAGESIZE;
-
-    minefield_admin_hide(0);
-
-    /*
-     * Search from current position until we find a contiguous
-     * bunch of npages+2 unused pages.
-     */
-    pos = minefield_curpos;
-    lim = minefield_npages;
-    while (1) {
-        /* Skip over used pages. */
-        while (pos < lim && minefield_admin[pos] != 0xFFFF)
-            pos++;
-        /* Count unused pages. */
-        start = pos;
-        while (pos < lim && pos - start < npages + 2 &&
-               minefield_admin[pos] == 0xFFFF)
-            pos++;
-        if (pos - start == npages + 2)
-            break;
-        /* If we've reached the limit, reset the limit or stop. */
-        if (pos >= lim) {
-            if (lim == minefield_npages) {
-                /* go round and start again at zero */
-                lim = minefield_curpos;
-                pos = 0;
-            } else {
-                minefield_admin_hide(1);
-                return NULL;
-            }
-        }
-    }
-
-    minefield_curpos = pos - 1;
-
-    /*
-     * We have npages+2 unused pages starting at start. We leave
-     * the first and last of these alone and use the rest.
-     */
-    region_end = (start + npages + 1) * PAGESIZE;
-    region_start = region_end - size;
-    /* FIXME: could align here if we wanted */
-
-    /*
-     * Update the admin region.
-     */
-    for (i = start + 2; i < start + npages + 1; i++)
-        minefield_admin[i] = 0xFFFE;   /* used but no region starts here */
-    minefield_admin[start + 1] = region_start % PAGESIZE;
-
-    minefield_admin_hide(1);
-
-    VirtualAlloc((char *) minefield_pages + region_start, size,
-                 MEM_COMMIT, PAGE_READWRITE);
-    return (char *) minefield_pages + region_start;
-}
-
-static void minefield_free(void *ptr)
-{
-    int region_start, i, j;
-
-    minefield_admin_hide(0);
-
-    region_start = (char *) ptr - (char *) minefield_pages;
-    i = region_start / PAGESIZE;
-    if (i < 0 || i >= minefield_npages ||
-        minefield_admin[i] != region_start % PAGESIZE)
-        minefield_bomb();
-    for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
-        minefield_admin[j] = 0xFFFF;
-    }
-
-    VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
-
-    minefield_admin_hide(1);
-}
-
-static int minefield_get_size(void *ptr)
-{
-    int region_start, i, j;
-
-    minefield_admin_hide(0);
-
-    region_start = (char *) ptr - (char *) minefield_pages;
-    i = region_start / PAGESIZE;
-    if (i < 0 || i >= minefield_npages ||
-        minefield_admin[i] != region_start % PAGESIZE)
-        minefield_bomb();
-    for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
-
-    minefield_admin_hide(1);
-
-    return j * PAGESIZE - region_start;
-}
-
-void *minefield_c_malloc(size_t size)
-{
-    if (!minefield_initialised)
-        minefield_init();
-    return minefield_alloc(size);
-}
-
-void minefield_c_free(void *p)
-{
-    if (!minefield_initialised)
-        minefield_init();
-    minefield_free(p);
-}
-
-/*
- * realloc _always_ moves the chunk, for rapid detection of code
- * that assumes it won't.
- */
-void *minefield_c_realloc(void *p, size_t size)
-{
-    size_t oldsize;
-    void *q;
-    if (!minefield_initialised)
-        minefield_init();
-    q = minefield_alloc(size);
-    oldsize = minefield_get_size(p);
-    memcpy(q, p, (oldsize < size ? oldsize : size));
-    minefield_free(p);
-    return q;
-}
-
-#endif                          /* MINEFIELD */
-
-#if defined _MSC_VER && _MSC_VER < 1800
-
-/*
- * Work around lack of strtoumax in older MSVC libraries
- */
-uintmax_t strtoumax(const char *nptr, char **endptr, int base)
-{
-    return _strtoui64(nptr, endptr, base);
-}
-
-#endif
-
-#if defined _M_ARM || defined _M_ARM64
-
-bool platform_aes_hw_available(void)
-{
-    return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
-}
-
-bool platform_sha256_hw_available(void)
-{
-    return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
-}
-
-bool platform_sha1_hw_available(void)
-{
-    return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
-}
-
-bool platform_sha512_hw_available(void)
-{
-    /* As of 2020-12-24, as far as I can tell from docs.microsoft.com,
-     * Windows on Arm does not yet provide a PF_ARM_V8_* flag for the
-     * SHA-512 architecture extension. */
-    return false;
-}
-
-#endif
-
-bool is_console_handle(HANDLE handle)
-{
-    DWORD ignored_output;
-    if (GetConsoleMode(handle, &ignored_output))
-        return true;
-    return false;
-}

+ 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,
+};

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

@@ -0,0 +1,281 @@
+/*
+ * Hardware-accelerated implementation of AES using x86 AES-NI.
+ */
+
+#include "ssh.h"
+#include "aes.h"
+
+#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 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;
+    }
+}
+
+/*
+ * 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_epi32(1,0,0,0);
+    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)
+{
+    v = _mm_shuffle_epi8(
+        v, _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0));
+    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 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 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 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 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 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 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 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 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)
+
+AES_EXTRA(_ni);
+AES_ALL_VTABLES(_ni, "AES-NI accelerated");

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

@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+    for (size_t 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");
+}
+
+#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 = {              \
+        .new = aes_select,                                              \
+        .ssh2_id = "aes" #bits "-" mode_protocol,                       \
+        .blksize = 16,                                                  \
+        .real_keybits = bits,                                           \
+        .padded_keybytes = bits/8,                                      \
+        .text_name = "AES-" #bits " " mode_display                      \
+        " (dummy selector vtable)",                                     \
+        .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,
+    .ssh2_id = "[email protected]",
+    .blksize = 16,
+    .real_keybits = 256,
+    .padded_keybytes = 256/8,
+    .text_name = "AES-256 CBC (dummy selector vtable)",
+    .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 };

+ 17 - 890
source/putty/sshaes.c → source/putty/crypto/aes-sw.c

@@ -1,247 +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
-
-#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);
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg);
-static void aes_hw_free(ssh_cipher *);
-static void aes_hw_setiv_cbc(ssh_cipher *, const void *iv);
-static void aes_hw_setiv_sdctr(ssh_cipher *, const void *iv);
-static void aes_hw_setkey(ssh_cipher *, const void *key);
-
-struct aes_extra {
-    const ssh_cipheralg *sw, *hw;
-};
-
-#define VTABLES_INNER(cid, pid, bits, name, encsuffix,                  \
-                      decsuffix, setivsuffix, flagsval)                 \
-    static void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    static void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_sw = {                              \
-        .new = aes_sw_new,                                              \
-        .free = aes_sw_free,                                            \
-        .setiv = aes_sw_##setivsuffix,                                  \
-        .setkey = aes_sw_setkey,                                        \
-        .encrypt = cid##_sw##encsuffix,                                 \
-        .decrypt = cid##_sw##decsuffix,                                 \
-        .ssh2_id = pid,                                                 \
-        .blksize = 16,                                                  \
-        .real_keybits = bits,                                           \
-        .padded_keybytes = bits/8,                                      \
-        .flags = flagsval,                                              \
-        .text_name = name " (unaccelerated)",                           \
-    };                                                                  \
-                                                                        \
-    static void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    static void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_hw = {                              \
-        .new = aes_hw_new,                                              \
-        .free = aes_hw_free,                                            \
-        .setiv = aes_hw_##setivsuffix,                                  \
-        .setkey = aes_hw_setkey,                                        \
-        .encrypt = cid##_hw##encsuffix,                                 \
-        .decrypt = cid##_hw##decsuffix,                                 \
-        .ssh2_id = pid,                                                 \
-        .blksize = 16,                                                  \
-        .real_keybits = bits,                                           \
-        .padded_keybytes = bits/8,                                      \
-        .flags = flagsval,                                              \
-        .text_name = name HW_NAME_SUFFIX,                               \
-    };                                                                  \
-                                                                        \
-    static const struct aes_extra extra_##cid = {                       \
-        &ssh_##cid##_sw, &ssh_##cid##_hw };                             \
-                                                                        \
-    const ssh_cipheralg ssh_##cid = {                                   \
-        .new = aes_select,                                              \
-        .ssh2_id = pid,                                                 \
-        .blksize = 16,                                                  \
-        .real_keybits = bits,                                           \
-        .padded_keybytes = bits/8,                                      \
-        .flags = flagsval,                                              \
-        .text_name = name " (dummy selector vtable)",                   \
-        .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 */
-    .new = aes_select,
-    .ssh2_id = "[email protected]",
-    .blksize = 16,
-    .real_keybits = 256,
-    .padded_keybytes = 256/8,
-    .flags = 0,
-    .text_name = "AES-256 CBC (dummy selector vtable)",
-    .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 };
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-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;
-}
-
-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);
-}
-
-/* ----------------------------------------------------------------------
- * 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
@@ -257,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 BITSLICED_DEBUG
@@ -922,8 +689,8 @@ static void aes_sliced_key_setup(
             }
 
             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);
             }
@@ -1255,13 +1022,13 @@ static inline void aes_sdctr_sw(
 }
 
 #define SW_ENC_DEC(len)                                 \
-    static void aes##len##_cbc_sw_encrypt(              \
+    static void aes##len##_sw_cbc_encrypt(              \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_encrypt(ciph, vblk, blklen); }         \
-    static void aes##len##_cbc_sw_decrypt(              \
+    static void aes##len##_sw_cbc_decrypt(              \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_cbc_sw_decrypt(ciph, vblk, blklen); }         \
-    static void aes##len##_sdctr_sw(                    \
+    static void aes##len##_sw_sdctr(                    \
         ssh_cipher *ciph, void *vblk, int blklen)       \
     { aes_sdctr_sw(ciph, vblk, blklen); }
 
@@ -1269,645 +1036,5 @@ SW_ENC_DEC(128)
 SW_ENC_DEC(192)
 SW_ENC_DEC(256)
 
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using x86 AES-NI.
- */
-
-#if HW_AES == HW_AES_NI
-
-/*
- * 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;
-    }
-}
-
-/*
- * 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_epi32(1,0,0,0);
-    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)
-{
-    v = _mm_shuffle_epi8(
-        v, _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0));
-    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 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 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 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 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 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;
-    }
-}
-
-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 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 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 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)
-
-/* ----------------------------------------------------------------------
- * 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 */
+AES_EXTRA(_sw);
+AES_ALL_VTABLES(_sw, "unaccelerated");

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

@@ -0,0 +1,109 @@
+/*
+ * 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 = {           \
+        .check_available = aes ## impl_c ## _available,                 \
+        .mut = &aes ## impl_c ## _extra_mut,                            \
+    }
+
+#define AES_CBC_VTABLE(impl_c, impl_display, bits)                      \
+    const ssh_cipheralg ssh_aes ## bits ## _cbc ## impl_c = {           \
+        .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,               \
+        .ssh2_id = "aes" #bits "-cbc",                                  \
+        .blksize = 16,                                                  \
+        .real_keybits = bits,                                           \
+        .padded_keybytes = bits/8,                                      \
+        .flags = SSH_CIPHER_IS_CBC,                                     \
+        .text_name = "AES-" #bits " CBC (" impl_display ")",            \
+        .extra = &aes ## impl_c ## _extra,                              \
+    }
+
+#define AES_SDCTR_VTABLE(impl_c, impl_display, bits)                    \
+    const ssh_cipheralg ssh_aes ## bits ## _sdctr ## impl_c = {         \
+        .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,                     \
+        .ssh2_id = "aes" #bits "-ctr",                                  \
+        .blksize = 16,                                                  \
+        .real_keybits = bits,                                           \
+        .padded_keybytes = bits/8,                                      \
+        .flags = 0,                                                     \
+        .text_name = "AES-" #bits " SDCTR (" impl_display ")",          \
+        .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/ssharcf.c → source/putty/crypto/arcfour.c


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

@@ -266,7 +266,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(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(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)
@@ -67,55 +67,55 @@ static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
     sfree(hex);
 }
 
-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);
@@ -155,8 +155,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);
@@ -166,7 +166,7 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
     /*
      * Step 1. w <- s^-1 mod q.
      */
-    mp_int *w = mp_invert(s, dss->q);
+    mp_int *w = mp_invert(s, dsa->q);
     if (!w) {
         mp_free(r);
         mp_free(s);
@@ -178,20 +178,20 @@ static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data)
      */
     hash_simple(&ssh_sha1, data, hash);
     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.
@@ -213,57 +213,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;
         }
     }
@@ -271,75 +271,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.
@@ -445,29 +445,29 @@ mp_int *dss_gen_k(const char *id_string, mp_int *modulus,
     return k;
 }
 
-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);
 
-    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);
 
     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 */
-    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);
@@ -484,20 +484,20 @@ static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
     mp_free(s);
 }
 
-const ssh_keyalg ssh_dss = {
-    .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,
+const ssh_keyalg ssh_dsa = {
+    .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",
 };

+ 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.
  */
 
 /*
@@ -556,8 +548,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;
 }
 
@@ -692,11 +684,11 @@ 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);
 
     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);
 
@@ -1125,7 +1117,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,20 +42,20 @@ 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, " (");
         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, ")");
     }
 
     ctx->mac.vt = alg;

+ 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


+ 182 - 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>
@@ -1535,41 +1539,112 @@ 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));
+    for (size_t i = 1; i < MODPOW_WINDOW_SIZE; i++)
+        table[i] = monty_mul(mc, table[i-1], base);
+
+    /* out accumulates the output value */
+    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 */
+    mp_int *table_entry = mp_make_sized(mc->rw);
 
-    /* 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]);
+    /* 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. */
+    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);
+    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;
+        for (size_t 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]);
+        for (size_t 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 (!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 (++i >= exponent->nw * BIGNUM_INT_BITS)
+        /* 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. */
+        for (size_t j = 0; j < MODPOW_LOG2_WINDOW_SIZE; j++)
+            monty_mul_into(mc, out, out, out);
+
+        i-= MODPOW_LOG2_WINDOW_SIZE;
     }
 
-    mp_free(square);
-    mp_free(tmp);
+    for (size_t i = 0; i < MODPOW_WINDOW_SIZE; i++)
+        mp_free(table[i]);
+    mp_free(table_entry);
     mp_clear(mc->scratch);
     return out;
 }
@@ -2145,9 +2220,9 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out)
      * Make the constant 2*R, which we'll need in the iteration.
      */
     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.
@@ -2259,6 +2334,87 @@ 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;
+    for (size_t 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. []
+         */
+        uint64_t unshifted_quot = accumulator * reciprocal;
+        uint64_t quot = unshifted_quot >> 48;
+        accumulator -= quot * m;
+    }
+
+    /*
+     * 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. []
+     */
+
+    uint32_t result = accumulator;
+    uint32_t reduced = result - m;
+    uint32_t select = -(reduced >> 31);
+    result = reduced ^ ((result ^ reduced) & select);
+    assert(result < m);
+    return result;
+}
+
 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);
+}

+ 29 - 0
source/putty/crypto/pubkey-ppk.c

@@ -0,0 +1,29 @@
+/*
+ * Convenience functions to encrypt and decrypt PuTTY's own .PPK
+ * format for SSH-2 private key files, which uses 256-bit AES in CBC
+ * mode.
+ */
+
+#include "ssh.h"
+
+static ssh_cipher *aes256_pubkey_cipher(const void *key, const void *iv)
+{
+    ssh_cipher *cipher = ssh_cipher_new(&ssh_aes256_cbc);
+    ssh_cipher_setkey(cipher, key);
+    ssh_cipher_setiv(cipher, iv);
+    return cipher;
+}
+
+void aes256_encrypt_pubkey(const void *key, const void *iv, void *blk, int len)
+{
+    ssh_cipher *c = aes256_pubkey_cipher(key, iv);
+    ssh_cipher_encrypt(c, blk, len);
+    ssh_cipher_free(c);
+}
+
+void aes256_decrypt_pubkey(const void *key, const void *iv, void *blk, int len)
+{
+    ssh_cipher *c = aes256_pubkey_cipher(key, iv);
+    ssh_cipher_decrypt(c, blk, len);
+    ssh_cipher_free(c);
+}

+ 3 - 3
source/putty/sshrsa.c → source/putty/crypto/rsa.c

@@ -311,11 +311,11 @@ char *rsa_ssh1_fingerprint(RSAKey *key)
     ssh_hash_final(hash, digest);
 
     out = strbuf_new();
-    strbuf_catf(out, "%"SIZEu" ", mp_get_nbits(key->modulus));
+    put_fmt(out, "%"SIZEu" ", mp_get_nbits(key->modulus));
     for (i = 0; i < 16; i++)
-        strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]);
+        put_fmt(out, "%s%02x", i ? ":" : "", digest[i]);
     if (key->comment)
-        strbuf_catf(out, " %s", key->comment);
+        put_fmt(out, " %s", key->comment);
     return strbuf_to_str(out);
 }
 

+ 10 - 0
source/putty/crypto/sha1-common.c

@@ -0,0 +1,10 @@
+/*
+ * Common variable definitions across all the SHA-1 implementations.
+ */
+
+#include "ssh.h"
+#include "sha1.h"
+
+const uint32_t sha1_initial_state[5] = {
+    0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0,
+};

+ 44 - 0
source/putty/crypto/sha1-select.c

@@ -0,0 +1,44 @@
+/*
+ * Top-level vtables to select a SHA-1 implementation.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sha1.h"
+
+static ssh_hash *sha1_select(const ssh_hashalg *alg)
+{
+    static const ssh_hashalg *const real_algs[] = {
+#if HAVE_SHA_NI
+        &ssh_sha1_ni,
+#endif
+#if HAVE_NEON_CRYPTO
+        &ssh_sha1_neon,
+#endif
+        &ssh_sha1_sw,
+        NULL,
+    };
+
+    for (size_t i = 0; real_algs[i]; i++) {
+        const ssh_hashalg *alg = real_algs[i];
+        const struct sha1_extra *alg_extra =
+            (const struct sha1_extra *)alg->extra;
+        if (check_availability(alg_extra))
+            return ssh_hash_new(alg);
+    }
+
+    /* We should never reach the NULL at the end of the list, because
+     * the last non-NULL entry should be software-only SHA-1, which
+     * is always available. */
+    unreachable("sha1_select ran off the end of its list");
+}
+
+const ssh_hashalg ssh_sha1 = {
+    .new = sha1_select,
+    .hlen = 20,
+    .blocklen = 64,
+    HASHALG_NAMES_ANNOTATED("SHA-1", "dummy selector vtable"),
+};

+ 155 - 0
source/putty/crypto/sha1-sw.c

@@ -0,0 +1,155 @@
+/*
+ * Software implementation of SHA-1.
+ */
+
+#include "ssh.h"
+#include "sha1.h"
+
+static bool sha1_sw_available(void)
+{
+    /* Software SHA-1 is always available */
+    return true;
+}
+
+static inline uint32_t rol(uint32_t x, unsigned y)
+{
+    return (x << (31 & y)) | (x >> (31 & -y));
+}
+
+static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0)
+{
+    return if0 ^ (ctrl & (if1 ^ if0));
+}
+
+static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
+{
+    return (x & y) | (z & (x | y));
+}
+
+static inline uint32_t Par(uint32_t x, uint32_t y, uint32_t z)
+{
+    return (x ^ y ^ z);
+}
+
+static inline void sha1_sw_round(
+    unsigned round_index, const uint32_t *schedule,
+    uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e,
+    uint32_t f, uint32_t constant)
+{
+    *e = rol(*a, 5) + f + *e + schedule[round_index] + constant;
+    *b = rol(*b, 30);
+}
+
+static void sha1_sw_block(uint32_t *core, const uint8_t *block)
+{
+    uint32_t w[SHA1_ROUNDS];
+    uint32_t a,b,c,d,e;
+
+    for (size_t t = 0; t < 16; t++)
+        w[t] = GET_32BIT_MSB_FIRST(block + 4*t);
+
+    for (size_t t = 16; t < SHA1_ROUNDS; t++)
+        w[t] = rol(w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16], 1);
+
+    a = core[0]; b = core[1]; c = core[2]; d = core[3];
+    e = core[4];
+
+    size_t t = 0;
+    for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) {
+        sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Ch(b,c,d), SHA1_STAGE0_CONSTANT);
+        sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Ch(a,b,c), SHA1_STAGE0_CONSTANT);
+        sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Ch(e,a,b), SHA1_STAGE0_CONSTANT);
+        sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Ch(d,e,a), SHA1_STAGE0_CONSTANT);
+        sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Ch(c,d,e), SHA1_STAGE0_CONSTANT);
+    }
+    for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) {
+        sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Par(b,c,d), SHA1_STAGE1_CONSTANT);
+        sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Par(a,b,c), SHA1_STAGE1_CONSTANT);
+        sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Par(e,a,b), SHA1_STAGE1_CONSTANT);
+        sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Par(d,e,a), SHA1_STAGE1_CONSTANT);
+        sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Par(c,d,e), SHA1_STAGE1_CONSTANT);
+    }
+    for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) {
+        sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Maj(b,c,d), SHA1_STAGE2_CONSTANT);
+        sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Maj(a,b,c), SHA1_STAGE2_CONSTANT);
+        sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Maj(e,a,b), SHA1_STAGE2_CONSTANT);
+        sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Maj(d,e,a), SHA1_STAGE2_CONSTANT);
+        sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Maj(c,d,e), SHA1_STAGE2_CONSTANT);
+    }
+    for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) {
+        sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Par(b,c,d), SHA1_STAGE3_CONSTANT);
+        sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Par(a,b,c), SHA1_STAGE3_CONSTANT);
+        sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Par(e,a,b), SHA1_STAGE3_CONSTANT);
+        sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Par(d,e,a), SHA1_STAGE3_CONSTANT);
+        sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Par(c,d,e), SHA1_STAGE3_CONSTANT);
+    }
+
+    core[0] += a; core[1] += b; core[2] += c; core[3] += d; core[4] += e;
+
+    smemclr(w, sizeof(w));
+}
+
+typedef struct sha1_sw {
+    uint32_t core[5];
+    sha1_block blk;
+    BinarySink_IMPLEMENTATION;
+    ssh_hash hash;
+} sha1_sw;
+
+static void sha1_sw_write(BinarySink *bs, const void *vp, size_t len);
+
+static ssh_hash *sha1_sw_new(const ssh_hashalg *alg)
+{
+    sha1_sw *s = snew(sha1_sw);
+
+    s->hash.vt = alg;
+    BinarySink_INIT(s, sha1_sw_write);
+    BinarySink_DELEGATE_INIT(&s->hash, s);
+    return &s->hash;
+}
+
+static void sha1_sw_reset(ssh_hash *hash)
+{
+    sha1_sw *s = container_of(hash, sha1_sw, hash);
+
+    memcpy(s->core, sha1_initial_state, sizeof(s->core));
+    sha1_block_setup(&s->blk);
+}
+
+static void sha1_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
+{
+    sha1_sw *copy = container_of(hcopy, sha1_sw, hash);
+    sha1_sw *orig = container_of(horig, sha1_sw, hash);
+
+    memcpy(copy, orig, sizeof(*copy));
+    BinarySink_COPIED(copy);
+    BinarySink_DELEGATE_INIT(&copy->hash, copy);
+}
+
+static void sha1_sw_free(ssh_hash *hash)
+{
+    sha1_sw *s = container_of(hash, sha1_sw, hash);
+
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+static void sha1_sw_write(BinarySink *bs, const void *vp, size_t len)
+{
+    sha1_sw *s = BinarySink_DOWNCAST(bs, sha1_sw);
+
+    while (len > 0)
+        if (sha1_block_write(&s->blk, &vp, &len))
+            sha1_sw_block(s->core, s->blk.block);
+}
+
+static void sha1_sw_digest(ssh_hash *hash, uint8_t *digest)
+{
+    sha1_sw *s = container_of(hash, sha1_sw, hash);
+
+    sha1_block_pad(&s->blk, BinarySink_UPCAST(s));
+    for (size_t i = 0; i < 5; i++)
+        PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]);
+}
+
+SHA1_VTABLE(sw, "unaccelerated");

+ 109 - 0
source/putty/crypto/sha1.h

@@ -0,0 +1,109 @@
+/*
+ * Definitions likely to be helpful to multiple SHA-1 implementations.
+ */
+
+/*
+ * The 'extra' structure used by SHA-1 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 sha1_extra_mutable;
+struct sha1_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 sha1_extra_mutable *mut;
+};
+struct sha1_extra_mutable {
+    bool checked_availability;
+    bool is_available;
+};
+static inline bool check_availability(const struct sha1_extra *extra)
+{
+    if (!extra->mut->checked_availability) {
+        extra->mut->is_available = extra->check_available();
+        extra->mut->checked_availability = true;
+    }
+
+    return extra->mut->is_available;
+}
+
+/*
+ * Macro to define a SHA-1 vtable together with its 'extra'
+ * structure.
+ */
+#define SHA1_VTABLE(impl_c, impl_display)                               \
+    static struct sha1_extra_mutable sha1_ ## impl_c ## _extra_mut;     \
+    static const struct sha1_extra sha1_ ## impl_c ## _extra = {        \
+        .check_available = sha1_ ## impl_c ## _available,               \
+        .mut = &sha1_ ## impl_c ## _extra_mut,                          \
+    };                                                                  \
+    const ssh_hashalg ssh_sha1_ ## impl_c = {                           \
+        .new = sha1_ ## impl_c ## _new,                                 \
+        .reset = sha1_ ## impl_c ## _reset,                             \
+        .copyfrom = sha1_ ## impl_c ## _copyfrom,                       \
+        .digest = sha1_ ## impl_c ## _digest,                           \
+        .free = sha1_ ## impl_c ## _free,                               \
+        .hlen = 20,                                                     \
+        .blocklen = 64,                                                 \
+        HASHALG_NAMES_ANNOTATED("SHA-1", impl_display),                 \
+        .extra = &sha1_ ## impl_c ## _extra,                            \
+    }
+
+extern const uint32_t sha1_initial_state[5];
+
+#define SHA1_ROUNDS_PER_STAGE 20
+#define SHA1_STAGE0_CONSTANT 0x5a827999
+#define SHA1_STAGE1_CONSTANT 0x6ed9eba1
+#define SHA1_STAGE2_CONSTANT 0x8f1bbcdc
+#define SHA1_STAGE3_CONSTANT 0xca62c1d6
+#define SHA1_ROUNDS (4 * SHA1_ROUNDS_PER_STAGE)
+
+typedef struct sha1_block sha1_block;
+struct sha1_block {
+    uint8_t block[64];
+    size_t used;
+    uint64_t len;
+};
+
+static inline void sha1_block_setup(sha1_block *blk)
+{
+    blk->used = 0;
+    blk->len = 0;
+}
+
+static inline bool sha1_block_write(
+    sha1_block *blk, const void **vdata, size_t *len)
+{
+    size_t blkleft = sizeof(blk->block) - blk->used;
+    size_t chunk = *len < blkleft ? *len : blkleft;
+
+    const uint8_t *p = *vdata;
+    memcpy(blk->block + blk->used, p, chunk);
+    *vdata = p + chunk;
+    *len -= chunk;
+    blk->used += chunk;
+    blk->len += chunk;
+
+    if (blk->used == sizeof(blk->block)) {
+        blk->used = 0;
+        return true;
+    }
+
+    return false;
+}
+
+static inline void sha1_block_pad(sha1_block *blk, BinarySink *bs)
+{
+    uint64_t final_len = blk->len << 3;
+    size_t pad = 1 + (63 & (55 - blk->used));
+
+    put_byte(bs, 0x80);
+    for (size_t i = 1; i < pad; i++)
+        put_byte(bs, 0);
+    put_uint64(bs, final_len);
+
+    assert(blk->used == 0 && "Should have exactly hit a block boundary");
+}

+ 30 - 0
source/putty/crypto/sha256-common.c

@@ -0,0 +1,30 @@
+/*
+ * Common variable definitions across all the SHA-256 implementations.
+ */
+
+#include "ssh.h"
+#include "sha256.h"
+
+const uint32_t sha256_initial_state[8] = {
+    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+    0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
+};
+
+const uint32_t sha256_round_constants[64] = {
+    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+};

+ 44 - 0
source/putty/crypto/sha256-select.c

@@ -0,0 +1,44 @@
+/*
+ * Top-level vtables to select a SHA-256 implementation.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sha256.h"
+
+static ssh_hash *sha256_select(const ssh_hashalg *alg)
+{
+    static const ssh_hashalg *const real_algs[] = {
+#if HAVE_SHA_NI
+        &ssh_sha256_ni,
+#endif
+#if HAVE_NEON_CRYPTO
+        &ssh_sha256_neon,
+#endif
+        &ssh_sha256_sw,
+        NULL,
+    };
+
+    for (size_t i = 0; real_algs[i]; i++) {
+        const ssh_hashalg *alg = real_algs[i];
+        const struct sha256_extra *alg_extra =
+            (const struct sha256_extra *)alg->extra;
+        if (check_availability(alg_extra))
+            return ssh_hash_new(alg);
+    }
+
+    /* We should never reach the NULL at the end of the list, because
+     * the last non-NULL entry should be software-only SHA-256, which
+     * is always available. */
+    unreachable("sha256_select ran off the end of its list");
+}
+
+const ssh_hashalg ssh_sha256 = {
+    .new = sha256_select,
+    .hlen = 32,
+    .blocklen = 64,
+    HASHALG_NAMES_ANNOTATED("SHA-256", "dummy selector vtable"),
+};

+ 157 - 0
source/putty/crypto/sha256-sw.c

@@ -0,0 +1,157 @@
+/*
+ * Software implementation of SHA-256.
+ */
+
+#include "ssh.h"
+#include "sha256.h"
+
+static bool sha256_sw_available(void)
+{
+    /* Software SHA-256 is always available */
+    return true;
+}
+
+static inline uint32_t ror(uint32_t x, unsigned y)
+{
+    return (x << (31 & -y)) | (x >> (31 & y));
+}
+
+static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0)
+{
+    return if0 ^ (ctrl & (if1 ^ if0));
+}
+
+static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
+{
+    return (x & y) | (z & (x | y));
+}
+
+static inline uint32_t Sigma_0(uint32_t x)
+{
+    return ror(x,2) ^ ror(x,13) ^ ror(x,22);
+}
+
+static inline uint32_t Sigma_1(uint32_t x)
+{
+    return ror(x,6) ^ ror(x,11) ^ ror(x,25);
+}
+
+static inline uint32_t sigma_0(uint32_t x)
+{
+    return ror(x,7) ^ ror(x,18) ^ (x >> 3);
+}
+
+static inline uint32_t sigma_1(uint32_t x)
+{
+    return ror(x,17) ^ ror(x,19) ^ (x >> 10);
+}
+
+static inline void sha256_sw_round(
+    unsigned round_index, const uint32_t *schedule,
+    uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d,
+    uint32_t *e, uint32_t *f, uint32_t *g, uint32_t *h)
+{
+    uint32_t t1 = *h + Sigma_1(*e) + Ch(*e,*f,*g) +
+        sha256_round_constants[round_index] + schedule[round_index];
+
+    uint32_t t2 = Sigma_0(*a) + Maj(*a,*b,*c);
+
+    *d += t1;
+    *h = t1 + t2;
+}
+
+static void sha256_sw_block(uint32_t *core, const uint8_t *block)
+{
+    uint32_t w[SHA256_ROUNDS];
+    uint32_t a,b,c,d,e,f,g,h;
+
+    for (size_t t = 0; t < 16; t++)
+        w[t] = GET_32BIT_MSB_FIRST(block + 4*t);
+
+    for (size_t t = 16; t < SHA256_ROUNDS; t++)
+        w[t] = sigma_1(w[t-2]) + w[t-7] + sigma_0(w[t-15]) + w[t-16];
+
+    a = core[0]; b = core[1]; c = core[2]; d = core[3];
+    e = core[4]; f = core[5]; g = core[6]; h = core[7];
+
+    for (size_t t = 0; t < SHA256_ROUNDS; t += 8) {
+        sha256_sw_round(t+0, w, &a,&b,&c,&d,&e,&f,&g,&h);
+        sha256_sw_round(t+1, w, &h,&a,&b,&c,&d,&e,&f,&g);
+        sha256_sw_round(t+2, w, &g,&h,&a,&b,&c,&d,&e,&f);
+        sha256_sw_round(t+3, w, &f,&g,&h,&a,&b,&c,&d,&e);
+        sha256_sw_round(t+4, w, &e,&f,&g,&h,&a,&b,&c,&d);
+        sha256_sw_round(t+5, w, &d,&e,&f,&g,&h,&a,&b,&c);
+        sha256_sw_round(t+6, w, &c,&d,&e,&f,&g,&h,&a,&b);
+        sha256_sw_round(t+7, w, &b,&c,&d,&e,&f,&g,&h,&a);
+    }
+
+    core[0] += a; core[1] += b; core[2] += c; core[3] += d;
+    core[4] += e; core[5] += f; core[6] += g; core[7] += h;
+
+    smemclr(w, sizeof(w));
+}
+
+typedef struct sha256_sw {
+    uint32_t core[8];
+    sha256_block blk;
+    BinarySink_IMPLEMENTATION;
+    ssh_hash hash;
+} sha256_sw;
+
+static void sha256_sw_write(BinarySink *bs, const void *vp, size_t len);
+
+static ssh_hash *sha256_sw_new(const ssh_hashalg *alg)
+{
+    sha256_sw *s = snew(sha256_sw);
+
+    s->hash.vt = alg;
+    BinarySink_INIT(s, sha256_sw_write);
+    BinarySink_DELEGATE_INIT(&s->hash, s);
+    return &s->hash;
+}
+
+static void sha256_sw_reset(ssh_hash *hash)
+{
+    sha256_sw *s = container_of(hash, sha256_sw, hash);
+
+    memcpy(s->core, sha256_initial_state, sizeof(s->core));
+    sha256_block_setup(&s->blk);
+}
+
+static void sha256_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
+{
+    sha256_sw *copy = container_of(hcopy, sha256_sw, hash);
+    sha256_sw *orig = container_of(horig, sha256_sw, hash);
+
+    memcpy(copy, orig, sizeof(*copy));
+    BinarySink_COPIED(copy);
+    BinarySink_DELEGATE_INIT(&copy->hash, copy);
+}
+
+static void sha256_sw_free(ssh_hash *hash)
+{
+    sha256_sw *s = container_of(hash, sha256_sw, hash);
+
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+static void sha256_sw_write(BinarySink *bs, const void *vp, size_t len)
+{
+    sha256_sw *s = BinarySink_DOWNCAST(bs, sha256_sw);
+
+    while (len > 0)
+        if (sha256_block_write(&s->blk, &vp, &len))
+            sha256_sw_block(s->core, s->blk.block);
+}
+
+static void sha256_sw_digest(ssh_hash *hash, uint8_t *digest)
+{
+    sha256_sw *s = container_of(hash, sha256_sw, hash);
+
+    sha256_block_pad(&s->blk, BinarySink_UPCAST(s));
+    for (size_t i = 0; i < 8; i++)
+        PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]);
+}
+
+SHA256_VTABLE(sw, "unaccelerated");

+ 105 - 0
source/putty/crypto/sha256.h

@@ -0,0 +1,105 @@
+/*
+ * Definitions likely to be helpful to multiple SHA-256 implementations.
+ */
+
+/*
+ * The 'extra' structure used by SHA-256 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 sha256_extra_mutable;
+struct sha256_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 sha256_extra_mutable *mut;
+};
+struct sha256_extra_mutable {
+    bool checked_availability;
+    bool is_available;
+};
+static inline bool check_availability(const struct sha256_extra *extra)
+{
+    if (!extra->mut->checked_availability) {
+        extra->mut->is_available = extra->check_available();
+        extra->mut->checked_availability = true;
+    }
+
+    return extra->mut->is_available;
+}
+
+/*
+ * Macro to define a SHA-256 vtable together with its 'extra'
+ * structure.
+ */
+#define SHA256_VTABLE(impl_c, impl_display)                             \
+    static struct sha256_extra_mutable sha256_ ## impl_c ## _extra_mut; \
+    static const struct sha256_extra sha256_ ## impl_c ## _extra = {    \
+        .check_available = sha256_ ## impl_c ## _available,             \
+        .mut = &sha256_ ## impl_c ## _extra_mut,                        \
+    };                                                                  \
+    const ssh_hashalg ssh_sha256_ ## impl_c = {                         \
+        .new = sha256_ ## impl_c ## _new,                               \
+        .reset = sha256_ ## impl_c ## _reset,                           \
+        .copyfrom = sha256_ ## impl_c ## _copyfrom,                     \
+        .digest = sha256_ ## impl_c ## _digest,                         \
+        .free = sha256_ ## impl_c ## _free,                             \
+        .hlen = 32,                                                     \
+        .blocklen = 64,                                                 \
+        HASHALG_NAMES_ANNOTATED("SHA-256", impl_display),               \
+        .extra = &sha256_ ## impl_c ## _extra,                          \
+    }
+
+extern const uint32_t sha256_initial_state[8];
+extern const uint32_t sha256_round_constants[64];
+
+#define SHA256_ROUNDS 64
+
+typedef struct sha256_block sha256_block;
+struct sha256_block {
+    uint8_t block[64];
+    size_t used;
+    uint64_t len;
+};
+
+static inline void sha256_block_setup(sha256_block *blk)
+{
+    blk->used = 0;
+    blk->len = 0;
+}
+
+static inline bool sha256_block_write(
+    sha256_block *blk, const void **vdata, size_t *len)
+{
+    size_t blkleft = sizeof(blk->block) - blk->used;
+    size_t chunk = *len < blkleft ? *len : blkleft;
+
+    const uint8_t *p = *vdata;
+    memcpy(blk->block + blk->used, p, chunk);
+    *vdata = p + chunk;
+    *len -= chunk;
+    blk->used += chunk;
+    blk->len += chunk;
+
+    if (blk->used == sizeof(blk->block)) {
+        blk->used = 0;
+        return true;
+    }
+
+    return false;
+}
+
+static inline void sha256_block_pad(sha256_block *blk, BinarySink *bs)
+{
+    uint64_t final_len = blk->len << 3;
+    size_t pad = 1 + (63 & (55 - blk->used));
+
+    put_byte(bs, 0x80);
+    for (size_t i = 1; i < pad; i++)
+        put_byte(bs, 0);
+    put_uint64(bs, final_len);
+
+    assert(blk->used == 0 && "Should have exactly hit a block boundary");
+}

+ 0 - 0
source/putty/sshsha3.c → source/putty/crypto/sha3.c


+ 71 - 0
source/putty/crypto/sha512-common.c

@@ -0,0 +1,71 @@
+/*
+ * Common variable definitions across all the SHA-512 implementations.
+ */
+
+#include "ssh.h"
+#include "sha512.h"
+
+const uint64_t sha512_initial_state[8] = {
+    0x6a09e667f3bcc908ULL,
+    0xbb67ae8584caa73bULL,
+    0x3c6ef372fe94f82bULL,
+    0xa54ff53a5f1d36f1ULL,
+    0x510e527fade682d1ULL,
+    0x9b05688c2b3e6c1fULL,
+    0x1f83d9abfb41bd6bULL,
+    0x5be0cd19137e2179ULL,
+};
+
+const uint64_t sha384_initial_state[8] = {
+    0xcbbb9d5dc1059ed8ULL,
+    0x629a292a367cd507ULL,
+    0x9159015a3070dd17ULL,
+    0x152fecd8f70e5939ULL,
+    0x67332667ffc00b31ULL,
+    0x8eb44a8768581511ULL,
+    0xdb0c2e0d64f98fa7ULL,
+    0x47b5481dbefa4fa4ULL,
+};
+
+const uint64_t sha512_round_constants[80] = {
+    0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+    0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+    0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+    0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+    0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+    0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+    0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+    0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+    0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+    0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+    0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+    0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+    0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+    0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+    0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+    0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+    0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+    0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+    0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+    0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+    0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+    0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+    0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+    0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+    0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+    0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+    0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+    0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+    0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+    0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+    0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+    0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+    0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+    0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+    0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+    0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+    0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+    0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+    0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+    0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL,
+};

+ 61 - 0
source/putty/crypto/sha512-select.c

@@ -0,0 +1,61 @@
+/*
+ * Top-level vtables to select a SHA-512 implementation.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sha512.h"
+
+static const ssh_hashalg *const real_sha512_algs[] = {
+#if HAVE_NEON_SHA512
+    &ssh_sha512_neon,
+#endif
+    &ssh_sha512_sw,
+    NULL,
+};
+
+static const ssh_hashalg *const real_sha384_algs[] = {
+#if HAVE_NEON_SHA512
+    &ssh_sha384_neon,
+#endif
+    &ssh_sha384_sw,
+    NULL,
+};
+
+static ssh_hash *sha512_select(const ssh_hashalg *alg)
+{
+    const ssh_hashalg *const *real_algs =
+        (const ssh_hashalg *const *)alg->extra;
+
+    for (size_t i = 0; real_algs[i]; i++) {
+        const ssh_hashalg *alg = real_algs[i];
+        const struct sha512_extra *alg_extra =
+            (const struct sha512_extra *)alg->extra;
+        if (check_availability(alg_extra))
+            return ssh_hash_new(alg);
+    }
+
+    /* We should never reach the NULL at the end of the list, because
+     * the last non-NULL entry should be software-only SHA-512, which
+     * is always available. */
+    unreachable("sha512_select ran off the end of its list");
+}
+
+const ssh_hashalg ssh_sha512 = {
+    .new = sha512_select,
+    .hlen = 64,
+    .blocklen = 128,
+    HASHALG_NAMES_ANNOTATED("SHA-512", "dummy selector vtable"),
+    .extra = real_sha512_algs,
+};
+
+const ssh_hashalg ssh_sha384 = {
+    .new = sha512_select,
+    .hlen = 48,
+    .blocklen = 128,
+    HASHALG_NAMES_ANNOTATED("SHA-384", "dummy selector vtable"),
+    .extra = real_sha384_algs,
+};

+ 168 - 0
source/putty/crypto/sha512-sw.c

@@ -0,0 +1,168 @@
+/*
+ * Software implementation of SHA-512.
+ */
+
+#include "ssh.h"
+#include "sha512.h"
+
+static bool sha512_sw_available(void)
+{
+    /* Software SHA-512 is always available */
+    return true;
+}
+
+static inline uint64_t ror(uint64_t x, unsigned y)
+{
+    return (x << (63 & -y)) | (x >> (63 & y));
+}
+
+static inline uint64_t Ch(uint64_t ctrl, uint64_t if1, uint64_t if0)
+{
+    return if0 ^ (ctrl & (if1 ^ if0));
+}
+
+static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z)
+{
+    return (x & y) | (z & (x | y));
+}
+
+static inline uint64_t Sigma_0(uint64_t x)
+{
+    return ror(x,28) ^ ror(x,34) ^ ror(x,39);
+}
+
+static inline uint64_t Sigma_1(uint64_t x)
+{
+    return ror(x,14) ^ ror(x,18) ^ ror(x,41);
+}
+
+static inline uint64_t sigma_0(uint64_t x)
+{
+    return ror(x,1) ^ ror(x,8) ^ (x >> 7);
+}
+
+static inline uint64_t sigma_1(uint64_t x)
+{
+    return ror(x,19) ^ ror(x,61) ^ (x >> 6);
+}
+
+static inline void sha512_sw_round(
+    unsigned round_index, const uint64_t *schedule,
+    uint64_t *a, uint64_t *b, uint64_t *c, uint64_t *d,
+    uint64_t *e, uint64_t *f, uint64_t *g, uint64_t *h)
+{
+    uint64_t t1 = *h + Sigma_1(*e) + Ch(*e,*f,*g) +
+        sha512_round_constants[round_index] + schedule[round_index];
+
+    uint64_t t2 = Sigma_0(*a) + Maj(*a,*b,*c);
+
+    *d += t1;
+    *h = t1 + t2;
+}
+
+static void sha512_sw_block(uint64_t *core, const uint8_t *block)
+{
+    uint64_t w[SHA512_ROUNDS];
+    uint64_t a,b,c,d,e,f,g,h;
+
+    int t;
+
+    for (t = 0; t < 16; t++)
+        w[t] = GET_64BIT_MSB_FIRST(block + 8*t);
+
+    for (t = 16; t < SHA512_ROUNDS; t++)
+        w[t] = w[t-16] + w[t-7] + sigma_0(w[t-15]) + sigma_1(w[t-2]);
+
+    a = core[0]; b = core[1]; c = core[2]; d = core[3];
+    e = core[4]; f = core[5]; g = core[6]; h = core[7];
+
+    for (t = 0; t < SHA512_ROUNDS; t+=8) {
+        sha512_sw_round(t+0, w, &a,&b,&c,&d,&e,&f,&g,&h);
+        sha512_sw_round(t+1, w, &h,&a,&b,&c,&d,&e,&f,&g);
+        sha512_sw_round(t+2, w, &g,&h,&a,&b,&c,&d,&e,&f);
+        sha512_sw_round(t+3, w, &f,&g,&h,&a,&b,&c,&d,&e);
+        sha512_sw_round(t+4, w, &e,&f,&g,&h,&a,&b,&c,&d);
+        sha512_sw_round(t+5, w, &d,&e,&f,&g,&h,&a,&b,&c);
+        sha512_sw_round(t+6, w, &c,&d,&e,&f,&g,&h,&a,&b);
+        sha512_sw_round(t+7, w, &b,&c,&d,&e,&f,&g,&h,&a);
+    }
+
+    core[0] += a; core[1] += b; core[2] += c; core[3] += d;
+    core[4] += e; core[5] += f; core[6] += g; core[7] += h;
+
+    smemclr(w, sizeof(w));
+}
+
+typedef struct sha512_sw {
+    uint64_t core[8];
+    sha512_block blk;
+    BinarySink_IMPLEMENTATION;
+    ssh_hash hash;
+} sha512_sw;
+
+static void sha512_sw_write(BinarySink *bs, const void *vp, size_t len);
+
+static ssh_hash *sha512_sw_new(const ssh_hashalg *alg)
+{
+    sha512_sw *s = snew(sha512_sw);
+
+    s->hash.vt = alg;
+    BinarySink_INIT(s, sha512_sw_write);
+    BinarySink_DELEGATE_INIT(&s->hash, s);
+    return &s->hash;
+}
+
+static void sha512_sw_reset(ssh_hash *hash)
+{
+    sha512_sw *s = container_of(hash, sha512_sw, hash);
+    const struct sha512_extra *extra =
+        (const struct sha512_extra *)hash->vt->extra;
+
+    memcpy(s->core, extra->initial_state, sizeof(s->core));
+    sha512_block_setup(&s->blk);
+}
+
+static void sha512_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
+{
+    sha512_sw *copy = container_of(hcopy, sha512_sw, hash);
+    sha512_sw *orig = container_of(horig, sha512_sw, hash);
+
+    memcpy(copy, orig, sizeof(*copy));
+    BinarySink_COPIED(copy);
+    BinarySink_DELEGATE_INIT(&copy->hash, copy);
+}
+
+static void sha512_sw_free(ssh_hash *hash)
+{
+    sha512_sw *s = container_of(hash, sha512_sw, hash);
+
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+static void sha512_sw_write(BinarySink *bs, const void *vp, size_t len)
+{
+    sha512_sw *s = BinarySink_DOWNCAST(bs, sha512_sw);
+
+    while (len > 0)
+        if (sha512_block_write(&s->blk, &vp, &len))
+            sha512_sw_block(s->core, s->blk.block);
+}
+
+static void sha512_sw_digest(ssh_hash *hash, uint8_t *digest)
+{
+    sha512_sw *s = container_of(hash, sha512_sw, hash);
+
+    sha512_block_pad(&s->blk, BinarySink_UPCAST(s));
+    for (size_t i = 0; i < hash->vt->hlen / 8; i++)
+        PUT_64BIT_MSB_FIRST(digest + 8*i, s->core[i]);
+}
+
+/*
+ * This implementation doesn't need separate digest methods for
+ * SHA-384 and SHA-512, because the above implementation reads the
+ * hash length out of the vtable.
+ */
+#define sha384_sw_digest sha512_sw_digest
+
+SHA512_VTABLES(sw, "unaccelerated");

+ 131 - 0
source/putty/crypto/sha512.h

@@ -0,0 +1,131 @@
+/*
+ * Definitions likely to be helpful to multiple SHA-512 implementations.
+ */
+
+/*
+ * The 'extra' structure used by SHA-512 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 sha512_extra_mutable;
+struct sha512_extra {
+    /* Pointer to the initial state (distinguishes SHA-384 from -512) */
+    const uint64_t *initial_state;
+
+    /* 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 sha512_extra_mutable *mut;
+};
+struct sha512_extra_mutable {
+    bool checked_availability;
+    bool is_available;
+};
+static inline bool check_availability(const struct sha512_extra *extra)
+{
+    if (!extra->mut->checked_availability) {
+        extra->mut->is_available = extra->check_available();
+        extra->mut->checked_availability = true;
+    }
+
+    return extra->mut->is_available;
+}
+
+/*
+ * Macro to define a pair of SHA-{384,512} vtables together with their
+ * 'extra' structure.
+ */
+#define SHA512_VTABLES(impl_c, impl_display)                            \
+    static struct sha512_extra_mutable sha512_ ## impl_c ## _extra_mut; \
+    static const struct sha512_extra sha384_ ## impl_c ## _extra = {    \
+        .initial_state = sha384_initial_state,                          \
+        .check_available = sha512_ ## impl_c ## _available,             \
+        .mut = &sha512_ ## impl_c ## _extra_mut,                        \
+    };                                                                  \
+    static const struct sha512_extra sha512_ ## impl_c ## _extra = {    \
+        .initial_state = sha512_initial_state,                          \
+        .check_available = sha512_ ## impl_c ## _available,             \
+        .mut = &sha512_ ## impl_c ## _extra_mut,                        \
+    };                                                                  \
+    const ssh_hashalg ssh_sha384_ ## impl_c = {                         \
+        .new = sha512_ ## impl_c ## _new,                               \
+        .reset = sha512_ ## impl_c ## _reset,                           \
+        .copyfrom = sha512_ ## impl_c ## _copyfrom,                     \
+        .digest = sha384_ ## impl_c ## _digest,                         \
+        .free = sha512_ ## impl_c ## _free,                             \
+        .hlen = 48,                                                     \
+        .blocklen = 128,                                                \
+        HASHALG_NAMES_ANNOTATED("SHA-384", impl_display),               \
+        .extra = &sha384_ ## impl_c ## _extra,                          \
+    };                                                                  \
+    const ssh_hashalg ssh_sha512_ ## impl_c = {                         \
+        .new = sha512_ ## impl_c ## _new,                               \
+        .reset = sha512_ ## impl_c ## _reset,                           \
+        .copyfrom = sha512_ ## impl_c ## _copyfrom,                     \
+        .digest = sha512_ ## impl_c ## _digest,                         \
+        .free = sha512_ ## impl_c ## _free,                             \
+        .hlen = 64,                                                     \
+        .blocklen = 128,                                                \
+        HASHALG_NAMES_ANNOTATED("SHA-512", impl_display),               \
+        .extra = &sha512_ ## impl_c ## _extra,                          \
+    }
+
+extern const uint64_t sha512_initial_state[8];
+extern const uint64_t sha384_initial_state[8];
+extern const uint64_t sha512_round_constants[80];
+
+#define SHA512_ROUNDS 80
+
+typedef struct sha512_block sha512_block;
+struct sha512_block {
+    uint8_t block[128];
+    size_t used;
+    uint64_t lenhi, lenlo;
+};
+
+static inline void sha512_block_setup(sha512_block *blk)
+{
+    blk->used = 0;
+    blk->lenhi = blk->lenlo = 0;
+}
+
+static inline bool sha512_block_write(
+    sha512_block *blk, const void **vdata, size_t *len)
+{
+    size_t blkleft = sizeof(blk->block) - blk->used;
+    size_t chunk = *len < blkleft ? *len : blkleft;
+
+    const uint8_t *p = *vdata;
+    memcpy(blk->block + blk->used, p, chunk);
+    *vdata = p + chunk;
+    *len -= chunk;
+    blk->used += chunk;
+
+    size_t chunkbits = chunk << 3;
+
+    blk->lenlo += chunkbits;
+    blk->lenhi += (blk->lenlo < chunkbits);
+
+    if (blk->used == sizeof(blk->block)) {
+        blk->used = 0;
+        return true;
+    }
+
+    return false;
+}
+
+static inline void sha512_block_pad(sha512_block *blk, BinarySink *bs)
+{
+    uint64_t final_lenhi = blk->lenhi;
+    uint64_t final_lenlo = blk->lenlo;
+    size_t pad = 127 & (111 - blk->used);
+
+    put_byte(bs, 0x80);
+    put_padding(bs, pad, 0);
+    put_uint64(bs, final_lenhi);
+    put_uint64(bs, final_lenlo);
+
+    assert(blk->used == 0 && "Should have exactly hit a block boundary");
+}

+ 48 - 25
source/putty/defs.h

@@ -11,6 +11,21 @@
 #ifndef PUTTY_DEFS_H
 #define PUTTY_DEFS_H
 
+#ifdef NDEBUG
+/*
+ * PuTTY is a security project, so assertions are important - if an
+ * assumption is violated, proceeding anyway may have far worse
+ * consequences than simple program termination. This check and #error
+ * should arrange that we don't ever accidentally compile assertions
+ * out.
+ */
+#error Do not compile this code base with NDEBUG defined!
+#endif
+
+#if HAVE_CMAKE_H
+#include "cmake.h"
+#endif
+
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>                     /* for __MINGW_PRINTF_FORMAT */
@@ -27,6 +42,8 @@
 #define SIZEx "Ix"
 #define SIZEu "Iu"
 uintmax_t strtoumax(const char *nptr, char **endptr, int base);
+/* Also, define a LEGACY_WINDOWS flag to enable other workarounds */
+#define LEGACY_WINDOWS
 #else
 #include <inttypes.h>
 /* Because we still support older MSVC libraries which don't recognise the
@@ -83,9 +100,14 @@ typedef struct SockAddr SockAddr;
 typedef struct Socket Socket;
 typedef struct Plug Plug;
 typedef struct SocketPeerInfo SocketPeerInfo;
+typedef struct DeferredSocketOpener DeferredSocketOpener;
+typedef struct DeferredSocketOpenerVtable DeferredSocketOpenerVtable;
 
 typedef struct Backend Backend;
 typedef struct BackendVtable BackendVtable;
+typedef struct Interactor Interactor;
+typedef struct InteractorVtable InteractorVtable;
+typedef struct InteractionReadySeat InteractionReadySeat;
 
 typedef struct Ldisc_tag Ldisc;
 typedef struct LogContext LogContext;
@@ -94,6 +116,7 @@ typedef struct LogPolicyVtable LogPolicyVtable;
 
 typedef struct Seat Seat;
 typedef struct SeatVtable SeatVtable;
+typedef struct SeatPromptResult SeatPromptResult;
 
 typedef struct TermWin TermWin;
 typedef struct TermWinVtable TermWinVtable;
@@ -155,6 +178,8 @@ typedef struct SessionSpecial SessionSpecial;
 
 typedef struct StripCtrlChars StripCtrlChars;
 
+typedef struct BidiContext BidiContext;
+
 /*
  * A small structure wrapping up a (pointer, length) pair so that it
  * can be conveniently passed to or from a function.
@@ -169,6 +194,8 @@ typedef struct logblank_t logblank_t;
 typedef struct BinaryPacketProtocol BinaryPacketProtocol;
 typedef struct PacketProtocolLayer PacketProtocolLayer;
 
+struct unicode_data;
+
 /* Do a compile-time type-check of 'to_check' (without evaluating it),
  * as a side effect of returning the value 'to_return'. Note that
  * although this macro double-*expands* to_return, it always
@@ -190,32 +217,28 @@ typedef struct PacketProtocolLayer PacketProtocolLayer;
 #define NORETURN
 #endif
 
-/* ----------------------------------------------------------------------
- * Platform-specific definitions.
+/*
+ * Standard macro definitions. STR() behaves like the preprocessor
+ * stringification # operator, and CAT() behaves like the token paste
+ * ## operator, except that each one macro-expands its argument(s)
+ * first, unlike the raw version. E.g.
+ *
+ *   #__LINE__               ->    "__LINE__"
+ *   STR(__LINE__)           ->    "1234"             (or whatever)
+ *
+ * and similarly,
+ *
+ *   foo ## __LINE__         ->    foo__LINE__
+ *   CAT(foo, __LINE__)      ->    foo1234            (or whatever)
  *
- * Most of these live in the per-platform header files, of which
- * puttyps.h selects the appropriate one. But some of the sources
- * (particularly standalone test applications) would prefer not to
- * have to include a per-platform header at all, because that makes it
- * more portable to platforms not supported by the code base as a
- * whole (for example, compiling purely computational parts of the
- * code for specialist platforms for test and analysis purposes). So
- * any definition that has to affect even _those_ modules will have to
- * go here, with the key constraint being that this code has to come
- * to _some_ decision even if the compilation platform is not a
- * recognised one at all.
+ * The expansion is achieved by having each macro pass its arguments
+ * to a secondary inner macro, because parameter lists of a macro call
+ * get expanded before the called macro is invoked. So STR(__LINE__)
+ * -> STR_INNER(1234) -> #1234 -> "1234", and similarly for CAT.
  */
-
-/* Purely computational code uses smemclr(), so we have to make the
- * decision here about whether that's provided by utils.c or by a
- * platform implementation. We define PLATFORM_HAS_SMEMCLR to suppress
- * utils.c's definition. */
-#ifdef _WINDOWS
-/* Windows provides the API function 'SecureZeroMemory', which we use
- * unless the user has told us not to by defining NO_SECUREZEROMEMORY. */
-#ifndef NO_SECUREZEROMEMORY
-#define PLATFORM_HAS_SMEMCLR
-#endif
-#endif
+#define STR_INNER(x) #x
+#define STR(x) STR_INNER(x)
+#define CAT_INNER(x,y) x ## y
+#define CAT(x,y) CAT_INNER(x,y)
 
 #endif /* PUTTY_DEFS_H */

+ 0 - 80
source/putty/doc/Makefile

@@ -1,80 +0,0 @@
-all: man index.html
-
-# Decide on the versionid policy.
-#
-# If the user has passed in $(VERSION) on the command line (`make
-# VERSION="Release 0.56"'), we use that as an explicit version string.
-# Otherwise, we use `svnversion' to examine the checked-out
-# documentation source, and if that returns a single revision number
-# then we invent a version string reflecting just that number. Failing
-# _that_, we resort to versionids.but which gives 'version
-# unavailable'.
-#
-# So here, we define VERSION using svnversion if it isn't already
-# defined ...
-ifndef VERSION
-SVNVERSION=$(shell test -d .svn && svnversion .)
-BADCHARS=$(findstring :,$(SVNVERSION))$(findstring S,$(SVNVERSION))
-ifeq ($(BADCHARS),)
-ifneq ($(SVNVERSION),)
-ifneq ($(SVNVERSION),exported)
-VERSION=Built from revision $(patsubst M,,$(SVNVERSION))
-endif
-endif
-endif
-endif
-# ... and now, we condition our build behaviour on whether or not
-# VERSION _is_ defined.
-ifdef VERSION
-VERSIONIDS=vstr
-vstr.but: FORCE
-	printf '\\versionid $(VERSION)\n' > vstr.but
-FORCE:;
-else
-VERSIONIDS=vids
-endif
-
-CHAPTERS := $(SITE) copy blurb intro gs using config pscp psftp plink
-CHAPTERS += pubkey pageant errors faq feedback pubkeyfmt licence udp
-CHAPTERS += pgpkeys sshnames
-CHAPTERS += index $(VERSIONIDS)
-
-INPUTS = $(patsubst %,%.but,$(CHAPTERS))
-
-# This is temporary. Hack it locally or something.
-HALIBUT = halibut
-
-index.html: $(INPUTS)
-	$(HALIBUT) --text --html --chm $(INPUTS)
-
-# During formal builds it's useful to be able to build this one alone.
-putty.chm: $(INPUTS)
-	$(HALIBUT) --chm $(INPUTS)
-
-# We don't ship this any more.
-putty.hlp: $(INPUTS)
-	$(HALIBUT) --winhelp $(INPUTS)
-
-putty.info: $(INPUTS)
-	$(HALIBUT) --info $(INPUTS)
-
-MKMAN = $(HALIBUT) --man=$@ mancfg.but $<
-MANPAGES = putty.1 puttygen.1 plink.1 pscp.1 psftp.1 puttytel.1 pterm.1 \
-           pageant.1 psocks.1 psusan.1
-man: $(MANPAGES)
-
-putty.1: man-putty.but mancfg.but; $(MKMAN)
-puttygen.1: man-puttygen.but mancfg.but; $(MKMAN)
-plink.1: man-plink.but mancfg.but; $(MKMAN)
-pscp.1: man-pscp.but mancfg.but; $(MKMAN)
-psftp.1: man-psftp.but mancfg.but; $(MKMAN)
-puttytel.1: man-puttytel.but mancfg.but; $(MKMAN)
-pterm.1: man-pterm.but mancfg.but; $(MKMAN)
-pageant.1: man-pageant.but mancfg.but; $(MKMAN)
-psocks.1: man-psocks.but mancfg.but; $(MKMAN)
-psusan.1: man-psusan.but mancfg.but; $(MKMAN)
-
-mostlyclean:
-	rm -f *.html *.txt *.hlp *.cnt *.1 *.info vstr.but *.hh[pck]
-clean: mostlyclean
-	rm -f *.chm

+ 0 - 1
source/putty/doc/blurb.but

@@ -17,7 +17,6 @@ page</a>.</p>}
 \cfg{chm-contents-filename}{index.html}
 \cfg{chm-template-filename}{%k.html}
 \cfg{chm-head-end}{<link rel="stylesheet" type="text/css" href="chm.css">}
-\cfg{chm-extra-file}{chm.css}
 
 \cfg{xhtml-contents-filename}{index.html}
 \cfg{text-filename}{puttydoc.txt}

+ 105 - 17
source/putty/doc/config.but

@@ -596,6 +596,33 @@ through to \c{ESC [j}.  With control they generate \c{ESC [k} through
 to \c{ESC [v}, and with shift and control together they generate
 \c{ESC [w} through to \c{ESC [\{}.
 
+\b In \I{xterm}Xterm 216 mode, the unshifted function keys behave the
+same as Xterm R6 mode. But pressing a function key together with Shift
+or Alt or Ctrl generates a different sequence containing an extra
+numeric parameter of the form (1 for Shift) + (2 for Alt) + (4 for
+Ctrl) + 1. For F1-F4, the basic sequences like \c{ESC OP} become
+\cw{ESC [1;}\e{bitmap}\cw{P} and similar; for F5 and above,
+\cw{ESC[}\e{index}\cw{~} becomes
+\cw{ESC[}\e{index}\cw{;}\e{bitmap}\cw{~}.
+
+If you don't know what any of this means, you probably don't need to
+fiddle with it.
+
+\S{config-sharrow} Changing the action of the \i{shifted arrow keys}
+
+This option affects the arrow keys, if you press one with any of the
+modifier keys Shift, Ctrl or Alt held down.
+
+\b In the default mode, labelled \c{Ctrl toggles app mode}, the Ctrl
+key toggles between the default arrow-key sequences like \c{ESC [A} and
+\c{ESC [B}, and the sequences Digital's terminals generate in
+\q{application cursor keys} mode, i.e. \c{ESC O A} and so on. Shift
+and Alt have no effect.
+
+\b In the \q{xterm-style bitmap} mode, Shift, Ctrl and Alt all
+generate different sequences, with a number indicating which set of
+modifiers is active.
+
 If you don't know what any of this means, you probably don't need to
 fiddle with it.
 
@@ -1342,7 +1369,7 @@ which one of the options is \q{Paste}). (This context menu is always
 available by holding down Ctrl and right-clicking, regardless of the
 setting of this option.)
 
-(When PuTTY iself is running on Unix, it follows the X Window System
+(When PuTTY itself is running on Unix, it follows the X Window System
 convention.)
 
 \S{config-mouseshift} \q{Shift overrides application's use of mouse}
@@ -1934,6 +1961,19 @@ and enter a command such as \c{connect myhost.com 22} to connect
 through to an external host. Selecting \I{Telnet proxy}\q{Telnet}
 allows you to tell PuTTY to use this type of proxy.
 
+\b Selecting \q{SSH} causes PuTTY to make a secondary SSH connection
+to the proxy host (sometimes called a \q{\i{jump host}} in this
+context), and then open a port-forwarding channel to the
+final destination host. 
+
+\lcont{
+The \q{Proxy hostname} field will be interpreted as the name of a
+PuTTY saved session if one exists, or a hostname if not. This
+allows multi-hop jump paths, if the referenced saved session is
+itself configured to use an SSH proxy; and it allows combining SSH
+and non-SSH proxying.
+}
+
 \b Selecting \I{Local proxy}\q{Local} allows you to specify an arbitrary
 command on the local machine to act as a proxy. When the session is
 started, instead of creating a TCP connection, PuTTY runs the command
@@ -1945,11 +1985,6 @@ This could be used, for instance, to talk to some kind of network proxy
 that PuTTY does not natively support; or you could tunnel a connection
 over something other than TCP/IP entirely.
 
-If you want your local proxy command to make a secondary SSH
-connection to a proxy host and then tunnel the primary connection
-over that, you might well want the \c{-nc} command-line option in
-Plink. See \k{using-cmdline-ncmode} for more information.
-
 You can also enable this mode on the command line; see
 \k{using-cmdline-proxycmd}.
 }
@@ -2007,9 +2042,9 @@ set it to \q{Yes}, PuTTY will always pass host names straight to the
 proxy without trying to look them up first.
 
 If you set this option to \q{Auto} (the default), PuTTY will do
-something it considers appropriate for each type of proxy. Telnet,
-HTTP, and SOCKS5 proxies will have host names passed straight to
-them; SOCKS4 proxies will not.
+something it considers appropriate for each type of proxy. Most
+types of proxy (HTTP, SOCK5, SSH, Telnet, and local) will have host
+names passed straight to them; SOCKS4 proxies will not.
 
 Note that if you are doing DNS at the proxy, you should make sure
 that your proxy exclusion settings (see \k{config-proxy-exclude}) do
@@ -2022,6 +2057,16 @@ is a protocol extension (SOCKS 4A) which does support it, but not
 all SOCKS 4 servers provide this extension. If you enable proxy DNS
 and your SOCKS 4 server cannot deal with it, this might be why.
 
+If you want to avoid PuTTY making \e{any} DNS query related to your
+destination host name (for example, because your local DNS resolver is
+very slow to return a negative response in that situation), then as
+well as setting this control to \q{Yes}, you may also need to turn off
+GSSAPI authentication and GSSAPI key exchange in SSH (see
+\k{config-ssh-auth-gssapi} and \k{config-ssh-gssapi-kex}
+respectively). This is because GSSAPI setup also involves a DNS query
+for the destination host name, and that query is performed by the
+separate GSSAPI library, so PuTTY can't override or reconfigure it.
+
 \S{config-proxy-auth} \I{proxy username}Username and \I{proxy password}password
 
 If your proxy requires \I{proxy authentication}authentication, you can
@@ -2042,18 +2087,35 @@ proxies and SOCKS 5 proxies.
 supports it (this is not supported in \i{PuTTYtel}); otherwise the
 password is sent to the proxy in \I{plaintext password}plain text.
 
-\b With HTTP proxying, the only currently supported authentication
-method is \I{HTTP basic}\q{basic}, where the password is sent to the proxy
-in \I{plaintext password}plain text.
+\b With HTTP proxying, authentication is via \q{\i{HTTP Digest}} if
+possible (again, not supported in PuTTYtel), or \q{\i{HTTP Basic}}. In
+the latter case, the password is sent to the proxy in \I{plaintext
+password}plain text.
 
 }
 
 \b SOCKS 4 can use the \q{Username} field, but does not support
 passwords.
 
+\b SSH proxying can use all the same forms of SSH authentication
+supported by PuTTY for its main connection. If the SSH server requests
+password authentication, the configured proxy password will be used,
+but other authentication methods such as public keys will be tried
+first, just as for a primary SSH connection.
+
 \b You can specify a way to include a username and password in the
 Telnet/Local proxy command (see \k{config-proxy-command}).
 
+If PuTTY discovers that it needs a proxy username or password and you
+have not specified one in the configuration, it will prompt for it
+interactively in the terminal.
+
+(For SSH proxying, this will also happen in the case of other
+interactive SSH login prompts, such as SSH key passphrases or GSSAPI.
+For the Telnet and Local proxy types, PuTTY will prompt for a username
+or password if you included \c{%user} or \c{%pass} in the command
+string and did not provide a corresponding configuration entry.)
+
 \S{config-proxy-command} Specifying the Telnet or Local proxy command
 
 If you are using the \i{Telnet proxy} type, the usual command required
@@ -2403,7 +2465,7 @@ protection than SSH-2 without rekeys.
 
 \H{config-ssh-hostkey} The Host Keys panel
 
-The Host Keys panel allows you to configure options related to SSH-2
+The Host Keys panel allows you to configure options related to
 \i{host key management}.
 
 Host keys are used to prove the server's identity, and assure you that
@@ -2411,8 +2473,8 @@ the server is not being spoofed (either by a man-in-the-middle attack
 or by completely replacing it on the network). See \k{gs-hostkey} for
 a basic introduction to host keys.
 
-This entire panel is only relevant to SSH protocol version 2; none of
-these settings affect SSH-1 at all.
+Much of this panel is only relevant to SSH protocol version 2; SSH-1
+only supports one type of host key.
 
 \S{config-ssh-hostkey-order} \ii{Host key type} selection
 
@@ -2452,7 +2514,7 @@ to that for cipher selection (see \k{config-ssh-encryption}).
 
 \S{config-ssh-prefer-known-hostkeys} Preferring known host keys
 
-By default, PuTTY will adjust the preference order for host key
+By default, PuTTY will adjust the preference order for SSH-2 host key
 algorithms so that any host keys it already knows are moved to the top
 of the list.
 
@@ -3206,7 +3268,10 @@ three states:
 \b \q{On}: PuTTY will assume the server \e{does} have the bug.
 
 \b \q{Auto}: PuTTY will use the server's version number announcement
-to try to guess whether or not the server has the bug.
+to try to guess whether or not the server has the bug. (This option is
+not available for bugs that \e{cannot} be detected from the server
+version, e.g. because they must be acted on before the server version
+is known.)
 
 \S{config-ssh-bug-ignore2} \q{Chokes on SSH-2 \i{ignore message}s}
 
@@ -3299,6 +3364,29 @@ send an over-sized packet.  If this bug is enabled when talking to a
 correct server, the session will work correctly, but download
 performance will be less than it could be.
 
+\S{config-ssh-bug-dropstart} \q{Discards data sent before its greeting}
+
+Just occasionally, an SSH connection can be established over some
+channel that will accidentally discard outgoing data very early in the
+connection.
+
+This is not typically seen as a bug in an actual SSH server, but it
+can sometimes occur in situations involving a complicated proxy
+process. An example is
+\W{https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=991958}{Debian
+bug #991958}, in which a connection going over the console of a User
+Mode Linux kernel can lose outgoing data before the kernel has fully
+booted.
+
+You can work around this problem by manually enabling this bug flag,
+which will cause PuTTY to wait to send its initial SSH greeting until
+after it sees the greeting from the server.
+
+Note that this bug flag can never be automatically detected, since
+auto-detection relies on the version string in the server's greeting,
+and PuTTY has to decide whether to expect this bug \e{before} it sees
+the server's greeting. So this is a manual workaround only.
+
 \S{config-ssh-bug-sig} \q{Requires padding on SSH-2 \i{RSA} \i{signatures}}
 
 Versions below 3.3 of \i{OpenSSH} require SSH-2 RSA signatures to be

+ 0 - 5
source/putty/doc/copy.but

@@ -1,5 +0,0 @@
-\# Generated by licence.pl from LICENCE.
-\# You should edit those files rather than editing this one.
-
-\define{shortcopyrightdetails} 1997-2021 Simon Tatham
-

+ 3 - 3
source/putty/doc/errors.but

@@ -10,8 +10,8 @@ self-explanatory. If you get an error message which is not listed in
 this chapter and which you don't understand, report it to us as a
 bug (see \k{feedback}) and we will add documentation for it.
 
-\H{errors-hostkey-absent} \q{The server's host key is not cached in
-the registry}
+\H{errors-hostkey-absent} \q{The host key is not cached for this
+server}
 
 This error message occurs when PuTTY connects to a new SSH server.
 Every server identifies itself by means of a host key; once PuTTY
@@ -35,7 +35,7 @@ See \k{gs-hostkey} for more information on host keys.
 \H{errors-hostkey-wrong} \q{WARNING - POTENTIAL SECURITY BREACH!}
 
 This message, followed by \q{The server's host key does not match
-the one PuTTY has cached in the registry}, means that PuTTY has
+the one PuTTY has cached for this server}, means that PuTTY has
 connected to the SSH server before, knows what its host key
 \e{should} be, but has found a different one.
 

+ 2 - 1
source/putty/doc/faq.but

@@ -325,7 +325,8 @@ unfinished.
 
 If any OS X and/or GTK programming experts are keen to have a finished
 version of this, we urge them to help out with some of the remaining
-problems! See the TODO list in \c{unix/gtkapp.c} in the source code.
+problems! See the TODO list in \c{unix/main-gtk-application.c} in the
+source code.
 
 \S{faq-epoc}{Question} Will there be a port to EPOC?
 

+ 3 - 2
source/putty/doc/gs.but

@@ -50,8 +50,9 @@ section.
 If you are using SSH to connect to a server for the first time, you
 will probably see a message looking something like this:
 
-\c The server's host key is not cached in the registry. You have no
-\c guarantee that the server is the computer you think it is.
+\c The host key is not cached for this server:
+\c  ssh.example.com (port 22)
+\c You have no guarantee that the server is the computer you think it is.
 \c The server's ssh-ed25519 key fingerprint is:
 \c  ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w
 \c If you trust this host, press "Accept" to add the key to PuTTY's

+ 9 - 2
source/putty/doc/index.but

@@ -245,6 +245,7 @@ saved sessions from
 \IM{-m} \c{-m} command-line option
 \IM{-P-upper} \c{-P} command-line option
 \IM{-pw} \c{-pw} command-line option
+\IM{-pwfile} \c{-pwfile} command-line option
 \IM{-A-upper} \c{-A} command-line option
 \IM{-a} \c{-a} command-line option
 \IM{-X-upper} \c{-X} command-line option
@@ -607,8 +608,11 @@ saved sessions from
 \IM{proxy authentication} proxy authentication
 \IM{proxy authentication} authentication, to proxy
 
-\IM{HTTP basic} HTTP \q{basic} authentication
-\IM{HTTP basic} \q{basic} authentication (HTTP)
+\IM{HTTP Basic} HTTP Basic authentication
+\IM{HTTP Basic} \q{basic} authentication (HTTP)
+
+\IM{HTTP Digest} HTTP Digest authentication
+\IM{HTTP Digest} \q{digest} authentication (HTTP)
 
 \IM{plaintext password} plain text password
 \IM{plaintext password} password, plain text
@@ -930,3 +934,6 @@ saved sessions from
 \IM{system tray} system tray, Windows
 \IM{system tray} notification area, Windows (aka system tray)
 \IM{system tray} taskbar notification area, Windows (aka system tray)
+
+\IM{shifted arrow keys} arrow keys, shifted
+\IM{shifted arrow keys} shifted arrow keys

+ 0 - 15
source/putty/doc/licence.but

@@ -1,15 +0,0 @@
-\# Generated by licence.pl from LICENCE.
-\# You should edit those files rather than editing this one.
-
-\A{licence} PuTTY \ii{Licence}
-
-PuTTY is \i{copyright} 1997-2021 Simon Tatham.
-
-Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \q{Software}), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED \q{AS IS}, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-

+ 4 - 4
source/putty/doc/man-pageant.but

@@ -41,7 +41,7 @@ extract their public half.
 
 The agent protocol used by \c{pageant} is compatible with the PuTTY
 tools and also with other implementations such as OpenSSH's SSH client
-and \e{ssh-agent(1)}. Some \c{pageant} features are implemented with
+and \cw{ssh-agent}(\e{1}). Some \c{pageant} features are implemented with
 protocol extensions, so will only work if \c{pageant} is on both ends.
 
 To run \c{pageant} as an agent, you must provide an option to tell it
@@ -317,15 +317,15 @@ by the SSH agent protocol.
 
 \dt \cw{--askpass} \e{prompt}
 
-\dd With this option, \c{pageant} acts as an \e{ssh-askpass(1)}
+\dd With this option, \c{pageant} acts as an \cw{ssh-askpass}(\e{1})
 replacement, rather than performing any SSH agent functionality. This
 may be useful if you prefer Pageant's GUI prompt style, which
 minimises information leakage about your passphrase length in its
-visual feedback, compared to other \e{ssh-askpass(1)} implementations.
+visual feedback, compared to other \cw{ssh-askpass}(\e{1}) implementations.
 
 \lcont{
 
-\c{pageant --askpass} implements the standard \e{ssh-askpass(1)}
+\c{pageant --askpass} implements the standard \cw{ssh-askpass}(\e{1})
 interface: it can be passed a prompt to display (as a single argument)
 and, if successful, prints the passphrase on standard output and
 returns a zero exit status. Typically you would use the environment

+ 9 - 4
source/putty/doc/man-plink.but

@@ -59,9 +59,9 @@ to aid in verifying new files released by the PuTTY team.
 \dt \cw{-ssh-connection}
 
 \dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is
-only likely to be useful when connecting to a \e{psusan(1)} server,
-most likely with an absolute path to a Unix-domain socket in place
-of \e{host}.
+only likely to be useful when connecting to a \cw{psusan}(\e{1})
+server, most likely with an absolute path to a Unix-domain socket in
+place of \e{host}.
 
 \dt \cw{\-proxycmd} \e{command}
 
@@ -114,11 +114,16 @@ sequences. These options override Plink's default behaviour to enable
 or disabling such filtering on the standard error and standard output
 channels.
 
+\dt \cw{-pwfile} \e{filename}
+
+\dd Open the specified file, and use the first line of text read from
+it as the remote password.
+
 \dt \cw{-pw} \e{password}
 
 \dd Set remote password to \e{password}. \e{CAUTION:} this will likely
 make the password visible to other users of the local machine (via
-commands such as \q{\c{w}}).
+commands such as \q{\c{ps}} or \q{\c{w}}). Use \cw{-pwfile} instead.
 
 \dt \cw{\-L} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport}
 

+ 9 - 4
source/putty/doc/man-pscp.but

@@ -101,11 +101,16 @@ channel from the server, to prevent remote processes sending confusing
 escape sequences. This option forces the standard error channel to not be
 filtered.
 
+\dt \cw{-pwfile} \e{filename}
+
+\dd Open the specified file, and use the first line of text read from
+it as the remote password.
+
 \dt \cw{-pw} \e{password}
 
 \dd Set remote password to \e{password}. \e{CAUTION:} this will likely
 make the password visible to other users of the local machine (via
-commands such as \q{\c{w}}).
+commands such as \q{\c{ps}} or \q{\c{w}}). Use \cw{-pwfile} instead.
 
 \dt \cw{-1}
 
@@ -118,9 +123,9 @@ commands such as \q{\c{w}}).
 \dt \cw{-ssh-connection}
 
 \dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is
-only likely to be useful when connecting to a \e{psusan(1)} server,
-most likely with an absolute path to a Unix-domain socket in place
-of \e{host}.
+only likely to be useful when connecting to a \cw{psusan}(\e{1})
+server, most likely with an absolute path to a Unix-domain socket in
+place of \e{host}.
 
 \dt \cw{-ssh}
 

+ 9 - 4
source/putty/doc/man-psftp.but

@@ -89,11 +89,16 @@ channel from the server, to prevent remote processes sending confusing
 escape sequences. This option forces the standard error channel to not be
 filtered.
 
+\dt \cw{-pwfile} \e{filename}
+
+\dd Open the specified file, and use the first line of text read from
+it as the remote password.
+
 \dt \cw{-pw} \e{password}
 
 \dd Set remote password to \e{password}. \e{CAUTION:} this will likely
 make the password visible to other users of the local machine (via
-commands such as \q{\c{w}}).
+commands such as \q{\c{ps}} or \q{\c{w}}). Use \cw{-pwfile} instead.
 
 \dt \cw{-1}
 
@@ -106,9 +111,9 @@ commands such as \q{\c{w}}).
 \dt \cw{-ssh-connection}
 
 \dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is
-only likely to be useful when connecting to a \e{psusan(1)} server,
-most likely with an absolute path to a Unix-domain socket in place
-of \e{host}.
+only likely to be useful when connecting to a \cw{psusan}(\e{1})
+server, most likely with an absolute path to a Unix-domain socket in
+place of \e{host}.
 
 \dt \cw{-ssh}
 

+ 4 - 4
source/putty/doc/man-psocks.but

@@ -18,8 +18,8 @@ IPv4 and IPv6 connections. It does not support requiring
 authentication of its clients.
 
 \cw{psocks} can be used together with an SSH client such as
-\cw{putty(1)} to implement a reverse dynamic SSH tunnel. It can also
-be used for network protocol debugging, as it can record all the
+\cw{putty}(\e{1}) to implement a reverse dynamic SSH tunnel. It can
+also be used for network protocol debugging, as it can record all the
 traffic passing through it in various ways.
 
 By default, \cw{psocks} listens to connections from localhost only,
@@ -84,8 +84,8 @@ have the connection's traffic piped into it, similar to \cw{-f}.
 
 \S{psocks-manpage-examples} EXAMPLES
 
-In combination with the \e{plink(1)} SSH client, to set up a reverse
-dynamic SSH tunnel, in which the remote listening port 1080 on
+In combination with the \cw{plink}(\e{1}) SSH client, to set up a
+reverse dynamic SSH tunnel, in which the remote listening port 1080 on
 remote host \cw{myhost} acts as a SOCKS server giving access to your
 local network:
 

+ 59 - 5
source/putty/doc/man-psusan.but

@@ -191,15 +191,36 @@ And the setup script \cw{uml-psusan.sh} might look like this:
 \c # Choose what shell you want to run inside psusan
 \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
 \c export SHELL=/bin/bash
+\c # Set up a default path
+\e iiiiiiiiiiiiiiiiiiiiiii
+\c export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
 \c # And now run psusan over the serial port
 \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
 \c exec /home/simon/src/putty/misc/psusan
 
-Now set up a PuTTY saved session as in the Docker example above, using
-that \cw{linux} command as the local proxy command, and you'll have a
-PuTTY session that starts up a clean UML instance when you run it, and
-(if you enabled connection sharing) further instances of the same
-session will connect to the same instance again.
+Now set up a PuTTY saved session as in the Docker example above.
+Basically you'll want to use the above \cw{linux} command as the local
+proxy command. However, it's worth wrapping it in \cw{setsid}(\e{1}),
+because when UML terminates, it kills its entire process group. So
+it's better that PuTTY should not be part of that group, and should
+have the opportunity to shut down cleanly by itself. So probably you
+end up setting the proxy command to be something more like:
+
+\c setsid linux mem=512M rootfstype=hostfs rootflags=/ rw \
+\c     con=fd:2,fd:2 ssl0=fd:0,fd:1 init=/some/path/to/uml-psusan.sh
+\e                                       iiiiiiiiiiiiiiiiiiiiiiiiiii
+
+You may also find that you have to enable the bug workaround that
+indicates that the server \q{Discards data sent before its greeting},
+because otherwise PuTTY's outgoing protocol greeting can be
+accidentally lost during UML startup. (See
+\W{https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=991958}{Debian
+bug #991958}.)
+
+Once you've done that, you'll have a PuTTY session that starts up a
+clean UML instance when you run it, and (if you enabled connection
+sharing) further instances of the same session will connect to the
+same instance again.
 
 \S2{psusan-manpage-examples-wsl} Windows Subsystem for Linux
 
@@ -231,6 +252,39 @@ ports in and out of the WSL environment (e.g. expose a WSL2 network
 service through the hypervisor's internal NAT), forward Pageant into
 it, and so on.
 
+\S2{psusan-manpage-examples-cygwin} Cygwin
+
+Another Unix-like environment on Windows is Cygwin. That comes with
+its own GUI terminal application, \cw{mintty} (as it happens, a
+derivative of PuTTY); but if you'd prefer to use PuTTY itself to talk
+to your Cygwin terminal sessions, \cw{psusan} can help.
+
+To do this, you'll first need to build the Unix PuTTY tools inside
+Cygwin (via the usual \cw{cmake} method). Then, copy the resulting
+\cw{psusan.exe} into Cygwin's \cw{/bin} directory. (It has to be
+in that directory for non-Cygwin programs to run it; otherwise it
+won't be able to find the Cygwin DLL at startup.)
+
+Then set up your PuTTY saved session like this:
+
+\b set the local proxy command to run \cw{psusan.exe} via its real
+Windows path. You might also want to add the \cw{--sessiondir} option
+so that shell sessions start up in your Cygwin home directory. For
+example, you might use the command \cq{c:\\cygwin64\\bin\\psusan.exe
+--sessiondir /home/simon} (changing the pathname and username to match
+your setup).
+
+\b enter anything you like in the host name box; \cq{Cygwin} is
+probably a good choice
+
+\b set the protocol to \q{Bare ssh-connection}, as usual.
+
+Port forwarding is probably not particularly useful in this case,
+since Cygwin shares the same network port space as the host machine.
+But turning on agent forwarding is useful, because then the Cygwin
+command-line SSH client can talk to Pageant without any further
+configuration.
+
 \S2{psusan-manpage-examples-schroot} \cw{schroot}
 
 Another example of a container-like environment is the alternative

+ 1 - 1
source/putty/doc/man-pterm.but

@@ -76,7 +76,7 @@ will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2.
 \dt \cw{\-geometry} \e{geometry}
 
 \dd Specify the size of the terminal, in rows and columns of text. See
-\e{X(7)} for more information on the syntax of geometry
+\cw{X}(\e{7}) for more information on the syntax of geometry
 specifications.
 
 \dt \cw{\-sl} \e{lines}

+ 1 - 1
source/putty/doc/man-putty.but

@@ -55,7 +55,7 @@ will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2.
 \dt \cw{\-geometry} \e{geometry}
 
 \dd Specify the size of the terminal, in rows and columns of text.
-See \e{X(7)} for more information on the syntax of geometry
+See \cw{X}(\e{7}) for more information on the syntax of geometry
 specifications.
 
 \dt \cw{\-sl} \e{lines}

+ 1 - 1
source/putty/doc/man-puttytel.but

@@ -56,7 +56,7 @@ will be ignored unless the \cw{BoldAsColour} resource is set to 0 or 2.
 \dt \cw{\-geometry} \e{geometry}
 
 \dd Specify the size of the terminal, in rows and columns of text. See
-\e{X(7)} for more information on the syntax of geometry
+\cw{X}(\e{7}) for more information on the syntax of geometry
 specifications.
 
 \dt \cw{\-sl} \e{lines}

+ 86 - 0
source/putty/doc/pageant.but

@@ -170,6 +170,92 @@ by the command, like this:
 
 \c C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe
 
+\S{pageant-cmdline-openssh} Integrating with Windows OpenSSH
+
+Windows's own port of OpenSSH uses the same mechanism as Pageant to
+talk to its SSH agent (Windows named pipes). This means that Windows
+OpenSSH can talk directly to Pageant, if it knows where to find
+Pageant's named pipe.
+
+When Pageant starts up, it can optionally write out a file containing
+an OpenSSH configuration directive that tells the Windows \c{ssh.exe}
+where to find Pageant. If you include this file from your Windows SSH
+configuration, then \c{ssh.exe} should automatically use Pageant as
+its agent, so that you can keep your keys in one place and have both
+SSH clients able to use them.
+
+The option is \c{--openssh-config}, and you follow it with a filename.
+
+To refer to this file from your main OpenSSH configuration, you can
+use the \cq{Include} directive. For example, you might run Pageant
+like this (with your own username substituted, of course):
+
+\c pageant --openssh-config C:\Users\Simon\.ssh\pageant.conf
+
+and then add a directive like this to your main \cq{.ssh\\config} file
+(assuming that lives in the same directory that you just put
+\cw{pageant.conf}):
+
+\c Include pageant.conf
+
+\s{Note}: this technique only works with \e{Windows's} port of
+OpenSSH, which lives at \cw{C:\\Windows\\System32\\OpenSSH\\ssh.exe}
+if you have it installed. (If not, it can be installed as a Windows
+optional feature, e.g., via Settings > Apps & features > Optional
+features > Add a feature > OpenSSH Client.)
+
+There are other versions of OpenSSH for Windows, notably the one that
+comes with Windows \cw{git}. Those will likely not work with the same
+configuration, because they tend to depend on Unix emulation layers
+like MinGW or MSys, so they won't speak Windows native pathname syntax
+or understand named pipes. The above instructions will only work with
+Windows's own version of OpenSSH.
+
+So, if you want to use Windows \cw{git} with an SSH key held in
+Pageant, you'll have to set the environment variable \cw{GIT_SSH}, to
+point at a different program. You could point it at
+\cw{c:\\Windows\\System32\\OpenSSH\\ssh.exe} once you've done this
+setup \dash but it's just as easy to point it at Plink!
+
+\S{pageant-cmdline-unix} Unix-domain sockets: integrating with WSL 1
+
+Pageant can listen on the WinSock implementation of \q{Unix-domain
+sockets}. These interoperate with the Unix-domain sockets found in the
+original Windows Subsystem for Linux (now known as WSL 1). So if you
+ask Pageant to listen on one of these, then your WSL 1 processes can
+talk directly to Pageant.
+
+To configure this, run Pageant with the option \c{--unix}, followed
+with a pathname. Then, in WSL 1, set the environment variable
+\cw{SSH_AUTH_SOCK} to point at the WSL translation of that pathname.
+
+For example, you might run
+
+\c pageant --unix C:\Users\Simon\.ssh\agent.sock
+
+and in WSL 1, set the environment variable
+
+\c SSH_AUTH_SOCK=/mnt/c/Users/Simon/.ssh/agent.sock
+
+Alternatively, you can add a line to your \cw{.ssh/config} file inside
+WSL that says
+
+\c IdentityAgent /mnt/c/Users/Simon/.ssh/agent.sock
+
+although doing it like that may mean that \cw{ssh-add} commands won't
+find the agent, even though \cw{ssh} itself will.
+
+\s{Security note}: Unix-domain sockets are protected against access by
+other users by the file protections on their containing directory. So
+if your Windows machine is multiuser, make sure you create the socket
+inside a directory that other users can't access at all. (In fact,
+that's a good idea on general principles.)
+
+\s{Compatibility note}: WSL 2 processes cannot talk to Pageant by this
+mechanism, because WSL 2's Unix-domain sockets are managed by a
+separate Linux kernel, and not by the same kernel that WinSock talks
+to.
+
 \S{pageant-cmdline-keylist} Starting with the key list visible
 
 Start Pageant with the \i\c{--keylist} option to show the main window

+ 34 - 12
source/putty/doc/pgpkeys.but

@@ -56,25 +56,25 @@ The current issue of those keys are available for download from the
 PuTTY website, and are also available on PGP keyservers using the key
 IDs listed below.
 
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2018.asc}{\s{Master Key} (2018)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2021.asc}{\s{Master Key} (2021)}
 
-\dd RSA, 4096-bit. Key ID: \cw{76BC7FE4EBFD2D9E}. Fingerprint:
-\cw{24E1\_B1C5\_75EA\_3C9F\_F752\_\_A922\_76BC\_7FE4\_EBFD\_2D9E}
+\dd RSA, 3072-bit. Key ID: \cw{DD4355EAAC1119DE}. Fingerprint:
+\cw{A872\_D42F\_1660\_890F\_0E05\_223E\_DD43\_55EA\_AC11\_19DE}
 
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2018.asc}{\s{Release Key} (2018)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2021.asc}{\s{Release Key} (2021)}
 
-\dd RSA, 3072-bit. Key ID: \cw{6289A25F4AE8DA82}. Fingerprint:
-\cw{E273\_94AC\_A3F9\_D904\_9522\_\_E054\_6289\_A25F\_4AE8\_DA82}
+\dd RSA, 3072-bit. Key ID: \cw{E4F83EA2AA4915EC}. Fingerprint:
+\cw{2CF6\_134B\_D3F7\_7A65\_88EB\_D668\_E4F8\_3EA2\_AA49\_15EC}
 
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2018.asc}{\s{Snapshot Key} (2018)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2021.asc}{\s{Snapshot Key} (2021)}
 
-\dd RSA, 3072-bit. Key ID: \cw{38BA7229B7588FD1}. Fingerprint:
-\cw{C92B\_52E9\_9AB6\_1DDA\_33DB\_\_2B7A\_38BA\_7229\_B758\_8FD1}
+\dd RSA, 3072-bit. Key ID: \cw{B43979F89F446CFD}. Fingerprint:
+\cw{1FD3\_BCAC\_E532\_FBE0\_6A8C\_09E2\_B439\_79F8\_9F44\_6CFD}
 
-\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2018.asc}{\s{Secure Contact Key} (2018)}
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2021.asc}{\s{Secure Contact Key} (2021)}
 
-\dd RSA, 3072-bit. Key ID: \cw{657D487977F95C98}. Fingerprint:
-\cw{A680\_0082\_2998\_6E46\_22CA\_\_0E43\_657D\_4879\_77F9\_5C98}
+\dd RSA, 3072-bit. Key ID: \cw{012C59D4211BD62A}. Fingerprint:
+\cw{E30F\_1354\_2A04\_BE0E\_56F0\_5801\_012C\_59D4\_211B\_D62A}
 
 \H{pgpkeys-security} Security details
 
@@ -169,6 +169,28 @@ generated keys.
 
 The details of all previous keys are given here.
 
+\s{Keys generated in the 2018 rollover}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2018.asc}{\s{Master Key} (2018)}
+
+\dd RSA, 4096-bit. Key ID: \cw{76BC7FE4EBFD2D9E}. Fingerprint:
+\cw{24E1\_B1C5\_75EA\_3C9F\_F752\_\_A922\_76BC\_7FE4\_EBFD\_2D9E}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2018.asc}{\s{Release Key} (2018)}
+
+\dd RSA, 3072-bit. Key ID: \cw{6289A25F4AE8DA82}. Fingerprint:
+\cw{E273\_94AC\_A3F9\_D904\_9522\_\_E054\_6289\_A25F\_4AE8\_DA82}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2018.asc}{\s{Snapshot Key} (2018)}
+
+\dd RSA, 3072-bit. Key ID: \cw{38BA7229B7588FD1}. Fingerprint:
+\cw{C92B\_52E9\_9AB6\_1DDA\_33DB\_\_2B7A\_38BA\_7229\_B758\_8FD1}
+
+\dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2018.asc}{\s{Secure Contact Key} (2018)}
+
+\dd RSA, 3072-bit. Key ID: \cw{657D487977F95C98}. Fingerprint:
+\cw{A680\_0082\_2998\_6E46\_22CA\_\_0E43\_657D\_4879\_77F9\_5C98}
+
 \s{Key generated in 2016} (when we first introduced the Secure Contact Key)
 
 \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2016.asc}{\s{Secure Contact Key} (2016)}

+ 1 - 1
source/putty/doc/plink.but

@@ -61,7 +61,7 @@ use Plink:
 \c   -sercfg configuration-string (e.g. 19200,8,n,1,X)
 \c             Specify the serial configuration (serial only)
 \c The following options only apply to SSH connections:
-\c   -pw passw login with specified password
+\c   -pwfile file   login with password read from specified file
 \c   -D [listen-IP:]listen-port
 \c             Dynamic SOCKS-based port forwarding
 \c   -L [listen-IP:]listen-port:host:port

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff