浏览代码

Merge branch 'thirdparty'

# Conflicts:

#	source/putty/misc.h
#	source/putty/network.h

#	source/putty/putty.h

#	source/putty/sshpubk.c

#	source/putty/storage.h

#	source/putty/windows/gss.c
#	source/putty/windows/handle-io.c
#	source/putty/windows/handle-socket.c
#	source/putty/windows/network.c
#	source/putty/windows/platform.h
#	source/putty/windows/storage.c

Source commit: 135140e906397a3426f77c0b3d2cf54f7a3ffed8
Martin Prikryl 3 年之前
父节点
当前提交
b21a098cc1
共有 100 个文件被更改,包括 1528 次插入46546 次删除
  1. 0 252
      source/putty/agentf.c
  2. 118 0
      source/putty/be_list.c
  3. 0 162
      source/putty/be_misc.c
  4. 0 18
      source/putty/be_ssh.c
  5. 0 593
      source/putty/conf.c
  6. 0 179
      source/putty/cproxy.c
  7. 44 25
      source/putty/defs.h
  8. 0 1206
      source/putty/ecc.c
  9. 0 243
      source/putty/ecc.h
  10. 11 12
      source/putty/import.c
  11. 5 0
      source/putty/logging.c
  12. 0 543
      source/putty/mainchan.c
  13. 0 320
      source/putty/marshal.c
  14. 21 2
      source/putty/marshal.h
  15. 0 146
      source/putty/memory.c
  16. 0 434
      source/putty/misc.c
  17. 30 12
      source/putty/misc.h
  18. 0 28
      source/putty/miscucs.c
  19. 0 2833
      source/putty/mpint.c
  20. 8 2
      source/putty/mpint.h
  21. 0 324
      source/putty/mpint_i.h
  22. 151 33
      source/putty/network.h
  23. 0 25
      source/putty/noshare.c
  24. 0 44
      source/putty/nullplug.c
  25. 0 105
      source/putty/pgssapi.c
  26. 0 333
      source/putty/pgssapi.h
  27. 0 1217
      source/putty/portfwd.c
  28. 0 1532
      source/putty/proxy.c
  29. 0 115
      source/putty/proxy.h
  30. 522 125
      source/putty/putty.h
  31. 7 6
      source/putty/puttymem.h
  32. 0 18
      source/putty/puttyps.h
  33. 10 5
      source/putty/settings.c
  34. 0 1325
      source/putty/ssh.c
  35. 61 33
      source/putty/ssh.h
  36. 0 205
      source/putty/ssh2bpp-bare.c
  37. 0 1009
      source/putty/ssh2bpp.c
  38. 0 107
      source/putty/ssh2censor.c
  39. 0 511
      source/putty/ssh2connection-client.c
  40. 0 1794
      source/putty/ssh2connection.c
  41. 0 236
      source/putty/ssh2connection.h
  42. 0 944
      source/putty/ssh2kex-client.c
  43. 0 126
      source/putty/ssh2transhk.c
  44. 0 2260
      source/putty/ssh2transport.c
  45. 0 246
      source/putty/ssh2transport.h
  46. 0 2035
      source/putty/ssh2userauth.c
  47. 0 2009
      source/putty/sshaes.c
  48. 0 147
      source/putty/ssharcf.c
  49. 0 623
      source/putty/sshargon2.c
  50. 0 170
      source/putty/sshauxcrypt.c
  51. 0 119
      source/putty/sshbcrypt.c
  52. 0 242
      source/putty/sshblake2.c
  53. 0 708
      source/putty/sshblowf.c
  54. 0 14
      source/putty/sshblowf.h
  55. 0 186
      source/putty/sshbpp.h
  56. 0 1066
      source/putty/sshccp.c
  57. 0 316
      source/putty/sshchan.h
  58. 0 982
      source/putty/sshcommon.c
  59. 0 1
      source/putty/sshcr.h
  60. 0 1092
      source/putty/sshdes.c
  61. 0 274
      source/putty/sshdh.c
  62. 0 525
      source/putty/sshdss.c
  63. 0 1949
      source/putty/sshecc.c
  64. 0 217
      source/putty/sshgss.h
  65. 0 297
      source/putty/sshgssc.c
  66. 0 24
      source/putty/sshgssc.h
  67. 0 272
      source/putty/sshhmac.c
  68. 0 43
      source/putty/sshmac.c
  69. 0 256
      source/putty/sshmd5.c
  70. 0 179
      source/putty/sshppl.h
  71. 0 307
      source/putty/sshprng.c
  72. 23 23
      source/putty/sshpubk.c
  73. 0 1147
      source/putty/sshrsa.c
  74. 0 995
      source/putty/sshsh256.c
  75. 0 853
      source/putty/sshsh512.c
  76. 0 954
      source/putty/sshsha.c
  77. 0 357
      source/putty/sshsha3.c
  78. 0 2187
      source/putty/sshshare.c
  79. 0 53
      source/putty/sshsignals.h
  80. 0 179
      source/putty/sshttymodes.h
  81. 0 128
      source/putty/sshutils.c
  82. 0 629
      source/putty/sshverstring.c
  83. 0 1254
      source/putty/sshzlib.c
  84. 2 2
      source/putty/storage.h
  85. 0 488
      source/putty/stripctrl.c
  86. 0 1611
      source/putty/tree234.c
  87. 0 1139
      source/putty/utils.c
  88. 4 5
      source/putty/version.h
  89. 0 486
      source/putty/wildcard.c
  90. 2 57
      source/putty/windows/agent-client.c
  91. 1 5
      source/putty/windows/cryptoapi.h
  92. 4 4
      source/putty/windows/gss.c
  93. 103 155
      source/putty/windows/handle-io.c
  94. 194 32
      source/putty/windows/handle-socket.c
  95. 143 0
      source/putty/windows/handle-wait.c
  96. 32 39
      source/putty/windows/local-proxy.c
  97. 9 12
      source/putty/windows/named-pipe-client.c
  98. 21 9
      source/putty/windows/network.c
  99. 2 2
      source/putty/windows/no-jump-list.c
  100. 0 0
      source/putty/windows/noise.c

+ 0 - 252
source/putty/agentf.c

@@ -1,252 +0,0 @@
-/*
- * SSH agent forwarding.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "pageant.h"
-#include "sshchan.h"
-
-typedef struct agentf {
-    Plug *plug; // WINSCP
-    SshChannel *c;
-    bufchain inbuffer;
-    agent_pending_query *pending;
-    bool input_wanted;
-    bool rcvd_eof;
-
-    Channel chan;
-} agentf;
-
-static void agentf_got_response(agentf *af, void *reply, int replylen)
-{
-    af->pending = NULL;
-
-    if (!reply) {
-        /* The real agent didn't send any kind of reply at all for
-         * some reason, so fake an SSH_AGENT_FAILURE. */
-        reply = "\0\0\0\1\5";
-        replylen = 5;
-    }
-
-    sshfwd_write(af->c, reply, replylen);
-}
-
-static void agentf_callback(void *vctx, void *reply, int replylen);
-
-static void agentf_try_forward(agentf *af)
-{
-    size_t datalen, length;
-    strbuf *message;
-    unsigned char msglen[4];
-    void *reply;
-    int replylen;
-
-    /*
-     * Don't try to parallelise agent requests. Wait for each one to
-     * return before attempting the next.
-     */
-    if (af->pending)
-        return;
-
-    /*
-     * If the outgoing side of the channel connection is currently
-     * throttled, don't submit any new forwarded requests to the real
-     * agent. This causes the input side of the agent forwarding not
-     * to be emptied, exerting the required back-pressure on the
-     * remote client, and encouraging it to read our responses before
-     * sending too many more requests.
-     */
-    if (!af->input_wanted)
-        return;
-
-    while (1) {
-        /*
-         * Try to extract a complete message from the input buffer.
-         */
-        datalen = bufchain_size(&af->inbuffer);
-        if (datalen < 4)
-            break;         /* not even a length field available yet */
-
-        bufchain_fetch(&af->inbuffer, msglen, 4);
-        length = GET_32BIT_MSB_FIRST(msglen);
-
-        if (length > AGENT_MAX_MSGLEN-4) {
-            /*
-             * If the remote has sent a message that's just _too_
-             * long, we should reject it in advance of seeing the rest
-             * of the incoming message, and also close the connection
-             * for good measure (which avoids us having to faff about
-             * with carefully ignoring just the right number of bytes
-             * from the overlong message).
-             */
-            agentf_got_response(af, NULL, 0);
-            sshfwd_write_eof(af->c);
-            return;
-        }
-
-        if (length > datalen - 4)
-            break;          /* a whole message is not yet available */
-
-        bufchain_consume(&af->inbuffer, 4);
-
-        message = strbuf_new_for_agent_query();
-        bufchain_fetch_consume(
-            &af->inbuffer, strbuf_append(message, length), length);
-        af->pending = agent_query(
-            message, &reply, &replylen, agentf_callback, af, get_callback_set(af->plug)); // WINSCP
-        strbuf_free(message);
-
-        if (af->pending)
-            return;   /* agent_query promised to reply in due course */
-
-        /*
-         * If the agent gave us an answer immediately, pass it
-         * straight on and go round this loop again.
-         */
-        agentf_got_response(af, reply, replylen);
-        sfree(reply);
-    }
-
-    /*
-     * If we get here (i.e. we left the above while loop via 'break'
-     * rather than 'return'), that means we've determined that the
-     * input buffer for the agent forwarding connection doesn't
-     * contain a complete request.
-     *
-     * So if there's potentially more data to come, we can return now,
-     * and wait for the remote client to send it. But if the remote
-     * has sent EOF, it would be a mistake to do that, because we'd be
-     * waiting a long time. So this is the moment to check for EOF,
-     * and respond appropriately.
-     */
-    if (af->rcvd_eof)
-        sshfwd_write_eof(af->c);
-}
-
-static void agentf_callback(void *vctx, void *reply, int replylen)
-{
-    agentf *af = (agentf *)vctx;
-
-    agentf_got_response(af, reply, replylen);
-    sfree(reply);
-
-    /*
-     * Now try to extract and send further messages from the channel's
-     * input-side buffer.
-     */
-    agentf_try_forward(af);
-}
-
-static void agentf_free(Channel *chan);
-static size_t agentf_send(Channel *chan, bool is_stderr, const void *, size_t);
-static void agentf_send_eof(Channel *chan);
-static char *agentf_log_close_msg(Channel *chan);
-static void agentf_set_input_wanted(Channel *chan, bool wanted);
-
-static const ChannelVtable agentf_channelvt = {
-    // WINSCP
-    /*.free =*/ agentf_free,
-    /*.open_confirmation =*/ chan_remotely_opened_confirmation,
-    /*.open_failed =*/ chan_remotely_opened_failure,
-    /*.send =*/ agentf_send,
-    /*.send_eof =*/ agentf_send_eof,
-    /*.set_input_wanted =*/ agentf_set_input_wanted,
-    /*.log_close_msg =*/ agentf_log_close_msg,
-    /*.want_close =*/ chan_default_want_close,
-    /*.rcvd_exit_status =*/ chan_no_exit_status,
-    /*.rcvd_exit_signal =*/ chan_no_exit_signal,
-    /*.rcvd_exit_signal_numeric =*/ chan_no_exit_signal_numeric,
-    /*.run_shell =*/ chan_no_run_shell,
-    /*.run_command =*/ chan_no_run_command,
-    /*.run_subsystem =*/ chan_no_run_subsystem,
-    /*.enable_x11_forwarding =*/ chan_no_enable_x11_forwarding,
-    /*.enable_agent_forwarding =*/ chan_no_enable_agent_forwarding,
-    /*.allocate_pty =*/ chan_no_allocate_pty,
-    /*.set_env =*/ chan_no_set_env,
-    /*.send_break =*/ chan_no_send_break,
-    /*.send_signal =*/ chan_no_send_signal,
-    /*.change_window_size =*/ chan_no_change_window_size,
-    /*.request_response =*/ chan_no_request_response,
-};
-
-Channel *agentf_new(SshChannel *c, Plug *plug) // WINSCP
-{
-    agentf *af = snew(agentf);
-    af->plug = plug; // WINSCP
-    af->c = c;
-    af->chan.vt = &agentf_channelvt;
-    af->chan.initial_fixed_window_size = 0;
-    af->rcvd_eof = false;
-    bufchain_init(&af->inbuffer);
-    af->pending = NULL;
-    af->input_wanted = true;
-    return &af->chan;
-}
-
-static void agentf_free(Channel *chan)
-{
-    pinitassert(chan->vt == &agentf_channelvt);
-    agentf *af = container_of(chan, agentf, chan);
-
-    if (af->pending)
-        agent_cancel_query(af->pending);
-    bufchain_clear(&af->inbuffer);
-    sfree(af);
-}
-
-static size_t agentf_send(Channel *chan, bool is_stderr,
-                          const void *data, size_t length)
-{
-    pinitassert(chan->vt == &agentf_channelvt);
-    agentf *af = container_of(chan, agentf, chan);
-    bufchain_add(&af->inbuffer, data, length);
-    agentf_try_forward(af);
-
-    /*
-     * We exert back-pressure on an agent forwarding client if and
-     * only if we're waiting for the response to an asynchronous agent
-     * request. This prevents the client running out of window while
-     * receiving the _first_ message, but means that if any message
-     * takes time to process, the client will be discouraged from
-     * sending an endless stream of further ones after it.
-     */
-    return (af->pending ? bufchain_size(&af->inbuffer) : 0);
-}
-
-static void agentf_send_eof(Channel *chan)
-{
-    pinitassert(chan->vt == &agentf_channelvt);
-    agentf *af = container_of(chan, agentf, chan);
-
-    af->rcvd_eof = true;
-
-    /* Call try_forward, which will respond to the EOF now if
-     * appropriate, or wait until the queue of outstanding requests is
-     * dealt with if not. */
-    agentf_try_forward(af);
-}
-
-static char *agentf_log_close_msg(Channel *chan)
-{
-    return dupstr("Agent-forwarding connection closed");
-}
-
-static void agentf_set_input_wanted(Channel *chan, bool wanted)
-{
-    pinitassert(chan->vt == &agentf_channelvt);
-    agentf *af = container_of(chan, agentf, chan);
-
-    af->input_wanted = wanted;
-
-    /* Agent forwarding channels are buffer-managed by not asking the
-     * agent questions if the SSH channel isn't accepting input. So if
-     * it's started again, we should ask a question if we have one
-     * pending.. */
-    if (wanted)
-        agentf_try_forward(af);
-}

+ 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 - 162
source/putty/be_misc.c

@@ -1,162 +0,0 @@
-/*
- * be_misc.c: helper functions shared between main network backends.
- */
-
-#include <assert.h>
-#include <string.h>
-
-#include "putty.h"
-#include "network.h"
-
-void backend_socket_log(Seat *seat, LogContext *logctx,
-                        PlugLogType type, SockAddr *addr, int port,
-                        const char *error_msg, int error_code, Conf *conf,
-                        bool session_started)
-{
-    char addrbuf[256], *msg;
-
-    switch (type) {
-      case PLUGLOG_CONNECT_TRYING:
-        sk_getaddr(addr, addrbuf, lenof(addrbuf));
-        if (sk_addr_needs_port(addr)) {
-            msg = dupprintf("Connecting to %s port %d", addrbuf, port);
-        } else {
-            msg = dupprintf("Connecting to %s", addrbuf);
-        }
-        break;
-      case PLUGLOG_CONNECT_FAILED:
-        sk_getaddr(addr, addrbuf, lenof(addrbuf));
-        msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);
-        break;
-      case PLUGLOG_CONNECT_SUCCESS:
-        sk_getaddr(addr, addrbuf, lenof(addrbuf));
-        msg = dupprintf("Connected to %s", addrbuf);
-        break;
-      case PLUGLOG_PROXY_MSG: {
-        /* Proxy-related log messages have their own identifying
-         * prefix already, put on by our caller. */
-        int len, log_to_term;
-
-        /* Suffix \r\n temporarily, so we can log to the terminal. */
-        msg = dupprintf("%s\r\n", error_msg);
-        len = strlen(msg);
-        assert(len >= 2);
-
-        log_to_term = conf_get_int(conf, CONF_proxy_log_to_term);
-        if (log_to_term == AUTO)
-            log_to_term = session_started ? FORCE_OFF : FORCE_ON;
-        if (log_to_term == FORCE_ON)
-            seat_stderr(seat, msg, len);
-
-        msg[len-2] = '\0';         /* remove the \r\n again */
-        break;
-      }
-      default:
-        msg = NULL;  /* shouldn't happen, but placate optimiser */
-        break;
-    }
-
-    if (msg) {
-        logevent(logctx, msg);
-        sfree(msg);
-    }
-}
-
-void psb_init(ProxyStderrBuf *psb)
-{
-    psb->size = 0;
-}
-
-void log_proxy_stderr(Plug *plug, ProxyStderrBuf *psb,
-                      const void *vdata, size_t len)
-{
-    const char *data = (const char *)vdata;
-
-    /*
-     * This helper function allows us to collect the data written to a
-     * local proxy command's standard error in whatever size chunks we
-     * happen to get from its pipe, and whenever we have a complete
-     * line, we pass it to plug_log.
-     *
-     * (We also do this when the buffer in psb fills up, to avoid just
-     * allocating more and more memory forever, and also to keep Event
-     * Log lines reasonably bounded in size.)
-     *
-     * Prerequisites: a plug to log to, and a ProxyStderrBuf stored
-     * somewhere to collect any not-yet-output partial line.
-     */
-
-    while (len > 0) {
-        /*
-         * Copy as much data into psb->buf as will fit.
-         */
-        pinitassert(psb->size < lenof(psb->buf));
-        size_t to_consume = lenof(psb->buf) - psb->size;
-        if (to_consume > len)
-            to_consume = len;
-        memcpy(psb->buf + psb->size, data, to_consume);
-        data += to_consume;
-        len -= to_consume;
-        psb->size += to_consume;
-
-        /*
-         * Output any full lines in psb->buf.
-         */
-        { // WINSCP
-        size_t pos = 0;
-        while (pos < psb->size) {
-            char *nlpos = memchr(psb->buf + pos, '\n', psb->size - pos);
-            if (!nlpos)
-                break;
-
-            /*
-             * Found a newline in the buffer, so we can output a line.
-             */
-            { // WINSCP
-            size_t endpos = nlpos - psb->buf;
-            while (endpos > pos && (psb->buf[endpos-1] == '\n' ||
-                                    psb->buf[endpos-1] == '\r'))
-                endpos--;
-            { // WINSCP
-            char *msg = dupprintf(
-                "proxy: %.*s", (int)(endpos - pos), psb->buf + pos);
-            plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0);
-            sfree(msg);
-
-            pos = nlpos - psb->buf + 1;
-            assert(pos <= psb->size);
-            } // WINSCP
-            } // WINSCP
-        }
-
-        /*
-         * If the buffer is completely full and we didn't output
-         * anything, then output the whole thing, flagging it as a
-         * truncated line.
-         */
-        if (pos == 0 && psb->size == lenof(psb->buf)) {
-            char *msg = dupprintf(
-                "proxy (partial line): %.*s", (int)psb->size, psb->buf);
-            plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0);
-            sfree(msg);
-
-            pos = psb->size = 0;
-        }
-
-        /*
-         * Now move any remaining data up to the front of the buffer.
-         */
-        { // WINSCP
-        size_t newsize = psb->size - pos;
-        if (newsize)
-            memmove(psb->buf, psb->buf + pos, newsize);
-        psb->size = newsize;
-
-        /*
-         * And loop round again if there's more data to be read from
-         * our input.
-         */
-        } // WINSCP
-        } // WINSCP
-    }
-}

+ 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 - 593
source/putty/conf.c

@@ -1,593 +0,0 @@
-/*
- * conf.c: implementation of the internal storage format used for
- * the configuration of a PuTTY session.
- */
-
-#include <stdio.h>
-#include <stddef.h>
-#include <assert.h>
-
-#include "tree234.h"
-#include "putty.h"
-
-/*
- * Enumeration of types used in keys and values.
- */
-typedef enum {
-    TYPE_NONE, TYPE_BOOL, TYPE_INT, TYPE_STR, TYPE_FILENAME, TYPE_FONT
-} Type;
-
-/*
- * Arrays which allow us to look up the subkey and value types for a
- * given primary key id.
- */
-#define CONF_SUBKEYTYPE_DEF(valtype, keytype, keyword) TYPE_ ## keytype,
-static int subkeytypes[] = { CONFIG_OPTIONS(CONF_SUBKEYTYPE_DEF) };
-#define CONF_VALUETYPE_DEF(valtype, keytype, keyword) TYPE_ ## valtype,
-static int valuetypes[] = { CONFIG_OPTIONS(CONF_VALUETYPE_DEF) };
-
-/*
- * Configuration keys are primarily integers (big enum of all the
- * different configurable options); some keys have string-designated
- * subkeys, such as the list of environment variables (subkeys
- * defined by the variable names); some have integer-designated
- * subkeys (wordness, colours, preference lists).
- */
-struct key {
-    int primary;
-    union {
-        int i;
-        char *s;
-    } secondary;
-};
-
-/* Variant form of struct key which doesn't contain dynamic data, used
- * for lookups. */
-struct constkey {
-    int primary;
-    union {
-        int i;
-        const char *s;
-    } secondary;
-};
-
-struct value {
-    union {
-        bool boolval;
-        int intval;
-        char *stringval;
-        Filename *fileval;
-        FontSpec *fontval;
-    } u;
-};
-
-struct conf_entry {
-    struct key key;
-    struct value value;
-};
-
-struct conf_tag {
-    tree234 *tree;
-};
-
-/*
- * Because 'struct key' is the first element in 'struct conf_entry',
- * it's safe (guaranteed by the C standard) to cast arbitrarily back
- * and forth between the two types. Therefore, we only need one
- * comparison function, which can double as a main sort function for
- * the tree (comparing two conf_entry structures with each other)
- * and a search function (looking up an externally supplied key).
- */
-static int conf_cmp(void *av, void *bv)
-{
-    struct key *a = (struct key *)av;
-    struct key *b = (struct key *)bv;
-
-    if (a->primary < b->primary)
-        return -1;
-    else if (a->primary > b->primary)
-        return +1;
-    switch (subkeytypes[a->primary]) {
-      case TYPE_INT:
-        if (a->secondary.i < b->secondary.i)
-            return -1;
-        else if (a->secondary.i > b->secondary.i)
-            return +1;
-        return 0;
-      case TYPE_STR:
-        return strcmp(a->secondary.s, b->secondary.s);
-      default:
-        return 0;
-    }
-}
-
-static int conf_cmp_constkey(void *av, void *bv)
-{
-    struct key *a = (struct key *)av;
-    struct constkey *b = (struct constkey *)bv;
-
-    if (a->primary < b->primary)
-        return -1;
-    else if (a->primary > b->primary)
-        return +1;
-    switch (subkeytypes[a->primary]) {
-      case TYPE_INT:
-        if (a->secondary.i < b->secondary.i)
-            return -1;
-        else if (a->secondary.i > b->secondary.i)
-            return +1;
-        return 0;
-      case TYPE_STR:
-        return strcmp(a->secondary.s, b->secondary.s);
-      default:
-        return 0;
-    }
-}
-
-/*
- * Free any dynamic data items pointed to by a 'struct key'. We
- * don't free the structure itself, since it's probably part of a
- * larger allocated block.
- */
-static void free_key(struct key *key)
-{
-    if (subkeytypes[key->primary] == TYPE_STR)
-        sfree(key->secondary.s);
-}
-
-/*
- * Copy a 'struct key' into another one, copying its dynamic data
- * if necessary.
- */
-static void copy_key(struct key *to, struct key *from)
-{
-    to->primary = from->primary;
-    switch (subkeytypes[to->primary]) {
-      case TYPE_INT:
-        to->secondary.i = from->secondary.i;
-        break;
-      case TYPE_STR:
-        to->secondary.s = dupstr(from->secondary.s);
-        break;
-    }
-}
-
-/*
- * Free any dynamic data items pointed to by a 'struct value'. We
- * don't free the value itself, since it's probably part of a larger
- * allocated block.
- */
-static void free_value(struct value *val, int type)
-{
-    if (type == TYPE_STR)
-        sfree(val->u.stringval);
-    else if (type == TYPE_FILENAME)
-        filename_free(val->u.fileval);
-    else if (type == TYPE_FONT)
-        fontspec_free(val->u.fontval);
-}
-
-/*
- * Copy a 'struct value' into another one, copying its dynamic data
- * if necessary.
- */
-static void copy_value(struct value *to, struct value *from, int type)
-{
-    switch (type) {
-      case TYPE_BOOL:
-        to->u.boolval = from->u.boolval;
-        break;
-      case TYPE_INT:
-        to->u.intval = from->u.intval;
-        break;
-      case TYPE_STR:
-        to->u.stringval = dupstr(from->u.stringval);
-        break;
-      case TYPE_FILENAME:
-        to->u.fileval = filename_copy(from->u.fileval);
-        break;
-      case TYPE_FONT:
-        to->u.fontval = fontspec_copy(from->u.fontval);
-        break;
-    }
-}
-
-/*
- * Free an entire 'struct conf_entry' and its dynamic data.
- */
-static void free_entry(struct conf_entry *entry)
-{
-    free_key(&entry->key);
-    free_value(&entry->value, valuetypes[entry->key.primary]);
-    sfree(entry);
-}
-
-Conf *conf_new(void)
-{
-    Conf *conf = snew(struct conf_tag);
-
-    conf->tree = newtree234(conf_cmp);
-
-    return conf;
-}
-
-static void conf_clear(Conf *conf)
-{
-    struct conf_entry *entry;
-
-    while ((entry = delpos234(conf->tree, 0)) != NULL)
-        free_entry(entry);
-}
-
-void conf_free(Conf *conf)
-{
-    conf_clear(conf);
-    freetree234(conf->tree);
-    sfree(conf);
-}
-
-static void conf_insert(Conf *conf, struct conf_entry *entry)
-{
-    struct conf_entry *oldentry = add234(conf->tree, entry);
-    if (oldentry && oldentry != entry) {
-        del234(conf->tree, oldentry);
-        free_entry(oldentry);
-        oldentry = add234(conf->tree, entry);
-        assert(oldentry == entry);
-    }
-}
-
-void conf_copy_into(Conf *newconf, Conf *oldconf)
-{
-    struct conf_entry *entry, *entry2;
-    int i;
-
-    conf_clear(newconf);
-
-    for (i = 0; (entry = index234(oldconf->tree, i)) != NULL; i++) {
-        entry2 = snew(struct conf_entry);
-        copy_key(&entry2->key, &entry->key);
-        copy_value(&entry2->value, &entry->value,
-                   valuetypes[entry->key.primary]);
-        add234(newconf->tree, entry2);
-    }
-}
-
-Conf *conf_copy(Conf *oldconf)
-{
-    Conf *newconf = conf_new();
-
-    conf_copy_into(newconf, oldconf);
-
-    return newconf;
-}
-
-bool conf_get_bool(Conf *conf, int primary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_BOOL);
-    key.primary = primary;
-    entry = find234(conf->tree, &key, NULL);
-    assert(entry);
-    return entry->value.u.boolval;
-}
-
-int conf_get_int(Conf *conf, int primary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_INT);
-    key.primary = primary;
-    entry = find234(conf->tree, &key, NULL);
-    assert(entry);
-    return entry->value.u.intval;
-}
-
-int conf_get_int_int(Conf *conf, int primary, int secondary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_INT);
-    assert(valuetypes[primary] == TYPE_INT);
-    key.primary = primary;
-    key.secondary.i = secondary;
-    entry = find234(conf->tree, &key, NULL);
-    assert(entry);
-    return entry->value.u.intval;
-}
-
-char *conf_get_str(Conf *conf, int primary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_STR);
-    key.primary = primary;
-    entry = find234(conf->tree, &key, NULL);
-    assert(entry);
-    return entry->value.u.stringval;
-}
-
-char *conf_get_str_str_opt(Conf *conf, int primary, const char *secondary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_STR);
-    assert(valuetypes[primary] == TYPE_STR);
-    key.primary = primary;
-    key.secondary.s = (char *)secondary;
-    entry = find234(conf->tree, &key, NULL);
-    return entry ? entry->value.u.stringval : NULL;
-}
-
-char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
-{
-    char *ret = conf_get_str_str_opt(conf, primary, secondary);
-    assert(ret);
-    return ret;
-}
-
-char *conf_get_str_strs(Conf *conf, int primary,
-                       char *subkeyin, char **subkeyout)
-{
-    struct constkey key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_STR);
-    assert(valuetypes[primary] == TYPE_STR);
-    key.primary = primary;
-    if (subkeyin) {
-        key.secondary.s = subkeyin;
-        entry = findrel234(conf->tree, &key, NULL, REL234_GT);
-    } else {
-        key.secondary.s = "";
-        entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);
-    }
-    if (!entry || entry->key.primary != primary)
-        return NULL;
-    *subkeyout = entry->key.secondary.s;
-    return entry->value.u.stringval;
-}
-
-char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
-{
-    struct constkey key;
-    struct conf_entry *entry;
-    int index;
-
-    assert(subkeytypes[primary] == TYPE_STR);
-    assert(valuetypes[primary] == TYPE_STR);
-    key.primary = primary;
-    key.secondary.s = "";
-    entry = findrelpos234(conf->tree, &key, conf_cmp_constkey,
-                          REL234_GE, &index);
-    if (!entry || entry->key.primary != primary)
-        return NULL;
-    entry = index234(conf->tree, index + n);
-    if (!entry || entry->key.primary != primary)
-        return NULL;
-    return entry->key.secondary.s;
-}
-
-Filename *conf_get_filename(Conf *conf, int primary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_FILENAME);
-    key.primary = primary;
-    entry = find234(conf->tree, &key, NULL);
-    assert(entry);
-    return entry->value.u.fileval;
-}
-
-FontSpec *conf_get_fontspec(Conf *conf, int primary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_FONT);
-    key.primary = primary;
-    entry = find234(conf->tree, &key, NULL);
-    assert(entry);
-    return entry->value.u.fontval;
-}
-
-void conf_set_bool(Conf *conf, int primary, bool value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_BOOL);
-    entry->key.primary = primary;
-    entry->value.u.boolval = value;
-    conf_insert(conf, entry);
-}
-
-void conf_set_int(Conf *conf, int primary, int value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_INT);
-    entry->key.primary = primary;
-    entry->value.u.intval = value;
-    conf_insert(conf, entry);
-}
-
-void conf_set_int_int(Conf *conf, int primary,
-                      int secondary, int value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_INT);
-    assert(valuetypes[primary] == TYPE_INT);
-    entry->key.primary = primary;
-    entry->key.secondary.i = secondary;
-    entry->value.u.intval = value;
-    conf_insert(conf, entry);
-}
-
-void conf_set_str(Conf *conf, int primary, const char *value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_STR);
-    entry->key.primary = primary;
-    entry->value.u.stringval = dupstr(value);
-    conf_insert(conf, entry);
-}
-
-void conf_set_str_str(Conf *conf, int primary, const char *secondary,
-                      const char *value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_STR);
-    assert(valuetypes[primary] == TYPE_STR);
-    entry->key.primary = primary;
-    entry->key.secondary.s = dupstr(secondary);
-    entry->value.u.stringval = dupstr(value);
-    conf_insert(conf, entry);
-}
-
-void conf_del_str_str(Conf *conf, int primary, const char *secondary)
-{
-    struct key key;
-    struct conf_entry *entry;
-
-    assert(subkeytypes[primary] == TYPE_STR);
-    assert(valuetypes[primary] == TYPE_STR);
-    key.primary = primary;
-    key.secondary.s = (char *)secondary;
-    entry = find234(conf->tree, &key, NULL);
-    if (entry) {
-        del234(conf->tree, entry);
-        free_entry(entry);
-    }
- }
-
-void conf_set_filename(Conf *conf, int primary, const Filename *value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_FILENAME);
-    entry->key.primary = primary;
-    entry->value.u.fileval = filename_copy(value);
-    conf_insert(conf, entry);
-}
-
-void conf_set_fontspec(Conf *conf, int primary, const FontSpec *value)
-{
-    struct conf_entry *entry = snew(struct conf_entry);
-
-    assert(subkeytypes[primary] == TYPE_NONE);
-    assert(valuetypes[primary] == TYPE_FONT);
-    entry->key.primary = primary;
-    entry->value.u.fontval = fontspec_copy(value);
-    conf_insert(conf, entry);
-}
-
-void conf_serialise(BinarySink *bs, Conf *conf)
-{
-    int i;
-    struct conf_entry *entry;
-
-    for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) {
-        put_uint32(bs, entry->key.primary);
-
-        switch (subkeytypes[entry->key.primary]) {
-          case TYPE_INT:
-            put_uint32(bs, entry->key.secondary.i);
-            break;
-          case TYPE_STR:
-            put_asciz(bs, entry->key.secondary.s);
-            break;
-        }
-        switch (valuetypes[entry->key.primary]) {
-          case TYPE_BOOL:
-            put_bool(bs, entry->value.u.boolval);
-            break;
-          case TYPE_INT:
-            put_uint32(bs, entry->value.u.intval);
-            break;
-          case TYPE_STR:
-            put_asciz(bs, entry->value.u.stringval);
-            break;
-          case TYPE_FILENAME:
-            filename_serialise(bs, entry->value.u.fileval);
-            break;
-          case TYPE_FONT:
-            fontspec_serialise(bs, entry->value.u.fontval);
-            break;
-        }
-    }
-
-    put_uint32(bs, 0xFFFFFFFFU);
-}
-
-bool conf_deserialise(Conf *conf, BinarySource *src)
-{
-    struct conf_entry *entry;
-    unsigned primary;
-
-    while (1) {
-        primary = get_uint32(src);
-
-        if (get_err(src))
-            return false;
-        if (primary == 0xFFFFFFFFU)
-            return true;
-        if (primary >= N_CONFIG_OPTIONS)
-            return false;
-
-        entry = snew(struct conf_entry);
-        entry->key.primary = primary;
-
-        switch (subkeytypes[entry->key.primary]) {
-          case TYPE_INT:
-            entry->key.secondary.i = toint(get_uint32(src));
-            break;
-          case TYPE_STR:
-            entry->key.secondary.s = dupstr(get_asciz(src));
-            break;
-        }
-
-        switch (valuetypes[entry->key.primary]) {
-          case TYPE_BOOL:
-            entry->value.u.boolval = get_bool(src);
-            break;
-          case TYPE_INT:
-            entry->value.u.intval = toint(get_uint32(src));
-            break;
-          case TYPE_STR:
-            entry->value.u.stringval = dupstr(get_asciz(src));
-            break;
-          case TYPE_FILENAME:
-            entry->value.u.fileval = filename_deserialise(src);
-            break;
-          case TYPE_FONT:
-            entry->value.u.fontval = fontspec_deserialise(src);
-            break;
-        }
-
-        if (get_err(src)) {
-            free_entry(entry);
-            return false;
-        }
-
-        conf_insert(conf, entry);
-    }
-}

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

+ 44 - 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 */
@@ -88,9 +103,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;
@@ -99,6 +119,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;
@@ -160,6 +181,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.
@@ -195,32 +218,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 - 1206
source/putty/ecc.c

@@ -1,1206 +0,0 @@
-#include <assert.h>
-
-#include "ssh.h"
-#include "mpint.h"
-#include "ecc.h"
-
-/* ----------------------------------------------------------------------
- * Weierstrass curves.
- */
-
-struct WeierstrassPoint {
-    /*
-     * Internally, we represent a point using 'Jacobian coordinates',
-     * which are three values X,Y,Z whose relation to the affine
-     * coordinates x,y is that x = X/Z^2 and y = Y/Z^3.
-     *
-     * This allows us to do most of our calculations without having to
-     * take an inverse mod p: every time the obvious affine formulae
-     * would need you to divide by something, you instead multiply it
-     * into the 'denominator' coordinate Z. You only have to actually
-     * take the inverse of Z when you need to get the affine
-     * coordinates back out, which means you do it once after your
-     * entire computation instead of at every intermediate step.
-     *
-     * The point at infinity is represented by setting all three
-     * coordinates to zero.
-     *
-     * These values are also stored in the Montgomery-multiplication
-     * transformed representation.
-     */
-    mp_int *X, *Y, *Z;
-
-    WeierstrassCurve *wc;
-};
-
-struct WeierstrassCurve {
-    /* Prime modulus of the finite field. */
-    mp_int *p;
-
-    /* Persistent Montgomery context for doing arithmetic mod p. */
-    MontyContext *mc;
-
-    /* Modsqrt context for point decompression. NULL if this curve was
-     * constructed without providing nonsquare_mod_p. */
-    ModsqrtContext *sc;
-
-    /* Parameters of the curve, in Montgomery-multiplication
-     * transformed form. */
-    mp_int *a, *b;
-};
-
-WeierstrassCurve *ecc_weierstrass_curve(
-    mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare_mod_p)
-{
-    WeierstrassCurve *wc = snew(WeierstrassCurve);
-    wc->p = mp_copy(p);
-    wc->mc = monty_new(p);
-    wc->a = monty_import(wc->mc, a);
-    wc->b = monty_import(wc->mc, b);
-
-    if (nonsquare_mod_p)
-        wc->sc = modsqrt_new(p, nonsquare_mod_p);
-    else
-        wc->sc = NULL;
-
-    return wc;
-}
-
-void ecc_weierstrass_curve_free(WeierstrassCurve *wc)
-{
-    mp_free(wc->p);
-    mp_free(wc->a);
-    mp_free(wc->b);
-    monty_free(wc->mc);
-    if (wc->sc)
-        modsqrt_free(wc->sc);
-    sfree(wc);
-}
-
-static WeierstrassPoint *ecc_weierstrass_point_new_empty(WeierstrassCurve *wc)
-{
-    WeierstrassPoint *wp = snew(WeierstrassPoint);
-    wp->wc = wc;
-    wp->X = wp->Y = wp->Z = NULL;
-    return wp;
-}
-
-static WeierstrassPoint *ecc_weierstrass_point_new_imported(
-    WeierstrassCurve *wc, mp_int *monty_x, mp_int *monty_y)
-{
-    WeierstrassPoint *wp = ecc_weierstrass_point_new_empty(wc);
-    wp->X = monty_x;
-    wp->Y = monty_y;
-    wp->Z = mp_copy(monty_identity(wc->mc));
-    return wp;
-}
-
-WeierstrassPoint *ecc_weierstrass_point_new(
-    WeierstrassCurve *wc, mp_int *x, mp_int *y)
-{
-    return ecc_weierstrass_point_new_imported(
-        wc, monty_import(wc->mc, x), monty_import(wc->mc, y));
-}
-
-WeierstrassPoint *ecc_weierstrass_point_new_identity(WeierstrassCurve *wc)
-{
-    WeierstrassPoint *wp = ecc_weierstrass_point_new_empty(wc);
-    size_t bits = mp_max_bits(wc->p);
-    wp->X = mp_new(bits);
-    wp->Y = mp_new(bits);
-    wp->Z = mp_new(bits);
-    return wp;
-}
-
-void ecc_weierstrass_point_copy_into(
-    WeierstrassPoint *dest, WeierstrassPoint *src)
-{
-    mp_copy_into(dest->X, src->X);
-    mp_copy_into(dest->Y, src->Y);
-    mp_copy_into(dest->Z, src->Z);
-}
-
-WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *orig)
-{
-    WeierstrassPoint *wp = ecc_weierstrass_point_new_empty(orig->wc);
-    wp->X = mp_copy(orig->X);
-    wp->Y = mp_copy(orig->Y);
-    wp->Z = mp_copy(orig->Z);
-    return wp;
-}
-
-void ecc_weierstrass_point_free(WeierstrassPoint *wp)
-{
-    mp_free(wp->X);
-    mp_free(wp->Y);
-    mp_free(wp->Z);
-    smemclr(wp, sizeof(*wp));
-    sfree(wp);
-}
-
-WeierstrassPoint *ecc_weierstrass_point_new_from_x(
-    WeierstrassCurve *wc, mp_int *xorig, unsigned desired_y_parity)
-{
-    pinitassert(wc->sc);
-
-    /*
-     * The curve equation is y^2 = x^3 + ax + b, which is already
-     * conveniently in a form where we can compute the RHS and take
-     * the square root of it to get y.
-     */
-    unsigned success;
-
-    mp_int *x = monty_import(wc->mc, xorig);
-
-    /*
-     * Compute the RHS of the curve equation. We don't need to take
-     * account of z here, because we're constructing the point from
-     * scratch. So it really is just x^3 + ax + b.
-     */
-    mp_int *x2 = monty_mul(wc->mc, x, x);
-    mp_int *x2_plus_a = monty_add(wc->mc, x2, wc->a);
-    mp_int *x3_plus_ax = monty_mul(wc->mc, x2_plus_a, x);
-    mp_int *rhs = monty_add(wc->mc, x3_plus_ax, wc->b);
-    mp_free(x2);
-    mp_free(x2_plus_a);
-    mp_free(x3_plus_ax);
-
-    { // WINSCP
-    mp_int *y = monty_modsqrt(wc->sc, rhs, &success);
-    mp_free(rhs);
-
-    if (!success) {
-        /* Failure! x^3+ax+b worked out to be a number that has no
-         * square root mod p. In this situation there's no point in
-         * trying to be time-constant, since the protocol sequence is
-         * going to diverge anyway when we complain to whoever gave us
-         * this bogus value. */
-        mp_free(x);
-        mp_free(y);
-        return NULL;
-    }
-
-    /*
-     * Choose whichever of y and p-y has the specified parity (of its
-     * lowest positive residue mod p).
-     */
-    { // WINSCP
-    mp_int *tmp = monty_export(wc->mc, y);
-    unsigned flip = (mp_get_bit(tmp, 0) ^ desired_y_parity) & 1;
-    mp_sub_into(tmp, wc->p, y);
-    mp_select_into(y, y, tmp, flip);
-    mp_free(tmp);
-    } // WINSCP
-
-    return ecc_weierstrass_point_new_imported(wc, x, y);
-    } // WINSCP
-}
-
-static void ecc_weierstrass_cond_overwrite(
-    WeierstrassPoint *dest, WeierstrassPoint *src, unsigned overwrite)
-{
-    mp_select_into(dest->X, dest->X, src->X, overwrite);
-    mp_select_into(dest->Y, dest->Y, src->Y, overwrite);
-    mp_select_into(dest->Z, dest->Z, src->Z, overwrite);
-}
-
-static void ecc_weierstrass_cond_swap(
-    WeierstrassPoint *P, WeierstrassPoint *Q, unsigned swap)
-{
-    mp_cond_swap(P->X, Q->X, swap);
-    mp_cond_swap(P->Y, Q->Y, swap);
-    mp_cond_swap(P->Z, Q->Z, swap);
-}
-
-/*
- * Shared code between all three of the basic arithmetic functions:
- * once we've determined the slope of the line that we're intersecting
- * the curve with, this takes care of finding the coordinates of the
- * third intersection point (given the two input x-coordinates and one
- * of the y-coords) and negating it to generate the output.
- */
-static inline void ecc_weierstrass_epilogue(
-    mp_int *Px, mp_int *Qx, mp_int *Py, mp_int *common_Z,
-    mp_int *lambda_n, mp_int *lambda_d, WeierstrassPoint *out)
-{
-    WeierstrassCurve *wc = out->wc;
-
-    /* Powers of the numerator and denominator of the slope lambda */
-    mp_int *lambda_n2 = monty_mul(wc->mc, lambda_n, lambda_n);
-    mp_int *lambda_d2 = monty_mul(wc->mc, lambda_d, lambda_d);
-    mp_int *lambda_d3 = monty_mul(wc->mc, lambda_d, lambda_d2);
-
-    /* Make the output x-coordinate */
-    mp_int *xsum = monty_add(wc->mc, Px, Qx);
-    mp_int *lambda_d2_xsum = monty_mul(wc->mc, lambda_d2, xsum);
-    out->X = monty_sub(wc->mc, lambda_n2, lambda_d2_xsum);
-
-    /* Make the output y-coordinate */
-    { // WINSCP
-    mp_int *lambda_d2_Px = monty_mul(wc->mc, lambda_d2, Px);
-    mp_int *xdiff = monty_sub(wc->mc, lambda_d2_Px, out->X);
-    mp_int *lambda_n_xdiff = monty_mul(wc->mc, lambda_n, xdiff);
-    mp_int *lambda_d3_Py = monty_mul(wc->mc, lambda_d3, Py);
-    out->Y = monty_sub(wc->mc, lambda_n_xdiff, lambda_d3_Py);
-
-    /* Make the output z-coordinate */
-    out->Z = monty_mul(wc->mc, common_Z, lambda_d);
-
-    mp_free(lambda_n2);
-    mp_free(lambda_d2);
-    mp_free(lambda_d3);
-    mp_free(xsum);
-    mp_free(xdiff);
-    mp_free(lambda_d2_xsum);
-    mp_free(lambda_n_xdiff);
-    mp_free(lambda_d2_Px);
-    mp_free(lambda_d3_Py);
-    } // WINSCP
-}
-
-/*
- * Shared code between add and add_general: put the two input points
- * over a common denominator, and determine the slope lambda of the
- * line through both of them. If the points have the same
- * x-coordinate, then the slope will be returned with a zero
- * denominator.
- */
-static inline void ecc_weierstrass_add_prologue(
-    WeierstrassPoint *P, WeierstrassPoint *Q,
-    mp_int **Px, mp_int **Py, mp_int **Qx, mp_int **denom,
-    mp_int **lambda_n, mp_int **lambda_d)
-{
-    WeierstrassCurve *wc = P->wc;
-
-    /* Powers of the points' denominators */
-    mp_int *Pz2 = monty_mul(wc->mc, P->Z, P->Z);
-    mp_int *Pz3 = monty_mul(wc->mc, Pz2, P->Z);
-    mp_int *Qz2 = monty_mul(wc->mc, Q->Z, Q->Z);
-    mp_int *Qz3 = monty_mul(wc->mc, Qz2, Q->Z);
-
-    /* Points' x,y coordinates scaled by the other one's denominator
-     * (raised to the appropriate power) */
-    *Px = monty_mul(wc->mc, P->X, Qz2);
-    *Py = monty_mul(wc->mc, P->Y, Qz3);
-    *Qx = monty_mul(wc->mc, Q->X, Pz2);
-    { // WINSCP
-    mp_int *Qy = monty_mul(wc->mc, Q->Y, Pz3);
-
-    /* Common denominator */
-    *denom = monty_mul(wc->mc, P->Z, Q->Z);
-
-    /* Slope of the line through the two points, if P != Q */
-    *lambda_n = monty_sub(wc->mc, Qy, *Py);
-    *lambda_d = monty_sub(wc->mc, *Qx, *Px);
-
-    mp_free(Pz2);
-    mp_free(Pz3);
-    mp_free(Qz2);
-    mp_free(Qz3);
-    mp_free(Qy);
-    } // WINSCP
-}
-
-WeierstrassPoint *ecc_weierstrass_add(WeierstrassPoint *P, WeierstrassPoint *Q)
-{
-    WeierstrassCurve *wc = P->wc;
-    assert(Q->wc == wc);
-
-    { // WINSCP
-    WeierstrassPoint *S = ecc_weierstrass_point_new_empty(wc);
-
-    mp_int *Px, *Py, *Qx, *denom, *lambda_n, *lambda_d;
-    ecc_weierstrass_add_prologue(
-        P, Q, &Px, &Py, &Qx, &denom, &lambda_n, &lambda_d);
-
-    /* Never expect to have received two mutually inverse inputs, or
-     * two identical ones (which would make this a doubling). In other
-     * words, the two input x-coordinates (after putting over a common
-     * denominator) should never have been equal. */
-    assert(!mp_eq_integer(lambda_n, 0));
-
-    /* Now go to the common epilogue code. */
-    ecc_weierstrass_epilogue(Px, Qx, Py, denom, lambda_n, lambda_d, S);
-
-    mp_free(Px);
-    mp_free(Py);
-    mp_free(Qx);
-    mp_free(denom);
-    mp_free(lambda_n);
-    mp_free(lambda_d);
-
-    return S;
-    } // WINSCP
-}
-
-/*
- * Code to determine the slope of the line you need to intersect with
- * the curve in the case where you're adding a point to itself. In
- * this situation you can't just say "the line through both input
- * points" because that's under-determined; instead, you have to take
- * the _tangent_ to the curve at the given point, by differentiating
- * the curve equation y^2=x^3+ax+b to get 2y dy/dx = 3x^2+a.
- */
-static inline void ecc_weierstrass_tangent_slope(
-    WeierstrassPoint *P, mp_int **lambda_n, mp_int **lambda_d)
-{
-    WeierstrassCurve *wc = P->wc;
-
-    mp_int *X2 = monty_mul(wc->mc, P->X, P->X);
-    mp_int *twoX2 = monty_add(wc->mc, X2, X2);
-    mp_int *threeX2 = monty_add(wc->mc, twoX2, X2);
-    mp_int *Z2 = monty_mul(wc->mc, P->Z, P->Z);
-    mp_int *Z4 = monty_mul(wc->mc, Z2, Z2);
-    mp_int *aZ4 = monty_mul(wc->mc, wc->a, Z4);
-
-    *lambda_n = monty_add(wc->mc, threeX2, aZ4);
-    *lambda_d = monty_add(wc->mc, P->Y, P->Y);
-
-    mp_free(X2);
-    mp_free(twoX2);
-    mp_free(threeX2);
-    mp_free(Z2);
-    mp_free(Z4);
-    mp_free(aZ4);
-}
-
-WeierstrassPoint *ecc_weierstrass_double(WeierstrassPoint *P)
-{
-    WeierstrassCurve *wc = P->wc;
-    WeierstrassPoint *D = ecc_weierstrass_point_new_empty(wc);
-
-    mp_int *lambda_n, *lambda_d;
-    ecc_weierstrass_tangent_slope(P, &lambda_n, &lambda_d);
-    ecc_weierstrass_epilogue(P->X, P->X, P->Y, P->Z, lambda_n, lambda_d, D);
-    mp_free(lambda_n);
-    mp_free(lambda_d);
-
-    return D;
-}
-
-static inline void ecc_weierstrass_select_into(
-    WeierstrassPoint *dest, WeierstrassPoint *P, WeierstrassPoint *Q,
-    unsigned choose_Q)
-{
-    mp_select_into(dest->X, P->X, Q->X, choose_Q);
-    mp_select_into(dest->Y, P->Y, Q->Y, choose_Q);
-    mp_select_into(dest->Z, P->Z, Q->Z, choose_Q);
-}
-
-WeierstrassPoint *ecc_weierstrass_add_general(
-    WeierstrassPoint *P, WeierstrassPoint *Q)
-{
-    WeierstrassCurve *wc = P->wc;
-    assert(Q->wc == wc);
-
-    { // WINSCP
-    WeierstrassPoint *S = ecc_weierstrass_point_new_empty(wc);
-
-    /* Parameters for the epilogue, and slope of the line if P != Q */
-    mp_int *Px, *Py, *Qx, *denom, *lambda_n, *lambda_d;
-    ecc_weierstrass_add_prologue(
-        P, Q, &Px, &Py, &Qx, &denom, &lambda_n, &lambda_d);
-
-    /* Slope if P == Q */
-    { // WINSCP
-    mp_int *lambda_n_tangent, *lambda_d_tangent;
-    ecc_weierstrass_tangent_slope(P, &lambda_n_tangent, &lambda_d_tangent);
-
-    /* Select between those slopes depending on whether P == Q */
-    { // WINSCP
-    unsigned same_x_coord = mp_eq_integer(lambda_d, 0);
-    unsigned same_y_coord = mp_eq_integer(lambda_n, 0);
-    unsigned equality = same_x_coord & same_y_coord;
-    mp_select_into(lambda_n, lambda_n, lambda_n_tangent, equality);
-    mp_select_into(lambda_d, lambda_d, lambda_d_tangent, equality);
-
-    /* Now go to the common code between addition and doubling */
-    ecc_weierstrass_epilogue(Px, Qx, Py, denom, lambda_n, lambda_d, S);
-
-    /* Check for the input identity cases, and overwrite the output if
-     * necessary. */
-    ecc_weierstrass_select_into(S, S, Q, mp_eq_integer(P->Z, 0));
-    ecc_weierstrass_select_into(S, S, P, mp_eq_integer(Q->Z, 0));
-
-    /*
-     * In the case where P == -Q and so the output is the identity,
-     * we'll have calculated lambda_d = 0 and so the output will have
-     * z==0 already. Detect that and use it to normalise the other two
-     * coordinates to zero.
-     */
-    { // WINSCP
-    unsigned output_id = mp_eq_integer(S->Z, 0);
-    mp_cond_clear(S->X, output_id);
-    mp_cond_clear(S->Y, output_id);
-
-    mp_free(Px);
-    mp_free(Py);
-    mp_free(Qx);
-    mp_free(denom);
-    mp_free(lambda_n);
-    mp_free(lambda_d);
-    mp_free(lambda_n_tangent);
-    mp_free(lambda_d_tangent);
-
-    return S;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-WeierstrassPoint *ecc_weierstrass_multiply(WeierstrassPoint *B, mp_int *n)
-{
-    WeierstrassPoint *two_B = ecc_weierstrass_double(B);
-    WeierstrassPoint *k_B = ecc_weierstrass_point_copy(B);
-    WeierstrassPoint *kplus1_B = ecc_weierstrass_point_copy(two_B);
-
-    /*
-     * This multiply routine more or less follows the shape of the
-     * 'Montgomery ladder' technique that you have to use under the
-     * extra constraint on addition in Montgomery curves, because it
-     * was fresh in my mind and easier to just do it the same way. See
-     * the comment in ecc_montgomery_multiply.
-     */
-
-    unsigned not_started_yet = 1;
-    size_t bitindex; // WINSCP
-    for (bitindex = mp_max_bits(n); bitindex-- > 0 ;) {
-        unsigned nbit = mp_get_bit(n, bitindex);
-
-        WeierstrassPoint *sum = ecc_weierstrass_add(k_B, kplus1_B);
-        ecc_weierstrass_cond_swap(k_B, kplus1_B, nbit);
-        { // WINSCP
-        WeierstrassPoint *other = ecc_weierstrass_double(k_B);
-        ecc_weierstrass_point_free(k_B);
-        ecc_weierstrass_point_free(kplus1_B);
-        k_B = other;
-        kplus1_B = sum;
-        ecc_weierstrass_cond_swap(k_B, kplus1_B, nbit);
-
-        ecc_weierstrass_cond_overwrite(k_B, B, not_started_yet);
-        ecc_weierstrass_cond_overwrite(kplus1_B, two_B, not_started_yet);
-        not_started_yet &= ~nbit;
-        } // WINSCP
-    }
-
-    ecc_weierstrass_point_free(two_B);
-    ecc_weierstrass_point_free(kplus1_B);
-    return k_B;
-}
-
-unsigned ecc_weierstrass_is_identity(WeierstrassPoint *wp)
-{
-    return mp_eq_integer(wp->Z, 0);
-}
-
-/*
- * Normalise a point by scaling its Jacobian coordinates so that Z=1.
- * This doesn't change what point is represented by the triple, but it
- * means the affine x,y can now be easily recovered from X and Y.
- */
-static void ecc_weierstrass_normalise(WeierstrassPoint *wp)
-{
-    WeierstrassCurve *wc = wp->wc;
-    mp_int *zinv = monty_invert(wc->mc, wp->Z);
-    mp_int *zinv2 = monty_mul(wc->mc, zinv, zinv);
-    mp_int *zinv3 = monty_mul(wc->mc, zinv2, zinv);
-    monty_mul_into(wc->mc, wp->X, wp->X, zinv2);
-    monty_mul_into(wc->mc, wp->Y, wp->Y, zinv3);
-    monty_mul_into(wc->mc, wp->Z, wp->Z, zinv);
-    mp_free(zinv);
-    mp_free(zinv2);
-    mp_free(zinv3);
-}
-
-void ecc_weierstrass_get_affine(
-    WeierstrassPoint *wp, mp_int **x, mp_int **y)
-{
-    WeierstrassCurve *wc = wp->wc;
-
-    ecc_weierstrass_normalise(wp);
-
-    if (x)
-        *x = monty_export(wc->mc, wp->X);
-    if (y)
-        *y = monty_export(wc->mc, wp->Y);
-}
-
-unsigned ecc_weierstrass_point_valid(WeierstrassPoint *P)
-{
-    WeierstrassCurve *wc = P->wc;
-
-    /*
-     * The projective version of the curve equation is
-     * Y^2 = X^3 + a X Z^4 + b Z^6
-     */
-    mp_int *lhs = monty_mul(P->wc->mc, P->Y, P->Y);
-    mp_int *x2 = monty_mul(wc->mc, P->X, P->X);
-    mp_int *x3 = monty_mul(wc->mc, x2, P->X);
-    mp_int *z2 = monty_mul(wc->mc, P->Z, P->Z);
-    mp_int *z4 = monty_mul(wc->mc, z2, z2);
-    mp_int *az4 = monty_mul(wc->mc, wc->a, z4);
-    mp_int *axz4 = monty_mul(wc->mc, az4, P->X);
-    mp_int *x3_plus_axz4 = monty_add(wc->mc, x3, axz4);
-    mp_int *z6 = monty_mul(wc->mc, z2, z4);
-    mp_int *bz6 = monty_mul(wc->mc, wc->b, z6);
-    mp_int *rhs = monty_add(wc->mc, x3_plus_axz4, bz6);
-
-    unsigned valid = mp_cmp_eq(lhs, rhs);
-
-    mp_free(lhs);
-    mp_free(x2);
-    mp_free(x3);
-    mp_free(z2);
-    mp_free(z4);
-    mp_free(az4);
-    mp_free(axz4);
-    mp_free(x3_plus_axz4);
-    mp_free(z6);
-    mp_free(bz6);
-    mp_free(rhs);
-
-    return valid;
-}
-
-/* ----------------------------------------------------------------------
- * Montgomery curves.
- */
-
-struct MontgomeryPoint {
-    /* XZ coordinates. These represent the affine x coordinate by the
-     * relationship x = X/Z. */
-    mp_int *X, *Z;
-
-    MontgomeryCurve *mc;
-};
-
-struct MontgomeryCurve {
-    /* Prime modulus of the finite field. */
-    mp_int *p;
-
-    /* Montgomery context for arithmetic mod p. */
-    MontyContext *mc;
-
-    /* Parameters of the curve, in Montgomery-multiplication
-     * transformed form. */
-    mp_int *a, *b;
-
-    /* (a+2)/4, also in Montgomery-multiplication form. */
-    mp_int *aplus2over4;
-};
-
-MontgomeryCurve *ecc_montgomery_curve(
-    mp_int *p, mp_int *a, mp_int *b)
-{
-    MontgomeryCurve *mc = snew(MontgomeryCurve);
-    mc->p = mp_copy(p);
-    mc->mc = monty_new(p);
-    mc->a = monty_import(mc->mc, a);
-    mc->b = monty_import(mc->mc, b);
-
-    { // WINSCP
-    mp_int *four = mp_from_integer(4);
-    mp_int *fourinverse = mp_invert(four, mc->p);
-    mp_int *aplus2 = mp_copy(a);
-    mp_add_integer_into(aplus2, aplus2, 2);
-    { // WINSCP
-    mp_int *aplus2over4 = mp_modmul(aplus2, fourinverse, mc->p);
-    mc->aplus2over4 = monty_import(mc->mc, aplus2over4);
-    mp_free(four);
-    mp_free(fourinverse);
-    mp_free(aplus2);
-    mp_free(aplus2over4);
-    } // WINSCP
-    } // WINSCP
-
-    return mc;
-}
-
-void ecc_montgomery_curve_free(MontgomeryCurve *mc)
-{
-    mp_free(mc->p);
-    mp_free(mc->a);
-    mp_free(mc->b);
-    mp_free(mc->aplus2over4);
-    monty_free(mc->mc);
-    sfree(mc);
-}
-
-static MontgomeryPoint *ecc_montgomery_point_new_empty(MontgomeryCurve *mc)
-{
-    MontgomeryPoint *mp = snew(MontgomeryPoint);
-    mp->mc = mc;
-    mp->X = mp->Z = NULL;
-    return mp;
-}
-
-MontgomeryPoint *ecc_montgomery_point_new(MontgomeryCurve *mc, mp_int *x)
-{
-    MontgomeryPoint *mp = ecc_montgomery_point_new_empty(mc);
-    mp->X = monty_import(mc->mc, x);
-    mp->Z = mp_copy(monty_identity(mc->mc));
-    return mp;
-}
-
-void ecc_montgomery_point_copy_into(
-    MontgomeryPoint *dest, MontgomeryPoint *src)
-{
-    mp_copy_into(dest->X, src->X);
-    mp_copy_into(dest->Z, src->Z);
-}
-
-MontgomeryPoint *ecc_montgomery_point_copy(MontgomeryPoint *orig)
-{
-    MontgomeryPoint *mp = ecc_montgomery_point_new_empty(orig->mc);
-    mp->X = mp_copy(orig->X);
-    mp->Z = mp_copy(orig->Z);
-    return mp;
-}
-
-void ecc_montgomery_point_free(MontgomeryPoint *mp)
-{
-    mp_free(mp->X);
-    mp_free(mp->Z);
-    smemclr(mp, sizeof(*mp));
-    sfree(mp);
-}
-
-static void ecc_montgomery_cond_overwrite(
-    MontgomeryPoint *dest, MontgomeryPoint *src, unsigned overwrite)
-{
-    mp_select_into(dest->X, dest->X, src->X, overwrite);
-    mp_select_into(dest->Z, dest->Z, src->Z, overwrite);
-}
-
-static void ecc_montgomery_cond_swap(
-    MontgomeryPoint *P, MontgomeryPoint *Q, unsigned swap)
-{
-    mp_cond_swap(P->X, Q->X, swap);
-    mp_cond_swap(P->Z, Q->Z, swap);
-}
-
-MontgomeryPoint *ecc_montgomery_diff_add(
-    MontgomeryPoint *P, MontgomeryPoint *Q, MontgomeryPoint *PminusQ)
-{
-    MontgomeryCurve *mc = P->mc;
-    assert(Q->mc == mc);
-    assert(PminusQ->mc == mc);
-
-    /*
-     * Differential addition is achieved using the following formula
-     * that relates the affine x-coordinates of P, Q, P+Q and P-Q:
-     *
-     * x(P+Q) x(P-Q) (x(Q)-x(P))^2 = (x(P)x(Q) - 1)^2
-     *
-     * As with the Weierstrass coordinates, the code below transforms
-     * that affine relation into a projective one to avoid having to
-     * do a division during the main arithmetic.
-     */
-
-    { // WINSCP
-    MontgomeryPoint *S = ecc_montgomery_point_new_empty(mc);
-
-    mp_int *Px_m_Pz = monty_sub(mc->mc, P->X, P->Z);
-    mp_int *Px_p_Pz = monty_add(mc->mc, P->X, P->Z);
-    mp_int *Qx_m_Qz = monty_sub(mc->mc, Q->X, Q->Z);
-    mp_int *Qx_p_Qz = monty_add(mc->mc, Q->X, Q->Z);
-    mp_int *PmQp = monty_mul(mc->mc, Px_m_Pz, Qx_p_Qz);
-    mp_int *PpQm = monty_mul(mc->mc, Px_p_Pz, Qx_m_Qz);
-    mp_int *Xpre = monty_add(mc->mc, PmQp, PpQm);
-    mp_int *Zpre = monty_sub(mc->mc, PmQp, PpQm);
-    mp_int *Xpre2 = monty_mul(mc->mc, Xpre, Xpre);
-    mp_int *Zpre2 = monty_mul(mc->mc, Zpre, Zpre);
-    S->X = monty_mul(mc->mc, Xpre2, PminusQ->Z);
-    S->Z = monty_mul(mc->mc, Zpre2, PminusQ->X);
-
-    mp_free(Px_m_Pz);
-    mp_free(Px_p_Pz);
-    mp_free(Qx_m_Qz);
-    mp_free(Qx_p_Qz);
-    mp_free(PmQp);
-    mp_free(PpQm);
-    mp_free(Xpre);
-    mp_free(Zpre);
-    mp_free(Xpre2);
-    mp_free(Zpre2);
-
-    return S;
-    } // WINSCP
-}
-
-MontgomeryPoint *ecc_montgomery_double(MontgomeryPoint *P)
-{
-    MontgomeryCurve *mc = P->mc;
-    MontgomeryPoint *D = ecc_montgomery_point_new_empty(mc);
-
-    /*
-     * To double a point in affine coordinates, in principle you can
-     * use the same technique as for Weierstrass: differentiate the
-     * curve equation to get the tangent line at the input point, use
-     * that to get an expression for y which you substitute back into
-     * the curve equation, and subtract the known two roots (in this
-     * case both the same) from the x^2 coefficient of the resulting
-     * cubic.
-     *
-     * In this case, we don't have an input y-coordinate, so you have
-     * to do a bit of extra transformation to find a formula that can
-     * work without it. The tangent formula is (3x^2 + 2ax + 1)/(2y),
-     * and when that appears in the final formula it will be squared -
-     * so we can substitute the y^2 in the denominator for the RHS of
-     * the curve equation. Put together, that gives
-     *
-     *   x_out = (x+1)^2 (x-1)^2 / 4(x^3+ax^2+x)
-     *
-     * and, as usual, the code below transforms that into projective
-     * form to avoid the division.
-     */
-
-    mp_int *Px_m_Pz = monty_sub(mc->mc, P->X, P->Z);
-    mp_int *Px_p_Pz = monty_add(mc->mc, P->X, P->Z);
-    mp_int *Px_m_Pz_2 = monty_mul(mc->mc, Px_m_Pz, Px_m_Pz);
-    mp_int *Px_p_Pz_2 = monty_mul(mc->mc, Px_p_Pz, Px_p_Pz);
-    D->X = monty_mul(mc->mc, Px_m_Pz_2, Px_p_Pz_2);
-    { // WINSCP
-    mp_int *XZ = monty_mul(mc->mc, P->X, P->Z);
-    mp_int *twoXZ = monty_add(mc->mc, XZ, XZ);
-    mp_int *fourXZ = monty_add(mc->mc, twoXZ, twoXZ);
-    mp_int *fourXZ_scaled = monty_mul(mc->mc, fourXZ, mc->aplus2over4);
-    mp_int *Zpre = monty_add(mc->mc, Px_m_Pz_2, fourXZ_scaled);
-    D->Z = monty_mul(mc->mc, fourXZ, Zpre);
-
-    mp_free(Px_m_Pz);
-    mp_free(Px_p_Pz);
-    mp_free(Px_m_Pz_2);
-    mp_free(Px_p_Pz_2);
-    mp_free(XZ);
-    mp_free(twoXZ);
-    mp_free(fourXZ);
-    mp_free(fourXZ_scaled);
-    mp_free(Zpre);
-    } // WINSCP
-
-    return D;
-}
-
-static void ecc_montgomery_normalise(MontgomeryPoint *mp)
-{
-    MontgomeryCurve *mc = mp->mc;
-    mp_int *zinv = monty_invert(mc->mc, mp->Z);
-    monty_mul_into(mc->mc, mp->X, mp->X, zinv);
-    monty_mul_into(mc->mc, mp->Z, mp->Z, zinv);
-    mp_free(zinv);
-}
-
-MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *B, mp_int *n)
-{
-    /*
-     * 'Montgomery ladder' technique, to compute an arbitrary integer
-     * multiple of B under the constraint that you can only add two
-     * unequal points if you also know their difference.
-     *
-     * The setup is that you maintain two curve points one of which is
-     * always the other one plus B. Call them kB and (k+1)B, where k
-     * is some integer that evolves as we go along. We begin by
-     * doubling the input B, to initialise those points to B and 2B,
-     * so that k=1.
-     *
-     * At each stage, we add kB and (k+1)B together - which we can do
-     * under the differential-addition constraint because we know
-     * their difference is always just B - to give us (2k+1)B. Then we
-     * double one of kB or (k+1)B, and depending on which one we
-     * choose, we end up with (2k)B or (2k+2)B. Either way, that
-     * differs by B from the other value we've just computed. So in
-     * each iteration, we do one diff-add and one doubling, plus a
-     * couple of conditional swaps to choose which value we double and
-     * which way round we put the output points, and the effect is to
-     * replace k with either 2k or 2k+1, which we choose based on the
-     * appropriate bit of the desired exponent.
-     *
-     * This routine doesn't assume we know the exact location of the
-     * topmost set bit of the exponent. So to maintain constant time
-     * it does an iteration for every _potential_ bit, starting from
-     * the top downwards; after each iteration in which we haven't
-     * seen a set exponent bit yet, we just overwrite the two points
-     * with B and 2B again,
-     */
-
-    MontgomeryPoint *two_B = ecc_montgomery_double(B);
-    MontgomeryPoint *k_B = ecc_montgomery_point_copy(B);
-    MontgomeryPoint *kplus1_B = ecc_montgomery_point_copy(two_B);
-
-    unsigned not_started_yet = 1;
-    size_t bitindex; // WINSCP
-    for (bitindex = mp_max_bits(n); bitindex-- > 0 ;) {
-        unsigned nbit = mp_get_bit(n, bitindex);
-
-        MontgomeryPoint *sum = ecc_montgomery_diff_add(k_B, kplus1_B, B);
-        ecc_montgomery_cond_swap(k_B, kplus1_B, nbit);
-        { // WINSCP
-        MontgomeryPoint *other = ecc_montgomery_double(k_B);
-        ecc_montgomery_point_free(k_B);
-        ecc_montgomery_point_free(kplus1_B);
-        k_B = other;
-        kplus1_B = sum;
-        ecc_montgomery_cond_swap(k_B, kplus1_B, nbit);
-
-        ecc_montgomery_cond_overwrite(k_B, B, not_started_yet);
-        ecc_montgomery_cond_overwrite(kplus1_B, two_B, not_started_yet);
-        not_started_yet &= ~nbit;
-        } // WINSCP
-    }
-
-    ecc_montgomery_point_free(two_B);
-    ecc_montgomery_point_free(kplus1_B);
-    return k_B;
-}
-
-void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x)
-{
-    MontgomeryCurve *mc = mp->mc;
-
-    ecc_montgomery_normalise(mp);
-
-    if (x)
-        *x = monty_export(mc->mc, mp->X);
-}
-
-unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp)
-{
-    return mp_eq_integer(mp->Z, 0);
-}
-
-/* ----------------------------------------------------------------------
- * Twisted Edwards curves.
- */
-
-struct EdwardsPoint {
-    /*
-     * We represent an Edwards curve point in 'extended coordinates'.
-     * There's more than one coordinate system going by that name,
-     * unfortunately. These ones have the semantics that X,Y,Z are
-     * ordinary projective coordinates (so x=X/Z and y=Y/Z), but also,
-     * we store the extra value T = xyZ = XY/Z.
-     */
-    mp_int *X, *Y, *Z, *T;
-
-    EdwardsCurve *ec;
-};
-
-struct EdwardsCurve {
-    /* Prime modulus of the finite field. */
-    mp_int *p;
-
-    /* Montgomery context for arithmetic mod p. */
-    MontyContext *mc;
-
-    /* Modsqrt context for point decompression. */
-    ModsqrtContext *sc;
-
-    /* Parameters of the curve, in Montgomery-multiplication
-     * transformed form. */
-    mp_int *d, *a;
-};
-
-EdwardsCurve *ecc_edwards_curve(mp_int *p, mp_int *d, mp_int *a,
-                                mp_int *nonsquare_mod_p)
-{
-    EdwardsCurve *ec = snew(EdwardsCurve);
-    ec->p = mp_copy(p);
-    ec->mc = monty_new(p);
-    ec->d = monty_import(ec->mc, d);
-    ec->a = monty_import(ec->mc, a);
-
-    if (nonsquare_mod_p)
-        ec->sc = modsqrt_new(p, nonsquare_mod_p);
-    else
-        ec->sc = NULL;
-
-    return ec;
-}
-
-void ecc_edwards_curve_free(EdwardsCurve *ec)
-{
-    mp_free(ec->p);
-    mp_free(ec->d);
-    mp_free(ec->a);
-    monty_free(ec->mc);
-    if (ec->sc)
-        modsqrt_free(ec->sc);
-    sfree(ec);
-}
-
-static EdwardsPoint *ecc_edwards_point_new_empty(EdwardsCurve *ec)
-{
-    EdwardsPoint *ep = snew(EdwardsPoint);
-    ep->ec = ec;
-    ep->X = ep->Y = ep->Z = ep->T = NULL;
-    return ep;
-}
-
-static EdwardsPoint *ecc_edwards_point_new_imported(
-    EdwardsCurve *ec, mp_int *monty_x, mp_int *monty_y)
-{
-    EdwardsPoint *ep = ecc_edwards_point_new_empty(ec);
-    ep->X = monty_x;
-    ep->Y = monty_y;
-    ep->T = monty_mul(ec->mc, ep->X, ep->Y);
-    ep->Z = mp_copy(monty_identity(ec->mc));
-    return ep;
-}
-
-EdwardsPoint *ecc_edwards_point_new(
-    EdwardsCurve *ec, mp_int *x, mp_int *y)
-{
-    return ecc_edwards_point_new_imported(
-        ec, monty_import(ec->mc, x), monty_import(ec->mc, y));
-}
-
-void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src)
-{
-    mp_copy_into(dest->X, src->X);
-    mp_copy_into(dest->Y, src->Y);
-    mp_copy_into(dest->Z, src->Z);
-    mp_copy_into(dest->T, src->T);
-}
-
-EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *orig)
-{
-    EdwardsPoint *ep = ecc_edwards_point_new_empty(orig->ec);
-    ep->X = mp_copy(orig->X);
-    ep->Y = mp_copy(orig->Y);
-    ep->Z = mp_copy(orig->Z);
-    ep->T = mp_copy(orig->T);
-    return ep;
-}
-
-void ecc_edwards_point_free(EdwardsPoint *ep)
-{
-    mp_free(ep->X);
-    mp_free(ep->Y);
-    mp_free(ep->Z);
-    mp_free(ep->T);
-    smemclr(ep, sizeof(*ep));
-    sfree(ep);
-}
-
-EdwardsPoint *ecc_edwards_point_new_from_y(
-    EdwardsCurve *ec, mp_int *yorig, unsigned desired_x_parity)
-{
-    pinitassert(ec->sc);
-
-    /*
-     * The curve equation is ax^2 + y^2 = 1 + dx^2y^2, which
-     * rearranges to x^2(dy^2-a) = y^2-1. So we compute
-     * (y^2-1)/(dy^2-a) and take its square root.
-     */
-    unsigned success;
-
-    mp_int *y = monty_import(ec->mc, yorig);
-    mp_int *y2 = monty_mul(ec->mc, y, y);
-    mp_int *dy2 = monty_mul(ec->mc, ec->d, y2);
-    mp_int *dy2ma = monty_sub(ec->mc, dy2, ec->a);
-    mp_int *y2m1 = monty_sub(ec->mc, y2, monty_identity(ec->mc));
-    mp_int *recip_denominator = monty_invert(ec->mc, dy2ma);
-    mp_int *radicand = monty_mul(ec->mc, y2m1, recip_denominator);
-    mp_int *x = monty_modsqrt(ec->sc, radicand, &success);
-    mp_free(y2);
-    mp_free(dy2);
-    mp_free(dy2ma);
-    mp_free(y2m1);
-    mp_free(recip_denominator);
-    mp_free(radicand);
-
-    if (!success) {
-        /* Failure! x^2 worked out to be a number that has no square
-         * root mod p. In this situation there's no point in trying to
-         * be time-constant, since the protocol sequence is going to
-         * diverge anyway when we complain to whoever gave us this
-         * bogus value. */
-        mp_free(x);
-        mp_free(y);
-        return NULL;
-    }
-
-    /*
-     * Choose whichever of x and p-x has the specified parity (of its
-     * lowest positive residue mod p).
-     */
-    { // WINSCP
-    mp_int *tmp = monty_export(ec->mc, x);
-    unsigned flip = (mp_get_bit(tmp, 0) ^ desired_x_parity) & 1;
-    mp_sub_into(tmp, ec->p, x);
-    mp_select_into(x, x, tmp, flip);
-    mp_free(tmp);
-    } // WINSCP
-
-    return ecc_edwards_point_new_imported(ec, x, y);
-}
-
-static void ecc_edwards_cond_overwrite(
-    EdwardsPoint *dest, EdwardsPoint *src, unsigned overwrite)
-{
-    mp_select_into(dest->X, dest->X, src->X, overwrite);
-    mp_select_into(dest->Y, dest->Y, src->Y, overwrite);
-    mp_select_into(dest->Z, dest->Z, src->Z, overwrite);
-    mp_select_into(dest->T, dest->T, src->T, overwrite);
-}
-
-static void ecc_edwards_cond_swap(
-    EdwardsPoint *P, EdwardsPoint *Q, unsigned swap)
-{
-    mp_cond_swap(P->X, Q->X, swap);
-    mp_cond_swap(P->Y, Q->Y, swap);
-    mp_cond_swap(P->Z, Q->Z, swap);
-    mp_cond_swap(P->T, Q->T, swap);
-}
-
-EdwardsPoint *ecc_edwards_add(EdwardsPoint *P, EdwardsPoint *Q)
-{
-    EdwardsCurve *ec = P->ec;
-    assert(Q->ec == ec);
-
-    { // WINSCP
-    EdwardsPoint *S = ecc_edwards_point_new_empty(ec);
-
-    /*
-     * The affine rule for Edwards addition of (x1,y1) and (x2,y2) is
-     *
-     *   x_out = (x1 y2 +   y1 x2) / (1 + d x1 x2 y1 y2)
-     *   y_out = (y1 y2 - a x1 x2) / (1 - d x1 x2 y1 y2)
-     *
-     * The formulae below are listed as 'add-2008-hwcd' in
-     * https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html
-     *
-     * and if you undo the careful optimisation to find out what
-     * they're actually computing, it comes out to
-     *
-     *   X_out = (X1 Y2 +   Y1 X2) (Z1 Z2 - d T1 T2)
-     *   Y_out = (Y1 Y2 - a X1 X2) (Z1 Z2 + d T1 T2)
-     *   Z_out = (Z1 Z2 - d T1 T2) (Z1 Z2 + d T1 T2)
-     *   T_out = (X1 Y2 +   Y1 X2) (Y1 Y2 - a X1 X2)
-     */
-    mp_int *PxQx = monty_mul(ec->mc, P->X, Q->X);
-    mp_int *PyQy = monty_mul(ec->mc, P->Y, Q->Y);
-    mp_int *PtQt = monty_mul(ec->mc, P->T, Q->T);
-    mp_int *PzQz = monty_mul(ec->mc, P->Z, Q->Z);
-    mp_int *Psum = monty_add(ec->mc, P->X, P->Y);
-    mp_int *Qsum = monty_add(ec->mc, Q->X, Q->Y);
-    mp_int *aPxQx = monty_mul(ec->mc, ec->a, PxQx);
-    mp_int *dPtQt = monty_mul(ec->mc, ec->d, PtQt);
-    mp_int *sumprod = monty_mul(ec->mc, Psum, Qsum);
-    mp_int *xx_p_yy = monty_add(ec->mc, PxQx, PyQy);
-    mp_int *E = monty_sub(ec->mc, sumprod, xx_p_yy);
-    mp_int *F = monty_sub(ec->mc, PzQz, dPtQt);
-    mp_int *G = monty_add(ec->mc, PzQz, dPtQt);
-    mp_int *H = monty_sub(ec->mc, PyQy, aPxQx);
-    S->X = monty_mul(ec->mc, E, F);
-    S->Z = monty_mul(ec->mc, F, G);
-    S->Y = monty_mul(ec->mc, G, H);
-    S->T = monty_mul(ec->mc, H, E);
-
-    mp_free(PxQx);
-    mp_free(PyQy);
-    mp_free(PtQt);
-    mp_free(PzQz);
-    mp_free(Psum);
-    mp_free(Qsum);
-    mp_free(aPxQx);
-    mp_free(dPtQt);
-    mp_free(sumprod);
-    mp_free(xx_p_yy);
-    mp_free(E);
-    mp_free(F);
-    mp_free(G);
-    mp_free(H);
-
-    return S;
-    } // WINSCP
-}
-
-static void ecc_edwards_normalise(EdwardsPoint *ep)
-{
-    EdwardsCurve *ec = ep->ec;
-    mp_int *zinv = monty_invert(ec->mc, ep->Z);
-    monty_mul_into(ec->mc, ep->X, ep->X, zinv);
-    monty_mul_into(ec->mc, ep->Y, ep->Y, zinv);
-    monty_mul_into(ec->mc, ep->Z, ep->Z, zinv);
-    mp_free(zinv);
-    monty_mul_into(ec->mc, ep->T, ep->X, ep->Y);
-}
-
-EdwardsPoint *ecc_edwards_multiply(EdwardsPoint *B, mp_int *n)
-{
-    EdwardsPoint *two_B = ecc_edwards_add(B, B);
-    EdwardsPoint *k_B = ecc_edwards_point_copy(B);
-    EdwardsPoint *kplus1_B = ecc_edwards_point_copy(two_B);
-
-    /*
-     * Another copy of the same exponentiation routine following the
-     * pattern of the Montgomery ladder, because it works as well as
-     * any other technique and this way I didn't have to debug two of
-     * them.
-     */
-
-    unsigned not_started_yet = 1;
-    size_t bitindex; // WINSCP
-    for (bitindex = mp_max_bits(n); bitindex-- > 0 ;) {
-        unsigned nbit = mp_get_bit(n, bitindex);
-
-        EdwardsPoint *sum = ecc_edwards_add(k_B, kplus1_B);
-        ecc_edwards_cond_swap(k_B, kplus1_B, nbit);
-        { // WINSCP
-        EdwardsPoint *other = ecc_edwards_add(k_B, k_B);
-        ecc_edwards_point_free(k_B);
-        ecc_edwards_point_free(kplus1_B);
-        k_B = other;
-        kplus1_B = sum;
-        ecc_edwards_cond_swap(k_B, kplus1_B, nbit);
-
-        ecc_edwards_cond_overwrite(k_B, B, not_started_yet);
-        ecc_edwards_cond_overwrite(kplus1_B, two_B, not_started_yet);
-        not_started_yet &= ~nbit;
-        } // WINSCP
-    }
-
-    ecc_edwards_point_free(two_B);
-    ecc_edwards_point_free(kplus1_B);
-    return k_B;
-}
-
-/*
- * Helper routine to determine whether two values each given as a pair
- * of projective coordinates represent the same affine value.
- */
-static inline unsigned projective_eq(
-    MontyContext *mc, mp_int *An, mp_int *Ad,
-    mp_int *Bn, mp_int *Bd)
-{
-    mp_int *AnBd = monty_mul(mc, An, Bd);
-    mp_int *BnAd = monty_mul(mc, Bn, Ad);
-    unsigned toret = mp_cmp_eq(AnBd, BnAd);
-    mp_free(AnBd);
-    mp_free(BnAd);
-    return toret;
-}
-
-unsigned ecc_edwards_eq(EdwardsPoint *P, EdwardsPoint *Q)
-{
-    EdwardsCurve *ec = P->ec;
-    assert(Q->ec == ec);
-
-    return (projective_eq(ec->mc, P->X, P->Z, Q->X, Q->Z) &
-            projective_eq(ec->mc, P->Y, P->Z, Q->Y, Q->Z));
-}
-
-void ecc_edwards_get_affine(EdwardsPoint *ep, mp_int **x, mp_int **y)
-{
-    EdwardsCurve *ec = ep->ec;
-
-    ecc_edwards_normalise(ep);
-
-    if (x)
-        *x = monty_export(ec->mc, ep->X);
-    if (y)
-        *y = monty_export(ec->mc, ep->Y);
-}

+ 0 - 243
source/putty/ecc.h

@@ -1,243 +0,0 @@
-#ifndef PUTTY_ECC_H
-#define PUTTY_ECC_H
-
-/*
- * Arithmetic functions for the various kinds of elliptic curves used
- * by PuTTY's public-key cryptography.
- *
- * All of these elliptic curves are over the finite field whose order
- * is a large prime p. (Elliptic curves over a field of order 2^n are
- * also known, but PuTTY currently has no need of them.)
- */
-
-/* ----------------------------------------------------------------------
- * Weierstrass curves (or rather, 'short form' Weierstrass curves).
- *
- * A curve in this form is defined by two parameters a,b, and the
- * non-identity points on the curve are represented by (x,y) (the
- * 'affine coordinates') such that y^2 = x^3 + ax + b.
- *
- * The identity element of the curve's group is an additional 'point
- * at infinity', which is considered to be the third point on the
- * intersection of the curve with any vertical line. Hence, the
- * inverse of the point (x,y) is (x,-y).
- */
-
-/*
- * Create and destroy Weierstrass curve data structures. The mandatory
- * parameters to the constructor are the prime modulus p, and the
- * curve parameters a,b.
- *
- * 'nonsquare_mod_p' is an optional extra parameter, only needed by
- * ecc_edwards_point_new_from_y which has to take a modular square
- * root. You can pass it as NULL if you don't need that function.
- */
-WeierstrassCurve *ecc_weierstrass_curve(
-    mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare_mod_p);
-void ecc_weierstrass_curve_free(WeierstrassCurve *);
-
-/*
- * Create points on a Weierstrass curve, given the curve.
- *
- * point_new_identity returns the special identity point.
- * point_new(x,y) returns the non-identity point with the given affine
- * coordinates.
- *
- * point_new_from_x constructs a non-identity point given only the
- * x-coordinate, by using the curve equation to work out what y has to
- * be. Of course the equation only tells you y^2, so it only
- * determines y up to sign; the parameter desired_y_parity controls
- * which of the two values of y you get, by saying whether you'd like
- * its minimal non-negative residue mod p to be even or odd. (Of
- * course, since p itself is odd, exactly one of y and p-y is odd.)
- * This function has to take a modular square root, so it will only
- * work if you passed in a non-square mod p when constructing the
- * curve.
- */
-WeierstrassPoint *ecc_weierstrass_point_new_identity(WeierstrassCurve *curve);
-WeierstrassPoint *ecc_weierstrass_point_new(
-    WeierstrassCurve *curve, mp_int *x, mp_int *y);
-WeierstrassPoint *ecc_weierstrass_point_new_from_x(
-    WeierstrassCurve *curve, mp_int *x, unsigned desired_y_parity);
-
-/* Memory management: copy and free points. */
-void ecc_weierstrass_point_copy_into(
-    WeierstrassPoint *dest, WeierstrassPoint *src);
-WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *wc);
-void ecc_weierstrass_point_free(WeierstrassPoint *point);
-
-/* Check whether a point is actually on the curve. */
-unsigned ecc_weierstrass_point_valid(WeierstrassPoint *);
-
-/*
- * Add two points and return their sum. This function is fully
- * general: it should do the right thing if the two inputs are the
- * same, or if either (or both) of the input points is the identity,
- * or if the two input points are inverses so the output is the
- * identity. However, it pays for that generality by being slower than
- * the special-purpose functions below..
- */
-WeierstrassPoint *ecc_weierstrass_add_general(
-    WeierstrassPoint *, WeierstrassPoint *);
-
-/*
- * Fast but less general arithmetic functions: add two points on the
- * condition that they are not equal and neither is the identity, and
- * add a point to itself.
- */
-WeierstrassPoint *ecc_weierstrass_add(WeierstrassPoint *, WeierstrassPoint *);
-WeierstrassPoint *ecc_weierstrass_double(WeierstrassPoint *);
-
-/*
- * Compute an integer multiple of a point. Not guaranteed to work
- * unless the integer argument is less than the order of the point in
- * the group (because it won't cope if an identity element shows up in
- * any intermediate product).
- */
-WeierstrassPoint *ecc_weierstrass_multiply(WeierstrassPoint *, mp_int *);
-
-/*
- * Query functions to get the value of a point back out. is_identity
- * tells you whether the point is the identity; if it isn't, then
- * get_affine will retrieve one or both of its affine coordinates.
- * (You can pass NULL as either output pointer, if you don't need that
- * coordinate as output.)
- */
-unsigned ecc_weierstrass_is_identity(WeierstrassPoint *wp);
-void ecc_weierstrass_get_affine(WeierstrassPoint *wp, mp_int **x, mp_int **y);
-
-/* ----------------------------------------------------------------------
- * Montgomery curves.
- *
- * A curve in this form is defined by two parameters a,b, and the
- * curve equation is by^2 = x^3 + ax^2 + x.
- *
- * As with Weierstrass curves, there's an additional point at infinity
- * that is the identity element, and the inverse of (x,y) is (x,-y).
- *
- * However, we don't actually work with full (x,y) pairs. We just
- * store the x-coordinate (so what we're really representing is not a
- * specific point on the curve but a two-point set {P,-P}). This means
- * you can't quite do point addition, because if you're given {P,-P}
- * and {Q,-Q} as input, you can work out a pair of x-coordinates that
- * are those of P-Q and P+Q, but you don't know which is which.
- *
- * Instead, the basic operation is 'differential addition', in which
- * you are given three parameters P, Q and P-Q and you return P+Q. (As
- * well as disambiguating which of the possible answers you want, that
- * extra input also enables a fast formulae for computing it. This
- * fast formula is more or less why Montgomery curves are useful in
- * the first place.)
- *
- * Doubling a point is still possible to do unambiguously, so you can
- * still compute an integer multiple of P if you start by making 2P
- * and then doing a series of differential additions.
- */
-
-/*
- * Create and destroy Montgomery curve data structures.
- */
-MontgomeryCurve *ecc_montgomery_curve(mp_int *p, mp_int *a, mp_int *b);
-void ecc_montgomery_curve_free(MontgomeryCurve *);
-
-/*
- * Create, copy and free points on the curve. We don't need to
- * explicitly represent the identity for this application.
- */
-MontgomeryPoint *ecc_montgomery_point_new(MontgomeryCurve *mc, mp_int *x);
-void ecc_montgomery_point_copy_into(
-    MontgomeryPoint *dest, MontgomeryPoint *src);
-MontgomeryPoint *ecc_montgomery_point_copy(MontgomeryPoint *orig);
-void ecc_montgomery_point_free(MontgomeryPoint *mp);
-
-/*
- * Basic arithmetic routines: differential addition and point-
- * doubling. Each of these assumes that no special cases come up - no
- * input or output point should be the identity, and in diff_add, P
- * and Q shouldn't be the same.
- */
-MontgomeryPoint *ecc_montgomery_diff_add(
-    MontgomeryPoint *P, MontgomeryPoint *Q, MontgomeryPoint *PminusQ);
-MontgomeryPoint *ecc_montgomery_double(MontgomeryPoint *P);
-
-/*
- * Compute an integer multiple of a point.
- */
-MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *, mp_int *);
-
-/*
- * Return the affine x-coordinate of a point.
- */
-void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x);
-
-/*
- * Test whether a point is the curve identity.
- */
-unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp);
-
-/* ----------------------------------------------------------------------
- * Twisted Edwards curves.
- *
- * A curve in this form is defined by two parameters d,a, and the
- * curve equation is a x^2 + y^2 = 1 + d x^2 y^2.
- *
- * Apparently if you ask a proper algebraic geometer they'll tell you
- * that this is technically not an actual elliptic curve. Certainly it
- * doesn't work quite the same way as the other kinds: in this form,
- * there is no need for a point at infinity, because the identity
- * element is represented by the affine coordinates (0,1). And you
- * invert a point by negating its x rather than y coordinate: the
- * inverse of (x,y) is (-x,y).
- *
- * The usefulness of this representation is that the addition formula
- * is 'strongly unified', meaning that the same formula works for any
- * input and output points, without needing special cases for the
- * identity or for doubling.
- */
-
-/*
- * Create and destroy Edwards curve data structures.
- *
- * Similarly to ecc_weierstrass_curve, you don't have to provide
- * nonsquare_mod_p if you don't need ecc_edwards_point_new_from_y.
- */
-EdwardsCurve *ecc_edwards_curve(
-    mp_int *p, mp_int *d, mp_int *a, mp_int *nonsquare_mod_p);
-void ecc_edwards_curve_free(EdwardsCurve *);
-
-/*
- * Create points.
- *
- * There's no need to have a separate function to create the identity
- * point, because you can just pass x=0 and y=1 to the usual function.
- *
- * Similarly to the Weierstrass curve, ecc_edwards_point_new_from_y
- * creates a point given only its y-coordinate and the desired parity
- * of its x-coordinate, and you can only call it if you provided the
- * optional nonsquare_mod_p argument when creating the curve.
- */
-EdwardsPoint *ecc_edwards_point_new(
-    EdwardsCurve *curve, mp_int *x, mp_int *y);
-EdwardsPoint *ecc_edwards_point_new_from_y(
-    EdwardsCurve *curve, mp_int *y, unsigned desired_x_parity);
-
-/* Copy and free points. */
-void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src);
-EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *ec);
-void ecc_edwards_point_free(EdwardsPoint *point);
-
-/*
- * Arithmetic: add two points, and calculate an integer multiple of a
- * point.
- */
-EdwardsPoint *ecc_edwards_add(EdwardsPoint *, EdwardsPoint *);
-EdwardsPoint *ecc_edwards_multiply(EdwardsPoint *, mp_int *);
-
-/*
- * Query functions: compare two points for equality, and return the
- * affine coordinates of a point.
- */
-unsigned ecc_edwards_eq(EdwardsPoint *, EdwardsPoint *);
-void ecc_edwards_get_affine(EdwardsPoint *wp, mp_int **x, mp_int **y);
-
-#endif /* PUTTY_ECC_H */

+ 11 - 12
source/putty/import.c

@@ -782,7 +782,7 @@ static ssh2_userkey *openssh_pem_read(
          */
         assert(privptr > 0);          /* should have bombed by now if not */
         retkey = snew(ssh2_userkey);
-        alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss);
+        alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dsa);
         retkey->key = ssh_key_new_priv(
             alg, make_ptrlen(blob->u, privptr),
             make_ptrlen(blob->u+privptr, blob->len-privptr));
@@ -849,11 +849,11 @@ static bool openssh_pem_write(
      * line.
      */
     if (ssh_key_alg(key->key) == &ssh_rsa ||
-        ssh_key_alg(key->key) == &ssh_dss) {
+        ssh_key_alg(key->key) == &ssh_dsa) {
         strbuf *seq;
 
         /*
-         * The RSA and DSS handlers share some code because the two
+         * The RSA and DSA handlers share some code because the two
          * key types have very similar ASN.1 representations, as a
          * plain SEQUENCE of big integers. So we set up a list of
          * bignums per key type and then construct the actual blob in
@@ -1371,9 +1371,8 @@ static ssh2_userkey *openssh_new_read(
             memset(keybuf, 0, keysize);
             break;
           case ON_K_BCRYPT:
-            openssh_bcrypt(passphrase,
-                           key->kdfopts.bcrypt.salt.ptr,
-                           key->kdfopts.bcrypt.salt.len,
+            openssh_bcrypt(ptrlen_from_asciz(passphrase),
+                           key->kdfopts.bcrypt.salt,
                            key->kdfopts.bcrypt.rounds,
                            keybuf, keysize);
             break;
@@ -1591,9 +1590,9 @@ static bool openssh_new_write(
             unsigned char keybuf[48];
             ssh_cipher *cipher;
 
-            openssh_bcrypt(passphrase,
-                           bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds,
-                           keybuf, sizeof(keybuf));
+            openssh_bcrypt(ptrlen_from_asciz(passphrase),
+                           make_ptrlen(bcrypt_salt, sizeof(bcrypt_salt)),
+                           bcrypt_rounds, keybuf, sizeof(keybuf));
 
             cipher = ssh_cipher_new(&ssh_aes256_sdctr);
             ssh_cipher_setkey(cipher, keybuf);
@@ -1642,7 +1641,7 @@ static bool openssh_auto_write(
      * assume that anything not in that fixed list is newer, and hence
      * will use the new format.
      */
-    if (ssh_key_alg(key->key) == &ssh_dss ||
+    if (ssh_key_alg(key->key) == &ssh_dsa ||
         ssh_key_alg(key->key) == &ssh_rsa ||
         ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 ||
         ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 ||
@@ -2119,7 +2118,7 @@ static ssh2_userkey *sshcom_read(
             goto error;
         }
 
-        alg = &ssh_dss;
+        alg = &ssh_dsa;
         put_stringz(blob, "ssh-dss");
         put_mp_ssh2_from_string(blob, p);
         put_mp_ssh2_from_string(blob, q);
@@ -2210,7 +2209,7 @@ static bool sshcom_write(
         nnumbers = 6;
         initial_zero = false;
         type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
-    } else if (ssh_key_alg(key->key) == &ssh_dss) {
+    } else if (ssh_key_alg(key->key) == &ssh_dsa) {
         ptrlen p, q, g, y, x;
 
         /*

+ 5 - 0
source/putty/logging.c

@@ -81,6 +81,11 @@ void logflush(LogContext *ctx)
             fflush(ctx->lgfp);
 }
 
+LogPolicy *log_get_policy(LogContext *ctx)
+{
+    return ctx->lp;
+}
+
 static void logfopen_callback(void *vctx, int mode)
 {
     LogContext *ctx = (LogContext *)vctx;

+ 0 - 543
source/putty/mainchan.c

@@ -1,543 +0,0 @@
-/*
- * SSH main session channel handling.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshppl.h"
-#include "sshchan.h"
-
-static void mainchan_free(Channel *chan);
-static void mainchan_open_confirmation(Channel *chan);
-static void mainchan_open_failure(Channel *chan, const char *errtext);
-static size_t mainchan_send(
-    Channel *chan, bool is_stderr, const void *, size_t);
-static void mainchan_send_eof(Channel *chan);
-static void mainchan_set_input_wanted(Channel *chan, bool wanted);
-static char *mainchan_log_close_msg(Channel *chan);
-static bool mainchan_rcvd_exit_status(Channel *chan, int status);
-static bool mainchan_rcvd_exit_signal(
-    Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg);
-static bool mainchan_rcvd_exit_signal_numeric(
-    Channel *chan, int signum, bool core_dumped, ptrlen msg);
-static void mainchan_request_response(Channel *chan, bool success);
-
-static const ChannelVtable mainchan_channelvt = {
-    // WINSCP
-    /*.free =*/ mainchan_free,
-    /*.open_confirmation =*/ mainchan_open_confirmation,
-    /*.open_failed =*/ mainchan_open_failure,
-    /*.send =*/ mainchan_send,
-    /*.send_eof =*/ mainchan_send_eof,
-    /*.set_input_wanted =*/ mainchan_set_input_wanted,
-    /*.log_close_msg =*/ mainchan_log_close_msg,
-    /*.want_close =*/ chan_default_want_close,
-    /*.rcvd_exit_status =*/ mainchan_rcvd_exit_status,
-    /*.rcvd_exit_signal =*/ mainchan_rcvd_exit_signal,
-    /*.rcvd_exit_signal_numeric =*/ mainchan_rcvd_exit_signal_numeric,
-    /*.run_shell =*/ chan_no_run_shell,
-    /*.run_command =*/ chan_no_run_command,
-    /*.run_subsystem =*/ chan_no_run_subsystem,
-    /*.enable_x11_forwarding =*/ chan_no_enable_x11_forwarding,
-    /*.enable_agent_forwarding =*/ chan_no_enable_agent_forwarding,
-    /*.allocate_pty =*/ chan_no_allocate_pty,
-    /*.set_env =*/ chan_no_set_env,
-    /*.send_break =*/ chan_no_send_break,
-    /*.send_signal =*/ chan_no_send_signal,
-    /*.change_window_size =*/ chan_no_change_window_size,
-    /*.request_response =*/ mainchan_request_response,
-};
-
-typedef enum MainChanType {
-    MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP
-} MainChanType;
-
-struct mainchan {
-    SshChannel *sc;
-    Conf *conf;
-    PacketProtocolLayer *ppl;
-    ConnectionLayer *cl;
-
-    MainChanType type;
-    bool is_simple;
-
-    bool req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback;
-    int n_req_env, n_env_replies, n_env_fails;
-    bool eof_pending, eof_sent, got_pty, ready;
-
-    int term_width, term_height;
-
-    Channel chan;
-};
-
-mainchan *mainchan_new(
-    PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
-    int term_width, int term_height, bool is_simple, SshChannel **sc_out)
-{
-    mainchan *mc;
-
-    if (conf_get_bool(conf, CONF_ssh_no_shell))
-        return NULL;                   /* no main channel at all */
-
-    mc = snew(mainchan);
-    memset(mc, 0, sizeof(mainchan));
-    mc->ppl = ppl;
-    mc->cl = cl;
-    mc->conf = conf_copy(conf);
-    mc->term_width = term_width;
-    mc->term_height = term_height;
-    mc->is_simple = is_simple;
-
-    mc->sc = NULL;
-    mc->chan.vt = &mainchan_channelvt;
-    mc->chan.initial_fixed_window_size = 0;
-
-    if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) {
-        const char *host = conf_get_str(mc->conf, CONF_ssh_nc_host);
-        int port = conf_get_int(mc->conf, CONF_ssh_nc_port);
-
-        mc->sc = ssh_lportfwd_open(cl, host, port, "main channel",
-                                   NULL, &mc->chan);
-        mc->type = MAINCHAN_DIRECT_TCPIP;
-    } else {
-        mc->sc = ssh_session_open(cl, &mc->chan);
-        mc->type = MAINCHAN_SESSION;
-    }
-
-    if (sc_out) *sc_out = mc->sc;
-    return mc;
-}
-
-static void mainchan_free(Channel *chan)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    conf_free(mc->conf);
-    sfree(mc);
-}
-
-static void mainchan_try_fallback_command(mainchan *mc);
-static void mainchan_ready(mainchan *mc);
-
-static void mainchan_open_confirmation(Channel *chan)
-{
-    mainchan *mc = container_of(chan, mainchan, chan);
-    PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
-
-    seat_update_specials_menu(mc->ppl->seat);
-    ppl_logevent("Opened main channel");
-
-    if (mc->is_simple)
-        sshfwd_hint_channel_is_simple(mc->sc);
-
-    if (mc->type == MAINCHAN_SESSION) {
-        /*
-         * Send the CHANNEL_REQUESTS for the main session channel.
-         */
-        char *key, *val, *cmd;
-        struct X11Display *x11disp;
-        struct X11FakeAuth *x11auth;
-        bool retry_cmd_now = false;
-
-        if (conf_get_bool(mc->conf, CONF_x11_forward)) {
-            char *x11_setup_err;
-            if ((x11disp = x11_setup_display(
-                     conf_get_str(mc->conf, CONF_x11_display),
-                     mc->conf, &x11_setup_err)) == NULL) {
-                ppl_logevent("X11 forwarding not enabled: unable to"
-                             " initialise X display: %s", x11_setup_err);
-                sfree(x11_setup_err);
-            } else {
-                x11auth = ssh_add_x11_display(
-                    mc->cl, conf_get_int(mc->conf, CONF_x11_auth), x11disp);
-
-                sshfwd_request_x11_forwarding(
-                    mc->sc, true, x11auth->protoname, x11auth->datastring,
-                    x11disp->screennum, false);
-                mc->req_x11 = true;
-            }
-        }
-
-        if (ssh_agent_forwarding_permitted(mc->cl)) {
-            sshfwd_request_agent_forwarding(mc->sc, true);
-            mc->req_agent = true;
-        }
-
-        if (!conf_get_bool(mc->conf, CONF_nopty)) {
-            sshfwd_request_pty(
-                mc->sc, true, mc->conf, mc->term_width, mc->term_height);
-            mc->req_pty = true;
-        }
-
-        for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key);
-             val != NULL;
-             val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) {
-            sshfwd_send_env_var(mc->sc, true, key, val);
-            mc->n_req_env++;
-        }
-        if (mc->n_req_env)
-            ppl_logevent("Sent %d environment variables", mc->n_req_env);
-
-        cmd = conf_get_str(mc->conf, CONF_remote_cmd);
-        if (conf_get_bool(mc->conf, CONF_ssh_subsys)) {
-            retry_cmd_now = !sshfwd_start_subsystem(mc->sc, true, cmd);
-        } else if (*cmd) {
-            sshfwd_start_command(mc->sc, true, cmd);
-        } else {
-            sshfwd_start_shell(mc->sc, true);
-        }
-
-        if (retry_cmd_now)
-            mainchan_try_fallback_command(mc);
-        else
-            mc->req_cmd_primary = true;
-
-    } else {
-        ssh_set_ldisc_option(mc->cl, LD_ECHO, true);
-        ssh_set_ldisc_option(mc->cl, LD_EDIT, true);
-        mainchan_ready(mc);
-    }
-}
-
-static void mainchan_try_fallback_command(mainchan *mc)
-{
-    const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2);
-    if (conf_get_bool(mc->conf, CONF_ssh_subsys2)) {
-        sshfwd_start_subsystem(mc->sc, true, cmd);
-    } else if (*cmd) {
-        sshfwd_start_command(mc->sc, true, cmd);
-    } else {
-        sshfwd_start_shell(mc->sc, true); // WINSCP
-    }
-    mc->req_cmd_fallback = true;
-}
-
-static void mainchan_request_response(Channel *chan, bool success)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
-
-    if (mc->req_x11) {
-        mc->req_x11 = false;
-
-        if (success) {
-            ppl_logevent("X11 forwarding enabled");
-            ssh_enable_x_fwd(mc->cl);
-        } else {
-            ppl_logevent("X11 forwarding refused");
-        }
-        return;
-    }
-
-    if (mc->req_agent) {
-        mc->req_agent = false;
-
-        if (success) {
-            ppl_logevent("Agent forwarding enabled");
-        } else {
-            ppl_logevent("Agent forwarding refused");
-        }
-        return;
-    }
-
-    if (mc->req_pty) {
-        mc->req_pty = false;
-
-        if (success) {
-            ppl_logevent("Allocated pty");
-            mc->got_pty = true;
-        } else {
-            ppl_logevent("Server refused to allocate pty");
-            ppl_printf("Server refused to allocate pty\r\n");
-            ssh_set_ldisc_option(mc->cl, LD_ECHO, true);
-            ssh_set_ldisc_option(mc->cl, LD_EDIT, true);
-        }
-        return;
-    }
-
-    if (mc->n_env_replies < mc->n_req_env) {
-        int j = mc->n_env_replies++;
-        if (!success) {
-            ppl_logevent("Server refused to set environment variable %s",
-                         conf_get_str_nthstrkey(mc->conf,
-                                                CONF_environmt, j));
-            mc->n_env_fails++;
-        }
-
-        if (mc->n_env_replies == mc->n_req_env) {
-            if (mc->n_env_fails == 0) {
-                ppl_logevent("All environment variables successfully set");
-            } else if (mc->n_env_fails == mc->n_req_env) {
-                ppl_logevent("All environment variables refused");
-                ppl_printf("Server refused to set environment "
-                           "variables\r\n");
-            } else {
-                ppl_printf("Server refused to set all environment "
-                           "variables\r\n");
-            }
-        }
-        return;
-    }
-
-    if (mc->req_cmd_primary) {
-        mc->req_cmd_primary = false;
-
-        if (success) {
-            ppl_logevent("Started a shell/command");
-            mainchan_ready(mc);
-        } else if (*conf_get_str(mc->conf, CONF_remote_cmd2) || conf_get_bool(mc->conf, CONF_force_remote_cmd2)) { // WINSCP
-            ppl_logevent("Primary command failed; attempting fallback");
-            mainchan_try_fallback_command(mc);
-        } else {
-            /*
-             * If there's no remote_cmd2 configured, then we have no
-             * fallback command, so we've run out of options.
-             */
-            ssh_sw_abort(mc->ppl->ssh,
-                         "Server refused to start a shell/command");
-        }
-        return;
-    }
-
-    if (mc->req_cmd_fallback) {
-        mc->req_cmd_fallback = false;
-
-        if (success) {
-            ppl_logevent("Started a shell/command");
-            ssh_got_fallback_cmd(mc->ppl->ssh);
-            mainchan_ready(mc);
-        } else {
-            ssh_sw_abort(mc->ppl->ssh,
-                         "Server refused to start a shell/command");
-        }
-        return;
-    }
-}
-
-static void mainchan_ready(mainchan *mc)
-{
-    mc->ready = true;
-
-    ssh_set_wants_user_input(mc->cl, true);
-    ssh_ppl_got_user_input(mc->ppl); /* in case any is already queued */
-
-    /* If an EOF arrived before we were ready, handle it now. */
-    if (mc->eof_pending) {
-        mc->eof_pending = false;
-        mainchan_special_cmd(mc, SS_EOF, 0);
-    }
-
-    ssh_ldisc_update(mc->ppl->ssh);
-    queue_idempotent_callback(&mc->ppl->ic_process_queue);
-}
-
-static void mainchan_open_failure(Channel *chan, const char *errtext)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-
-    ssh_sw_abort_deferred(mc->ppl->ssh,
-                          "Server refused to open main channel: %s", errtext);
-}
-
-static size_t mainchan_send(Channel *chan, bool is_stderr,
-                         const void *data, size_t length)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    return seat_output(mc->ppl->seat, is_stderr, data, length);
-}
-
-static void mainchan_send_eof(Channel *chan)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
-
-    if (!mc->eof_sent && (seat_eof(mc->ppl->seat) || mc->got_pty)) {
-        /*
-         * Either seat_eof told us that the front end wants us to
-         * close the outgoing side of the connection as soon as we see
-         * EOF from the far end, or else we've unilaterally decided to
-         * do that because we've allocated a remote pty and hence EOF
-         * isn't a particularly meaningful concept.
-         */
-        sshfwd_write_eof(mc->sc);
-        ppl_logevent("Sent EOF message");
-        mc->eof_sent = true;
-        ssh_set_wants_user_input(mc->cl, false); /* stop reading from stdin */
-    }
-}
-
-static void mainchan_set_input_wanted(Channel *chan, bool wanted)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-
-    /*
-     * This is the main channel of the SSH session, i.e. the one tied
-     * to the standard input (or GUI) of the primary SSH client user
-     * interface. So ssh->send_ok is how we control whether we're
-     * reading from that input.
-     */
-    ssh_set_wants_user_input(mc->cl, wanted);
-}
-
-static char *mainchan_log_close_msg(Channel *chan)
-{
-    return dupstr("Main session channel closed");
-}
-
-static bool mainchan_rcvd_exit_status(Channel *chan, int status)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
-
-    ssh_got_exitcode(mc->ppl->ssh, status);
-    ppl_logevent("Session sent command exit status %d", status);
-    return true;
-}
-
-static void mainchan_log_exit_signal_common(
-    mainchan *mc, const char *sigdesc,
-    bool core_dumped, ptrlen msg)
-{
-    PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
-
-    const char *core_msg = core_dumped ? " (core dumped)" : "";
-    const char *msg_pre = (msg.len ? " (" : "");
-    const char *msg_post = (msg.len ? ")" : "");
-    ppl_logevent("Session exited on %s%s%s%.*s%s",
-                 sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post);
-}
-
-static bool mainchan_rcvd_exit_signal(
-    Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    int exitcode;
-    char *signame_str;
-
-    /*
-     * Translate the signal description back into a locally meaningful
-     * number, or 128 if the string didn't match any we recognise.
-     */
-    exitcode = 128;
-
-    #define SIGNAL_SUB(s) \
-        if (ptrlen_eq_string(signame, #s))      \
-            exitcode = 128 + SIG ## s;
-    #define SIGNAL_MAIN(s, text) SIGNAL_SUB(s)
-    #define SIGNALS_LOCAL_ONLY
-    #include "sshsignals.h"
-    #undef SIGNAL_SUB
-    #undef SIGNAL_MAIN
-    #undef SIGNALS_LOCAL_ONLY
-
-    ssh_got_exitcode(mc->ppl->ssh, exitcode);
-    if (exitcode == 128)
-        signame_str = dupprintf("unrecognised signal \"%.*s\"",
-                                PTRLEN_PRINTF(signame));
-    else
-        signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame));
-    mainchan_log_exit_signal_common(mc, signame_str, core_dumped, msg);
-    sfree(signame_str);
-    return true;
-}
-
-static bool mainchan_rcvd_exit_signal_numeric(
-    Channel *chan, int signum, bool core_dumped, ptrlen msg)
-{
-    pinitassert(chan->vt == &mainchan_channelvt);
-    mainchan *mc = container_of(chan, mainchan, chan);
-    char *signum_str;
-
-    ssh_got_exitcode(mc->ppl->ssh, 128 + signum);
-    signum_str = dupprintf("signal %d", signum);
-    mainchan_log_exit_signal_common(mc, signum_str, core_dumped, msg);
-    sfree(signum_str);
-    return true;
-}
-
-void mainchan_get_specials(
-    mainchan *mc, add_special_fn_t add_special, void *ctx)
-{
-    /* FIXME: this _does_ depend on whether these services are supported */
-
-    /* WINSCP (causes linker warning and we do not use these)
-    add_special(ctx, "Break", SS_BRK, 0);
-
-    #define SIGNAL_MAIN(name, desc) \
-    add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0);
-    #define SIGNAL_SUB(name)
-    #include "sshsignals.h"
-    #undef SIGNAL_MAIN
-    #undef SIGNAL_SUB
-
-    add_special(ctx, "More signals", SS_SUBMENU, 0);
-
-    #define SIGNAL_MAIN(name, desc)
-    #define SIGNAL_SUB(name) \
-    add_special(ctx, "SIG" #name, SS_SIG ## name, 0);
-    #include "sshsignals.h"
-    #undef SIGNAL_MAIN
-    #undef SIGNAL_SUB
-
-    add_special(ctx, NULL, SS_EXITMENU, 0);
-    */
-}
-
-static const char *ssh_signal_lookup(SessionSpecialCode code)
-{
-    #define SIGNAL_SUB(name) \
-    if (code == SS_SIG ## name) return #name;
-    #define SIGNAL_MAIN(name, desc) SIGNAL_SUB(name)
-    #include "sshsignals.h"
-    #undef SIGNAL_MAIN
-    #undef SIGNAL_SUB
-
-    /* If none of those clauses matched, fail lookup. */
-    return NULL;
-}
-
-void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg)
-{
-    PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
-    const char *signame;
-
-    if (code == SS_EOF) {
-        if (!mc->ready) {
-            /*
-             * Buffer the EOF to send as soon as the main channel is
-             * fully set up.
-             */
-            mc->eof_pending = true;
-        } else if (!mc->eof_sent) {
-            sshfwd_write_eof(mc->sc);
-            mc->eof_sent = true;
-        }
-    } else if (code == SS_BRK) {
-        sshfwd_send_serial_break(
-            mc->sc, false, 0 /* default break length */);
-    } else if ((signame = ssh_signal_lookup(code)) != NULL) {
-        /* It's a signal. */
-        sshfwd_send_signal(mc->sc, false, signame);
-        ppl_logevent("Sent signal SIG%s", signame);
-    }
-}
-
-void mainchan_terminal_size(mainchan *mc, int width, int height)
-{
-    mc->term_width = width;
-    mc->term_height = height;
-
-    if (mc->req_pty || mc->got_pty)
-        sshfwd_send_terminal_size_change(mc->sc, width, height);
-}

+ 0 - 320
source/putty/marshal.c

@@ -1,320 +0,0 @@
-#include <assert.h>
-#include <stddef.h>
-#include <string.h>
-
-#include "marshal.h"
-#include "misc.h"
-
-void BinarySink_put_data(BinarySink *bs, const void *data, size_t len)
-{
-    bs->write(bs, data, len);
-}
-
-void BinarySink_put_datapl(BinarySink *bs, ptrlen pl)
-{
-    BinarySink_put_data(bs, pl.ptr, pl.len);
-}
-
-void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte)
-{
-    char buf[16];
-    memset(buf, padbyte, sizeof(buf));
-    while (len > 0) {
-        size_t thislen = len < sizeof(buf) ? len : sizeof(buf);
-        bs->write(bs, buf, thislen);
-        len -= thislen;
-    }
-}
-
-void BinarySink_put_byte(BinarySink *bs, unsigned char val)
-{
-    bs->write(bs, &val, 1);
-}
-
-void BinarySink_put_bool(BinarySink *bs, bool val)
-{
-    unsigned char cval = val ? 1 : 0;
-    bs->write(bs, &cval, 1);
-}
-
-void BinarySink_put_uint16(BinarySink *bs, unsigned long val)
-{
-    unsigned char data[2];
-    PUT_16BIT_MSB_FIRST(data, val);
-    bs->write(bs, data, sizeof(data));
-}
-
-void BinarySink_put_uint32(BinarySink *bs, unsigned long val)
-{
-    unsigned char data[4];
-    PUT_32BIT_MSB_FIRST(data, val);
-    bs->write(bs, data, sizeof(data));
-}
-
-void BinarySink_put_uint64(BinarySink *bs, uint64_t val)
-{
-    unsigned char data[8];
-    PUT_64BIT_MSB_FIRST(data, val);
-    bs->write(bs, data, sizeof(data));
-}
-
-void BinarySink_put_string(BinarySink *bs, const void *data, size_t len)
-{
-    /* Check that the string length fits in a uint32, without doing a
-     * potentially implementation-defined shift of more than 31 bits */
-    assert((len >> 31) < 2);
-
-    BinarySink_put_uint32(bs, len);
-    bs->write(bs, data, len);
-}
-
-void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl)
-{
-    BinarySink_put_string(bs, pl.ptr, pl.len);
-}
-
-void BinarySink_put_stringz(BinarySink *bs, const char *str)
-{
-    BinarySink_put_string(bs, str, strlen(str));
-}
-
-void BinarySink_put_stringsb(BinarySink *bs, struct strbuf *buf)
-{
-    BinarySink_put_string(bs, buf->s, buf->len);
-    strbuf_free(buf);
-}
-
-void BinarySink_put_asciz(BinarySink *bs, const char *str)
-{
-    bs->write(bs, str, strlen(str) + 1);
-}
-
-bool BinarySink_put_pstring(BinarySink *bs, const char *str)
-{
-    size_t len = strlen(str);
-    if (len > 255)
-        return false; /* can't write a Pascal-style string this long */
-    BinarySink_put_byte(bs, len);
-    bs->write(bs, str, len);
-    return true;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static bool BinarySource_data_avail(BinarySource *src, size_t wanted)
-{
-    if (src->err)
-        return false;
-
-    if (wanted <= src->len - src->pos)
-        return true;
-
-    src->err = BSE_OUT_OF_DATA;
-    return false;
-}
-
-#define avail(wanted) BinarySource_data_avail(src, wanted)
-#define advance(dist) (src->pos += dist)
-#define here ((const void *)((const unsigned char *)src->data + src->pos))
-#define consume(dist)                           \
-    ((const void *)((const unsigned char *)src->data + \
-                    ((src->pos += dist) - dist)))
-
-ptrlen BinarySource_get_data(BinarySource *src, size_t wanted)
-{
-    if (!avail(wanted))
-        return make_ptrlen("", 0);
-
-    return make_ptrlen(consume(wanted), wanted);
-}
-
-unsigned char BinarySource_get_byte(BinarySource *src)
-{
-    const unsigned char *ucp;
-
-    if (!avail(1))
-        return 0;
-
-    ucp = consume(1);
-    return *ucp;
-}
-
-bool BinarySource_get_bool(BinarySource *src)
-{
-    const unsigned char *ucp;
-
-    if (!avail(1))
-        return false;
-
-    ucp = consume(1);
-    return *ucp != 0;
-}
-
-unsigned BinarySource_get_uint16(BinarySource *src)
-{
-    const unsigned char *ucp;
-
-    if (!avail(2))
-        return 0;
-
-    ucp = consume(2);
-    return GET_16BIT_MSB_FIRST(ucp);
-}
-
-unsigned long BinarySource_get_uint32(BinarySource *src)
-{
-    const unsigned char *ucp;
-
-    if (!avail(4))
-        return 0;
-
-    ucp = consume(4);
-    return GET_32BIT_MSB_FIRST(ucp);
-}
-
-uint64_t BinarySource_get_uint64(BinarySource *src)
-{
-    const unsigned char *ucp;
-
-    if (!avail(8))
-        return 0;
-
-    ucp = consume(8);
-    return GET_64BIT_MSB_FIRST(ucp);
-}
-
-ptrlen BinarySource_get_string(BinarySource *src)
-{
-    const unsigned char *ucp;
-    size_t len;
-
-    if (!avail(4))
-        return make_ptrlen("", 0);
-
-    ucp = consume(4);
-    len = GET_32BIT_MSB_FIRST(ucp);
-
-    if (!avail(len))
-        return make_ptrlen("", 0);
-
-    return make_ptrlen(consume(len), len);
-}
-
-const char *BinarySource_get_asciz(BinarySource *src)
-{
-    const char *start, *end;
-
-    if (src->err)
-        return "";
-
-    start = here;
-    end = memchr(start, '\0', src->len - src->pos);
-    if (!end) {
-        src->err = BSE_OUT_OF_DATA;
-        return "";
-    }
-
-    advance(end + 1 - start);
-    return start;
-}
-
-static ptrlen BinarySource_get_chars_internal(
-    BinarySource *src, const char *set, bool include)
-{
-    const char *start = here;
-    while (avail(1)) {
-        bool present = NULL != strchr(set, *(const char *)consume(0));
-        if (present != include)
-            break;
-        (void) consume(1);
-    }
-    { // WINSCP
-    const char *end = here;
-    return make_ptrlen(start, end - start);
-    } // WINSCP
-}
-
-ptrlen BinarySource_get_chars(BinarySource *src, const char *include_set)
-{
-    return BinarySource_get_chars_internal(src, include_set, true);
-}
-
-ptrlen BinarySource_get_nonchars(BinarySource *src, const char *exclude_set)
-{
-    return BinarySource_get_chars_internal(src, exclude_set, false);
-}
-
-ptrlen BinarySource_get_chomped_line(BinarySource *src)
-{
-    const char *start, *end;
-
-    if (src->err)
-        return make_ptrlen(here, 0);
-
-    start = here;
-    end = memchr(start, '\n', src->len - src->pos);
-    if (end)
-        advance(end + 1 - start);
-    else
-        advance(src->len - src->pos);
-    end = here;
-
-    if (end > start && end[-1] == '\n')
-        end--;
-    if (end > start && end[-1] == '\r')
-        end--;
-
-    return make_ptrlen(start, end - start);
-}
-
-ptrlen BinarySource_get_pstring(BinarySource *src)
-{
-    const unsigned char *ucp;
-    size_t len;
-
-    if (!avail(1))
-        return make_ptrlen("", 0);
-
-    ucp = consume(1);
-    len = *ucp;
-
-    if (!avail(len))
-        return make_ptrlen("", 0);
-
-    return make_ptrlen(consume(len), len);
-}
-
-void BinarySource_REWIND_TO__(BinarySource *src, size_t pos)
-{
-    if (pos <= src->len) {
-        src->pos = pos;
-        src->err = BSE_NO_ERROR;    /* clear any existing error */
-    } else {
-        src->pos = src->len;
-        src->err = BSE_OUT_OF_DATA; /* new error if we rewind out of range */
-    }
-}
-
-static void stdio_sink_write(BinarySink *bs, const void *data, size_t len)
-{
-    stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink);
-    fwrite(data, 1, len, sink->fp);
-}
-
-void stdio_sink_init(stdio_sink *sink, FILE *fp)
-{
-    sink->fp = fp;
-    BinarySink_INIT(sink, stdio_sink_write);
-}
-
-static void bufchain_sink_write(BinarySink *bs, const void *data, size_t len)
-{
-    bufchain_sink *sink = BinarySink_DOWNCAST(bs, bufchain_sink);
-    bufchain_add(sink->ch, data, len);
-}
-
-void bufchain_sink_init(bufchain_sink *sink, bufchain *ch)
-{
-    sink->ch = ch;
-    BinarySink_INIT(sink, bufchain_sink_write);
-}

+ 21 - 2
source/putty/marshal.h

@@ -4,6 +4,7 @@
 #include "defs.h"
 
 #include <stdio.h>
+#include <stdarg.h>
 
 /*
  * A sort of 'abstract base class' or 'interface' or 'trait' which is
@@ -12,6 +13,7 @@
  */
 struct BinarySink {
     void (*write)(BinarySink *sink, const void *data, size_t len);
+    void (*writefmtv)(BinarySink *sink, const char *fmt, va_list ap);
     BinarySink *binarysink_;
 };
 
@@ -25,6 +27,7 @@ struct BinarySink {
 #define BinarySink_IMPLEMENTATION BinarySink binarysink_[1]
 #define BinarySink_INIT(obj, writefn) \
     ((obj)->binarysink_->write = (writefn), \
+     (obj)->binarysink_->writefmtv = NULL, \
      (obj)->binarysink_->binarysink_ = (obj)->binarysink_)
 
 /*
@@ -138,6 +141,20 @@ struct BinarySink {
     BinarySink_put_data(BinarySink_UPCAST(bs), val, len)
 #define put_datapl(bs, pl) \
     BinarySink_put_datapl(BinarySink_UPCAST(bs), pl)
+#define put_dataz(bs, val) \
+    BinarySink_put_datapl(BinarySink_UPCAST(bs), ptrlen_from_asciz(val))
+#define put_datalit(bs, val) \
+    BinarySink_put_datapl(BinarySink_UPCAST(bs), PTRLEN_LITERAL(val))
+
+/* Emit printf-formatted data, with no terminator. */
+#define put_fmt(bs, ...) \
+    BinarySink_put_fmt(BinarySink_UPCAST(bs), __VA_ARGS__)
+#define put_fmtv(bs, fmt, ap) \
+    BinarySink_put_fmtv(BinarySink_UPCAST(bs), fmt, ap)
+
+/* More complicated function implemented in write_c_string_literal.c */
+#define put_c_string_literal(bs, str) \
+    BinarySink_put_c_string_literal(BinarySink_UPCAST(bs), str)
 
 /*
  * The underlying real C functions that implement most of those
@@ -160,12 +177,14 @@ void BinarySink_put_uint64(BinarySink *, uint64_t);
 void BinarySink_put_string(BinarySink *, const void *data, size_t len);
 void BinarySink_put_stringpl(BinarySink *, ptrlen);
 void BinarySink_put_stringz(BinarySink *, const char *str);
-struct strbuf;
-void BinarySink_put_stringsb(BinarySink *, struct strbuf *);
+void BinarySink_put_stringsb(BinarySink *, strbuf *);
 void BinarySink_put_asciz(BinarySink *, const char *str);
 bool BinarySink_put_pstring(BinarySink *, const char *str);
 void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x);
 void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x);
+void BinarySink_put_fmt(BinarySink *, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void BinarySink_put_fmtv(BinarySink *, const char *fmt, va_list ap);
+void BinarySink_put_c_string_literal(BinarySink *, ptrlen);
 
 /* ---------------------------------------------------------------------- */
 

+ 0 - 146
source/putty/memory.c

@@ -1,146 +0,0 @@
-/*
- * PuTTY's memory allocation wrappers.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <limits.h>
-
-#include "defs.h"
-#include "puttymem.h"
-#include "misc.h"
-
-void *safemalloc(size_t factor1, size_t factor2, size_t addend)
-{
-    if (factor1 > SIZE_MAX / factor2)
-        goto fail;
-    { // WINSCP
-    size_t product = factor1 * factor2;
-
-    if (addend > SIZE_MAX)
-        goto fail;
-    if (product > SIZE_MAX - addend)
-        goto fail;
-    { // WINSCP
-    size_t size = product + addend;
-
-    if (size == 0)
-        size = 1;
-
-    { // WINSCP
-    void *p;
-#ifdef MINEFIELD
-    p = minefield_c_malloc(size);
-#else
-    p = malloc(size);
-#endif
-
-    if (!p)
-        goto fail;
-
-    return p;
-
-  fail:
-    out_of_memory();
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-void *saferealloc(void *ptr, size_t n, size_t size)
-{
-    void *p;
-
-    if (n > INT_MAX / size) {
-        p = NULL;
-    } else {
-        size *= n;
-        if (!ptr) {
-#ifdef MINEFIELD
-            p = minefield_c_malloc(size);
-#else
-            p = malloc(size);
-#endif
-        } else {
-#ifdef MINEFIELD
-            p = minefield_c_realloc(ptr, size);
-#else
-            p = realloc(ptr, size);
-#endif
-        }
-    }
-
-    if (!p)
-        out_of_memory();
-
-    return p;
-}
-
-void safefree(void *ptr)
-{
-    if (ptr) {
-#ifdef MINEFIELD
-        minefield_c_free(ptr);
-#else
-        free(ptr);
-#endif
-    }
-}
-
-void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
-                    size_t oldlen, size_t extralen, bool secret)
-{
-    /* The largest value we can safely multiply by eltsize */
-    pinitassert(eltsize > 0);
-    size_t maxsize = (~(size_t)0) / eltsize;
-
-    size_t oldsize = *allocated;
-
-    /* Range-check the input values */
-    assert(oldsize <= maxsize);
-    assert(oldlen <= maxsize);
-    assert(extralen <= maxsize - oldlen);
-
-    /* If the size is already enough, don't bother doing anything! */
-    if (oldsize > oldlen + extralen)
-        return ptr;
-
-    /* Find out how much we need to grow the array by. */
-    { // WINSCP
-    size_t increment = (oldlen + extralen) - oldsize;
-
-    /* Invent a new size. We want to grow the array by at least
-     * 'increment' elements; by at least a fixed number of bytes (to
-     * get things started when sizes are small); and by some constant
-     * factor of its old size (to avoid repeated calls to this
-     * function taking quadratic time overall). */
-    if (increment < 256 / eltsize)
-        increment = 256 / eltsize;
-    if (increment < oldsize / 16)
-        increment = oldsize / 16;
-
-    /* But we also can't grow beyond maxsize. */
-    { // WINSCP
-    size_t maxincr = maxsize - oldsize;
-    if (increment > maxincr)
-        increment = maxincr;
-
-    { // WINSCP
-    size_t newsize = oldsize + increment;
-    void *toret;
-    if (secret) {
-        toret = safemalloc(newsize, eltsize, 0);
-        if (oldsize) {
-            memcpy(toret, ptr, oldsize * eltsize);
-            smemclr(ptr, oldsize * eltsize);
-            sfree(ptr);
-        }
-    } else {
-        toret = saferealloc(ptr, newsize, eltsize);
-    }
-    *allocated = newsize;
-    return toret;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}

+ 0 - 434
source/putty/misc.c

@@ -1,434 +0,0 @@
-/*
- * Platform-independent routines shared between all PuTTY programs.
- *
- * This file contains functions that use the kind of infrastructure
- * like conf.c that tends to only live in the main applications, or
- * that do things that only something like a main PuTTY application
- * would need. So standalone test programs should generally be able to
- * avoid linking against it.
- *
- * More standalone functions that depend on nothing but the C library
- * live in utils.c.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <ctype.h>
-#include <assert.h>
-
-#include "defs.h"
-#include "putty.h"
-#include "misc.h"
-
-#define BASE64_CHARS_NOEQ \
-    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
-#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
-
-void seat_connection_fatal(Seat *seat, const char *fmt, ...)
-{
-    va_list ap;
-    char *msg;
-
-    va_start(ap, fmt);
-    msg = dupvprintf(fmt, ap);
-    va_end(ap);
-
-    seat->vt->connection_fatal(seat, msg);
-    sfree(msg);                        /* if we return */
-}
-
-prompts_t *new_prompts(void)
-{
-    prompts_t *p = snew(prompts_t);
-    p->prompts = NULL;
-    p->n_prompts = p->prompts_size = 0;
-    p->data = NULL;
-    p->to_server = true; /* to be on the safe side */
-    p->name = p->instruction = NULL;
-    p->name_reqd = p->instr_reqd = false;
-    return p;
-}
-void add_prompt(prompts_t *p, char *promptstr, bool echo)
-{
-    prompt_t *pr = snew(prompt_t);
-    pr->prompt = promptstr;
-    pr->echo = echo;
-    pr->result = strbuf_new_nm();
-    sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
-    p->prompts[p->n_prompts++] = pr;
-}
-void prompt_set_result(prompt_t *pr, const char *newstr)
-{
-    strbuf_clear(pr->result);
-    put_datapl(pr->result, ptrlen_from_asciz(newstr));
-}
-const char *prompt_get_result_ref(prompt_t *pr)
-{
-    return pr->result->s;
-}
-char *prompt_get_result(prompt_t *pr)
-{
-    return dupstr(pr->result->s);
-}
-void free_prompts(prompts_t *p)
-{
-    size_t i;
-    for (i=0; i < p->n_prompts; i++) {
-        prompt_t *pr = p->prompts[i];
-        strbuf_free(pr->result);
-        sfree(pr->prompt);
-        sfree(pr);
-    }
-    sfree(p->prompts);
-    sfree(p->name);
-    sfree(p->instruction);
-    sfree(p);
-}
-
-/*
- * Determine whether or not a Conf represents a session which can
- * sensibly be launched right now.
- */
-bool conf_launchable(Conf *conf)
-{
-    if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
-        return conf_get_str(conf, CONF_serline)[0] != 0;
-    else
-        return conf_get_str(conf, CONF_host)[0] != 0;
-}
-
-char const *conf_dest(Conf *conf)
-{
-    if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
-        return conf_get_str(conf, CONF_serline);
-    else
-        return conf_get_str(conf, CONF_host);
-}
-
-/*
- * Validate a manual host key specification (either entered in the
- * GUI, or via -hostkey). If valid, we return true, and update 'key'
- * to contain a canonicalised version of the key string in 'key'
- * (which is guaranteed to take up at most as much space as the
- * original version), suitable for putting into the Conf. If not
- * valid, we return false.
- */
-bool validate_manual_hostkey(char *key)
-{
-    char *p, *q, *r, *s;
-
-    /*
-     * Step through the string word by word, looking for a word that's
-     * in one of the formats we like.
-     */
-    p = key;
-    while ((p += strspn(p, " \t"))[0]) {
-        q = p;
-        p += strcspn(p, " \t");
-        if (*p) *p++ = '\0';
-
-        /*
-         * Now q is our word.
-         */
-
-        if (strstartswith(q, "SHA256:")) {
-            /* Test for a valid SHA256 key fingerprint. */
-            r = q + 7;
-            if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
-                return true;
-        }
-
-        r = q;
-        if (strstartswith(r, "MD5:"))
-            r += 4;
-        if (strlen(r) == 16*3 - 1 &&
-            r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
-            /*
-             * Test for a valid MD5 key fingerprint. Check the colons
-             * are in the right places, and if so, return the same
-             * fingerprint canonicalised into lowercase.
-             */
-            int i;
-            for (i = 0; i < 16; i++)
-                if (r[3*i] == ':' || r[3*i+1] == ':')
-                    goto not_fingerprint; /* sorry */
-            for (i = 0; i < 15; i++)
-                if (r[3*i+2] != ':')
-                    goto not_fingerprint; /* sorry */
-            for (i = 0; i < 16*3 - 1; i++)
-                key[i] = tolower(r[i]);
-            key[16*3 - 1] = '\0';
-            return true;
-        }
-      not_fingerprint:;
-
-        /*
-         * Before we check for a public-key blob, trim newlines out of
-         * the middle of the word, in case someone's managed to paste
-         * in a public-key blob _with_ them.
-         */
-        for (r = s = q; *r; r++)
-            if (*r != '\n' && *r != '\r')
-                *s++ = *r;
-        *s = '\0';
-
-        if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
-            q[strspn(q, BASE64_CHARS_ALL)] == 0) {
-            /*
-             * Might be a base64-encoded SSH-2 public key blob. Check
-             * that it starts with a sensible algorithm string. No
-             * canonicalisation is necessary for this string type.
-             *
-             * The algorithm string must be at most 64 characters long
-             * (RFC 4251 section 6).
-             */
-            unsigned char decoded[6];
-            unsigned alglen;
-            int minlen;
-            int len = 0;
-
-            len += base64_decode_atom(q, decoded+len);
-            if (len < 3)
-                goto not_ssh2_blob;    /* sorry */
-            len += base64_decode_atom(q+4, decoded+len);
-            if (len < 4)
-                goto not_ssh2_blob;    /* sorry */
-
-            alglen = GET_32BIT_MSB_FIRST(decoded);
-            if (alglen > 64)
-                goto not_ssh2_blob;    /* sorry */
-
-            minlen = ((alglen + 4) + 2) / 3;
-            if (strlen(q) < minlen)
-                goto not_ssh2_blob;    /* sorry */
-
-            strcpy(key, q);
-            return true;
-        }
-      not_ssh2_blob:;
-    }
-
-    return false;
-}
-
-#ifndef WINSCP
-char *buildinfo(const char *newline)
-{
-    strbuf *buf = strbuf_new();
-
-    strbuf_catf(buf, "Build platform: %d-bit %s",
-                (int)(CHAR_BIT * sizeof(void *)),
-                BUILDINFO_PLATFORM);
-
-#ifdef __clang_version__
-#define FOUND_COMPILER
-    strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
-#elif defined __GNUC__ && defined __VERSION__
-#define FOUND_COMPILER
-    strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
-#endif
-
-#if defined _MSC_VER
-#ifndef FOUND_COMPILER
-#define FOUND_COMPILER
-    strbuf_catf(buf, "%sCompiler: ", newline);
-#else
-    strbuf_catf(buf, ", emulating ");
-#endif
-    strbuf_catf(buf, "Visual Studio");
-
-#if 0
-    /*
-     * List of _MSC_VER values and their translations taken from
-     * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
-     *
-     * The pointless #if 0 branch containing this comment is there so
-     * that every real clause can start with #elif and there's no
-     * anomalous first clause. That way the patch looks nicer when you
-     * add extra ones.
-     */
-#elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500
-    /*
-     * 16.9 and 16.8 have the same _MSC_VER value, and have to be
-     * distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not
-     * mentioned on the above page, but see e.g.
-     * https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120
-     * which says that 16.9 builds will have versions starting at
-     * 19.28.29500.* and going up. Hence, 19 28 29500 is what we
-     * compare _MSC_FULL_VER against above.
-     */
-    strbuf_catf(buf, " 2019 (16.9)");
-#elif _MSC_VER == 1928
-    strbuf_catf(buf, " 2019 (16.8)");
-#elif _MSC_VER == 1927
-    strbuf_catf(buf, " 2019 (16.7)");
-#elif _MSC_VER == 1926
-    strbuf_catf(buf, " 2019 (16.6)");
-#elif _MSC_VER == 1925
-    strbuf_catf(buf, " 2019 (16.5)");
-#elif _MSC_VER == 1924
-    strbuf_catf(buf, " 2019 (16.4)");
-#elif _MSC_VER == 1923
-    strbuf_catf(buf, " 2019 (16.3)");
-#elif _MSC_VER == 1922
-    strbuf_catf(buf, " 2019 (16.2)");
-#elif _MSC_VER == 1921
-    strbuf_catf(buf, " 2019 (16.1)");
-#elif _MSC_VER == 1920
-    strbuf_catf(buf, " 2019 (16.0)");
-#elif _MSC_VER == 1916
-    strbuf_catf(buf, " 2017 version 15.9");
-#elif _MSC_VER == 1915
-    strbuf_catf(buf, " 2017 version 15.8");
-#elif _MSC_VER == 1914
-    strbuf_catf(buf, " 2017 version 15.7");
-#elif _MSC_VER == 1913
-    strbuf_catf(buf, " 2017 version 15.6");
-#elif _MSC_VER == 1912
-    strbuf_catf(buf, " 2017 version 15.5");
-#elif _MSC_VER == 1911
-    strbuf_catf(buf, " 2017 version 15.3");
-#elif _MSC_VER == 1910
-    strbuf_catf(buf, " 2017 RTW (15.0)");
-#elif _MSC_VER == 1900
-    strbuf_catf(buf, " 2015 (14.0)");
-#elif _MSC_VER == 1800
-    strbuf_catf(buf, " 2013 (12.0)");
-#elif _MSC_VER == 1700
-    strbuf_catf(buf, " 2012 (11.0)");
-#elif _MSC_VER == 1600
-    strbuf_catf(buf, " 2010 (10.0)");
-#elif _MSC_VER == 1500
-    strbuf_catf(buf, " 2008 (9.0)");
-#elif _MSC_VER == 1400
-    strbuf_catf(buf, " 2005 (8.0)");
-#elif _MSC_VER == 1310
-    strbuf_catf(buf, " .NET 2003 (7.1)");
-#elif _MSC_VER == 1300
-    strbuf_catf(buf, " .NET 2002 (7.0)");
-#elif _MSC_VER == 1200
-    strbuf_catf(buf, " 6.0");
-#else
-    strbuf_catf(buf, ", unrecognised version");
-#endif
-    strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
-#endif
-
-#ifdef BUILDINFO_GTK
-    {
-        char *gtk_buildinfo = buildinfo_gtk_version();
-        if (gtk_buildinfo) {
-            strbuf_catf(buf, "%sCompiled against GTK version %s",
-                        newline, gtk_buildinfo);
-            sfree(gtk_buildinfo);
-        }
-    }
-#endif
-#if defined _WINDOWS
-    {
-        int echm = has_embedded_chm();
-        if (echm >= 0)
-            strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
-                        echm ? "yes" : "no");
-    }
-#endif
-
-#if defined _WINDOWS && defined MINEFIELD
-    strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
-#endif
-#ifdef NO_SECURITY
-    strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline);
-#endif
-#ifdef NO_SECUREZEROMEMORY
-    strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
-#endif
-#ifdef NO_IPV6
-    strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
-#endif
-#ifdef NO_GSSAPI
-    strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
-#endif
-#ifdef STATIC_GSSAPI
-    strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
-#endif
-#ifdef UNPROTECT
-    strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
-#endif
-#ifdef FUZZING
-    strbuf_catf(buf, "%sBuild option: FUZZING", newline);
-#endif
-#ifdef DEBUG
-    strbuf_catf(buf, "%sBuild option: DEBUG", newline);
-#endif
-
-    strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
-
-    return strbuf_to_str(buf);
-}
-#endif !WINSCP
-
-#ifdef MPEXT
-
-#include "version.h"
-
-const char * get_putty_version()
-{
-    return TEXTVER;
-}
-
-#endif
-size_t nullseat_output(
-    Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
-bool nullseat_eof(Seat *seat) { return true; }
-int nullseat_get_userpass_input(
-    Seat *seat, prompts_t *p, bufchain *input) { return 0; }
-void nullseat_notify_remote_exit(Seat *seat) {}
-void nullseat_connection_fatal(Seat *seat, const char *message) {}
-void nullseat_update_specials_menu(Seat *seat) {}
-char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
-void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
-int nullseat_verify_ssh_host_key(
-    Seat *seat, const char *host, int port, const char *keytype,
-    char *keystr, const char *keydisp, char **key_fingerprints,
-    void (*callback)(void *ctx, int result), void *ctx) { return 0; }
-int nullseat_confirm_weak_crypto_primitive(
-    Seat *seat, const char *algtype, const char *algname,
-    void (*callback)(void *ctx, int result), void *ctx) { return 0; }
-int nullseat_confirm_weak_cached_hostkey(
-    Seat *seat, const char *algname, const char *betteralgs,
-    void (*callback)(void *ctx, int result), void *ctx) { return 0; }
-bool nullseat_is_never_utf8(Seat *seat) { return false; }
-bool nullseat_is_always_utf8(Seat *seat) { return true; }
-void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
-const char *nullseat_get_x_display(Seat *seat) { return NULL; }
-bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
-bool nullseat_get_window_pixel_size(
-    Seat *seat, int *width, int *height) { return false; }
-StripCtrlChars *nullseat_stripctrl_new(
-    Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
-bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
-bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
-bool nullseat_verbose_no(Seat *seat) { return false; }
-bool nullseat_verbose_yes(Seat *seat) { return true; }
-bool nullseat_interactive_no(Seat *seat) { return false; }
-bool nullseat_interactive_yes(Seat *seat) { return true; }
-bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; }
-
-bool null_lp_verbose_no(LogPolicy *lp) { return false; }
-bool null_lp_verbose_yes(LogPolicy *lp) { return true; }
-
-void sk_free_peer_info(SocketPeerInfo *pi)
-{
-    if (pi) {
-        sfree((char *)pi->addr_text);
-        sfree((char *)pi->log_text);
-        sfree(pi);
-    }
-}
-
-void out_of_memory(void)
-{
-    modalfatalbox("Out of memory");
-}

+ 30 - 12
source/putty/misc.h

@@ -1,5 +1,6 @@
 /*
- * Header for misc.c.
+ * Header for miscellaneous helper functions, mostly defined in the
+ * utils subdirectory.
  */
 
 #ifndef PUTTY_MISC_H
@@ -33,7 +34,7 @@ void burnstr(char *string);
 
 /*
  * The visible part of a strbuf structure. There's a surrounding
- * implementation struct in misc.c, which isn't exposed to client
+ * implementation struct in strbuf.c, which isn't exposed to client
  * code.
  */
 struct strbuf {
@@ -56,8 +57,6 @@ void *strbuf_append(strbuf *buf, size_t len);
 void strbuf_shrink_to(strbuf *buf, size_t new_len);
 void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove);
 char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
-void strbuf_catf(strbuf *buf, const char *fmt, ...) PRINTF_LIKE(2, 3);
-void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
 static inline void strbuf_clear(strbuf *buf) { strbuf_shrink_to(buf, 0); }
 bool strbuf_chomp(strbuf *buf, char char_to_remove);
 
@@ -65,11 +64,7 @@ strbuf *strbuf_new_for_agent_query(void);
 void strbuf_finalise_agent_query(strbuf *buf);
 
 /* String-to-Unicode converters that auto-allocate the destination and
- * work around the rather deficient interface of mb_to_wc.
- *
- * These actually live in miscucs.c, not misc.c (the distinction being
- * that the former is only linked into tools that also have the main
- * Unicode support). */
+ * work around the rather deficient interface of mb_to_wc. */
 wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len);
 wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string);
 
@@ -123,6 +118,8 @@ ptrlen bufchain_prefix(bufchain *ch);
 void bufchain_consume(bufchain *ch, size_t len);
 void bufchain_fetch(bufchain *ch, void *data, size_t len);
 void bufchain_fetch_consume(bufchain *ch, void *data, size_t len);
+bool bufchain_try_consume(bufchain *ch, size_t len);
+bool bufchain_try_fetch(bufchain *ch, void *data, size_t len);
 bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len);
 size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len);
 void bufchain_set_callback_inner(
@@ -132,9 +129,9 @@ static inline void bufchain_set_callback(bufchain *ch, IdempotentCallback *ic)
 {
     extern void queue_idempotent_callback(struct IdempotentCallback *ic);
     /* Wrapper that puts in the standard queue_idempotent_callback
-     * function. Lives here rather than in utils.c so that standalone
-     * programs can use the bufchain facility without this optional
-     * callback feature and not need to provide a stub of
+     * function. Lives here rather than in bufchain.c so that
+     * standalone programs can use the bufchain facility without this
+     * optional callback feature and not need to provide a stub of
      * queue_idempotent_callback. */
     bufchain_set_callback_inner(ch, ic, queue_idempotent_callback);
 }
@@ -215,6 +212,11 @@ bool smemeq(const void *av, const void *bv, size_t len);
  * been removed. */
 size_t encode_utf8(void *output, unsigned long ch);
 
+/* Encode a wide-character string into UTF-8. Tolerates surrogates if
+ * sizeof(wchar_t) == 2, assuming that in that case the wide string is
+ * encoded in UTF-16. */
+char *encode_wide_string_as_utf8(const wchar_t *wstr);
+
 /* Write a string out in C string-literal format. */
 void write_c_string_literal(FILE *fp, ptrlen str);
 
@@ -379,6 +381,22 @@ static inline void PUT_16BIT_MSB_FIRST(void *vp, uint16_t value)
     p[0] = (uint8_t)(value >> 8);
 }
 
+/* For use in X11-related applications, an endianness-variable form of
+ * {GET,PUT}_16BIT which expects 'endian' to be either 'B' or 'l' */
+
+static inline uint16_t GET_16BIT_X11(char endian, const void *p)
+{
+    return endian == 'B' ? GET_16BIT_MSB_FIRST(p) : GET_16BIT_LSB_FIRST(p);
+}
+
+static inline void PUT_16BIT_X11(char endian, void *p, uint16_t value)
+{
+    if (endian == 'B')
+        PUT_16BIT_MSB_FIRST(p, value);
+    else
+        PUT_16BIT_LSB_FIRST(p, value);
+}
+
 /* Replace NULL with the empty string, permitting an idiom in which we
  * get a string (pointer,length) pair that might be NULL,0 and can
  * then safely say things like printf("%.*s", length, NULLTOEMPTY(ptr)) */

+ 0 - 28
source/putty/miscucs.c

@@ -1,28 +0,0 @@
-/*
- * Centralised Unicode-related helper functions, separate from misc.c
- * so that they can be omitted from tools that aren't including
- * Unicode handling.
- */
-
-#include "putty.h"
-#include "misc.h"
-
-wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len)
-{
-    int mult;
-    for (mult = 1 ;; mult++) {
-        wchar_t *ret = snewn(mult*len + 2, wchar_t);
-        int outlen;
-        outlen = mb_to_wc(codepage, flags, string, len, ret, mult*len + 1);
-        if (outlen < mult*len+1) {
-            ret[outlen] = L'\0';
-            return ret;
-        }
-        sfree(ret);
-    }
-}
-
-wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string)
-{
-    return dup_mb_to_wc_c(codepage, flags, string, strlen(string));
-}

+ 0 - 2833
source/putty/mpint.c

@@ -1,2833 +0,0 @@
-#include <assert.h>
-#include <limits.h>
-#include <stdio.h>
-
-#include "defs.h"
-#include "misc.h"
-#include "puttymem.h"
-
-#include "mpint.h"
-#include "mpint_i.h"
-
-#pragma warn -ngu // WINSCP
-
-#define SIZE_T_BITS (CHAR_BIT * sizeof(size_t))
-
-/*
- * Inline helpers to take min and max of size_t values, used
- * throughout this code.
- */
-static inline size_t size_t_min(size_t a, size_t b)
-{
-    return a < b ? a : b;
-}
-static inline size_t size_t_max(size_t a, size_t b)
-{
-    return a > b ? a : b;
-}
-
-/*
- * Helper to fetch a word of data from x with array overflow checking.
- * If x is too short to have that word, 0 is returned.
- */
-static inline BignumInt mp_word(mp_int *x, size_t i)
-{
-    return i < x->nw ? x->w[i] : 0;
-}
-
-/*
- * Shift an ordinary C integer by BIGNUM_INT_BITS, in a way that
- * avoids writing a shift operator whose RHS is greater or equal to
- * the size of the type, because that's undefined behaviour in C.
- *
- * In fact we must avoid even writing it in a definitely-untaken
- * branch of an if, because compilers will sometimes warn about
- * that. So you can't just write 'shift too big ? 0 : n >> shift',
- * because even if 'shift too big' is a constant-expression
- * evaluating to false, you can still get complaints about the
- * else clause of the ?:.
- *
- * So we have to re-check _inside_ that clause, so that the shift
- * count is reset to something nonsensical but safe in the case
- * where the clause wasn't going to be taken anyway.
- */
-static uintmax_t shift_right_by_one_word(uintmax_t n)
-{
-    bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n);
-    return shift_too_big ? 0 :
-        n >> (shift_too_big ? 0 : BIGNUM_INT_BITS);
-}
-static uintmax_t shift_left_by_one_word(uintmax_t n)
-{
-    bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n);
-    return shift_too_big ? 0 :
-        n << (shift_too_big ? 0 : BIGNUM_INT_BITS);
-}
-
-mp_int *mp_make_sized(size_t nw)
-{
-    mp_int *x = snew_plus(mp_int, nw * sizeof(BignumInt));
-    assert(nw);                   /* we outlaw the zero-word mp_int */
-    x->nw = nw;
-    x->w = snew_plus_get_aux(x);
-    mp_clear(x);
-    return x;
-}
-
-mp_int *mp_new(size_t maxbits)
-{
-    size_t words = (maxbits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-    return mp_make_sized(words);
-}
-
-mp_int *mp_from_integer(uintmax_t n)
-{
-    mp_int *x = mp_make_sized(
-        (sizeof(n) + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES);
-    size_t i; // WINSCP
-    for (i = 0; i < x->nw; i++)
-        x->w[i] = n >> (i * BIGNUM_INT_BITS);
-    return x;
-}
-
-size_t mp_max_bytes(mp_int *x)
-{
-    return x->nw * BIGNUM_INT_BYTES;
-}
-
-size_t mp_max_bits(mp_int *x)
-{
-    return x->nw * BIGNUM_INT_BITS;
-}
-
-void mp_free(mp_int *x)
-{
-    mp_clear(x);
-    smemclr(x, sizeof(*x));
-    sfree(x);
-}
-
-void mp_dump(FILE *fp, const char *prefix, mp_int *x, const char *suffix)
-{
-    size_t i; // WINSCP
-    fprintf(fp, "%s0x", prefix);
-    for (i = mp_max_bytes(x); i-- > 0 ;)
-        fprintf(fp, "%02X", mp_get_byte(x, i));
-    fputs(suffix, fp);
-}
-
-void mp_copy_into(mp_int *dest, mp_int *src)
-{
-    size_t copy_nw = size_t_min(dest->nw, src->nw);
-    memmove(dest->w, src->w, copy_nw * sizeof(BignumInt));
-    smemclr(dest->w + copy_nw, (dest->nw - copy_nw) * sizeof(BignumInt));
-}
-
-void mp_copy_integer_into(mp_int *r, uintmax_t n)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        r->w[i] = n;
-        n = shift_right_by_one_word(n);
-    }
-}
-
-/*
- * Conditional selection is done by negating 'which', to give a mask
- * word which is all 1s if which==1 and all 0s if which==0. Then you
- * can select between two inputs a,b without data-dependent control
- * flow by XORing them to get their difference; ANDing with the mask
- * word to replace that difference with 0 if which==0; and XORing that
- * into a, which will either turn it into b or leave it alone.
- *
- * This trick will be used throughout this code and taken as read the
- * rest of the time (or else I'd be here all week typing comments),
- * but I felt I ought to explain it in words _once_.
- */
-void mp_select_into(mp_int *dest, mp_int *src0, mp_int *src1,
-                    unsigned which)
-{
-    BignumInt mask = -(BignumInt)(1 & which);
-    size_t i; // WINSCP
-    for (i = 0; i < dest->nw; i++) {
-        BignumInt srcword0 = mp_word(src0, i), srcword1 = mp_word(src1, i);
-        dest->w[i] = srcword0 ^ ((srcword1 ^ srcword0) & mask);
-    }
-}
-
-void mp_cond_swap(mp_int *x0, mp_int *x1, unsigned swap)
-{
-    pinitassert(x0->nw == x1->nw);
-    volatile BignumInt mask = -(BignumInt)(1 & swap);
-    size_t i; // WINSCP
-    for (i = 0; i < x0->nw; i++) {
-        BignumInt diff = (x0->w[i] ^ x1->w[i]) & mask;
-        x0->w[i] ^= diff;
-        x1->w[i] ^= diff;
-    }
-}
-
-void mp_clear(mp_int *x)
-{
-    smemclr(x->w, x->nw * sizeof(BignumInt));
-}
-
-void mp_cond_clear(mp_int *x, unsigned clear)
-{
-    BignumInt mask = ~-(BignumInt)(1 & clear);
-    size_t i; // WINSCP
-    for (i = 0; i < x->nw; i++)
-        x->w[i] &= mask;
-}
-
-/*
- * Common code between mp_from_bytes_{le,be} which reads bytes in an
- * arbitrary arithmetic progression.
- */
-static mp_int *mp_from_bytes_int(ptrlen bytes, size_t m, size_t c)
-{
-    size_t nw = (bytes.len + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES;
-    nw = size_t_max(nw, 1);
-    { // WINSCP
-    mp_int *n = mp_make_sized(nw);
-    size_t i; // WINSCP
-    for (i = 0; i < bytes.len; i++)
-        n->w[i / BIGNUM_INT_BYTES] |=
-            (BignumInt)(((const unsigned char *)bytes.ptr)[m*i+c]) <<
-            (8 * (i % BIGNUM_INT_BYTES));
-    return n;
-    } // WINSCP
-}
-
-mp_int *mp_from_bytes_le(ptrlen bytes)
-{
-    return mp_from_bytes_int(bytes, 1, 0);
-}
-
-mp_int *mp_from_bytes_be(ptrlen bytes)
-{
-    return mp_from_bytes_int(bytes, -1, bytes.len - 1);
-}
-
-static mp_int *mp_from_words(size_t nw, const BignumInt *w)
-{
-    mp_int *x = mp_make_sized(nw);
-    memcpy(x->w, w, x->nw * sizeof(BignumInt));
-    return x;
-}
-
-/*
- * Decimal-to-binary conversion: just go through the input string
- * adding on the decimal value of each digit, and then multiplying the
- * number so far by 10.
- */
-mp_int *mp_from_decimal_pl(ptrlen decimal)
-{
-    /* 196/59 is an upper bound (and also a continued-fraction
-     * convergent) for log2(10), so this conservatively estimates the
-     * number of bits that will be needed to store any number that can
-     * be written in this many decimal digits. */
-    pinitassert(decimal.len < (~(size_t)0) / 196);
-    size_t bits = 196 * decimal.len / 59;
-
-    /* Now round that up to words. */
-    size_t words = bits / BIGNUM_INT_BITS + 1;
-
-    mp_int *x = mp_make_sized(words);
-    size_t i; // WINSCP
-    for (i = 0; i < decimal.len; i++) {
-        mp_add_integer_into(x, x, ((const char *)decimal.ptr)[i] - '0');
-
-        if (i+1 == decimal.len)
-            break;
-
-        mp_mul_integer_into(x, x, 10);
-    }
-    return x;
-}
-
-mp_int *mp_from_decimal(const char *decimal)
-{
-    return mp_from_decimal_pl(ptrlen_from_asciz(decimal));
-}
-
-/*
- * Hex-to-binary conversion: _algorithmically_ simpler than decimal
- * (none of those multiplications by 10), but there's some fiddly
- * bit-twiddling needed to process each hex digit without diverging
- * control flow depending on whether it's a letter or a number.
- */
-mp_int *mp_from_hex_pl(ptrlen hex)
-{
-    pinitassert(hex.len <= (~(size_t)0) / 4);
-    size_t bits = hex.len * 4;
-    size_t words = (bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-    words = size_t_max(words, 1);
-    { // WINSCP
-    mp_int *x = mp_make_sized(words);
-    size_t nibble; // WINSCP
-    for (nibble = 0; nibble < hex.len; nibble++) {
-        BignumInt digit = ((const char *)hex.ptr)[hex.len-1 - nibble];
-
-        BignumInt lmask = ~-((BignumInt)((digit-'a')|('f'-digit))
-                             >> (BIGNUM_INT_BITS-1));
-        BignumInt umask = ~-((BignumInt)((digit-'A')|('F'-digit))
-                             >> (BIGNUM_INT_BITS-1));
-
-        BignumInt digitval = digit - '0';
-        digitval ^= (digitval ^ (digit - 'a' + 10)) & lmask;
-        digitval ^= (digitval ^ (digit - 'A' + 10)) & umask;
-        digitval &= 0xF; /* at least be _slightly_ nice about weird input */
-
-        { // WINSCP
-        size_t word_idx = nibble / (BIGNUM_INT_BYTES*2);
-        size_t nibble_within_word = nibble % (BIGNUM_INT_BYTES*2);
-        x->w[word_idx] |= digitval << (nibble_within_word * 4);
-        } // WINSCP
-    }
-    return x;
-    } // WINSCP
-}
-
-mp_int *mp_from_hex(const char *hex)
-{
-    return mp_from_hex_pl(ptrlen_from_asciz(hex));
-}
-
-mp_int *mp_copy(mp_int *x)
-{
-    return mp_from_words(x->nw, x->w);
-}
-
-uint8_t mp_get_byte(mp_int *x, size_t byte)
-{
-    return 0xFF & (mp_word(x, byte / BIGNUM_INT_BYTES) >>
-                   (8 * (byte % BIGNUM_INT_BYTES)));
-}
-
-unsigned mp_get_bit(mp_int *x, size_t bit)
-{
-    return 1 & (mp_word(x, bit / BIGNUM_INT_BITS) >>
-                (bit % BIGNUM_INT_BITS));
-}
-
-uintmax_t mp_get_integer(mp_int *x)
-{
-    uintmax_t toret = 0;
-    size_t i; // WINSCP 
-    for (i = x->nw; i-- > 0 ;)
-        toret = shift_left_by_one_word(toret) | x->w[i];
-    return toret;
-}
-
-void mp_set_bit(mp_int *x, size_t bit, unsigned val)
-{
-    size_t word = bit / BIGNUM_INT_BITS;
-    pinitassert(word < x->nw);
-
-    unsigned shift = (bit % BIGNUM_INT_BITS);
-
-    x->w[word] &= ~((BignumInt)1 << shift);
-    x->w[word] |= (BignumInt)(val & 1) << shift;
-}
-
-/*
- * Helper function used here and there to normalise any nonzero input
- * value to 1.
- */
-static inline unsigned normalise_to_1(BignumInt n)
-{
-    n = (n >> 1) | (n & 1);            /* ensure top bit is clear */
-    n = (BignumInt)(-n) >> (BIGNUM_INT_BITS - 1); /* normalise to 0 or 1 */
-    return n;
-}
-static inline unsigned normalise_to_1_u64(uint64_t n)
-{
-    n = (n >> 1) | (n & 1);            /* ensure top bit is clear */
-    n = (-n) >> 63;                    /* normalise to 0 or 1 */
-    return n;
-}
-
-/*
- * Find the highest nonzero word in a number. Returns the index of the
- * word in x->w, and also a pair of output uint64_t in which that word
- * appears in the high one shifted left by 'shift_wanted' bits, the
- * words immediately below it occupy the space to the right, and the
- * words below _that_ fill up the low one.
- *
- * If there is no nonzero word at all, the passed-by-reference output
- * variables retain their original values.
- */
-static inline void mp_find_highest_nonzero_word_pair(
-    mp_int *x, size_t shift_wanted, size_t *index,
-    uint64_t *hi, uint64_t *lo)
-{
-    uint64_t curr_hi = 0, curr_lo = 0;
-
-    size_t curr_index; // WINSCP
-    for (curr_index = 0; curr_index < x->nw; curr_index++) {
-        BignumInt curr_word = x->w[curr_index];
-        unsigned indicator = normalise_to_1(curr_word);
-
-        curr_lo = (BIGNUM_INT_BITS < 64 ? (curr_lo >> BIGNUM_INT_BITS) : 0) |
-            (curr_hi << (64 - BIGNUM_INT_BITS));
-        curr_hi = (BIGNUM_INT_BITS < 64 ? (curr_hi >> BIGNUM_INT_BITS) : 0) |
-            ((uint64_t)curr_word << shift_wanted);
-
-        if (hi)    *hi    ^= (curr_hi    ^ *hi   ) & -(uint64_t)indicator;
-        if (lo)    *lo    ^= (curr_lo    ^ *lo   ) & -(uint64_t)indicator;
-        if (index) *index ^= (curr_index ^ *index) & -(size_t)  indicator;
-    }
-}
-
-size_t mp_get_nbits(mp_int *x)
-{
-    /* Sentinel values in case there are no bits set at all: we
-     * imagine that there's a word at position -1 (i.e. the topmost
-     * fraction word) which is all 1s, because that way, we handle a
-     * zero input by considering its highest set bit to be the top one
-     * of that word, i.e. just below the units digit, i.e. at bit
-     * index -1, i.e. so we'll return 0 on output. */
-    size_t hiword_index = -(size_t)1;
-    uint64_t hiword64 = ~(BignumInt)0;
-
-    /*
-     * Find the highest nonzero word and its index.
-     */
-    mp_find_highest_nonzero_word_pair(x, 0, &hiword_index, &hiword64, NULL);
-    { // WINSCP
-    BignumInt hiword = hiword64; /* in case BignumInt is a narrower type */
-
-    /*
-     * Find the index of the highest set bit within hiword.
-     */
-    BignumInt hibit_index = 0;
-    size_t i; // WINSCP
-    for (i = (1 << (BIGNUM_INT_BITS_BITS-1)); i != 0; i >>= 1) {
-        BignumInt shifted_word = hiword >> i;
-        BignumInt indicator =
-            (BignumInt)(-shifted_word) >> (BIGNUM_INT_BITS-1);
-        hiword ^= (shifted_word ^ hiword ) & -indicator;
-        hibit_index += i & -(size_t)indicator;
-    }
-
-    /*
-     * Put together the result.
-     */
-    return (hiword_index << BIGNUM_INT_BITS_BITS) + hibit_index + 1;
-    } // WINSCP
-}
-
-/*
- * Shared code between the hex and decimal output functions to get rid
- * of leading zeroes on the output string. The idea is that we wrote
- * out a fixed number of digits and a trailing \0 byte into 'buf', and
- * now we want to shift it all left so that the first nonzero digit
- * moves to buf[0] (or, if there are no nonzero digits at all, we move
- * up by 'maxtrim', so that we return 0 as "0" instead of "").
- */
-static void trim_leading_zeroes(char *buf, size_t bufsize, size_t maxtrim)
-{
-    size_t trim = maxtrim;
-
-    /*
-     * Look for the first character not equal to '0', to find the
-     * shift count.
-     */
-    if (trim > 0) {
-        size_t pos; // WINSCP
-        for (pos = trim; pos-- > 0 ;) {
-            uint8_t diff = buf[pos] ^ '0';
-            size_t mask = -((((size_t)diff) - 1) >> (SIZE_T_BITS - 1));
-            trim ^= (trim ^ pos) & ~mask;
-        }
-    }
-
-    /*
-     * Now do the shift, in log n passes each of which does a
-     * conditional shift by 2^i bytes if bit i is set in the shift
-     * count.
-     */
-    { // WINSCP
-    uint8_t *ubuf = (uint8_t *)buf;
-    size_t logd; // WINSCP
-    for (logd = 0; bufsize >> logd; logd++) {
-        uint8_t mask = -(uint8_t)((trim >> logd) & 1);
-        size_t d = (size_t)1 << logd;
-        size_t i; // WINSCP
-        for (i = 0; i+d < bufsize; i++) {
-            uint8_t diff = mask & (ubuf[i] ^ ubuf[i+d]);
-            ubuf[i] ^= diff;
-            ubuf[i+d] ^= diff;
-        }
-    }
-    } // WINSCP
-}
-
-/*
- * Binary to decimal conversion. Our strategy here is to extract each
- * decimal digit by finding the input number's residue mod 10, then
- * subtract that off to give an exact multiple of 10, which then means
- * you can safely divide by 10 by means of shifting right one bit and
- * then multiplying by the inverse of 5 mod 2^n.
- */
-char *mp_get_decimal(mp_int *x_orig)
-{
-    mp_int *x = mp_copy(x_orig), *y = mp_make_sized(x->nw);
-
-    /*
-     * The inverse of 5 mod 2^lots is 0xccccccccccccccccccccd, for an
-     * appropriate number of 'c's. Manually construct an integer the
-     * right size.
-     */
-    mp_int *inv5 = mp_make_sized(x->nw);
-    pinitassert(BIGNUM_INT_BITS % 8 == 0);
-    size_t i; // WINSCP
-    for (i = 0; i < inv5->nw; i++)
-        inv5->w[i] = BIGNUM_INT_MASK / 5 * 4;
-    inv5->w[0]++;
-
-    /*
-     * 146/485 is an upper bound (and also a continued-fraction
-     * convergent) of log10(2), so this is a conservative estimate of
-     * the number of decimal digits needed to store a value that fits
-     * in this many binary bits.
-     */
-    assert(x->nw < (~(size_t)1) / (146 * BIGNUM_INT_BITS));
-    { // WINSCP
-    size_t bufsize = size_t_max(x->nw * (146 * BIGNUM_INT_BITS) / 485, 1) + 2;
-    char *outbuf = snewn(bufsize, char);
-    outbuf[bufsize - 1] = '\0';
-
-    /*
-     * Loop over the number generating digits from the least
-     * significant upwards, so that we write to outbuf in reverse
-     * order.
-     */
-    { // WINSCP
-    size_t pos; // WINSCP
-    for (pos = bufsize - 1; pos-- > 0 ;) {
-        /*
-         * Find the current residue mod 10. We do this by first
-         * summing the bytes of the number, with all but the lowest
-         * one multiplied by 6 (because 256^i == 6 mod 10 for all
-         * i>0). That gives us a single word congruent mod 10 to the
-         * input number, and then we reduce it further by manual
-         * multiplication and shifting, just in case the compiler
-         * target implements the C division operator in a way that has
-         * input-dependent timing.
-         */
-        uint32_t low_digit = 0, maxval = 0, mult = 1;
-        size_t i; // WINSCP
-        for (i = 0; i < x->nw; i++) {
-            unsigned j; // WINSCP
-            for (j = 0; j < BIGNUM_INT_BYTES; j++) {
-                low_digit += mult * (0xFF & (x->w[i] >> (8*j)));
-                maxval += mult * 0xFF;
-                mult = 6;
-            }
-            /*
-             * For _really_ big numbers, prevent overflow of t by
-             * periodically folding the top half of the accumulator
-             * into the bottom half, using the same rule 'multiply by
-             * 6 when shifting down by one or more whole bytes'.
-             */
-            if (maxval > UINT32_MAX - (6 * 0xFF * BIGNUM_INT_BYTES)) {
-                low_digit = (low_digit & 0xFFFF) + 6 * (low_digit >> 16);
-                maxval = (maxval & 0xFFFF) + 6 * (maxval >> 16);
-            }
-        }
-
-        /*
-         * Final reduction of low_digit. We multiply by 2^32 / 10
-         * (that's the constant 0x19999999) to get a 64-bit value
-         * whose top 32 bits are the approximate quotient
-         * low_digit/10; then we subtract off 10 times that; and
-         * finally we do one last trial subtraction of 10 by adding 6
-         * (which sets bit 4 if the number was just over 10) and then
-         * testing bit 4.
-         */
-        low_digit -= 10 * ((0x19999999ULL * low_digit) >> 32);
-        low_digit -= 10 * ((low_digit + 6) >> 4);
-
-        assert(low_digit < 10);        /* make sure we did reduce fully */
-        outbuf[pos] = '0' + low_digit;
-
-        /*
-         * Now subtract off that digit, divide by 2 (using a right
-         * shift) and by 5 (using the modular inverse), to get the
-         * next output digit into the units position.
-         */
-        mp_sub_integer_into(x, x, low_digit);
-        mp_rshift_fixed_into(y, x, 1);
-        mp_mul_into(x, y, inv5);
-    }
-
-    mp_free(x);
-    mp_free(y);
-    mp_free(inv5);
-
-    trim_leading_zeroes(outbuf, bufsize, bufsize - 2);
-    return outbuf;
-    } // WINSCP
-    } // WINSCP
-}
-
-/*
- * Binary to hex conversion. Reasonably simple (only a spot of bit
- * twiddling to choose whether to output a digit or a letter for each
- * nibble).
- */
-static char *mp_get_hex_internal(mp_int *x, uint8_t letter_offset)
-{
-    size_t nibbles = x->nw * BIGNUM_INT_BYTES * 2;
-    size_t bufsize = nibbles + 1;
-    char *outbuf = snewn(bufsize, char);
-    size_t nibble; // WINSCP
-    outbuf[nibbles] = '\0';
-
-    for (nibble = 0; nibble < nibbles; nibble++) {
-        size_t word_idx = nibble / (BIGNUM_INT_BYTES*2);
-        size_t nibble_within_word = nibble % (BIGNUM_INT_BYTES*2);
-        uint8_t digitval = 0xF & (x->w[word_idx] >> (nibble_within_word * 4));
-
-        uint8_t mask = -((digitval + 6) >> 4);
-        char digit = digitval + '0' + (letter_offset & mask);
-        outbuf[nibbles-1 - nibble] = digit;
-    }
-
-    trim_leading_zeroes(outbuf, bufsize, nibbles - 1);
-    return outbuf;
-}
-
-char *mp_get_hex(mp_int *x)
-{
-    return mp_get_hex_internal(x, 'a' - ('0'+10));
-}
-
-char *mp_get_hex_uppercase(mp_int *x)
-{
-    return mp_get_hex_internal(x, 'A' - ('0'+10));
-}
-
-/*
- * Routines for reading and writing the SSH-1 and SSH-2 wire formats
- * for multiprecision integers, declared in marshal.h.
- *
- * These can't avoid having control flow dependent on the true bit
- * size of the number, because the wire format requires the number of
- * output bytes to depend on that.
- */
-void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x)
-{
-    size_t bits = mp_get_nbits(x);
-    size_t bytes = (bits + 7) / 8;
-
-    size_t i; // WINSCP
-    assert(bits < 0x10000);
-    put_uint16(bs, bits);
-    for (i = bytes; i-- > 0 ;)
-        put_byte(bs, mp_get_byte(x, i));
-}
-
-void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x)
-{
-    size_t bytes = (mp_get_nbits(x) + 8) / 8;
-
-    size_t i; // WINSCP
-    put_uint32(bs, bytes);
-    for (i = bytes; i-- > 0 ;)
-        put_byte(bs, mp_get_byte(x, i));
-}
-
-mp_int *BinarySource_get_mp_ssh1(BinarySource *src)
-{
-    unsigned bitc = get_uint16(src);
-    ptrlen bytes = get_data(src, (bitc + 7) / 8);
-    if (get_err(src)) {
-        return mp_from_integer(0);
-    } else {
-        mp_int *toret = mp_from_bytes_be(bytes);
-        /* SSH-1.5 spec says that it's OK for the prefix uint16 to be
-         * _greater_ than the actual number of bits */
-        if (mp_get_nbits(toret) > bitc) {
-            src->err = BSE_INVALID;
-            mp_free(toret);
-            toret = mp_from_integer(0);
-        }
-        return toret;
-    }
-}
-
-mp_int *BinarySource_get_mp_ssh2(BinarySource *src)
-{
-    ptrlen bytes = get_string(src);
-    if (get_err(src)) {
-        return mp_from_integer(0);
-    } else {
-        const unsigned char *p = bytes.ptr;
-        if ((bytes.len > 0 &&
-             ((p[0] & 0x80) ||
-              (p[0] == 0 && (bytes.len <= 1 || !(p[1] & 0x80)))))) {
-            src->err = BSE_INVALID;
-            return mp_from_integer(0);
-        }
-        return mp_from_bytes_be(bytes);
-    }
-}
-
-/*
- * Make an mp_int structure whose words array aliases a subinterval of
- * some other mp_int. This makes it easy to read or write just the low
- * or high words of a number, e.g. to add a number starting from a
- * high bit position, or to reduce mod 2^{n*BIGNUM_INT_BITS}.
- *
- * The convention throughout this code is that when we store an mp_int
- * directly by value, we always expect it to be an alias of some kind,
- * so its words array won't ever need freeing. Whereas an 'mp_int *'
- * has an owner, who knows whether it needs freeing or whether it was
- * created by address-taking an alias.
- */
-static mp_int mp_make_alias(mp_int *in, size_t offset, size_t len)
-{
-    /*
-     * Bounds-check the offset and length so that we always return
-     * something valid, even if it's not necessarily the length the
-     * caller asked for.
-     */
-    if (offset > in->nw)
-        offset = in->nw;
-    if (len > in->nw - offset)
-        len = in->nw - offset;
-
-    { // WINSCP
-    mp_int toret;
-    toret.nw = len;
-    toret.w = in->w + offset;
-    return toret;
-    } // WINSCP
-}
-
-/*
- * A special case of mp_make_alias: in some cases we preallocate a
- * large mp_int to use as scratch space (to avoid pointless
- * malloc/free churn in recursive or iterative work).
- *
- * mp_alloc_from_scratch creates an alias of size 'len' to part of
- * 'pool', and adjusts 'pool' itself so that further allocations won't
- * overwrite that space.
- *
- * There's no free function to go with this. Typically you just copy
- * the pool mp_int by value, allocate from the copy, and when you're
- * done with those allocations, throw the copy away and go back to the
- * original value of pool. (A mark/release system.)
- */
-static mp_int mp_alloc_from_scratch(mp_int *pool, size_t len)
-{
-    pinitassert(len <= pool->nw);
-    mp_int toret = mp_make_alias(pool, 0, len);
-    *pool = mp_make_alias(pool, len, pool->nw);
-    return toret;
-}
-
-/*
- * Internal component common to lots of assorted add/subtract code.
- * Reads words from a,b; writes into w_out (which might be NULL if the
- * output isn't even needed). Takes an input carry flag in 'carry',
- * and returns the output carry. Each word read from b is ANDed with
- * b_and and then XORed with b_xor.
- *
- * So you can implement addition by setting b_and to all 1s and b_xor
- * to 0; you can subtract by making b_xor all 1s too (effectively
- * bit-flipping b) and also passing 1 as the input carry (to turn
- * one's complement into two's complement). And you can do conditional
- * add/subtract by choosing b_and to be all 1s or all 0s based on a
- * condition, because the value of b will be totally ignored if b_and
- * == 0.
- */
-static BignumCarry mp_add_masked_into(
-    BignumInt *w_out, size_t rw, mp_int *a, mp_int *b,
-    BignumInt b_and, BignumInt b_xor, BignumCarry carry)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < rw; i++) {
-        BignumInt aword = mp_word(a, i), bword = mp_word(b, i), out;
-        bword = (bword & b_and) ^ b_xor;
-        BignumADC(out, carry, aword, bword, carry);
-        if (w_out)
-            w_out[i] = out;
-    }
-    return carry;
-}
-
-/*
- * Like the public mp_add_into except that it returns the output carry.
- */
-static inline BignumCarry mp_add_into_internal(mp_int *r, mp_int *a, mp_int *b)
-{
-    return mp_add_masked_into(r->w, r->nw, a, b, ~(BignumInt)0, 0, 0);
-}
-
-void mp_add_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    mp_add_into_internal(r, a, b);
-}
-
-void mp_sub_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    mp_add_masked_into(r->w, r->nw, a, b, ~(BignumInt)0, ~(BignumInt)0, 1);
-}
-
-void mp_and_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        BignumInt aword = mp_word(a, i), bword = mp_word(b, i);
-        r->w[i] = aword & bword;
-    }
-}
-
-void mp_or_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        BignumInt aword = mp_word(a, i), bword = mp_word(b, i);
-        r->w[i] = aword | bword;
-    }
-}
-
-void mp_xor_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        BignumInt aword = mp_word(a, i), bword = mp_word(b, i);
-        r->w[i] = aword ^ bword;
-    }
-}
-
-void mp_bic_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        BignumInt aword = mp_word(a, i), bword = mp_word(b, i);
-        r->w[i] = aword & ~bword;
-    }
-}
-
-static void mp_cond_negate(mp_int *r, mp_int *x, unsigned yes)
-{
-    BignumCarry carry = yes;
-    BignumInt flip = -(BignumInt)yes;
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        BignumInt xword = mp_word(x, i);
-        xword ^= flip;
-        BignumADC(r->w[i], carry, 0, xword, carry);
-    }
-}
-
-/*
- * Similar to mp_add_masked_into, but takes a C integer instead of an
- * mp_int as the masked operand.
- */
-static BignumCarry mp_add_masked_integer_into(
-    BignumInt *w_out, size_t rw, mp_int *a, uintmax_t b,
-    BignumInt b_and, BignumInt b_xor, BignumCarry carry)
-{
-    size_t i; // WINSCP
-    for (i = 0; i < rw; i++) {
-        BignumInt aword = mp_word(a, i);
-        BignumInt bword = b;
-        b = shift_right_by_one_word(b);
-        { // WINSCP
-        BignumInt out;
-        bword = (bword ^ b_xor) & b_and;
-        BignumADC(out, carry, aword, bword, carry);
-        if (w_out)
-            w_out[i] = out;
-        } // WINSCP
-    }
-    return carry;
-}
-
-void mp_add_integer_into(mp_int *r, mp_int *a, uintmax_t n)
-{
-    mp_add_masked_integer_into(r->w, r->nw, a, n, ~(BignumInt)0, 0, 0);
-}
-
-void mp_sub_integer_into(mp_int *r, mp_int *a, uintmax_t n)
-{
-    mp_add_masked_integer_into(r->w, r->nw, a, n,
-                               ~(BignumInt)0, ~(BignumInt)0, 1);
-}
-
-/*
- * Sets r to a + n << (word_index * BIGNUM_INT_BITS), treating
- * word_index as secret data.
- */
-static void mp_add_integer_into_shifted_by_words(
-    mp_int *r, mp_int *a, uintmax_t n, size_t word_index)
-{
-    unsigned indicator = 0;
-    BignumCarry carry = 0;
-    size_t i; // WINSCP
-
-    for (i = 0; i < r->nw; i++) {
-        /* indicator becomes 1 when we reach the index that the least
-         * significant bits of n want to be placed at, and it stays 1
-         * thereafter. */
-        indicator |= 1 ^ normalise_to_1(i ^ word_index);
-
-        /* If indicator is 1, we add the low bits of n into r, and
-         * shift n down. If it's 0, we add zero bits into r, and
-         * leave n alone. */
-        { // WINSCP
-        BignumInt bword = n & -(BignumInt)indicator;
-        uintmax_t new_n = shift_right_by_one_word(n);
-        n ^= (n ^ new_n) & -(uintmax_t)indicator;
-
-        { // WINSCP
-        BignumInt aword = mp_word(a, i);
-        BignumInt out;
-        BignumADC(out, carry, aword, bword, carry);
-        r->w[i] = out;
-        } // WINSCP
-        } // WINSCP
-    }
-}
-
-void mp_mul_integer_into(mp_int *r, mp_int *a, uint16_t n)
-{
-    BignumInt carry = 0, mult = n;
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        BignumInt aword = mp_word(a, i);
-        BignumMULADD(carry, r->w[i], aword, mult, carry);
-    }
-    assert(!carry);
-}
-
-void mp_cond_add_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes)
-{
-    BignumInt mask = -(BignumInt)(yes & 1);
-    mp_add_masked_into(r->w, r->nw, a, b, mask, 0, 0);
-}
-
-void mp_cond_sub_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes)
-{
-    BignumInt mask = -(BignumInt)(yes & 1);
-    mp_add_masked_into(r->w, r->nw, a, b, mask, mask, 1 & mask);
-}
-
-/*
- * Ordered comparison between unsigned numbers is done by subtracting
- * one from the other and looking at the output carry.
- */
-unsigned mp_cmp_hs(mp_int *a, mp_int *b)
-{
-    size_t rw = size_t_max(a->nw, b->nw);
-    return mp_add_masked_into(NULL, rw, a, b, ~(BignumInt)0, ~(BignumInt)0, 1);
-}
-
-unsigned mp_hs_integer(mp_int *x, uintmax_t n)
-{
-    BignumInt carry = 1;
-    size_t nwords = sizeof(n)/BIGNUM_INT_BYTES;
-    size_t i, e; // WINSCP
-    for (i = 0, e = size_t_max(x->nw, nwords); i < e; i++) {
-        BignumInt nword = n;
-        n = shift_right_by_one_word(n);
-        { // WINSCP
-        BignumInt dummy_out;
-        BignumADC(dummy_out, carry, mp_word(x, i), ~nword, carry);
-        (void)dummy_out;
-        } // WINSCP
-    }
-    return carry;
-}
-
-/*
- * Equality comparison is done by bitwise XOR of the input numbers,
- * ORing together all the output words, and normalising the result
- * using our careful normalise_to_1 helper function.
- */
-unsigned mp_cmp_eq(mp_int *a, mp_int *b)
-{
-    BignumInt diff = 0;
-    size_t i, limit; // WINSCP
-    for (i = 0, limit = size_t_max(a->nw, b->nw); i < limit; i++)
-        diff |= mp_word(a, i) ^ mp_word(b, i);
-    return 1 ^ normalise_to_1(diff);   /* return 1 if diff _is_ zero */
-}
-
-unsigned mp_eq_integer(mp_int *x, uintmax_t n)
-{
-    BignumInt diff = 0;
-    size_t nwords = sizeof(n)/BIGNUM_INT_BYTES;
-    size_t i, e; // WINSCP
-    for (i = 0, e = size_t_max(x->nw, nwords); i < e; i++) {
-        BignumInt nword = n;
-        n = shift_right_by_one_word(n);
-        diff |= mp_word(x, i) ^ nword;
-    }
-    return 1 ^ normalise_to_1(diff);   /* return 1 if diff _is_ zero */
-}
-
-static void mp_neg_into(mp_int *r, mp_int *a)
-{
-    mp_int zero;
-    zero.nw = 0;
-    mp_sub_into(r, &zero, a);
-}
-
-mp_int *mp_add(mp_int *x, mp_int *y)
-{
-    mp_int *r = mp_make_sized(size_t_max(x->nw, y->nw) + 1);
-    mp_add_into(r, x, y);
-    return r;
-}
-
-mp_int *mp_sub(mp_int *x, mp_int *y)
-{
-    mp_int *r = mp_make_sized(size_t_max(x->nw, y->nw));
-    mp_sub_into(r, x, y);
-    return r;
-}
-
-/*
- * Internal routine: multiply and accumulate in the trivial O(N^2)
- * way. Sets r <- r + a*b.
- */
-static void mp_mul_add_simple(mp_int *r, mp_int *a, mp_int *b)
-{
-    BignumInt *aend = a->w + a->nw, *bend = b->w + b->nw, *rend = r->w + r->nw;
-
-    BignumInt *ap, *rp; // WINSCP
-    for (ap = a->w, rp = r->w;
-         ap < aend && rp < rend; ap++, rp++) {
-
-        BignumInt adata = *ap, carry = 0, *rq = rp;
-
-        { // WINSCP
-        BignumInt *bp; // WINSCP
-        for (bp = b->w; bp < bend && rq < rend; bp++, rq++) {
-            BignumInt bdata = bp < bend ? *bp : 0;
-            BignumMULADD2(carry, *rq, adata, bdata, *rq, carry);
-        }
-        } // WINSCP
-
-        for (; rq < rend; rq++)
-            BignumADC(*rq, carry, carry, *rq, 0);
-    }
-}
-
-#ifndef KARATSUBA_THRESHOLD      /* allow redefinition via -D for testing */
-#define KARATSUBA_THRESHOLD 24
-#endif
-
-static inline size_t mp_mul_scratchspace_unary(size_t n)
-{
-    /*
-     * Simplistic and overcautious bound on the amount of scratch
-     * space that the recursive multiply function will need.
-     *
-     * The rationale is: on the main Karatsuba branch of
-     * mp_mul_internal, which is the most space-intensive one, we
-     * allocate space for (a0+a1) and (b0+b1) (each just over half the
-     * input length n) and their product (the sum of those sizes, i.e.
-     * just over n itself). Then in order to actually compute the
-     * product, we do a recursive multiplication of size just over n.
-     *
-     * If all those 'just over' weren't there, and everything was
-     * _exactly_ half the length, you'd get the amount of space for a
-     * size-n multiply defined by the recurrence M(n) = 2n + M(n/2),
-     * which is satisfied by M(n) = 4n. But instead it's (2n plus a
-     * word or two) and M(n/2 plus a word or two). On the assumption
-     * that there's still some constant k such that M(n) <= kn, this
-     * gives us kn = 2n + w + k(n/2 + w), where w is a small constant
-     * (one or two words). That simplifies to kn/2 = 2n + (k+1)w, and
-     * since we don't even _start_ needing scratch space until n is at
-     * least 50, we can bound 2n + (k+1)w above by 3n, giving k=6.
-     *
-     * So I claim that 6n words of scratch space will suffice, and I
-     * check that by assertion at every stage of the recursion.
-     */
-    return n * 6;
-}
-
-static size_t mp_mul_scratchspace(size_t rw, size_t aw, size_t bw)
-{
-    size_t inlen = size_t_min(rw, size_t_max(aw, bw));
-    return mp_mul_scratchspace_unary(inlen);
-}
-
-static void mp_mul_internal(mp_int *r, mp_int *a, mp_int *b, mp_int scratch)
-{
-    size_t inlen = size_t_min(r->nw, size_t_max(a->nw, b->nw));
-    assert(scratch.nw >= mp_mul_scratchspace_unary(inlen));
-
-    mp_clear(r);
-
-    if (inlen < KARATSUBA_THRESHOLD || a->nw == 0 || b->nw == 0) {
-        /*
-         * The input numbers are too small to bother optimising. Go
-         * straight to the simple primitive approach.
-         */
-        mp_mul_add_simple(r, a, b);
-        return;
-    }
-
-    /*
-     * Karatsuba divide-and-conquer algorithm. We cut each input in
-     * half, so that it's expressed as two big 'digits' in a giant
-     * base D:
-     *
-     *   a = a_1 D + a_0
-     *   b = b_1 D + b_0
-     *
-     * Then the product is of course
-     *
-     *   ab = a_1 b_1 D^2 + (a_1 b_0 + a_0 b_1) D + a_0 b_0
-     *
-     * and we compute the three coefficients by recursively calling
-     * ourself to do half-length multiplications.
-     *
-     * The clever bit that makes this worth doing is that we only need
-     * _one_ half-length multiplication for the central coefficient
-     * rather than the two that it obviouly looks like, because we can
-     * use a single multiplication to compute
-     *
-     *   (a_1 + a_0) (b_1 + b_0) = a_1 b_1 + a_1 b_0 + a_0 b_1 + a_0 b_0
-     *
-     * and then we subtract the other two coefficients (a_1 b_1 and
-     * a_0 b_0) which we were computing anyway.
-     *
-     * Hence we get to multiply two numbers of length N in about three
-     * times as much work as it takes to multiply numbers of length
-     * N/2, which is obviously better than the four times as much work
-     * it would take if we just did a long conventional multiply.
-     */
-
-    { // WINSCP
-    /* Break up the input as botlen + toplen, with botlen >= toplen.
-     * The 'base' D is equal to 2^{botlen * BIGNUM_INT_BITS}. */
-    size_t toplen = inlen / 2;
-    size_t botlen = inlen - toplen;
-
-    /* Alias bignums that address the two halves of a,b, and useful
-     * pieces of r. */
-    mp_int a0 = mp_make_alias(a, 0, botlen);
-    mp_int b0 = mp_make_alias(b, 0, botlen);
-    mp_int a1 = mp_make_alias(a, botlen, toplen);
-    mp_int b1 = mp_make_alias(b, botlen, toplen);
-    mp_int r0 = mp_make_alias(r, 0, botlen*2);
-    mp_int r1 = mp_make_alias(r, botlen, r->nw);
-    mp_int r2 = mp_make_alias(r, botlen*2, r->nw);
-
-    /* Recurse to compute a0*b0 and a1*b1, in their correct positions
-     * in the output bignum. They can't overlap. */
-    mp_mul_internal(&r0, &a0, &b0, scratch);
-    mp_mul_internal(&r2, &a1, &b1, scratch);
-
-    if (r->nw < inlen*2) {
-        /*
-         * The output buffer isn't large enough to require the whole
-         * product, so some of a1*b1 won't have been stored. In that
-         * case we won't try to do the full Karatsuba optimisation;
-         * we'll just recurse again to compute a0*b1 and a1*b0 - or at
-         * least as much of them as the output buffer size requires -
-         * and add each one in.
-         */
-        mp_int s = mp_alloc_from_scratch(
-            &scratch, size_t_min(botlen+toplen, r1.nw));
-
-        mp_mul_internal(&s, &a0, &b1, scratch);
-        mp_add_into(&r1, &r1, &s);
-        mp_mul_internal(&s, &a1, &b0, scratch);
-        mp_add_into(&r1, &r1, &s);
-        return;
-    }
-
-    { // WINSCP
-    /* a0+a1 and b0+b1 */
-    mp_int asum = mp_alloc_from_scratch(&scratch, botlen+1);
-    mp_int bsum = mp_alloc_from_scratch(&scratch, botlen+1);
-    mp_add_into(&asum, &a0, &a1);
-    mp_add_into(&bsum, &b0, &b1);
-
-    { // WINSCP
-    /* Their product */
-    mp_int product = mp_alloc_from_scratch(&scratch, botlen*2+1);
-    mp_mul_internal(&product, &asum, &bsum, scratch);
-
-    /* Subtract off the outer terms we already have */
-    mp_sub_into(&product, &product, &r0);
-    mp_sub_into(&product, &product, &r2);
-
-    /* And add it in with the right offset. */
-    mp_add_into(&r1, &r1, &product);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-void mp_mul_into(mp_int *r, mp_int *a, mp_int *b)
-{
-    mp_int *scratch = mp_make_sized(mp_mul_scratchspace(r->nw, a->nw, b->nw));
-    mp_mul_internal(r, a, b, *scratch);
-    mp_free(scratch);
-}
-
-mp_int *mp_mul(mp_int *x, mp_int *y)
-{
-    mp_int *r = mp_make_sized(x->nw + y->nw);
-    mp_mul_into(r, x, y);
-    return r;
-}
-
-void mp_lshift_fixed_into(mp_int *r, mp_int *a, size_t bits)
-{
-    size_t words = bits / BIGNUM_INT_BITS;
-    size_t bitoff = bits % BIGNUM_INT_BITS;
-
-    size_t i; // WINSCP
-    for (i = r->nw; i-- > 0 ;) {
-        if (i < words) {
-            r->w[i] = 0;
-        } else {
-            r->w[i] = mp_word(a, i - words);
-            if (bitoff != 0) {
-                r->w[i] <<= bitoff;
-                if (i > words)
-                    r->w[i] |= mp_word(a, i - words - 1) >>
-                        (BIGNUM_INT_BITS - bitoff);
-            }
-        }
-    }
-}
-
-void mp_rshift_fixed_into(mp_int *r, mp_int *a, size_t bits)
-{
-    size_t words = bits / BIGNUM_INT_BITS;
-    size_t bitoff = bits % BIGNUM_INT_BITS;
-
-    size_t i; // WINSCP
-    for (i = 0; i < r->nw; i++) {
-        r->w[i] = mp_word(a, i + words);
-        if (bitoff != 0) {
-            r->w[i] >>= bitoff;
-            r->w[i] |= mp_word(a, i + words + 1) << (BIGNUM_INT_BITS - bitoff);
-        }
-    }
-}
-
-mp_int *mp_lshift_fixed(mp_int *x, size_t bits)
-{
-    size_t words = (bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-    mp_int *r = mp_make_sized(x->nw + words);
-    mp_lshift_fixed_into(r, x, bits);
-    return r;
-}
-
-mp_int *mp_rshift_fixed(mp_int *x, size_t bits)
-{
-    size_t words = bits / BIGNUM_INT_BITS;
-    size_t nw = x->nw - size_t_min(x->nw, words);
-    mp_int *r = mp_make_sized(size_t_max(nw, 1));
-    mp_rshift_fixed_into(r, x, bits);
-    return r;
-}
-
-/*
- * Safe right shift is done using the same technique as
- * trim_leading_zeroes above: you make an n-word left shift by
- * composing an appropriate subset of power-of-2-sized shifts, so it
- * takes log_2(n) loop iterations each of which does a different shift
- * by a power of 2 words, using the usual bit twiddling to make the
- * whole shift conditional on the appropriate bit of n.
- */
-static void mp_rshift_safe_in_place(mp_int *r, size_t bits)
-{
-    size_t wordshift = bits / BIGNUM_INT_BITS;
-    size_t bitshift = bits % BIGNUM_INT_BITS;
-
-    unsigned bit; // WINSCP
-    unsigned clear = (r->nw - wordshift) >> (CHAR_BIT * sizeof(size_t) - 1);
-    mp_cond_clear(r, clear);
-
-    for (bit = 0; r->nw >> bit; bit++) {
-        size_t word_offset = (size_t)1 << bit;
-        BignumInt mask = -(BignumInt)((wordshift >> bit) & 1);
-        size_t i; // WINSCP
-        for (i = 0; i < r->nw; i++) {
-            BignumInt w = mp_word(r, i + word_offset);
-            r->w[i] ^= (r->w[i] ^ w) & mask;
-        }
-    }
-
-    /*
-     * That's done the shifting by words; now we do the shifting by
-     * bits.
-     */
-    for (bit = 0; bit < BIGNUM_INT_BITS_BITS; bit++) { // WINSCP
-        unsigned shift = 1 << bit, upshift = BIGNUM_INT_BITS - shift;
-        BignumInt mask = -(BignumInt)((bitshift >> bit) & 1);
-        size_t i; // WINSCP
-        for (i = 0; i < r->nw; i++) {
-            BignumInt w = ((r->w[i] >> shift) | (mp_word(r, i+1) << upshift));
-            r->w[i] ^= (r->w[i] ^ w) & mask;
-        }
-    }
-}
-
-mp_int *mp_rshift_safe(mp_int *x, size_t bits)
-{
-    mp_int *r = mp_copy(x);
-    mp_rshift_safe_in_place(r, bits);
-    return r;
-}
-
-void mp_rshift_safe_into(mp_int *r, mp_int *x, size_t bits)
-{
-    mp_copy_into(r, x);
-    mp_rshift_safe_in_place(r, bits);
-}
-
-static void mp_lshift_safe_in_place(mp_int *r, size_t bits)
-{
-    size_t wordshift = bits / BIGNUM_INT_BITS;
-    size_t bitshift = bits % BIGNUM_INT_BITS;
-
-    /*
-     * Same strategy as mp_rshift_safe_in_place, but of course the
-     * other way up.
-     */
-
-    unsigned clear = (r->nw - wordshift) >> (CHAR_BIT * sizeof(size_t) - 1);
-    mp_cond_clear(r, clear);
-
-    { // WINSCP
-    unsigned bit; // WINSCP
-    for (bit = 0; r->nw >> bit; bit++) {
-        size_t word_offset = (size_t)1 << bit;
-        BignumInt mask = -(BignumInt)((wordshift >> bit) & 1);
-        size_t i; // WINSCP
-        for (i = r->nw; i-- > 0 ;) {
-            BignumInt w = mp_word(r, i - word_offset);
-            r->w[i] ^= (r->w[i] ^ w) & mask;
-        }
-    }
-
-    { // WINSCP
-    size_t downshift = BIGNUM_INT_BITS - bitshift;
-    size_t no_shift = (downshift >> BIGNUM_INT_BITS_BITS);
-    downshift &= ~-(size_t)no_shift;
-    { // WINSCP
-    BignumInt downshifted_mask = ~-(BignumInt)no_shift;
-
-    size_t i; // WINSCP
-    for (i = r->nw; i-- > 0 ;) {
-        r->w[i] = (r->w[i] << bitshift) |
-            ((mp_word(r, i-1) >> downshift) & downshifted_mask);
-    }
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-void mp_lshift_safe_into(mp_int *r, mp_int *x, size_t bits)
-{
-    mp_copy_into(r, x);
-    mp_lshift_safe_in_place(r, bits);
-}
-
-void mp_reduce_mod_2to(mp_int *x, size_t p)
-{
-    size_t word = p / BIGNUM_INT_BITS;
-    size_t mask = ((size_t)1 << (p % BIGNUM_INT_BITS)) - 1;
-    for (; word < x->nw; word++) {
-        x->w[word] &= mask;
-        mask = 0;
-    }
-}
-
-/*
- * Inverse mod 2^n is computed by an iterative technique which doubles
- * the number of bits at each step.
- */
-mp_int *mp_invert_mod_2to(mp_int *x, size_t p)
-{
-    /* Input checks: x must be coprime to the modulus, i.e. odd, and p
-     * can't be zero */
-    assert(x->nw > 0);
-    assert(x->w[0] & 1);
-    assert(p > 0);
-
-    { // WINSCP
-    size_t rw = (p + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-    rw = size_t_max(rw, 1);
-    { // WINSCP
-    mp_int *r = mp_make_sized(rw);
-
-    size_t mul_scratchsize = mp_mul_scratchspace(2*rw, rw, rw);
-    mp_int *scratch_orig = mp_make_sized(6 * rw + mul_scratchsize);
-    mp_int scratch_per_iter = *scratch_orig;
-    mp_int mul_scratch = mp_alloc_from_scratch(
-        &scratch_per_iter, mul_scratchsize);
-    size_t b; // WINSCP
-
-    r->w[0] = 1;
-
-    for (b = 1; b < p; b <<= 1) {
-        /*
-         * In each step of this iteration, we have the inverse of x
-         * mod 2^b, and we want the inverse of x mod 2^{2b}.
-         *
-         * Write B = 2^b for convenience, so we want x^{-1} mod B^2.
-         * Let x = x_0 + B x_1 + k B^2, with 0 <= x_0,x_1 < B.
-         *
-         * We want to find r_0 and r_1 such that
-         *    (r_1 B + r_0) (x_1 B + x_0) == 1 (mod B^2)
-         *
-         * To begin with, we know r_0 must be the inverse mod B of
-         * x_0, i.e. of x, i.e. it is the inverse we computed in the
-         * previous iteration. So now all we need is r_1.
-         *
-         * Multiplying out, neglecting multiples of B^2, and writing
-         * x_0 r_0 = K B + 1, we have
-         *
-         *    r_1 x_0 B + r_0 x_1 B + K B == 0                    (mod B^2)
-         * =>                   r_1 x_0 B == - r_0 x_1 B - K B    (mod B^2)
-         * =>                     r_1 x_0 == - r_0 x_1 - K        (mod B)
-         * =>                         r_1 == r_0 (- r_0 x_1 - K)  (mod B)
-         *
-         * (the last step because we multiply through by the inverse
-         * of x_0, which we already know is r_0).
-         */
-
-        mp_int scratch_this_iter = scratch_per_iter;
-        size_t Bw = (b + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-        size_t B2w = (2*b + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-
-        /* Start by finding K: multiply x_0 by r_0, and shift down. */
-        mp_int x0 = mp_alloc_from_scratch(&scratch_this_iter, Bw);
-        mp_copy_into(&x0, x);
-        mp_reduce_mod_2to(&x0, b);
-        { // WINSCP
-        mp_int r0 = mp_make_alias(r, 0, Bw);
-        mp_int Kshift = mp_alloc_from_scratch(&scratch_this_iter, B2w);
-        mp_mul_internal(&Kshift, &x0, &r0, mul_scratch);
-        { // WINSCP
-        mp_int K = mp_alloc_from_scratch(&scratch_this_iter, Bw);
-        mp_rshift_fixed_into(&K, &Kshift, b);
-
-        /* Now compute the product r_0 x_1, reusing the space of Kshift. */
-        { // WINSCP
-        mp_int x1 = mp_alloc_from_scratch(&scratch_this_iter, Bw);
-        mp_rshift_fixed_into(&x1, x, b);
-        mp_reduce_mod_2to(&x1, b);
-        { // WINSCP
-        mp_int r0x1 = mp_make_alias(&Kshift, 0, Bw);
-        mp_mul_internal(&r0x1, &r0, &x1, mul_scratch);
-
-        /* Add K to that. */
-        mp_add_into(&r0x1, &r0x1, &K);
-
-        /* Negate it. */
-        mp_neg_into(&r0x1, &r0x1);
-
-        /* Multiply by r_0. */
-        { // WINSCP
-        mp_int r1 = mp_alloc_from_scratch(&scratch_this_iter, Bw);
-        mp_mul_internal(&r1, &r0, &r0x1, mul_scratch);
-        mp_reduce_mod_2to(&r1, b);
-
-        /* That's our r_1, so add it on to r_0 to get the full inverse
-         * output from this iteration. */
-        mp_lshift_fixed_into(&K, &r1, (b % BIGNUM_INT_BITS));
-        { // WINSCP
-        size_t Bpos = b / BIGNUM_INT_BITS;
-        mp_int r1_position = mp_make_alias(r, Bpos, B2w-Bpos);
-        mp_add_into(&r1_position, &r1_position, &K);
-        } // WINSCP
-        } // WINSCP
-        } // WINSCP
-        } // WINSCP
-        } // WINSCP
-        } // WINSCP
-    }
-
-    /* Finally, reduce mod the precise desired number of bits. */
-    mp_reduce_mod_2to(r, p);
-
-    mp_free(scratch_orig);
-    return r;
-    } // WINSCP
-    } // WINSCP
-}
-
-static size_t monty_scratch_size(MontyContext *mc)
-{
-    return 3*mc->rw + mc->pw + mp_mul_scratchspace(mc->pw, mc->rw, mc->rw);
-}
-
-MontyContext *monty_new(mp_int *modulus)
-{
-    MontyContext *mc = snew(MontyContext);
-
-    mc->rw = modulus->nw;
-    mc->rbits = mc->rw * BIGNUM_INT_BITS;
-    mc->pw = mc->rw * 2 + 1;
-
-    mc->m = mp_copy(modulus);
-
-    mc->minus_minv_mod_r = mp_invert_mod_2to(mc->m, mc->rbits);
-    mp_neg_into(mc->minus_minv_mod_r, mc->minus_minv_mod_r);
-
-    { // WINSCP
-    size_t j; // WINSCP
-    mp_int *r = mp_make_sized(mc->rw + 1);
-    r->w[mc->rw] = 1;
-    mc->powers_of_r_mod_m[0] = mp_mod(r, mc->m);
-    mp_free(r);
-
-    for (j = 1; j < lenof(mc->powers_of_r_mod_m); j++)
-        mc->powers_of_r_mod_m[j] = mp_modmul(
-            mc->powers_of_r_mod_m[0], mc->powers_of_r_mod_m[j-1], mc->m);
-
-    mc->scratch = mp_make_sized(monty_scratch_size(mc));
-
-    return mc;
-    } // WINSCP
-}
-
-void monty_free(MontyContext *mc)
-{
-    size_t j; // WINSCP
-    mp_free(mc->m);
-    for (j = 0; j < 3; j++)
-        mp_free(mc->powers_of_r_mod_m[j]);
-    mp_free(mc->minus_minv_mod_r);
-    mp_free(mc->scratch);
-    smemclr(mc, sizeof(*mc));
-    sfree(mc);
-}
-
-/*
- * The main Montgomery reduction step.
- */
-static mp_int monty_reduce_internal(MontyContext *mc, mp_int *x, mp_int scratch)
-{
-    /*
-     * The trick with Montgomery reduction is that on the one hand we
-     * want to reduce the size of the input by a factor of about r,
-     * and on the other hand, the two numbers we just multiplied were
-     * both stored with an extra factor of r multiplied in. So we
-     * computed ar*br = ab r^2, but we want to return abr, so we need
-     * to divide by r - and if we can do that by _actually dividing_
-     * by r then this also reduces the size of the number.
-     *
-     * But we can only do that if the number we're dividing by r is a
-     * multiple of r. So first we must add an adjustment to it which
-     * clears its bottom 'rbits' bits. That adjustment must be a
-     * multiple of m in order to leave the residue mod n unchanged, so
-     * the question is, what multiple of m can we add to x to make it
-     * congruent to 0 mod r? And the answer is, x * (-m)^{-1} mod r.
-     */
-
-    /* x mod r */
-    mp_int x_lo = mp_make_alias(x, 0, mc->rbits);
-
-    /* x * (-m)^{-1}, i.e. the number we want to multiply by m */
-    mp_int k = mp_alloc_from_scratch(&scratch, mc->rw);
-    mp_mul_internal(&k, &x_lo, mc->minus_minv_mod_r, scratch);
-
-    /* m times that, i.e. the number we want to add to x */
-    { // WINSCP
-    mp_int mk = mp_alloc_from_scratch(&scratch, mc->pw);
-    mp_mul_internal(&mk, mc->m, &k, scratch);
-
-    /* Add it to x */
-    mp_add_into(&mk, x, &mk);
-
-    /* Reduce mod r, by simply making an alias to the upper words of x */
-    { // WINSCP
-    mp_int toret = mp_make_alias(&mk, mc->rw, mk.nw - mc->rw);
-
-    /*
-     * We'll generally be doing this after a multiplication of two
-     * fully reduced values. So our input could be anything up to m^2,
-     * and then we added up to rm to it. Hence, the maximum value is
-     * rm+m^2, and after dividing by r, that becomes r + m(m/r) < 2r.
-     * So a single trial-subtraction will finish reducing to the
-     * interval [0,m).
-     */
-    mp_cond_sub_into(&toret, &toret, mc->m, mp_cmp_hs(&toret, mc->m));
-    return toret;
-    } // WINSCP
-    } // WINSCP
-}
-
-void monty_mul_into(MontyContext *mc, mp_int *r, mp_int *x, mp_int *y)
-{
-    assert(x->nw <= mc->rw);
-    assert(y->nw <= mc->rw);
-
-    { // WINSCP
-    mp_int scratch = *mc->scratch;
-    mp_int tmp = mp_alloc_from_scratch(&scratch, 2*mc->rw);
-    mp_mul_into(&tmp, x, y);
-    { // WINSCP
-    mp_int reduced = monty_reduce_internal(mc, &tmp, scratch);
-    mp_copy_into(r, &reduced);
-    mp_clear(mc->scratch);
-    } // WINSCP
-    } // WINSCP
-}
-
-mp_int *monty_mul(MontyContext *mc, mp_int *x, mp_int *y)
-{
-    mp_int *toret = mp_make_sized(mc->rw);
-    monty_mul_into(mc, toret, x, y);
-    return toret;
-}
-
-mp_int *monty_modulus(MontyContext *mc)
-{
-    return mc->m;
-}
-
-mp_int *monty_identity(MontyContext *mc)
-{
-    return mc->powers_of_r_mod_m[0];
-}
-
-mp_int *monty_invert(MontyContext *mc, mp_int *x)
-{
-    /* Given xr, we want to return x^{-1}r = (xr)^{-1} r^2 =
-     * monty_reduce((xr)^{-1} r^3) */
-    mp_int *tmp = mp_invert(x, mc->m);
-    mp_int *toret = monty_mul(mc, tmp, mc->powers_of_r_mod_m[2]);
-    mp_free(tmp);
-    return toret;
-}
-
-/*
- * Importing a number into Montgomery representation involves
- * multiplying it by r and reducing mod m. We use the general-purpose
- * mp_modmul for this, in case the input number is out of range.
- */
-mp_int *monty_import(MontyContext *mc, mp_int *x)
-{
-    return mp_modmul(x, mc->powers_of_r_mod_m[0], mc->m);
-}
-
-void monty_import_into(MontyContext *mc, mp_int *r, mp_int *x)
-{
-    mp_int *imported = monty_import(mc, x);
-    mp_copy_into(r, imported);
-    mp_free(imported);
-}
-
-/*
- * Exporting a number means multiplying it by r^{-1}, which is exactly
- * what monty_reduce does anyway, so we just do that.
- */
-void monty_export_into(MontyContext *mc, mp_int *r, mp_int *x)
-{
-    pinitassert(x->nw <= 2*mc->rw);
-    mp_int reduced = monty_reduce_internal(mc, x, *mc->scratch);
-    mp_copy_into(r, &reduced);
-    mp_clear(mc->scratch);
-}
-
-mp_int *monty_export(MontyContext *mc, mp_int *x)
-{
-    mp_int *toret = mp_make_sized(mc->rw);
-    monty_export_into(mc, toret, 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);
-}
-
-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;
-
-    /* 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]);
-
-    /* tmp holds each product we compute and reduce. */
-    mp_int *tmp = mp_make_sized(mc->rw * 2);
-
-    while (true) {
-        mp_mul_into(tmp, out, square);
-        monty_reduce(mc, tmp);
-        mp_select_into(out, out, tmp, mp_get_bit(exponent, i));
-
-        if (++i >= exponent->nw * BIGNUM_INT_BITS)
-            break;
-
-        mp_mul_into(tmp, square, square);
-        monty_reduce(mc, tmp);
-        mp_copy_into(square, tmp);
-    }
-
-    mp_free(square);
-    mp_free(tmp);
-    mp_clear(mc->scratch);
-    return out;
-}
-
-mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus)
-{
-    assert(modulus->nw > 0);
-    assert(modulus->w[0] & 1);
-
-    { // WINSCP
-    MontyContext *mc = monty_new(modulus);
-    mp_int *m_base = monty_import(mc, base);
-    mp_int *m_out = monty_pow(mc, m_base, exponent);
-    mp_int *out = monty_export(mc, m_out);
-    mp_free(m_base);
-    mp_free(m_out);
-    monty_free(mc);
-    return out;
-    } // WINSCP
-}
-
-/*
- * Given two input integers a,b which are not both even, computes d =
- * gcd(a,b) and also two integers A,B such that A*a - B*b = d. A,B
- * will be the minimal non-negative pair satisfying that criterion,
- * which is equivalent to saying that 0 <= A < b/d and 0 <= B < a/d.
- *
- * This algorithm is an adapted form of Stein's algorithm, which
- * computes gcd(a,b) using only addition and bit shifts (i.e. without
- * needing general division), using the following rules:
- *
- *  - if both of a,b are even, divide off a common factor of 2
- *  - if one of a,b (WLOG a) is even, then gcd(a,b) = gcd(a/2,b), so
- *    just divide a by 2
- *  - if both of a,b are odd, then WLOG a>b, and gcd(a,b) =
- *    gcd(b,(a-b)/2).
- *
- * Sometimes this function is used for modular inversion, in which
- * case we already know we expect the two inputs to be coprime, so to
- * save time the 'both even' initial case is assumed not to arise (or
- * to have been handled already by the caller). So this function just
- * performs a sequence of reductions in the following form:
- *
- *  - if a,b are both odd, sort them so that a > b, and replace a with
- *    b-a; otherwise sort them so that a is the even one
- *  - either way, now a is even and b is odd, so divide a by 2.
- *
- * The big change to Stein's algorithm is that we need the Bezout
- * coefficients as output, not just the gcd. So we need to know how to
- * generate those in each case, based on the coefficients from the
- * reduced pair of numbers:
- *
- *  - If a is even, and u,v are such that u*(a/2) + v*b = d:
- *     + if u is also even, then this is just (u/2)*a + v*b = d
- *     + otherwise, (u+b)*(a/2) + (v-a/2)*b is also equal to d, and
- *       since u and b are both odd, (u+b)/2 is an integer, so we have
- *       ((u+b)/2)*a + (v-a/2)*b = d.
- *
- *  - If a,b are both odd, and u,v are such that u*b + v*(a-b) = d,
- *    then v*a + (u-v)*b = d.
- *
- * In the case where we passed from (a,b) to (b,(a-b)/2), we regard it
- * as having first subtracted b from a and then halved a, so both of
- * these transformations must be done in sequence.
- *
- * The code below transforms this from a recursive to an iterative
- * algorithm. We first reduce a,b to 0,1, recording at each stage
- * whether we did the initial subtraction, and whether we had to swap
- * the two values; then we iterate backwards over that record of what
- * we did, applying the above rules for building up the Bezout
- * coefficients as we go. Of course, all the case analysis is done by
- * the usual bit-twiddling conditionalisation to avoid data-dependent
- * control flow.
- *
- * Also, since these mp_ints are generally treated as unsigned, we
- * store the coefficients by absolute value, with the semantics that
- * they always have opposite sign, and in the unwinding loop we keep a
- * bit indicating whether Aa-Bb is currently expected to be +d or -d,
- * so that we can do one final conditional adjustment if it's -d.
- *
- * Once the reduction rules have managed to reduce the input numbers
- * to (0,d), then they are stable (the next reduction will always
- * divide the even one by 2, which maps 0 to 0). So it doesn't matter
- * if we do more steps of the algorithm than necessary; hence, for
- * constant time, we just need to find the maximum number we could
- * _possibly_ require, and do that many.
- *
- * If a,b < 2^n, at most 2n iterations are required. Proof: consider
- * the quantity Q = log_2(a) + log_2(b). Every step halves one of the
- * numbers (and may also reduce one of them further by doing a
- * subtraction beforehand, but in the worst case, not by much or not
- * at all). So Q reduces by at least 1 per iteration, and it starts
- * off with a value at most 2n.
- *
- * The worst case inputs (I think) are where x=2^{n-1} and y=2^n-1
- * (i.e. x is a power of 2 and y is all 1s). In that situation, the
- * first n-1 steps repeatedly halve x until it's 1, and then there are
- * n further steps each of which subtracts 1 from y and halves it.
- */
-static void mp_bezout_into(mp_int *a_coeff_out, mp_int *b_coeff_out,
-                           mp_int *gcd_out, mp_int *a_in, mp_int *b_in)
-{
-    size_t nw = size_t_max(1, size_t_max(a_in->nw, b_in->nw));
-
-    /* Make mutable copies of the input numbers */
-    mp_int *a = mp_make_sized(nw), *b = mp_make_sized(nw);
-    mp_copy_into(a, a_in);
-    mp_copy_into(b, b_in);
-
-    /* Space to build up the output coefficients, with an extra word
-     * so that intermediate values can overflow off the top and still
-     * right-shift back down to the correct value */
-    { // WINSCP
-    mp_int *ac = mp_make_sized(nw + 1), *bc = mp_make_sized(nw + 1);
-
-    /* And a general-purpose temp register */
-    mp_int *tmp = mp_make_sized(nw);
-
-    /* Space to record the sequence of reduction steps to unwind. We
-     * make it a BignumInt for no particular reason except that (a)
-     * mp_make_sized conveniently zeroes the allocation and mp_free
-     * wipes it, and (b) this way I can use mp_dump() if I have to
-     * debug this code. */
-    size_t steps = 2 * nw * BIGNUM_INT_BITS;
-    mp_int *record = mp_make_sized(
-        (steps*2 + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS);
-    size_t step; // WINSCP
-
-    for (step = 0; step < steps; step++) {
-        /*
-         * If a and b are both odd, we want to sort them so that a is
-         * larger. But if one is even, we want to sort them so that a
-         * is the even one.
-         */
-        unsigned swap_if_both_odd = mp_cmp_hs(b, a);
-        unsigned swap_if_one_even = a->w[0] & 1;
-        unsigned both_odd = a->w[0] & b->w[0] & 1;
-        unsigned swap = swap_if_one_even ^ (
-            (swap_if_both_odd ^ swap_if_one_even) & both_odd);
-
-        mp_cond_swap(a, b, swap);
-
-        /*
-         * If a,b are both odd, then a is the larger number, so
-         * subtract the smaller one from it.
-         */
-        mp_cond_sub_into(a, a, b, both_odd);
-
-        /*
-         * Now a is even, so divide it by two.
-         */
-        mp_rshift_fixed_into(a, a, 1);
-
-        /*
-         * Record the two 1-bit values both_odd and swap.
-         */
-        mp_set_bit(record, step*2, both_odd);
-        mp_set_bit(record, step*2+1, swap);
-    }
-
-    /*
-     * Now we expect to have reduced the two numbers to 0 and d,
-     * although we don't know which way round. (But we avoid checking
-     * this by assertion; sometimes we'll need to do this computation
-     * without giving away that we already know the inputs were bogus.
-     * So we'd prefer to just press on and return nonsense.)
-     */
-
-    if (gcd_out) {
-        /*
-         * At this point we can return the actual gcd. Since one of
-         * a,b is it and the other is zero, the easiest way to get it
-         * is to add them together.
-         */
-        mp_add_into(gcd_out, a, b);
-    }
-
-    /*
-     * If the caller _only_ wanted the gcd, and neither Bezout
-     * coefficient is even required, we can skip the entire unwind
-     * stage.
-     */
-    if (a_coeff_out || b_coeff_out) {
-
-        /*
-         * The Bezout coefficients of a,b at this point are simply 0
-         * for whichever of a,b is zero, and 1 for whichever is
-         * nonzero. The nonzero number equals gcd(a,b), which by
-         * assumption is odd, so we can do this by just taking the low
-         * bit of each one.
-         */
-        ac->w[0] = mp_get_bit(a, 0);
-        bc->w[0] = mp_get_bit(b, 0);
-
-        /*
-         * Overwrite a,b themselves with those same numbers. This has
-         * the effect of dividing both of them by d, which will
-         * arrange that during the unwind stage we generate the
-         * minimal coefficients instead of a larger pair.
-         */
-        mp_copy_into(a, ac);
-        mp_copy_into(b, bc);
-
-        /*
-         * We'll maintain the invariant as we unwind that ac * a - bc
-         * * b is either +d or -d (or rather, +1/-1 after scaling by
-         * d), and we'll remember which. (We _could_ keep it at +d the
-         * whole time, but it would cost more work every time round
-         * the loop, so it's cheaper to fix that up once at the end.)
-         *
-         * Initially, the result is +d if a was the nonzero value after
-         * reduction, and -d if b was.
-         */
-        { // WINSCP
-        unsigned minus_d = b->w[0];
-
-        size_t step; // WINSCP
-        for (step = steps; step-- > 0 ;) {
-            /*
-             * Recover the data from the step we're unwinding.
-             */
-            unsigned both_odd = mp_get_bit(record, step*2);
-            unsigned swap = mp_get_bit(record, step*2+1);
-
-            /*
-             * Unwind the division: if our coefficient of a is odd, we
-             * adjust the coefficients by +b and +a respectively.
-             */
-            unsigned adjust = ac->w[0] & 1;
-            mp_cond_add_into(ac, ac, b, adjust);
-            mp_cond_add_into(bc, bc, a, adjust);
-
-            /*
-             * Now ac is definitely even, so we divide it by two.
-             */
-            mp_rshift_fixed_into(ac, ac, 1);
-
-            /*
-             * Now unwind the subtraction, if there was one, by adding
-             * ac to bc.
-             */
-            mp_cond_add_into(bc, bc, ac, both_odd);
-
-            /*
-             * Undo the transformation of the input numbers, by
-             * multiplying a by 2 and then adding b to a (the latter
-             * only if both_odd).
-             */
-            mp_lshift_fixed_into(a, a, 1);
-            mp_cond_add_into(a, a, b, both_odd);
-
-            /*
-             * Finally, undo the swap. If we do swap, this also
-             * reverses the sign of the current result ac*a+bc*b.
-             */
-            mp_cond_swap(a, b, swap);
-            mp_cond_swap(ac, bc, swap);
-            minus_d ^= swap;
-        }
-
-        /*
-         * Now we expect to have recovered the input a,b (or rather,
-         * the versions of them divided by d). But we might find that
-         * our current result is -d instead of +d, that is, we have
-         * A',B' such that A'a - B'b = -d.
-         *
-         * In that situation, we set A = b-A' and B = a-B', giving us
-         * Aa-Bb = ab - A'a - ab + B'b = +1.
-         */
-        mp_sub_into(tmp, b, ac);
-        mp_select_into(ac, ac, tmp, minus_d);
-        mp_sub_into(tmp, a, bc);
-        mp_select_into(bc, bc, tmp, minus_d);
-
-        /*
-         * Now we really are done. Return the outputs.
-         */
-        if (a_coeff_out)
-            mp_copy_into(a_coeff_out, ac);
-        if (b_coeff_out)
-            mp_copy_into(b_coeff_out, bc);
-
-        } // WINSCP
-    }
-
-    mp_free(a);
-    mp_free(b);
-    mp_free(ac);
-    mp_free(bc);
-    mp_free(tmp);
-    mp_free(record);
-    } // WINSCP
-}
-
-mp_int *mp_invert(mp_int *x, mp_int *m)
-{
-    mp_int *result = mp_make_sized(m->nw);
-    mp_bezout_into(result, NULL, NULL, x, m);
-    return result;
-}
-
-void mp_gcd_into(mp_int *a, mp_int *b, mp_int *gcd, mp_int *A, mp_int *B)
-{
-    /*
-     * Identify shared factors of 2. To do this we OR the two numbers
-     * to get something whose lowest set bit is in the right place,
-     * remove all higher bits by ANDing it with its own negation, and
-     * use mp_get_nbits to find the location of the single remaining
-     * set bit.
-     */
-    mp_int *tmp = mp_make_sized(size_t_max(a->nw, b->nw));
-    size_t i; // WINSCP
-    for (i = 0; i < tmp->nw; i++)
-        tmp->w[i] = mp_word(a, i) | mp_word(b, i);
-    { // WINSCP
-    BignumCarry carry = 1;
-    size_t i;
-    for (i = 0; i < tmp->nw; i++) {
-        BignumInt negw;
-        BignumADC(negw, carry, 0, ~tmp->w[i], carry);
-        tmp->w[i] &= negw;
-    }
-    { // WINSCP
-    size_t shift = mp_get_nbits(tmp) - 1;
-    mp_free(tmp);
-
-    /*
-     * Make copies of a,b with those shared factors of 2 divided off,
-     * so that at least one is odd (which is the precondition for
-     * mp_bezout_into). Compute the gcd of those.
-     */
-    { // WINSCP
-    mp_int *as = mp_rshift_safe(a, shift);
-    mp_int *bs = mp_rshift_safe(b, shift);
-    mp_bezout_into(A, B, gcd, as, bs);
-    mp_free(as);
-    mp_free(bs);
-
-    /*
-     * And finally shift the gcd back up (unless the caller didn't
-     * even ask for it), to put the shared factors of 2 back in.
-     */
-    if (gcd)
-        mp_lshift_safe_in_place(gcd, shift);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-mp_int *mp_gcd(mp_int *a, mp_int *b)
-{
-    mp_int *gcd = mp_make_sized(size_t_min(a->nw, b->nw));
-    mp_gcd_into(a, b, gcd, NULL, NULL);
-    return gcd;
-}
-
-unsigned mp_coprime(mp_int *a, mp_int *b)
-{
-    mp_int *gcd = mp_gcd(a, b);
-    unsigned toret = mp_eq_integer(gcd, 1);
-    mp_free(gcd);
-    return toret;
-}
-
-static uint32_t recip_approx_32(uint32_t x)
-{
-    /*
-     * Given an input x in [2^31,2^32), i.e. a uint32_t with its high
-     * bit set, this function returns an approximation to 2^63/x,
-     * computed using only multiplications and bit shifts just in case
-     * the C divide operator has non-constant time (either because the
-     * underlying machine instruction does, or because the operator
-     * expands to a library function on a CPU without hardware
-     * division).
-     *
-     * The coefficients are derived from those of the degree-9
-     * polynomial which is the minimax-optimal approximation to that
-     * function on the given interval (generated using the Remez
-     * algorithm), converted into integer arithmetic with shifts used
-     * to maximise the number of significant bits at every state. (A
-     * sort of 'static floating point' - the exponent is statically
-     * known at every point in the code, so it never needs to be
-     * stored at run time or to influence runtime decisions.)
-     *
-     * Exhaustive iteration over the whole input space shows the
-     * largest possible error to be 1686.54. (The input value
-     * attaining that bound is 4226800006 == 0xfbefd986, whose true
-     * reciprocal is 2182116973.540... == 0x8210766d.8a6..., whereas
-     * this function returns 2182115287 == 0x82106fd7.)
-     */
-    uint64_t r = 0x92db03d6ULL;
-    r = 0xf63e71eaULL - ((r*x) >> 34);
-    r = 0xb63721e8ULL - ((r*x) >> 34);
-    r = 0x9c2da00eULL - ((r*x) >> 33);
-    r = 0xaada0bb8ULL - ((r*x) >> 32);
-    r = 0xf75cd403ULL - ((r*x) >> 31);
-    r = 0xecf97a41ULL - ((r*x) >> 31);
-    r = 0x90d876cdULL - ((r*x) >> 31);
-    r = 0x6682799a0ULL - ((r*x) >> 26);
-    return r;
-}
-
-void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out)
-{
-    pinitassert(!mp_eq_integer(d, 0));
-
-    /*
-     * We do division by using Newton-Raphson iteration to converge to
-     * the reciprocal of d (or rather, R/d for R a sufficiently large
-     * power of 2); then we multiply that reciprocal by n; and we
-     * finish up with conditional subtraction.
-     *
-     * But we have to do it in a fixed number of N-R iterations, so we
-     * need some error analysis to know how many we might need.
-     *
-     * The iteration is derived by defining f(r) = d - R/r.
-     * Differentiating gives f'(r) = R/r^2, and the Newton-Raphson
-     * formula applied to those functions gives
-     *
-     *      r_{i+1} = r_i - f(r_i) / f'(r_i)
-     *              = r_i - (d - R/r_i) r_i^2 / R
-     *              = r_i (2 R - d r_i) / R
-     *
-     * Now let e_i be the error in a given iteration, in the sense
-     * that
-     *
-     *        d r_i = R + e_i
-     *  i.e.  e_i/R = (r_i - r_true) / r_true
-     *
-     * so e_i is the _relative_ error in r_i.
-     *
-     * We must also introduce a rounding-error term, because the
-     * division by R always gives an integer. This might make the
-     * output off by up to 1 (in the negative direction, because
-     * right-shifting gives floor of the true quotient). So when we
-     * divide by R, we must imagine adding some f in [0,1). Then we
-     * have
-     *
-     *    d r_{i+1} = d r_i (2 R - d r_i) / R - d f
-     *              = (R + e_i) (R - e_i) / R - d f
-     *              = (R^2 - e_i^2) / R - d f
-     *              = R - (e_i^2 / R + d f)
-     * =>   e_{i+1} = - (e_i^2 / R + d f)
-     *
-     * The sum of two positive quantities is bounded above by twice
-     * their max, and max |f| = 1, so we can bound this as follows:
-     *
-     *               |e_{i+1}| <= 2 max (e_i^2/R, d)
-     *             |e_{i+1}/R| <= 2 max ((e_i/R)^2, d/R)
-     *        log2 |R/e_{i+1}| <= min (2 log2 |R/e_i|, log2 |R/d|) - 1
-     *
-     * which tells us that the number of 'good' bits - i.e.
-     * log2(R/e_i) - very nearly doubles at every iteration (apart
-     * from that subtraction of 1), until it gets to the same size as
-     * log2(R/d). In other words, the size of R in bits has to be the
-     * size of denominator we're putting in, _plus_ the amount of
-     * precision we want to get back out.
-     *
-     * So when we multiply n (the input numerator) by our final
-     * reciprocal approximation r, but actually r differs from R/d by
-     * up to 2, then it follows that
-     *
-     *   n/d - nr/R = n/d - [ n (R/d + e) ] / R
-     *              = n/d - [ (n/d) R + n e ] / R
-     *              = -ne/R
-     *      =>   0 <= n/d - nr/R < 2n/R
-     *
-     * so our computed quotient can differ from the true n/d by up to
-     * 2n/R. Hence, as long as we also choose R large enough that 2n/R
-     * is bounded above by a constant, we can guarantee a bounded
-     * number of final conditional-subtraction steps.
-     */
-
-    /*
-     * Get at least 32 of the most significant bits of the input
-     * number.
-     */
-    size_t hiword_index = 0;
-    uint64_t hibits = 0, lobits = 0;
-    mp_find_highest_nonzero_word_pair(d, 64 - BIGNUM_INT_BITS,
-                                      &hiword_index, &hibits, &lobits);
-
-    /*
-     * Make a shifted combination of those two words which puts the
-     * topmost bit of the number at bit 63.
-     */
-    { // WINSCP
-    size_t shift_up = 0;
-    size_t i; // WINSCP
-    for (i = BIGNUM_INT_BITS_BITS; i-- > 0;) {
-        size_t sl = (size_t)1 << i;       /* left shift count */
-        size_t sr = 64 - sl;     /* complementary right-shift count */
-
-        /* Should we shift up? */
-        unsigned indicator = 1 ^ normalise_to_1_u64(hibits >> sr);
-
-        /* If we do, what will we get? */
-        uint64_t new_hibits = (hibits << sl) | (lobits >> sr);
-        uint64_t new_lobits = lobits << sl;
-        size_t new_shift_up = shift_up + sl;
-
-        /* Conditionally swap those values in. */
-        hibits    ^= (hibits    ^ new_hibits   ) & -(uint64_t)indicator;
-        lobits    ^= (lobits    ^ new_lobits   ) & -(uint64_t)indicator;
-        shift_up  ^= (shift_up  ^ new_shift_up ) & -(size_t)  indicator;
-    }
-
-    /*
-     * So now we know the most significant 32 bits of d are at the top
-     * of hibits. Approximate the reciprocal of those bits.
-     */
-    lobits = (uint64_t)recip_approx_32(hibits >> 32) << 32;
-    hibits = 0;
-
-    /*
-     * And shift that up by as many bits as the input was shifted up
-     * just now, so that the product of this approximation and the
-     * actual input will be close to a fixed power of two regardless
-     * of where the MSB was.
-     *
-     * I do this in another log n individual passes, partly in case
-     * the CPU's register-controlled shift operation isn't
-     * time-constant, and also in case the compiler code-generates
-     * uint64_t shifts out of a variable number of smaller-word shift
-     * instructions, e.g. by splitting up into cases.
-     */
-    for (i = BIGNUM_INT_BITS_BITS; i-- > 0;) {
-        size_t sl = (size_t)1 << i;       /* left shift count */
-        size_t sr = 64 - sl;     /* complementary right-shift count */
-
-        /* Should we shift up? */
-        unsigned indicator = 1 & (shift_up >> i);
-
-        /* If we do, what will we get? */
-        uint64_t new_hibits = (hibits << sl) | (lobits >> sr);
-        uint64_t new_lobits = lobits << sl;
-
-        /* Conditionally swap those values in. */
-        hibits    ^= (hibits    ^ new_hibits   ) & -(uint64_t)indicator;
-        lobits    ^= (lobits    ^ new_lobits   ) & -(uint64_t)indicator;
-    }
-
-    /*
-     * The product of the 128-bit value now in hibits:lobits with the
-     * 128-bit value we originally retrieved in the same variables
-     * will be in the vicinity of 2^191. So we'll take log2(R) to be
-     * 191, plus a multiple of BIGNUM_INT_BITS large enough to allow R
-     * to hold the combined sizes of n and d.
-     */
-    { // WINSCP
-    size_t log2_R;
-    {
-        size_t max_log2_n = (n->nw + d->nw) * BIGNUM_INT_BITS;
-        log2_R = max_log2_n + 3;
-        log2_R -= size_t_min(191, log2_R);
-        log2_R = (log2_R + BIGNUM_INT_BITS - 1) & ~(BIGNUM_INT_BITS - 1);
-        log2_R += 191;
-    }
-
-    /* Number of words in a bignum capable of holding numbers the size
-     * of twice R. */
-    { // WINSCP
-    size_t rw = ((log2_R+2) + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS;
-
-    /*
-     * Now construct our full-sized starting reciprocal approximation.
-     */
-    mp_int *r_approx = mp_make_sized(rw);
-    size_t output_bit_index;
-    {
-        /* Where in the input number did the input 128-bit value come from? */
-        size_t input_bit_index =
-            (hiword_index * BIGNUM_INT_BITS) - (128 - BIGNUM_INT_BITS);
-
-        /* So how far do we need to shift our 64-bit output, if the
-         * product of those two fixed-size values is 2^191 and we want
-         * to make it 2^log2_R instead? */
-        output_bit_index = log2_R - 191 - input_bit_index;
-
-        /* If we've done all that right, it should be a whole number
-         * of words. */
-        assert(output_bit_index % BIGNUM_INT_BITS == 0);
-        { // WINSCP
-        size_t output_word_index = output_bit_index / BIGNUM_INT_BITS;
-
-        mp_add_integer_into_shifted_by_words(
-            r_approx, r_approx, lobits, output_word_index);
-        mp_add_integer_into_shifted_by_words(
-            r_approx, r_approx, hibits,
-            output_word_index + 64 / BIGNUM_INT_BITS);
-        } // WINSCP
-    }
-
-    /*
-     * Make the constant 2*R, which we'll need in the iteration.
-     */
-    { // WINSCP
-    mp_int *two_R = mp_make_sized(rw);
-    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);
-
-    /*
-     * Scratch space.
-     */
-    { // WINSCP
-    mp_int *dr = mp_make_sized(rw + d->nw);
-    mp_int *diff = mp_make_sized(size_t_max(rw, dr->nw));
-    mp_int *product = mp_make_sized(rw + diff->nw);
-    size_t scratchsize = size_t_max(
-        mp_mul_scratchspace(dr->nw, r_approx->nw, d->nw),
-        mp_mul_scratchspace(product->nw, r_approx->nw, diff->nw));
-    mp_int *scratch = mp_make_sized(scratchsize);
-    mp_int product_shifted = mp_make_alias(
-        product, log2_R / BIGNUM_INT_BITS, product->nw);
-
-    /*
-     * Initial error estimate: the 32-bit output of recip_approx_32
-     * differs by less than 2048 (== 2^11) from the true top 32 bits
-     * of the reciprocal, so the relative error is at most 2^11
-     * divided by the 32-bit reciprocal, which at worst is 2^11/2^31 =
-     * 2^-20. So even in the worst case, we have 20 good bits of
-     * reciprocal to start with.
-     */
-    size_t good_bits = 31 - 11;
-    size_t good_bits_needed = BIGNUM_INT_BITS * n->nw + 4; /* add a few */
-
-    /*
-     * Now do Newton-Raphson iterations until we have reason to think
-     * they're not converging any more.
-     */
-    while (good_bits < good_bits_needed) {
-        /*
-         * Compute the next iterate.
-         */
-        mp_mul_internal(dr, r_approx, d, *scratch);
-        mp_sub_into(diff, two_R, dr);
-        mp_mul_internal(product, r_approx, diff, *scratch);
-        mp_rshift_fixed_into(r_approx, &product_shifted,
-                             log2_R % BIGNUM_INT_BITS);
-
-        /*
-         * Adjust the error estimate.
-         */
-        good_bits = good_bits * 2 - 1;
-    }
-
-    mp_free(dr);
-    mp_free(diff);
-    mp_free(product);
-    mp_free(scratch);
-
-    /*
-     * Now we've got our reciprocal, we can compute the quotient, by
-     * multiplying in n and then shifting down by log2_R bits.
-     */
-    { // WINSCP
-    mp_int *quotient_full = mp_mul(r_approx, n);
-    mp_int quotient_alias = mp_make_alias(
-        quotient_full, log2_R / BIGNUM_INT_BITS, quotient_full->nw);
-    mp_int *quotient = mp_make_sized(n->nw);
-    mp_rshift_fixed_into(quotient, &quotient_alias, log2_R % BIGNUM_INT_BITS);
-
-    /*
-     * Next, compute the remainder.
-     */
-    { // WINSCP
-    mp_int *remainder = mp_make_sized(d->nw);
-    mp_mul_into(remainder, quotient, d);
-    mp_sub_into(remainder, n, remainder);
-
-    /*
-     * Finally, two conditional subtractions to fix up any remaining
-     * rounding error. (I _think_ one should be enough, but this
-     * routine isn't time-critical enough to take chances.)
-     */
-    { // WINSCP
-    unsigned q_correction = 0;
-    unsigned iter; // WINSCP
-    for (iter = 0; iter < 2; iter++) {
-        unsigned need_correction = mp_cmp_hs(remainder, d);
-        mp_cond_sub_into(remainder, remainder, d, need_correction);
-        q_correction += need_correction;
-    }
-    mp_add_integer_into(quotient, quotient, q_correction);
-
-    /*
-     * Now we should have a perfect answer, i.e. 0 <= r < d.
-     */
-    assert(!mp_cmp_hs(remainder, d));
-
-    if (q_out)
-        mp_copy_into(q_out, quotient);
-    if (r_out)
-        mp_copy_into(r_out, remainder);
-
-    mp_free(r_approx);
-    mp_free(two_R);
-    mp_free(quotient_full);
-    mp_free(quotient);
-    mp_free(remainder);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-mp_int *mp_div(mp_int *n, mp_int *d)
-{
-    mp_int *q = mp_make_sized(n->nw);
-    mp_divmod_into(n, d, q, NULL);
-    return q;
-}
-
-mp_int *mp_mod(mp_int *n, mp_int *d)
-{
-    mp_int *r = mp_make_sized(d->nw);
-    mp_divmod_into(n, d, NULL, r);
-    return r;
-}
-
-mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder_out)
-{
-    /*
-     * Allocate scratch space.
-     */
-    mp_int **alloc, **powers, **newpowers, *scratch;
-    size_t nalloc = 2*(n+1)+1;
-    alloc = snewn(nalloc, mp_int *);
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < nalloc; i++)
-        alloc[i] = mp_make_sized(y->nw + 1);
-    powers = alloc;
-    newpowers = alloc + (n+1);
-    scratch = alloc[2*n+2];
-
-    /*
-     * We're computing the rounded-down nth root of y, i.e. the
-     * maximal x such that x^n <= y. We try to add 2^i to it for each
-     * possible value of i, starting from the largest one that might
-     * fit (i.e. such that 2^{n*i} fits in the size of y) downwards to
-     * i=0.
-     *
-     * We track all the smaller powers of x in the array 'powers'. In
-     * each iteration, if we update x, we update all of those values
-     * to match.
-     */
-    mp_copy_integer_into(powers[0], 1);
-    { // WINSCP
-    size_t s; // WINSCP
-    for (s = mp_max_bits(y) / n + 1; s-- > 0 ;) {
-        /*
-         * Let b = 2^s. We need to compute the powers (x+b)^i for each
-         * i, starting from our recorded values of x^i.
-         */
-        size_t i; // WINSCP
-        for (i = 0; i < n+1; i++) {
-            /*
-             * (x+b)^i = x^i
-             *         + (i choose 1) x^{i-1} b
-             *         + (i choose 2) x^{i-2} b^2
-             *         + ...
-             *         + b^i
-             */
-            uint16_t binom = 1;       /* coefficient of b^i */
-            mp_copy_into(newpowers[i], powers[i]);
-            { // WINSCP
-            size_t j; // WINSCP
-            for (j = 0; j < i; j++) {
-                /* newpowers[i] += binom * powers[j] * 2^{(i-j)*s} */
-                mp_mul_integer_into(scratch, powers[j], binom);
-                mp_lshift_fixed_into(scratch, scratch, (i-j) * s);
-                mp_add_into(newpowers[i], newpowers[i], scratch);
-
-                { // WINSCP
-                uint32_t binom_mul = binom;
-                binom_mul *= (i-j);
-                binom_mul /= (j+1);
-                assert(binom_mul < 0x10000);
-                binom = binom_mul;
-                } // WINSCP
-            }
-            } // WINSCP
-        }
-
-        /*
-         * Now, is the new value of x^n still <= y? If so, update.
-         */
-        { // WINSCP
-        unsigned newbit = mp_cmp_hs(y, newpowers[n]);
-        size_t i; // WINSCP
-        for (i = 0; i < n+1; i++)
-            mp_select_into(powers[i], powers[i], newpowers[i], newbit);
-        } // WINSCP
-    }
-
-    if (remainder_out)
-        mp_sub_into(remainder_out, y, powers[n]);
-
-    { // WINSCP
-    mp_int *root = mp_new(mp_max_bits(y) / n);
-    mp_copy_into(root, powers[1]);
-
-    { // WINSCP
-    size_t i;
-    for (i = 0; i < nalloc; i++)
-        mp_free(alloc[i]);
-    sfree(alloc);
-
-    return root;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-mp_int *mp_modmul(mp_int *x, mp_int *y, mp_int *modulus)
-{
-    mp_int *product = mp_mul(x, y);
-    mp_int *reduced = mp_mod(product, modulus);
-    mp_free(product);
-    return reduced;
-}
-
-mp_int *mp_modadd(mp_int *x, mp_int *y, mp_int *modulus)
-{
-    mp_int *sum = mp_add(x, y);
-    mp_int *reduced = mp_mod(sum, modulus);
-    mp_free(sum);
-    return reduced;
-}
-
-mp_int *mp_modsub(mp_int *x, mp_int *y, mp_int *modulus)
-{
-    mp_int *diff = mp_make_sized(size_t_max(x->nw, y->nw));
-    mp_sub_into(diff, x, y);
-    { // WINSCP
-    unsigned negate = mp_cmp_hs(y, x);
-    mp_cond_negate(diff, diff, negate);
-    { // WINSCP
-    mp_int *residue = mp_mod(diff, modulus);
-    mp_cond_negate(residue, residue, negate);
-    /* If we've just negated the residue, then it will be < 0 and need
-     * the modulus adding to it to make it positive - *except* if the
-     * residue was zero when we negated it. */
-    { // WINSCP
-    unsigned make_positive = negate & ~mp_eq_integer(residue, 0);
-    mp_cond_add_into(residue, residue, modulus, make_positive);
-    mp_free(diff);
-    return residue;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static mp_int *mp_modadd_in_range(mp_int *x, mp_int *y, mp_int *modulus)
-{
-    mp_int *sum = mp_make_sized(modulus->nw);
-    unsigned carry = mp_add_into_internal(sum, x, y);
-    mp_cond_sub_into(sum, sum, modulus, carry | mp_cmp_hs(sum, modulus));
-    return sum;
-}
-
-static mp_int *mp_modsub_in_range(mp_int *x, mp_int *y, mp_int *modulus)
-{
-    mp_int *diff = mp_make_sized(modulus->nw);
-    mp_sub_into(diff, x, y);
-    mp_cond_add_into(diff, diff, modulus, 1 ^ mp_cmp_hs(x, y));
-    return diff;
-}
-
-mp_int *monty_add(MontyContext *mc, mp_int *x, mp_int *y)
-{
-    return mp_modadd_in_range(x, y, mc->m);
-}
-
-mp_int *monty_sub(MontyContext *mc, mp_int *x, mp_int *y)
-{
-    return mp_modsub_in_range(x, y, mc->m);
-}
-
-void mp_min_into(mp_int *r, mp_int *x, mp_int *y)
-{
-    mp_select_into(r, x, y, mp_cmp_hs(x, y));
-}
-
-void mp_max_into(mp_int *r, mp_int *x, mp_int *y)
-{
-    mp_select_into(r, y, x, mp_cmp_hs(x, y));
-}
-
-mp_int *mp_min(mp_int *x, mp_int *y)
-{
-    mp_int *r = mp_make_sized(size_t_min(x->nw, y->nw));
-    mp_min_into(r, x, y);
-    return r;
-}
-
-mp_int *mp_max(mp_int *x, mp_int *y)
-{
-    mp_int *r = mp_make_sized(size_t_max(x->nw, y->nw));
-    mp_max_into(r, x, y);
-    return r;
-}
-
-mp_int *mp_power_2(size_t power)
-{
-    mp_int *x = mp_new(power + 1);
-    mp_set_bit(x, power, 1);
-    return x;
-}
-
-struct ModsqrtContext {
-    mp_int *p;                      /* the prime */
-    MontyContext *mc;                  /* for doing arithmetic mod p */
-
-    /* Decompose p-1 as 2^e k, for positive integer e and odd k */
-    size_t e;
-    mp_int *k;
-    mp_int *km1o2;                  /* (k-1)/2 */
-
-    /* The user-provided value z which is not a quadratic residue mod
-     * p, and its kth power. Both in Montgomery form. */
-    mp_int *z, *zk;
-};
-
-ModsqrtContext *modsqrt_new(mp_int *p, mp_int *any_nonsquare_mod_p)
-{
-    ModsqrtContext *sc = snew(ModsqrtContext);
-    memset(sc, 0, sizeof(ModsqrtContext));
-
-    sc->p = mp_copy(p);
-    sc->mc = monty_new(sc->p);
-    sc->z = monty_import(sc->mc, any_nonsquare_mod_p);
-
-    /* Find the lowest set bit in p-1. Since this routine expects p to
-     * be non-secret (typically a well-known standard elliptic curve
-     * parameter), for once we don't need clever bit tricks. */
-    for (sc->e = 1; sc->e < BIGNUM_INT_BITS * p->nw; sc->e++)
-        if (mp_get_bit(p, sc->e))
-            break;
-
-    sc->k = mp_rshift_fixed(p, sc->e);
-    sc->km1o2 = mp_rshift_fixed(sc->k, 1);
-
-    /* Leave zk to be filled in lazily, since it's more expensive to
-     * compute. If this context turns out never to be needed, we can
-     * save the bulk of the setup time this way. */
-
-    return sc;
-}
-
-static void modsqrt_lazy_setup(ModsqrtContext *sc)
-{
-    if (!sc->zk)
-        sc->zk = monty_pow(sc->mc, sc->z, sc->k);
-}
-
-void modsqrt_free(ModsqrtContext *sc)
-{
-    monty_free(sc->mc);
-    mp_free(sc->p);
-    mp_free(sc->z);
-    mp_free(sc->k);
-    mp_free(sc->km1o2);
-
-    if (sc->zk)
-        mp_free(sc->zk);
-
-    sfree(sc);
-}
-
-mp_int *mp_modsqrt(ModsqrtContext *sc, mp_int *x, unsigned *success)
-{
-    mp_int *mx = monty_import(sc->mc, x);
-    mp_int *mroot = monty_modsqrt(sc, mx, success);
-    mp_free(mx);
-    { // WINSCP
-    mp_int *root = monty_export(sc->mc, mroot);
-    mp_free(mroot);
-    return root;
-    } // WINSCP
-}
-
-/*
- * Modular square root, using an algorithm more or less similar to
- * Tonelli-Shanks but adapted for constant time.
- *
- * The basic idea is to write p-1 = k 2^e, where k is odd and e > 0.
- * Then the multiplicative group mod p (call it G) has a sequence of
- * e+1 nested subgroups G = G_0 > G_1 > G_2 > ... > G_e, where each
- * G_i is exactly half the size of G_{i-1} and consists of all the
- * squares of elements in G_{i-1}. So the innermost group G_e has
- * order k, which is odd, and hence within that group you can take a
- * square root by raising to the power (k+1)/2.
- *
- * Our strategy is to iterate over these groups one by one and make
- * sure the number x we're trying to take the square root of is inside
- * each one, by adjusting it if it isn't.
- *
- * Suppose g is a primitive root of p, i.e. a generator of G_0. (We
- * don't actually need to know what g _is_; we just imagine it for the
- * sake of understanding.) Then G_i consists of precisely the (2^i)th
- * powers of g, and hence, you can tell if a number is in G_i if
- * raising it to the power k 2^{e-i} gives 1. So the conceptual
- * algorithm goes: for each i, test whether x is in G_i by that
- * method. If it isn't, then the previous iteration ensured it's in
- * G_{i-1}, so it will be an odd power of g^{2^{i-1}}, and hence
- * multiplying by any other odd power of g^{2^{i-1}} will give x' in
- * G_i. And we have one of those, because our non-square z is an odd
- * power of g, so z^{2^{i-1}} is an odd power of g^{2^{i-1}}.
- *
- * (There's a special case in the very first iteration, where we don't
- * have a G_{i-1}. If it turns out that x is not even in G_1, that
- * means it's not a square, so we set *success to 0. We still run the
- * rest of the algorithm anyway, for the sake of constant time, but we
- * don't give a hoot what it returns.)
- *
- * When we get to the end and have x in G_e, then we can take its
- * square root by raising to (k+1)/2. But of course that's not the
- * square root of the original input - it's only the square root of
- * the adjusted version we produced during the algorithm. To get the
- * true output answer we also have to multiply by a power of z,
- * namely, z to the power of _half_ whatever we've been multiplying in
- * as we go along. (The power of z we multiplied in must have been
- * even, because the case in which we would have multiplied in an odd
- * power of z is the i=0 case, in which we instead set the failure
- * flag.)
- *
- * The code below is an optimised version of that basic idea, in which
- * we _start_ by computing x^k so as to be able to test membership in
- * G_i by only a few squarings rather than a full from-scratch modpow
- * every time; we also start by computing our candidate output value
- * x^{(k+1)/2}. So when the above description says 'adjust x by z^i'
- * for some i, we have to adjust our running values of x^k and
- * x^{(k+1)/2} by z^{ik} and z^{ik/2} respectively (the latter is safe
- * because, as above, i is always even). And it turns out that we
- * don't actually have to store the adjusted version of x itself at
- * all - we _only_ keep those two powers of it.
- */
-mp_int *monty_modsqrt(ModsqrtContext *sc, mp_int *x, unsigned *success)
-{
-    modsqrt_lazy_setup(sc);
-
-    { // WINSCP
-    mp_int *scratch_to_free = mp_make_sized(3 * sc->mc->rw);
-    mp_int scratch = *scratch_to_free;
-
-    /*
-     * Compute toret = x^{(k+1)/2}, our starting point for the output
-     * square root, and also xk = x^k which we'll use as we go along
-     * for knowing when to apply correction factors. We do this by
-     * first computing x^{(k-1)/2}, then multiplying it by x, then
-     * multiplying the two together.
-     */
-    mp_int *toret = monty_pow(sc->mc, x, sc->km1o2);
-    mp_int xk = mp_alloc_from_scratch(&scratch, sc->mc->rw);
-    mp_copy_into(&xk, toret);
-    monty_mul_into(sc->mc, toret, toret, x);
-    monty_mul_into(sc->mc, &xk, toret, &xk);
-
-    { // WINSCP
-    mp_int tmp = mp_alloc_from_scratch(&scratch, sc->mc->rw);
-
-    mp_int power_of_zk = mp_alloc_from_scratch(&scratch, sc->mc->rw);
-    size_t i; // WINSCP
-    mp_copy_into(&power_of_zk, sc->zk);
-
-    for (i = 0; i < sc->e; i++) {
-        size_t j; // WINSCP
-        mp_copy_into(&tmp, &xk);
-        for (j = i+1; j < sc->e; j++)
-            monty_mul_into(sc->mc, &tmp, &tmp, &tmp);
-        { // WINSCP
-        unsigned eq1 = mp_cmp_eq(&tmp, monty_identity(sc->mc));
-
-        if (i == 0) {
-            /* One special case: if x=0, then no power of x will ever
-             * equal 1, but we should still report success on the
-             * grounds that 0 does have a square root mod p. */
-            *success = eq1 | mp_eq_integer(x, 0);
-        } else {
-            monty_mul_into(sc->mc, &tmp, toret, &power_of_zk);
-            mp_select_into(toret, &tmp, toret, eq1);
-
-            monty_mul_into(sc->mc, &power_of_zk,
-                           &power_of_zk, &power_of_zk);
-
-            monty_mul_into(sc->mc, &tmp, &xk, &power_of_zk);
-            mp_select_into(&xk, &tmp, &xk, eq1);
-        }
-        } // WINSCP
-    }
-
-    mp_free(scratch_to_free);
-
-    return toret;
-    } // WINSCP
-    } // WINSCP
-}
-
-mp_int *mp_random_bits_fn(size_t bits, random_read_fn_t random_read)
-{
-    size_t bytes = (bits + 7) / 8;
-    uint8_t *randbuf = snewn(bytes, uint8_t);
-    random_read(randbuf, bytes);
-    if (bytes)
-        randbuf[0] &= (2 << ((bits-1) & 7)) - 1;
-    { // WINSCP
-    mp_int *toret = mp_from_bytes_be(make_ptrlen(randbuf, bytes));
-    smemclr(randbuf, bytes);
-    sfree(randbuf);
-    return toret;
-    } // WINSCP
-}
-
-mp_int *mp_random_upto_fn(mp_int *limit, random_read_fn_t rf)
-{
-    /*
-     * It would be nice to generate our random numbers in such a way
-     * as to make every possible outcome literally equiprobable. But
-     * we can't do that in constant time, so we have to go for a very
-     * close approximation instead. I'm going to take the view that a
-     * factor of (1+2^-128) between the probabilities of two outcomes
-     * is acceptable on the grounds that you'd have to examine so many
-     * outputs to even detect it.
-     */
-    mp_int *unreduced = mp_random_bits_fn(mp_max_bits(limit) + 128, rf);
-    mp_int *reduced = mp_mod(unreduced, limit);
-    mp_free(unreduced);
-    return reduced;
-}
-
-mp_int *mp_random_in_range_fn(mp_int *lo, mp_int *hi, random_read_fn_t rf)
-{
-    mp_int *n_outcomes = mp_sub(hi, lo);
-    mp_int *addend = mp_random_upto_fn(n_outcomes, rf);
-    mp_int *result = mp_make_sized(hi->nw);
-    mp_add_into(result, addend, lo);
-    mp_free(addend);
-    mp_free(n_outcomes);
-    return result;
-}

+ 8 - 2
source/putty/mpint.h

@@ -268,6 +268,12 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q, mp_int *r);
 mp_int *mp_div(mp_int *n, mp_int *d);
 mp_int *mp_mod(mp_int *x, mp_int *modulus);
 
+/*
+ * Compute the residue of x mod m, where m is a small integer. x is
+ * kept secret, but m is not.
+ */
+uint32_t mp_mod_known_integer(mp_int *x, uint32_t m);
+
 /*
  * Integer nth root. mp_nthroot returns the largest integer x such
  * that x^n <= y, and if 'remainder' is non-NULL then it fills it with
@@ -433,10 +439,10 @@ mp_int *mp_rshift_fixed(mp_int *x, size_t shift);
  *
  * The _function_ definitions here will expect to be given a gen_data
  * function that provides random data. Normally you'd use this using
- * random_read() from random.c, and the macro wrappers automate that.
+ * random_read() from sshrand.c, and the macro wrappers automate that.
  *
  * (This is a bit of a dodge to avoid mpint.c having a link-time
- * dependency on random.c, so that programs can link against one but
+ * dependency on sshrand.c, so that programs can link against one but
  * not the other: if a client of this header uses one of these macros
  * then _they_ have link-time dependencies on both modules.)
  *

+ 0 - 324
source/putty/mpint_i.h

@@ -1,324 +0,0 @@
-/*
- * mpint_i.h: definitions used internally by the bignum code, and
- * also a few other vaguely-bignum-like places.
- */
-
-/* ----------------------------------------------------------------------
- * The assorted conditional definitions of BignumInt and multiply
- * macros used throughout the bignum code to treat numbers as arrays
- * of the most conveniently sized word for the target machine.
- * Exported so that other code (e.g. poly1305) can use it too.
- *
- * This code must export, in whatever ifdef branch it ends up in:
- *
- *  - two types: 'BignumInt' and 'BignumCarry'. BignumInt is an
- *    unsigned integer type which will be used as the base word size
- *    for all bignum operations. BignumCarry is an unsigned integer
- *    type used to hold the carry flag taken as input and output by
- *    the BignumADC macro (see below).
- *
- *  - five constant macros:
- *     + BIGNUM_INT_BITS, the number of bits in BignumInt,
- *     + BIGNUM_INT_BYTES, the number of bytes that works out to
- *     + BIGNUM_TOP_BIT, the BignumInt value consisting of only the top bit
- *     + BIGNUM_INT_MASK, the BignumInt value with all bits set
- *     + BIGNUM_INT_BITS_BITS, log to the base 2 of BIGNUM_INT_BITS.
- *
- *  - four statement macros: BignumADC, BignumMUL, BignumMULADD,
- *    BignumMULADD2. These do various kinds of multi-word arithmetic,
- *    and all produce two output values.
- *     * BignumADC(ret,retc,a,b,c) takes input BignumInt values a,b
- *       and a BignumCarry c, and outputs a BignumInt ret = a+b+c and
- *       a BignumCarry retc which is the carry off the top of that
- *       addition.
- *     * BignumMUL(rh,rl,a,b) returns the two halves of the
- *       double-width product a*b.
- *     * BignumMULADD(rh,rl,a,b,addend) returns the two halves of the
- *       double-width value a*b + addend.
- *     * BignumMULADD2(rh,rl,a,b,addend1,addend2) returns the two
- *       halves of the double-width value a*b + addend1 + addend2.
- *
- * Every branch of the main ifdef below defines the type BignumInt and
- * the value BIGNUM_INT_BITS_BITS. The other constant macros are
- * filled in by common code further down.
- *
- * Most branches also define a macro DEFINE_BIGNUMDBLINT containing a
- * typedef statement which declares a type _twice_ the length of a
- * BignumInt. This causes the common code further down to produce a
- * default implementation of the four statement macros in terms of
- * that double-width type, and also to defined BignumCarry to be
- * BignumInt.
- *
- * However, if a particular compile target does not have a type twice
- * the length of the BignumInt you want to use but it does provide
- * some alternative means of doing add-with-carry and double-word
- * multiply, then the ifdef branch in question can just define
- * BignumCarry and the four statement macros itself, and that's fine
- * too.
- */
-
-/* You can lower the BignumInt size by defining BIGNUM_OVERRIDE on the
- * command line to be your chosen max value of BIGNUM_INT_BITS_BITS */
-#if defined BIGNUM_OVERRIDE
-#define BB_OK(b) ((b) <= BIGNUM_OVERRIDE)
-#else
-#define BB_OK(b) (1)
-#endif
-
-#if defined __SIZEOF_INT128__ && BB_OK(6)
-
-  /*
-   * 64-bit BignumInt using gcc/clang style 128-bit BignumDblInt.
-   *
-   * gcc and clang both provide a __uint128_t type on 64-bit targets
-   * (and, when they do, indicate its presence by the above macro),
-   * using the same 'two machine registers' kind of code generation
-   * that 32-bit targets use for 64-bit ints.
-   */
-
-  typedef unsigned long long BignumInt;
-  #define BIGNUM_INT_BITS_BITS 6
-  #define DEFINE_BIGNUMDBLINT typedef __uint128_t BignumDblInt
-
-#elif defined _MSC_VER && defined _M_AMD64 && BB_OK(6)
-
-  /*
-   * 64-bit BignumInt, using Visual Studio x86-64 compiler intrinsics.
-   *
-   * 64-bit Visual Studio doesn't provide very much in the way of help
-   * here: there's no int128 type, and also no inline assembler giving
-   * us direct access to the x86-64 MUL or ADC instructions. However,
-   * there are compiler intrinsics giving us that access, so we can
-   * use those - though it turns out we have to be a little careful,
-   * since they seem to generate wrong code if their pointer-typed
-   * output parameters alias their inputs. Hence all the internal temp
-   * variables inside the macros.
-   */
-
-  #include <intrin.h>
-  typedef unsigned char BignumCarry; /* the type _addcarry_u64 likes to use */
-  typedef unsigned __int64 BignumInt;
-  #define BIGNUM_INT_BITS_BITS 6
-  #define BignumADC(ret, retc, a, b, c) do                \
-      {                                                   \
-          BignumInt ADC_tmp;                              \
-          (retc) = _addcarry_u64(c, a, b, &ADC_tmp);      \
-          (ret) = ADC_tmp;                                \
-      } while (0)
-  #define BignumMUL(rh, rl, a, b) do              \
-      {                                           \
-          BignumInt MULADD_hi;                    \
-          (rl) = _umul128(a, b, &MULADD_hi);      \
-          (rh) = MULADD_hi;                       \
-      } while (0)
-  #define BignumMULADD(rh, rl, a, b, addend) do                           \
-      {                                                                   \
-          BignumInt MULADD_lo, MULADD_hi;                                 \
-          MULADD_lo = _umul128(a, b, &MULADD_hi);                         \
-          MULADD_hi += _addcarry_u64(0, MULADD_lo, (addend), &(rl));     \
-          (rh) = MULADD_hi;                                               \
-      } while (0)
-  #define BignumMULADD2(rh, rl, a, b, addend1, addend2) do                \
-      {                                                                   \
-          BignumInt MULADD_lo1, MULADD_lo2, MULADD_hi;                    \
-          MULADD_lo1 = _umul128(a, b, &MULADD_hi);                        \
-          MULADD_hi += _addcarry_u64(0, MULADD_lo1, (addend1), &MULADD_lo2); \
-          MULADD_hi += _addcarry_u64(0, MULADD_lo2, (addend2), &(rl));    \
-          (rh) = MULADD_hi;                                               \
-      } while (0)
-
-#elif (defined __GNUC__ || defined _LLP64 || __STDC__ >= 199901L) && BB_OK(5)
-
-  /* 32-bit BignumInt, using C99 unsigned long long as BignumDblInt */
-
-  typedef unsigned int BignumInt;
-  #define BIGNUM_INT_BITS_BITS 5
-  #define DEFINE_BIGNUMDBLINT typedef unsigned long long BignumDblInt
-
-#elif defined _MSC_VER && BB_OK(5) || defined(MPEXT)
-
-  /* 32-bit BignumInt, using Visual Studio __int64 as BignumDblInt */
-
-  typedef unsigned int BignumInt;
-  #define BIGNUM_INT_BITS_BITS 5
-  #define DEFINE_BIGNUMDBLINT typedef unsigned __int64 BignumDblInt
-
-#elif defined _LP64 && BB_OK(5)
-
-  /*
-   * 32-bit BignumInt, using unsigned long itself as BignumDblInt.
-   *
-   * Only for platforms where long is 64 bits, of course.
-   */
-
-  typedef unsigned int BignumInt;
-  #define BIGNUM_INT_BITS_BITS 5
-  #define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt
-
-#elif BB_OK(4)
-
-  /*
-   * 16-bit BignumInt, using unsigned long as BignumDblInt.
-   *
-   * This is the final fallback for real emergencies: C89 guarantees
-   * unsigned short/long to be at least the required sizes, so this
-   * should work on any C implementation at all. But it'll be
-   * noticeably slow, so if you find yourself in this case you
-   * probably want to move heaven and earth to find an alternative!
-   */
-
-  typedef unsigned short BignumInt;
-  #define BIGNUM_INT_BITS_BITS 4
-  #define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt
-
-#else
-
-  /* Should only get here if BB_OK(4) evaluated false, i.e. the
-   * command line defined BIGNUM_OVERRIDE to an absurdly small
-   * value. */
-  #error Must define BIGNUM_OVERRIDE to at least 4
-
-#endif
-
-#undef BB_OK
-
-/*
- * Common code across all branches of that ifdef: define all the
- * easy constant macros in terms of BIGNUM_INT_BITS_BITS.
- */
-#define BIGNUM_INT_BITS (1 << BIGNUM_INT_BITS_BITS)
-#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)
-#define BIGNUM_TOP_BIT (((BignumInt)1) << (BIGNUM_INT_BITS-1))
-#define BIGNUM_INT_MASK (BIGNUM_TOP_BIT | (BIGNUM_TOP_BIT-1))
-
-/*
- * Just occasionally, we might need a GET_nnBIT_xSB_FIRST macro to
- * operate on whatever BignumInt is.
- */
-#if BIGNUM_INT_BITS_BITS == 4
-#define GET_BIGNUMINT_MSB_FIRST GET_16BIT_MSB_FIRST
-#define GET_BIGNUMINT_LSB_FIRST GET_16BIT_LSB_FIRST
-#define PUT_BIGNUMINT_MSB_FIRST PUT_16BIT_MSB_FIRST
-#define PUT_BIGNUMINT_LSB_FIRST PUT_16BIT_LSB_FIRST
-#elif BIGNUM_INT_BITS_BITS == 5
-#define GET_BIGNUMINT_MSB_FIRST GET_32BIT_MSB_FIRST
-#define GET_BIGNUMINT_LSB_FIRST GET_32BIT_LSB_FIRST
-#define PUT_BIGNUMINT_MSB_FIRST PUT_32BIT_MSB_FIRST
-#define PUT_BIGNUMINT_LSB_FIRST PUT_32BIT_LSB_FIRST
-#elif BIGNUM_INT_BITS_BITS == 6
-#define GET_BIGNUMINT_MSB_FIRST GET_64BIT_MSB_FIRST
-#define GET_BIGNUMINT_LSB_FIRST GET_64BIT_LSB_FIRST
-#define PUT_BIGNUMINT_MSB_FIRST PUT_64BIT_MSB_FIRST
-#define PUT_BIGNUMINT_LSB_FIRST PUT_64BIT_LSB_FIRST
-#else
-  #error Ran out of options for GET_BIGNUMINT_xSB_FIRST
-#endif
-
-/*
- * Common code across _most_ branches of the ifdef: define a set of
- * statement macros in terms of the BignumDblInt type provided. In
- * this case, we also define BignumCarry to be the same thing as
- * BignumInt, for simplicity.
- */
-#ifdef DEFINE_BIGNUMDBLINT
-
-  typedef BignumInt BignumCarry;
-  #define BignumADC(ret, retc, a, b, c) do                        \
-      {                                                           \
-          DEFINE_BIGNUMDBLINT;                                    \
-          BignumDblInt ADC_temp = (BignumInt)(a);                 \
-          ADC_temp += (BignumInt)(b);                             \
-          ADC_temp += (c);                                        \
-          (ret) = (BignumInt)ADC_temp;                            \
-          (retc) = (BignumCarry)(ADC_temp >> BIGNUM_INT_BITS);    \
-      } while (0)
-
-  #define BignumMUL(rh, rl, a, b) do                              \
-      {                                                           \
-          DEFINE_BIGNUMDBLINT;                                    \
-          BignumDblInt MUL_temp = (BignumInt)(a);                 \
-          MUL_temp *= (BignumInt)(b);                             \
-          (rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS);        \
-          (rl) = (BignumInt)(MUL_temp);                           \
-      } while (0)
-
-  #define BignumMULADD(rh, rl, a, b, addend) do                   \
-      {                                                           \
-          DEFINE_BIGNUMDBLINT;                                    \
-          BignumDblInt MUL_temp = (BignumInt)(a);                 \
-          MUL_temp *= (BignumInt)(b);                             \
-          MUL_temp += (BignumInt)(addend);                        \
-          (rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS);        \
-          (rl) = (BignumInt)(MUL_temp);                           \
-      } while (0)
-
-  #define BignumMULADD2(rh, rl, a, b, addend1, addend2) do        \
-      {                                                           \
-          DEFINE_BIGNUMDBLINT;                                    \
-          BignumDblInt MUL_temp = (BignumInt)(a);                 \
-          MUL_temp *= (BignumInt)(b);                             \
-          MUL_temp += (BignumInt)(addend1);                       \
-          MUL_temp += (BignumInt)(addend2);                       \
-          (rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS);        \
-          (rl) = (BignumInt)(MUL_temp);                           \
-      } while (0)
-
-#endif /* DEFINE_BIGNUMDBLINT */
-
-/* ----------------------------------------------------------------------
- * Data structures used inside bignum.c.
- */
-
-struct mp_int {
-    size_t nw;
-    BignumInt *w;
-};
-
-struct MontyContext {
-    /*
-     * The actual modulus.
-     */
-    mp_int *m;
-
-    /*
-     * Montgomery multiplication works by selecting a value r > m,
-     * coprime to m, which is really easy to divide by. In binary
-     * arithmetic, that means making it a power of 2; in fact we make
-     * it a whole number of BignumInt.
-     *
-     * We don't store r directly as an mp_int (there's no need). But
-     * its value is 2^rbits; we also store rw = rbits/BIGNUM_INT_BITS
-     * (the corresponding word offset within an mp_int).
-     *
-     * pw is the number of words needed to store an mp_int you're
-     * doing reduction on: it has to be big enough to hold the sum of
-     * an input value up to m^2 plus an extra addend up to m*r.
-     */
-    size_t rbits, rw, pw;
-
-    /*
-     * The key step in Montgomery reduction requires the inverse of -m
-     * mod r.
-     */
-    mp_int *minus_minv_mod_r;
-
-    /*
-     * r^1, r^2 and r^3 mod m, which are used for various purposes.
-     *
-     * (Annoyingly, this is one of the rare cases where it would have
-     * been nicer to have a Pascal-style 1-indexed array. I couldn't
-     * _quite_ bring myself to put a gratuitous zero element in here.
-     * So you just have to live with getting r^k by taking the [k-1]th
-     * element of this array.)
-     */
-    mp_int *powers_of_r_mod_m[3];
-
-    /*
-     * Persistent scratch space from which monty_* functions can
-     * allocate storage for intermediate values.
-     */
-    mp_int *scratch;
-};
-
-/* Functions shared between mpint.c and mpunsafe.c */
-mp_int *mp_make_sized(size_t nw);

+ 151 - 33
source/putty/network.h

@@ -51,9 +51,14 @@ typedef enum PlugLogType {
     PLUGLOG_PROXY_MSG,
 } PlugLogType;
 
+typedef enum PlugCloseType {
+    PLUGCLOSE_NORMAL,
+    PLUGCLOSE_ERROR,
+    PLUGCLOSE_BROKEN_PIPE,
+    PLUGCLOSE_USER_ABORT,
+} PlugCloseType;
+
 struct PlugVtable {
-    void (*log)(Plug *p, PlugLogType type, SockAddr *addr, int port,
-                const char *error_msg, int error_code);
     /*
      * Passes the client progress reports on the process of setting
      * up the connection.
@@ -67,22 +72,63 @@ struct PlugVtable {
      *    addresses to fall back to. When it _is_ fatal, the closing()
      *    function will be called.
      *
-     *  - PLUGLOG_CONNECT_SUCCESS means we have succeeded in
-     *    connecting to address `addr'.
+     *  - PLUGLOG_CONNECT_SUCCESS means we have succeeded in making a
+     *    connection. `addr' gives the address we connected to, if
+     *    available. (But sometimes, in cases of complicated proxy
+     *    setups, it might not be available, so receivers of this log
+     *    event should be prepared to deal with addr==NULL.)
      *
      *  - PLUGLOG_PROXY_MSG means that error_msg contains a line of
      *    logging information from whatever the connection is being
      *    proxied through. This will typically be a wodge of
      *    standard-error output from a local proxy command, so the
      *    receiver should probably prefix it to indicate this.
+     *
+     * Note that sometimes log messages may be sent even to Socket
+     * types that don't involve making an outgoing connection, e.g.
+     * because the same core implementation (such as Windows handle
+     * sockets) is shared between listening and connecting sockets. So
+     * all Plugs must implement this method, even if only to ignore
+     * the logged events.
      */
-    void (*closing)
-     (Plug *p, const char *error_msg, int error_code, bool calling_back);
-    /* error_msg is NULL iff it is not an error (ie it closed normally) */
-    /* calling_back != 0 iff there is a Plug function */
-    /* currently running (would cure the fixme in try_send()) */
-    void (*receive) (Plug *p, int urgent, const char *data, size_t len);
+    void (*log)(Plug *p, PlugLogType type, SockAddr *addr, int port,
+                const char *error_msg, int error_code);
+
+    /*
+     * Notifies the Plug that the socket is closing, and something
+     * about why.
+     *
+     *  - PLUGCLOSE_NORMAL means an ordinary non-error closure. In
+     *    this case, error_msg should be ignored (and hopefully
+     *    callers will have passed NULL).
+     *
+     *  - PLUGCLOSE_ERROR indicates that an OS error occurred, and
+     *    'error_msg' contains a string describing it, for use in
+     *    diagnostics. (Ownership of the string is not transferred.)
+     *    This error class covers anything other than the special
+     *    case below:
+     *
+     *  - PLUGCLOSE_BROKEN_PIPE behaves like PLUGCLOSE_ERROR (in
+     *    particular, there's still an error message provided), but
+     *    distinguishes the particular error condition signalled by
+     *    EPIPE / ERROR_BROKEN_PIPE, which ssh/sharing.c needs to
+     *    recognise and handle specially in one situation.
+     *
+     *  - PLUGCLOSE_USER_ABORT means that the close has happened as a
+     *    result of some kind of deliberate user action (e.g. hitting
+     *    ^C at a password prompt presented by a proxy socket setup
+     *    phase). This can be used to suppress interactive error
+     *    messages sent to the user (such as dialog boxes), on the
+     *    grounds that the user already knows. However, 'error_msg'
+     *    will still contain some appropriate text, so that
+     *    non-interactive error reporting (e.g. event logs) can still
+     *    record why the connection terminated.
+     */
+    void (*closing)(Plug *p, PlugCloseType type, const char *error_msg);
+
     /*
+     * Provides incoming socket data to the Plug. Three cases:
+     *
      *  - urgent==0. `data' points to `len' bytes of perfectly
      *    ordinary data.
      *
@@ -92,28 +138,52 @@ struct PlugVtable {
      *  - urgent==2. `data' points to `len' bytes of data,
      *    the first of which was the one at the Urgent mark.
      */
-    void (*sent) (Plug *p, size_t bufsize);
+    void (*receive) (Plug *p, int urgent, const char *data, size_t len);
+
     /*
-     * The `sent' function is called when the pending send backlog
-     * on a socket is cleared or partially cleared. The new backlog
-     * size is passed in the `bufsize' parameter.
+     * Called when the pending send backlog on a socket is cleared or
+     * partially cleared. The new backlog size is passed in the
+     * `bufsize' parameter.
      */
-    int (*accepting)(Plug *p, accept_fn_t constructor, accept_ctx_t ctx);
+    void (*sent) (Plug *p, size_t bufsize);
+
     /*
-     * `accepting' is called only on listener-type sockets, and is
-     * passed a constructor function+context that will create a fresh
-     * Socket describing the connection. It returns nonzero if it
-     * doesn't want the connection for some reason, or 0 on success.
+     * Only called on listener-type sockets, and is passed a
+     * constructor function+context that will create a fresh Socket
+     * describing the connection. It returns nonzero if it doesn't
+     * want the connection for some reason, or 0 on success.
      */
+    int (*accepting)(Plug *p, accept_fn_t constructor, accept_ctx_t ctx);
 };
 
-/* proxy indirection layer */
-/* NB, control of 'addr' is passed via new_connection, which takes
- * responsibility for freeing it */
+/* Proxy indirection layer.
+ *
+ * Calling new_connection transfers ownership of 'addr': the proxy
+ * layer is now responsible for freeing it, and the caller shouldn't
+ * assume it exists any more.
+ *
+ * If calling this from a backend with a Seat, you can also give it a
+ * pointer to the backend's Interactor trait. In that situation, it
+ * might replace the backend's seat with a temporary seat of its own,
+ * and give the real Seat to an Interactor somewhere in the proxy
+ * system so that it can ask for passwords (and, in the case of SSH
+ * proxying, other prompts like host key checks). If that happens,
+ * then the resulting 'temp seat' is the backend's property, and it
+ * will have to remember to free it when cleaning up, or after
+ * flushing it back into the real seat when the network connection
+ * attempt completes.
+ *
+ * You can free your TempSeat and resume using the real Seat when one
+ * of two things happens: either your Plug's closing() method is
+ * called (indicating failure to connect), or its log() method is
+ * called with PLUGLOG_CONNECT_SUCCESS. In the latter case, you'll
+ * probably want to flush the TempSeat's contents into the real Seat,
+ * of course.
+ */
 Socket *new_connection(SockAddr *addr, const char *hostname,
                        int port, bool privport,
                        bool oobinline, bool nodelay, bool keepalive,
-                       Plug *plug, Conf *conf);
+                       Plug *plug, Conf *conf, Interactor *interactor);
 Socket *new_listener(const char *srcaddr, int port, Plug *plug,
                      bool local_host_only, Conf *conf, int addressfamily);
 SockAddr *name_lookup(const char *host, int port, char **canonicalname,
@@ -125,7 +195,13 @@ SockAddr *name_lookup(const char *host, int port, char **canonicalname,
 Socket *platform_new_connection(SockAddr *addr, const char *hostname,
                                 int port, bool privport,
                                 bool oobinline, bool nodelay, bool keepalive,
-                                Plug *plug, Conf *conf);
+                                Plug *plug, Conf *conf, Interactor *itr);
+
+/* callback for SSH jump-host proxying */
+Socket *sshproxy_new_connection(SockAddr *addr, const char *hostname,
+                                int port, bool privport,
+                                bool oobinline, bool nodelay, bool keepalive,
+                                Plug *plug, Conf *conf, Interactor *itr);
 
 /* socket functions */
 
@@ -184,9 +260,14 @@ static inline void plug_log(
     Plug *p, int type, SockAddr *addr, int port, const char *msg, int code)
 { p->vt->log(p, type, addr, port, msg, code); }
 #pragma option pop // WINSCP
-static inline void plug_closing(
-    Plug *p, const char *msg, int code, bool calling_back)
-{ p->vt->closing(p, msg, code, calling_back); }
+static inline void plug_closing(Plug *p, PlugCloseType type, const char *msg)
+{ p->vt->closing(p, type, msg); }
+static inline void plug_closing_normal(Plug *p)
+{ p->vt->closing(p, PLUGCLOSE_NORMAL, NULL); }
+static inline void plug_closing_error(Plug *p, const char *msg)
+{ p->vt->closing(p, PLUGCLOSE_ERROR, msg); }
+static inline void plug_closing_user_abort(Plug *p)
+{ p->vt->closing(p, PLUGCLOSE_USER_ABORT, "User aborted connection setup"); }
 static inline void plug_receive(Plug *p, int urg, const char *data, size_t len)
 { p->vt->receive(p, urg, data, len); }
 static inline void plug_sent (Plug *p, size_t bufsize)
@@ -234,7 +315,7 @@ static inline SocketPeerInfo *sk_peer_info(Socket *s)
 
 /*
  * The structure returned from sk_peer_info, and a function to free
- * one (in misc.c).
+ * one (in utils).
  */
 struct SocketPeerInfo {
     int addressfamily;
@@ -272,13 +353,13 @@ struct SocketPeerInfo {
 void sk_free_peer_info(SocketPeerInfo *pi);
 
 /*
- * Simple wrapper on getservbyname(), needed by ssh.c. Returns the
+ * Simple wrapper on getservbyname(), needed by portfwd.c. Returns the
  * port number, in host byte order (suitable for printf and so on).
  * Returns 0 on failure. Any platform not supporting getservbyname
  * can just return 0 - this function is not required to handle
  * numeric port specifications.
  */
-int net_service_lookup(char *service);
+int net_service_lookup(const char *service);
 
 /*
  * Look up the local hostname; return value needs freeing.
@@ -302,15 +383,25 @@ Socket *new_error_socket_consume_string(Plug *plug, char *errmsg);
  */
 extern Plug *const nullplug;
 
+/*
+ * Some trivial no-op plug functions, also in nullplug.c; exposed here
+ * so that other Plug implementations can use them too.
+ *
+ * In particular, nullplug_log is useful to Plugs that don't need to
+ * worry about logging.
+ */
+void nullplug_log(Plug *plug, PlugLogType type, SockAddr *addr,
+                  int port, const char *err_msg, int err_code);
+void nullplug_closing(Plug *plug, PlugCloseType type, const char *error_msg);
+void nullplug_receive(Plug *plug, int urgent, const char *data, size_t len);
+void nullplug_sent(Plug *plug, size_t bufsize);
+
 /* ----------------------------------------------------------------------
  * Functions defined outside the network code, which have to be
  * declared in this header file rather than the main putty.h because
  * they use types defined here.
  */
 
-/*
- * Exports from be_misc.c.
- */
 void backend_socket_log(Seat *seat, LogContext *logctx,
                         PlugLogType type, SockAddr *addr, int port,
                         const char *error_msg, int error_code, Conf *conf,
@@ -324,4 +415,31 @@ void psb_init(ProxyStderrBuf *psb);
 void log_proxy_stderr(
     Plug *plug, ProxyStderrBuf *psb, const void *vdata, size_t len);
 
+/* ----------------------------------------------------------------------
+ * The DeferredSocketOpener trait. This is a thing that some Socket
+ * implementations may choose to own if they need to delay actually
+ * setting up the underlying connection. For example, sockets used in
+ * local-proxy handling (Unix FdSocket / Windows HandleSocket) might
+ * need to do this if they have to prompt the user interactively for
+ * parts of the command they'll run.
+ *
+ * Mostly, a DeferredSocketOpener implementation will keep to itself,
+ * arrange its own callbacks in order to do whatever setup it needs,
+ * and when it's ready, call back to its parent Socket via some
+ * implementation-specific API of its own. So the shared API here
+ * requires almost nothing: the only thing we need is a free function,
+ * so that if the owner of a Socket of this kind needs to close it
+ * before the deferred connection process is finished, the Socket can
+ * also clean up the DeferredSocketOpener dangling off it.
+ */
+
+struct DeferredSocketOpener {
+    const DeferredSocketOpenerVtable *vt;
+};
+struct DeferredSocketOpenerVtable {
+    void (*free)(DeferredSocketOpener *);
+};
+static inline void deferred_socket_opener_free(DeferredSocketOpener *dso)
+{ dso->vt->free(dso); }
+
 #endif

+ 0 - 25
source/putty/noshare.c

@@ -1,25 +0,0 @@
-/*
- * Stub implementation of SSH connection-sharing IPC, for any
- * platform which can't support it at all.
- */
-
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-
-#include "tree234.h"
-#include "putty.h"
-#include "ssh.h"
-#include "network.h"
-
-int platform_ssh_share(const char *name, Conf *conf,
-                       Plug *downplug, Plug *upplug, Socket **sock,
-                       char **logtext, char **ds_err, char **us_err,
-                       bool can_upstream, bool can_downstream)
-{
-    return SHARE_NONE;
-}
-
-void platform_ssh_share_cleanup(const char *name)
-{
-}

+ 0 - 44
source/putty/nullplug.c

@@ -1,44 +0,0 @@
-/*
- * nullplug.c: provide a null implementation of the Plug vtable which
- * ignores all calls. Occasionally useful in cases where we want to
- * make a network connection just to see if it works, but not do
- * anything with it afterwards except close it again.
- */
-
-#include "putty.h"
-
-static void nullplug_socket_log(Plug *plug, PlugLogType type, SockAddr *addr,
-                                int port, const char *err_msg, int err_code)
-{
-}
-
-static void nullplug_closing(Plug *plug, const char *error_msg, int error_code,
-                             bool calling_back)
-{
-}
-
-static void nullplug_receive(
-    Plug *plug, int urgent, const char *data, size_t len)
-{
-}
-
-static void nullplug_sent(Plug *plug, size_t bufsize)
-{
-}
-
-static const PlugVtable nullplug_plugvt = {
-    // WINSCP
-    /*.log =*/ nullplug_socket_log,
-    /*.closing =*/ nullplug_closing,
-    /*.receive =*/ nullplug_receive,
-    /*.sent =*/ nullplug_sent,
-    NULL, // WINSCP
-};
-
-static Plug nullplug_plug = { &nullplug_plugvt };
-
-/*
- * There's a singleton instance of nullplug, because it's not
- * interesting enough to worry about making more than one of them.
- */
-Plug *const nullplug = &nullplug_plug;

+ 0 - 105
source/putty/pgssapi.c

@@ -1,105 +0,0 @@
-/* This file actually defines the GSSAPI function pointers for
- * functions we plan to import from a GSSAPI library.
- */
-#include "putty.h"
-
-#ifndef NO_GSSAPI
-
-#include "pgssapi.h"
-
-#ifndef NO_LIBDL
-
-/* Reserved static storage for GSS_oids.  Comments are quotes from RFC 2744. */
-static const gss_OID_desc oids[] = {
-    /* The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},
-    /* corresponding to an object-identifier value of
-     * {iso(1) member-body(2) United States(840) mit(113554)
-     * infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant
-     * GSS_C_NT_USER_NAME should be initialized to point
-     * to that gss_OID_desc.
-
-     * The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},
-    /* corresponding to an object-identifier value of
-     * {iso(1) member-body(2) United States(840) mit(113554)
-     * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
-     * The constant GSS_C_NT_MACHINE_UID_NAME should be
-     * initialized to point to that gss_OID_desc.
-
-     * The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},
-    /* corresponding to an object-identifier value of
-     * {iso(1) member-body(2) United States(840) mit(113554)
-     * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
-     * The constant GSS_C_NT_STRING_UID_NAME should be
-     * initialized to point to that gss_OID_desc.
-     *
-     * The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-    {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
-    /* corresponding to an object-identifier value of
-     * {iso(1) org(3) dod(6) internet(1) security(5)
-     * nametypes(6) gss-host-based-services(2))}.  The constant
-     * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
-     * to that gss_OID_desc.  This is a deprecated OID value, and
-     * implementations wishing to support hostbased-service names
-     * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
-     * defined below, to identify such names;
-     * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
-     * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
-     * parameter, but should not be emitted by GSS-API
-     * implementations
-     *
-     * The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-     {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"},
-    /* corresponding to an object-identifier value of {iso(1)
-     * member-body(2) Unites States(840) mit(113554) infosys(1)
-     * gssapi(2) generic(1) service_name(4)}.  The constant
-     * GSS_C_NT_HOSTBASED_SERVICE should be initialized
-     * to point to that gss_OID_desc.
-     *
-     * The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-    {6, (void *)"\x2b\x06\01\x05\x06\x03"},
-    /* corresponding to an object identifier value of
-     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
-     * 6(nametypes), 3(gss-anonymous-name)}.  The constant
-     * and GSS_C_NT_ANONYMOUS should be initialized to point
-     * to that gss_OID_desc.
-     *
-     * The implementation must reserve static storage for a
-     * gss_OID_desc object containing the value */
-    {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
-     /* corresponding to an object-identifier value of
-     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
-     * 6(nametypes), 4(gss-api-exported-name)}.  The constant
-     * GSS_C_NT_EXPORT_NAME should be initialized to point
-     * to that gss_OID_desc.
-     */
-};
-
-/* Here are the constants which point to the static structure above.
- *
- * Constants of the form GSS_C_NT_* are specified by rfc 2744.
- */
-const_gss_OID GSS_C_NT_USER_NAME           = oids+0;
-const_gss_OID GSS_C_NT_MACHINE_UID_NAME    = oids+1;
-const_gss_OID GSS_C_NT_STRING_UID_NAME     = oids+2;
-const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3;
-const_gss_OID GSS_C_NT_HOSTBASED_SERVICE   = oids+4;
-const_gss_OID GSS_C_NT_ANONYMOUS           = oids+5;
-const_gss_OID GSS_C_NT_EXPORT_NAME         = oids+6;
-
-#endif /* NO_LIBDL */
-
-static gss_OID_desc gss_mech_krb5_desc =
-{ 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
-/* iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2)*/
-const gss_OID GSS_MECH_KRB5 = &gss_mech_krb5_desc;
-
-#endif /* NO_GSSAPI */

+ 0 - 333
source/putty/pgssapi.h

@@ -1,333 +0,0 @@
-#ifndef PUTTY_PGSSAPI_H
-#define PUTTY_PGSSAPI_H
-
-#include "putty.h"
-
-#ifndef NO_GSSAPI
-
-/*
- * On Unix, if we're statically linking against GSSAPI, we leave the
- * declaration of all this lot to the official header. If we're
- * dynamically linking, we declare it ourselves, because that avoids
- * us needing the official header at compile time.
- *
- * However, we still need the function pointer types, because even
- * with statically linked GSSAPI we use the ssh_gss_library wrapper.
- */
-#ifdef STATIC_GSSAPI
-#include <gssapi/gssapi.h>
-typedef gss_OID const_gss_OID;         /* for our prototypes below */
-#else /* STATIC_GSSAPI */
-
-/*******************************************************************************
- *  GSSAPI Definitions, taken from RFC 2744
- ******************************************************************************/
-
-/* GSSAPI Type Definitions */
-typedef uint32_t OM_uint32;
-
-typedef struct gss_OID_desc_struct {
-    OM_uint32 length;
-    void *elements;
-} gss_OID_desc;
-typedef const gss_OID_desc *const_gss_OID;
-typedef gss_OID_desc *gss_OID;
-
-typedef struct gss_OID_set_desc_struct  {
-    size_t  count;
-    gss_OID elements;
-} gss_OID_set_desc;
-typedef const gss_OID_set_desc *const_gss_OID_set;
-typedef gss_OID_set_desc *gss_OID_set;
-
-typedef struct gss_buffer_desc_struct {
-    size_t length;
-    void *value;
-} gss_buffer_desc, *gss_buffer_t;
-
-typedef struct gss_channel_bindings_struct {
-    OM_uint32 initiator_addrtype;
-    gss_buffer_desc initiator_address;
-    OM_uint32 acceptor_addrtype;
-    gss_buffer_desc acceptor_address;
-    gss_buffer_desc application_data;
-} *gss_channel_bindings_t;
-
-typedef void * gss_ctx_id_t;
-typedef void * gss_name_t;
-typedef void * gss_cred_id_t;
-
-typedef OM_uint32 gss_qop_t;
-typedef int gss_cred_usage_t;
-
-/* Flag bits for context-level services. */
-
-#define GSS_C_DELEG_FLAG      1
-#define GSS_C_MUTUAL_FLAG     2
-#define GSS_C_REPLAY_FLAG     4
-#define GSS_C_SEQUENCE_FLAG   8
-#define GSS_C_CONF_FLAG       16
-#define GSS_C_INTEG_FLAG      32
-#define GSS_C_ANON_FLAG       64
-#define GSS_C_PROT_READY_FLAG 128
-#define GSS_C_TRANS_FLAG      256
-
-/* Credential usage options */
-#define GSS_C_BOTH     0
-#define GSS_C_INITIATE 1
-#define GSS_C_ACCEPT   2
-
-/*-
- * RFC 2744 Page 86
- * Expiration time of 2^32-1 seconds means infinite lifetime for a
- * credential or security context
- */
-#define GSS_C_INDEFINITE 0xfffffffful
-
-/* Status code types for gss_display_status */
-#define GSS_C_GSS_CODE  1
-#define GSS_C_MECH_CODE 2
-
-/* The constant definitions for channel-bindings address families */
-#define GSS_C_AF_UNSPEC     0
-#define GSS_C_AF_LOCAL      1
-#define GSS_C_AF_INET       2
-#define GSS_C_AF_IMPLINK    3
-#define GSS_C_AF_PUP        4
-#define GSS_C_AF_CHAOS      5
-#define GSS_C_AF_NS         6
-#define GSS_C_AF_NBS        7
-#define GSS_C_AF_ECMA       8
-#define GSS_C_AF_DATAKIT    9
-#define GSS_C_AF_CCITT      10
-#define GSS_C_AF_SNA        11
-#define GSS_C_AF_DECnet     12
-#define GSS_C_AF_DLI        13
-#define GSS_C_AF_LAT        14
-#define GSS_C_AF_HYLINK     15
-#define GSS_C_AF_APPLETALK  16
-#define GSS_C_AF_BSC        17
-#define GSS_C_AF_DSS        18
-#define GSS_C_AF_OSI        19
-#define GSS_C_AF_X25        21
-
-#define GSS_C_AF_NULLADDR   255
-
-/* Various Null values */
-#define GSS_C_NO_NAME ((gss_name_t) 0)
-#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
-#define GSS_C_NO_OID ((gss_OID) 0)
-#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
-#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
-#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
-#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
-#define GSS_C_EMPTY_BUFFER {0, NULL}
-
-/* Major status codes */
-#define GSS_S_COMPLETE 0
-
-/* Some "helper" definitions to make the status code macros obvious. */
-#define GSS_C_CALLING_ERROR_OFFSET 24
-#define GSS_C_ROUTINE_ERROR_OFFSET 16
-
-#define GSS_C_SUPPLEMENTARY_OFFSET 0
-#define GSS_C_CALLING_ERROR_MASK 0377ul
-#define GSS_C_ROUTINE_ERROR_MASK 0377ul
-#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
-
-/*
- * The macros that test status codes for error conditions.
- * Note that the GSS_ERROR() macro has changed slightly from
- * the V1 GSS-API so that it now evaluates its argument
- * only once.
- */
-#define GSS_CALLING_ERROR(x)                                            \
-    (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
-#define GSS_ROUTINE_ERROR(x)                                            \
-    (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
-#define GSS_SUPPLEMENTARY_INFO(x)                                       \
-    (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
-#define GSS_ERROR(x)                                                    \
-    (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) |    \
-          (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
-
-/* Now the actual status code definitions */
-
-/* Calling errors: */
-#define GSS_S_CALL_INACCESSIBLE_READ            \
-    (1ul << GSS_C_CALLING_ERROR_OFFSET)
-#define GSS_S_CALL_INACCESSIBLE_WRITE           \
-    (2ul << GSS_C_CALLING_ERROR_OFFSET)
-#define GSS_S_CALL_BAD_STRUCTURE                \
-    (3ul << GSS_C_CALLING_ERROR_OFFSET)
-
-/* Routine errors: */
-#define GSS_S_BAD_MECH             (1ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_NAME             (2ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_NAMETYPE         (3ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_BINDINGS         (4ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_STATUS           (5ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_SIG              (6ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_MIC GSS_S_BAD_SIG
-#define GSS_S_NO_CRED              (7ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_NO_CONTEXT           (8ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_DEFECTIVE_TOKEN      (9ul <<                      \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_DEFECTIVE_CREDENTIAL (10ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_CREDENTIALS_EXPIRED  (11ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_CONTEXT_EXPIRED      (12ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_FAILURE              (13ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_QOP              (14ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_UNAUTHORIZED         (15ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_UNAVAILABLE          (16ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_DUPLICATE_ELEMENT    (17ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_NAME_NOT_MN          (18ul <<                     \
-                                    GSS_C_ROUTINE_ERROR_OFFSET)
-
-/* Supplementary info bits: */
-#define GSS_S_CONTINUE_NEEDED                                           \
-                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
-#define GSS_S_DUPLICATE_TOKEN                                           \
-                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
-#define GSS_S_OLD_TOKEN                                                 \
-                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
-#define GSS_S_UNSEQ_TOKEN                                               \
-                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
-#define GSS_S_GAP_TOKEN                                                 \
-                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
-
-extern const_gss_OID GSS_C_NT_USER_NAME;
-extern const_gss_OID GSS_C_NT_MACHINE_UID_NAME;
-extern const_gss_OID GSS_C_NT_STRING_UID_NAME;
-extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
-extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE;
-extern const_gss_OID GSS_C_NT_ANONYMOUS;
-extern const_gss_OID GSS_C_NT_EXPORT_NAME;
-
-#endif /* STATIC_GSSAPI */
-
-extern const gss_OID GSS_MECH_KRB5;
-
-/* GSSAPI functions we use.
- * TODO: Replace with all GSSAPI functions from RFC?
- */
-
-/* Calling convention, just in case we need one. */
-#ifndef GSS_CC
-#define GSS_CC
-#endif /*GSS_CC*/
-
-typedef OM_uint32 (GSS_CC *t_gss_release_cred)
-            (OM_uint32                    * /*minor_status*/,
-             gss_cred_id_t                * /*cred_handle*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_init_sec_context)
-            (OM_uint32                    * /*minor_status*/,
-             const gss_cred_id_t            /*initiator_cred_handle*/,
-             gss_ctx_id_t                 * /*context_handle*/,
-             const gss_name_t               /*target_name*/,
-             const gss_OID                  /*mech_type*/,
-             OM_uint32                      /*req_flags*/,
-             OM_uint32                      /*time_req*/,
-             const gss_channel_bindings_t   /*input_chan_bindings*/,
-             const gss_buffer_t             /*input_token*/,
-             gss_OID                      * /*actual_mech_type*/,
-             gss_buffer_t                   /*output_token*/,
-             OM_uint32                    * /*ret_flags*/,
-             OM_uint32                    * /*time_rec*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_delete_sec_context)
-            (OM_uint32                    * /*minor_status*/,
-             gss_ctx_id_t                 * /*context_handle*/,
-             gss_buffer_t                   /*output_token*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_get_mic)
-            (OM_uint32                    * /*minor_status*/,
-             const gss_ctx_id_t             /*context_handle*/,
-             gss_qop_t                      /*qop_req*/,
-             const gss_buffer_t             /*message_buffer*/,
-             gss_buffer_t                   /*msg_token*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_verify_mic)
-            (OM_uint32                    * /*minor_status*/,
-             const gss_ctx_id_t             /*context_handle*/,
-             const gss_buffer_t             /*message_buffer*/,
-             const gss_buffer_t             /*msg_token*/,
-             gss_qop_t                    * /*qop_state*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_display_status)
-            (OM_uint32                   * /*minor_status*/,
-             OM_uint32                     /*status_value*/,
-             int                           /*status_type*/,
-             const gss_OID                 /*mech_type*/,
-             OM_uint32                   * /*message_context*/,
-             gss_buffer_t                  /*status_string*/);
-
-
-typedef OM_uint32 (GSS_CC *t_gss_import_name)
-            (OM_uint32                   * /*minor_status*/,
-             const gss_buffer_t            /*input_name_buffer*/,
-             const_gss_OID                 /*input_name_type*/,
-             gss_name_t                  * /*output_name*/);
-
-
-typedef OM_uint32 (GSS_CC *t_gss_release_name)
-            (OM_uint32                   * /*minor_status*/,
-             gss_name_t                  * /*name*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_release_buffer)
-            (OM_uint32                   * /*minor_status*/,
-             gss_buffer_t                  /*buffer*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_acquire_cred)
-            (OM_uint32                    * /*minor_status*/,
-             const gss_name_t               /*desired_name*/,
-             OM_uint32                      /*time_req*/,
-             const gss_OID_set              /*desired_mechs*/,
-             gss_cred_usage_t               /*cred_usage*/,
-             gss_cred_id_t                * /*output_cred_handle*/,
-             gss_OID_set                  * /*actual_mechs*/,
-             OM_uint32                    * /*time_rec*/);
-
-typedef OM_uint32 (GSS_CC *t_gss_inquire_cred_by_mech)
-            (OM_uint32                    * /*minor_status*/,
-             const gss_cred_id_t            /*cred_handle*/,
-             const gss_OID                  /*mech_type*/,
-             gss_name_t                   * /*name*/,
-             OM_uint32                    * /*initiator_lifetime*/,
-             OM_uint32                    * /*acceptor_lifetime*/,
-             gss_cred_usage_t             * /*cred_usage*/);
-
-struct gssapi_functions {
-    t_gss_delete_sec_context delete_sec_context;
-    t_gss_display_status display_status;
-    t_gss_get_mic get_mic;
-    t_gss_verify_mic verify_mic;
-    t_gss_import_name import_name;
-    t_gss_init_sec_context init_sec_context;
-    t_gss_release_buffer release_buffer;
-    t_gss_release_cred release_cred;
-    t_gss_release_name release_name;
-    t_gss_acquire_cred acquire_cred;
-    t_gss_inquire_cred_by_mech inquire_cred_by_mech;
-};
-
-#endif /* NO_GSSAPI */
-
-#endif /* PUTTY_PGSSAPI_H */

+ 0 - 1217
source/putty/portfwd.c

@@ -1,1217 +0,0 @@
-/*
- * SSH port forwarding.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshchan.h"
-
-/*
- * Enumeration of values that live in the 'socks_state' field of
- * struct PortForwarding.
- */
-typedef enum {
-    SOCKS_NONE, /* direct connection (no SOCKS, or SOCKS already done) */
-    SOCKS_INITIAL,       /* don't know if we're SOCKS 4 or 5 yet */
-    SOCKS_4,             /* expect a SOCKS 4 (or 4A) connection message */
-    SOCKS_5_INITIAL,     /* expect a SOCKS 5 preliminary message */
-    SOCKS_5_CONNECT      /* expect a SOCKS 5 connection message */
-} SocksState;
-
-typedef struct PortForwarding {
-    SshChannel *c;         /* channel structure held by SSH connection layer */
-    ConnectionLayer *cl;   /* the connection layer itself */
-    /* Note that ssh need not be filled in if c is non-NULL */
-    Socket *s;
-    bool input_wanted;
-    bool ready;
-    SocksState socks_state;
-    /*
-     * `hostname' and `port' are the real hostname and port, once
-     * we know what we're connecting to.
-     */
-    char *hostname;
-    int port;
-    /*
-     * `socksbuf' is the buffer we use to accumulate the initial SOCKS
-     * segment of the incoming data, plus anything after that that we
-     * receive before we're ready to send data to the SSH server.
-     */
-    strbuf *socksbuf;
-    size_t socksbuf_consumed;
-
-    Plug plug;
-    Channel chan;
-} PortForwarding;
-
-struct PortListener {
-    ConnectionLayer *cl;
-    Socket *s;
-    bool is_dynamic;
-    /*
-     * `hostname' and `port' are the real hostname and port, for
-     * ordinary forwardings.
-     */
-    char *hostname;
-    int port;
-
-    Plug plug;
-};
-
-static struct PortForwarding *new_portfwd_state(void)
-{
-    struct PortForwarding *pf = snew(struct PortForwarding);
-    pf->hostname = NULL;
-    pf->socksbuf = NULL;
-    return pf;
-}
-
-static void free_portfwd_state(struct PortForwarding *pf)
-{
-    if (!pf)
-        return;
-    sfree(pf->hostname);
-    if (pf->socksbuf)
-        strbuf_free(pf->socksbuf);
-    sfree(pf);
-}
-
-static struct PortListener *new_portlistener_state(void)
-{
-    struct PortListener *pl = snew(struct PortListener);
-    pl->hostname = NULL;
-    return pl;
-}
-
-static void free_portlistener_state(struct PortListener *pl)
-{
-    if (!pl)
-        return;
-    sfree(pl->hostname);
-    sfree(pl);
-}
-
-static void pfd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
-                    const char *error_msg, int error_code)
-{
-    /* we have to dump these since we have no interface to logging.c */
-}
-
-static void pfl_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
-                    const char *error_msg, int error_code)
-{
-    /* we have to dump these since we have no interface to logging.c */
-}
-
-static void pfd_close(struct PortForwarding *pf);
-
-static void pfd_closing(Plug *plug, const char *error_msg, int error_code,
-                        bool calling_back)
-{
-    struct PortForwarding *pf =
-        container_of(plug, struct PortForwarding, plug);
-
-    if (error_msg) {
-        /*
-         * Socket error. Slam the connection instantly shut.
-         */
-        if (pf->c) {
-            sshfwd_initiate_close(pf->c, error_msg);
-        } else {
-            /*
-             * We might not have an SSH channel, if a socket error
-             * occurred during SOCKS negotiation. If not, we must
-             * clean ourself up without sshfwd_initiate_close's call
-             * back to pfd_close.
-             */
-            pfd_close(pf);
-        }
-    } else {
-        /*
-         * Ordinary EOF received on socket. Send an EOF on the SSH
-         * channel.
-         */
-        if (pf->c)
-            sshfwd_write_eof(pf->c);
-    }
-}
-
-static void pfl_terminate(struct PortListener *pl);
-
-static void pfl_closing(Plug *plug, const char *error_msg, int error_code,
-                        bool calling_back)
-{
-    struct PortListener *pl = (struct PortListener *) plug;
-    pfl_terminate(pl);
-}
-
-static SshChannel *wrap_lportfwd_open(
-    ConnectionLayer *cl, const char *hostname, int port,
-    Socket *s, Channel *chan)
-{
-    SocketPeerInfo *pi;
-    char *description;
-    SshChannel *toret;
-
-    pi = sk_peer_info(s);
-    if (pi && pi->log_text) {
-        description = dupprintf("forwarding from %s", pi->log_text);
-    } else {
-        description = dupstr("forwarding");
-    }
-    toret = ssh_lportfwd_open(cl, hostname, port, description, pi, chan);
-    sk_free_peer_info(pi);
-
-    sfree(description);
-    return toret;
-}
-
-static char *ipv4_to_string(unsigned ipv4)
-{
-    return dupprintf("%u.%u.%u.%u",
-                     (ipv4 >> 24) & 0xFF, (ipv4 >> 16) & 0xFF,
-                     (ipv4 >>  8) & 0xFF, (ipv4      ) & 0xFF);
-}
-
-static char *ipv6_to_string(ptrlen ipv6)
-{
-    const unsigned char *addr = ipv6.ptr;
-    assert(ipv6.len == 16);
-    return dupprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 0),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 2),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 4),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 6),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 8),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 10),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 12),
-                     (unsigned)GET_16BIT_MSB_FIRST(addr + 14));
-}
-
-static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)
-{
-    struct PortForwarding *pf =
-        container_of(plug, struct PortForwarding, plug);
-
-    if (len == 0)
-        return;
-
-    if (pf->socks_state != SOCKS_NONE) {
-        BinarySource src[1];
-
-        /*
-         * Store all the data we've got in socksbuf.
-         */
-        put_data(pf->socksbuf, data, len);
-
-        /*
-         * Check the start of socksbuf to see if it's a valid and
-         * complete message in the SOCKS exchange.
-         */
-
-        if (pf->socks_state == SOCKS_INITIAL) {
-            /* Preliminary: check the first byte of the data (which we
-             * _must_ have by now) to find out which SOCKS major
-             * version we're speaking. */
-            switch (pf->socksbuf->u[0]) {
-              case 4:
-                pf->socks_state = SOCKS_4;
-                break;
-              case 5:
-                pf->socks_state = SOCKS_5_INITIAL;
-                break;
-              default:
-                pfd_close(pf);         /* unrecognised version */
-                return;
-            }
-        }
-
-        BinarySource_BARE_INIT(src, pf->socksbuf->u, pf->socksbuf->len);
-        get_data(src, pf->socksbuf_consumed);
-
-        while (pf->socks_state != SOCKS_NONE) {
-            unsigned socks_version, message_type, reserved_byte;
-            unsigned reply_code, port, ipv4, method;
-            ptrlen methods;
-            const char *socks4_hostname;
-            strbuf *output;
-
-            switch (pf->socks_state) {
-              case SOCKS_INITIAL:
-              case SOCKS_NONE:
-                unreachable("These case values cannot appear");
-
-              case SOCKS_4:
-                /* SOCKS 4/4A connect message */
-                socks_version = get_byte(src);
-                message_type = get_byte(src);
-
-                if (get_err(src) == BSE_OUT_OF_DATA)
-                    return;
-                if (socks_version == 4 && message_type == 1) {
-                    /* CONNECT message */
-                    bool name_based = false;
-
-                    port = get_uint16(src);
-                    ipv4 = get_uint32(src);
-                    if (ipv4 > 0x00000000 && ipv4 < 0x00000100) {
-                        /*
-                         * Addresses in this range indicate the SOCKS 4A
-                         * extension to specify a hostname, which comes
-                         * after the username.
-                         */
-                        name_based = true;
-                    }
-                    get_asciz(src);        /* skip username */
-                    socks4_hostname = name_based ? get_asciz(src) : NULL;
-
-                    if (get_err(src) == BSE_OUT_OF_DATA)
-                        return;
-                    if (get_err(src))
-                        goto socks4_reject;
-
-                    pf->port = port;
-                    if (name_based) {
-                        pf->hostname = dupstr(socks4_hostname);
-                    } else {
-                        pf->hostname = ipv4_to_string(ipv4);
-                    }
-
-                    output = strbuf_new();
-                    put_byte(output, 0);       /* reply version */
-                    put_byte(output, 90);      /* SOCKS 4 'request granted' */
-                    put_uint16(output, 0);     /* null port field */
-                    put_uint32(output, 0);     /* null address field */
-                    sk_write(pf->s, output->u, output->len);
-                    strbuf_free(output);
-
-                    pf->socks_state = SOCKS_NONE;
-                    pf->socksbuf_consumed = src->pos;
-                    break;
-                }
-
-              socks4_reject:
-                output = strbuf_new();
-                put_byte(output, 0);       /* reply version */
-                put_byte(output, 91);      /* SOCKS 4 'request rejected' */
-                put_uint16(output, 0);     /* null port field */
-                put_uint32(output, 0);     /* null address field */
-                sk_write(pf->s, output->u, output->len);
-                strbuf_free(output);
-                pfd_close(pf);
-                return;
-
-              case SOCKS_5_INITIAL:
-                /* SOCKS 5 initial method list */
-                socks_version = get_byte(src);
-                methods = get_pstring(src);
-
-                method = 0xFF;         /* means 'no usable method found' */
-                {
-                    int i;
-                    for (i = 0; i < methods.len; i++) {
-                        if (((const unsigned char *)methods.ptr)[i] == 0 ) {
-                            method = 0;        /* no auth */
-                            break;
-                        }
-                    }
-                }
-
-                if (get_err(src) == BSE_OUT_OF_DATA)
-                    return;
-                if (get_err(src))
-                    method = 0xFF;
-
-                output = strbuf_new();
-                put_byte(output, 5);       /* SOCKS version */
-                put_byte(output, method);  /* selected auth method */
-                sk_write(pf->s, output->u, output->len);
-                strbuf_free(output);
-
-                if (method == 0xFF) {
-                    pfd_close(pf);
-                    return;
-                }
-
-                pf->socks_state = SOCKS_5_CONNECT;
-                pf->socksbuf_consumed = src->pos;
-                break;
-
-              case SOCKS_5_CONNECT:
-                /* SOCKS 5 connect message */
-                socks_version = get_byte(src);
-                message_type = get_byte(src);
-                reserved_byte = get_byte(src);
-
-                if (socks_version == 5 && message_type == 1 &&
-                    reserved_byte == 0) {
-
-                    reply_code = 0;        /* success */
-
-                    switch (get_byte(src)) {
-                      case 1:              /* IPv4 */
-                        pf->hostname = ipv4_to_string(get_uint32(src));
-                        break;
-                      case 4:              /* IPv6 */
-                        pf->hostname = ipv6_to_string(get_data(src, 16));
-                        break;
-                      case 3:              /* unresolved domain name */
-                        pf->hostname = mkstr(get_pstring(src));
-                        break;
-                      default:
-                        pf->hostname = NULL;
-                        reply_code = 8;    /* address type not supported */
-                        break;
-                    }
-
-                    pf->port = get_uint16(src);
-                } else {
-                    reply_code = 7;        /* command not supported */
-                }
-
-                if (get_err(src) == BSE_OUT_OF_DATA)
-                    return;
-                if (get_err(src))
-                    reply_code = 1;        /* general server failure */
-
-                output = strbuf_new();
-                put_byte(output, 5);       /* SOCKS version */
-                put_byte(output, reply_code);
-                put_byte(output, 0);       /* reserved */
-                put_byte(output, 1);       /* IPv4 address follows */
-                put_uint32(output, 0);     /* bound IPv4 address (unused) */
-                put_uint16(output, 0);     /* bound port number (unused) */
-                sk_write(pf->s, output->u, output->len);
-                strbuf_free(output);
-
-                if (reply_code != 0) {
-                    pfd_close(pf);
-                    return;
-                }
-
-                pf->socks_state = SOCKS_NONE;
-                pf->socksbuf_consumed = src->pos;
-                break;
-            }
-        }
-
-        /*
-         * We come here when we're ready to make an actual
-         * connection.
-         */
-
-        /*
-         * Freeze the socket until the SSH server confirms the
-         * connection.
-         */
-        sk_set_frozen(pf->s, true);
-
-        pf->c = wrap_lportfwd_open(pf->cl, pf->hostname, pf->port, pf->s,
-                                   &pf->chan);
-    }
-    if (pf->ready)
-        sshfwd_write(pf->c, data, len);
-}
-
-static void pfd_sent(Plug *plug, size_t bufsize)
-{
-    struct PortForwarding *pf =
-        container_of(plug, struct PortForwarding, plug);
-
-    if (pf->c)
-        sshfwd_unthrottle(pf->c, bufsize);
-}
-
-static const PlugVtable PortForwarding_plugvt = {
-    // WINSCP
-    /*.log =*/ pfd_log,
-    /*.closing =*/ pfd_closing,
-    /*.receive =*/ pfd_receive,
-    /*.sent =*/ pfd_sent,
-    NULL,
-};
-
-static void pfd_chan_free(Channel *chan);
-static void pfd_open_confirmation(Channel *chan);
-static void pfd_open_failure(Channel *chan, const char *errtext);
-static size_t pfd_send(
-    Channel *chan, bool is_stderr, const void *data, size_t len);
-static void pfd_send_eof(Channel *chan);
-static void pfd_set_input_wanted(Channel *chan, bool wanted);
-static char *pfd_log_close_msg(Channel *chan);
-
-static const ChannelVtable PortForwarding_channelvt = {
-    // WINSCP
-    /*.free =*/ pfd_chan_free,
-    /*.open_confirmation =*/ pfd_open_confirmation,
-    /*.open_failed =*/ pfd_open_failure,
-    /*.send =*/ pfd_send,
-    /*.send_eof =*/ pfd_send_eof,
-    /*.set_input_wanted =*/ pfd_set_input_wanted,
-    /*.log_close_msg =*/ pfd_log_close_msg,
-    /*.want_close =*/ chan_default_want_close,
-    /*.rcvd_exit_status =*/ chan_no_exit_status,
-    /*.rcvd_exit_signal =*/ chan_no_exit_signal,
-    /*.rcvd_exit_signal_numeric =*/ chan_no_exit_signal_numeric,
-    /*.run_shell =*/ chan_no_run_shell,
-    /*.run_command =*/ chan_no_run_command,
-    /*.run_subsystem =*/ chan_no_run_subsystem,
-    /*.enable_x11_forwarding =*/ chan_no_enable_x11_forwarding,
-    /*.enable_agent_forwarding =*/ chan_no_enable_agent_forwarding,
-    /*.allocate_pty =*/ chan_no_allocate_pty,
-    /*.set_env =*/ chan_no_set_env,
-    /*.send_break =*/ chan_no_send_break,
-    /*.send_signal =*/ chan_no_send_signal,
-    /*.change_window_size =*/ chan_no_change_window_size,
-    /*.request_response =*/ chan_no_request_response,
-};
-
-Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug, bool start_ready)
-{
-    struct PortForwarding *pf;
-
-    pf = new_portfwd_state();
-    pf->plug.vt = &PortForwarding_plugvt;
-    pf->chan.initial_fixed_window_size = 0;
-    pf->chan.vt = &PortForwarding_channelvt;
-    pf->input_wanted = true;
-
-    pf->c = NULL;
-
-    pf->cl = cl;
-    pf->input_wanted = true;
-    pf->ready = start_ready;
-
-    pf->socks_state = SOCKS_NONE;
-    pf->hostname = NULL;
-    pf->port = 0;
-
-    *plug = &pf->plug;
-    return &pf->chan;
-}
-
-void portfwd_raw_free(Channel *pfchan)
-{
-    struct PortForwarding *pf;
-    assert(pfchan->vt == &PortForwarding_channelvt);
-    pf = container_of(pfchan, struct PortForwarding, chan);
-    free_portfwd_state(pf);
-}
-
-void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc)
-{
-    struct PortForwarding *pf;
-    assert(pfchan->vt == &PortForwarding_channelvt);
-    pf = container_of(pfchan, struct PortForwarding, chan);
-
-    pf->s = s;
-    pf->c = sc;
-}
-
-/*
- called when someone connects to the local port
- */
-
-static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
-{
-    struct PortListener *pl = container_of(p, struct PortListener, plug);
-    struct PortForwarding *pf;
-    Channel *chan;
-    Plug *plug;
-    Socket *s;
-    const char *err;
-
-    chan = portfwd_raw_new(pl->cl, &plug, false);
-    s = constructor(ctx, plug);
-    if ((err = sk_socket_error(s)) != NULL) {
-        portfwd_raw_free(chan);
-        return 1;
-    }
-
-    pf = container_of(chan, struct PortForwarding, chan);
-
-    if (pl->is_dynamic) {
-        pf->s = s;
-        pf->socks_state = SOCKS_INITIAL;
-        pf->socksbuf = strbuf_new();
-        pf->socksbuf_consumed = 0;
-        pf->port = 0;                  /* "hostname" buffer is so far empty */
-        sk_set_frozen(s, false);       /* we want to receive SOCKS _now_! */
-    } else {
-        pf->hostname = dupstr(pl->hostname);
-        pf->port = pl->port;
-        portfwd_raw_setup(
-            chan, s,
-            wrap_lportfwd_open(pl->cl, pf->hostname, pf->port, s, &pf->chan));
-    }
-
-    return 0;
-}
-
-static const PlugVtable PortListener_plugvt = {
-    // WINSCP
-    /*.log =*/ pfl_log,
-    /*.closing =*/ pfl_closing,
-    NULL,
-    NULL,
-    /*.accepting =*/ pfl_accepting,
-};
-
-/*
- * Add a new port-forwarding listener from srcaddr:port -> desthost:destport.
- *
- * desthost == NULL indicates dynamic SOCKS port forwarding.
- *
- * On success, returns NULL and fills in *pl_ret. On error, returns a
- * dynamically allocated error message string.
- */
-static char *pfl_listen(const char *desthost, int destport,
-                        const char *srcaddr, int port,
-                        ConnectionLayer *cl, Conf *conf,
-                        struct PortListener **pl_ret, int address_family)
-{
-    const char *err;
-    struct PortListener *pl;
-
-    /*
-     * Open socket.
-     */
-    pl = *pl_ret = new_portlistener_state();
-    pl->plug.vt = &PortListener_plugvt;
-    if (desthost) {
-        pl->hostname = dupstr(desthost);
-        pl->port = destport;
-        pl->is_dynamic = false;
-    } else
-        pl->is_dynamic = true;
-    pl->cl = cl;
-
-    pl->s = new_listener(srcaddr, port, &pl->plug,
-                         !conf_get_bool(conf, CONF_lport_acceptall),
-                         conf, address_family);
-    if ((err = sk_socket_error(pl->s)) != NULL) {
-        char *err_ret = dupstr(err);
-        sk_close(pl->s);
-        free_portlistener_state(pl);
-        *pl_ret = NULL;
-        return err_ret;
-    }
-
-    return NULL;
-}
-
-static char *pfd_log_close_msg(Channel *chan)
-{
-    return dupstr("Forwarded port closed");
-}
-
-static void pfd_close(struct PortForwarding *pf)
-{
-    if (!pf)
-        return;
-
-    sk_close(pf->s);
-    free_portfwd_state(pf);
-}
-
-/*
- * Terminate a listener.
- */
-static void pfl_terminate(struct PortListener *pl)
-{
-    if (!pl)
-        return;
-
-    sk_close(pl->s);
-    free_portlistener_state(pl);
-}
-
-static void pfd_set_input_wanted(Channel *chan, bool wanted)
-{
-    pinitassert(chan->vt == &PortForwarding_channelvt);
-    PortForwarding *pf = container_of(chan, PortForwarding, chan);
-    pf->input_wanted = wanted;
-    sk_set_frozen(pf->s, !pf->input_wanted);
-}
-
-static void pfd_chan_free(Channel *chan)
-{
-    pinitassert(chan->vt == &PortForwarding_channelvt);
-    PortForwarding *pf = container_of(chan, PortForwarding, chan);
-    pfd_close(pf);
-}
-
-/*
- * Called to send data down the raw connection.
- */
-static size_t pfd_send(
-    Channel *chan, bool is_stderr, const void *data, size_t len)
-{
-    pinitassert(chan->vt == &PortForwarding_channelvt);
-    PortForwarding *pf = container_of(chan, PortForwarding, chan);
-    return sk_write(pf->s, data, len);
-}
-
-static void pfd_send_eof(Channel *chan)
-{
-    pinitassert(chan->vt == &PortForwarding_channelvt);
-    PortForwarding *pf = container_of(chan, PortForwarding, chan);
-    sk_write_eof(pf->s);
-}
-
-static void pfd_open_confirmation(Channel *chan)
-{
-    pinitassert(chan->vt == &PortForwarding_channelvt);
-    PortForwarding *pf = container_of(chan, PortForwarding, chan);
-
-    pf->ready = true;
-    sk_set_frozen(pf->s, false);
-    sk_write(pf->s, NULL, 0);
-    if (pf->socksbuf) {
-        sshfwd_write(pf->c, pf->socksbuf->u + pf->socksbuf_consumed,
-                     pf->socksbuf->len - pf->socksbuf_consumed);
-        strbuf_free(pf->socksbuf);
-        pf->socksbuf = NULL;
-    }
-}
-
-static void pfd_open_failure(Channel *chan, const char *errtext)
-{
-    pinitassert(chan->vt == &PortForwarding_channelvt);
-    PortForwarding *pf = container_of(chan, PortForwarding, chan);
-
-    logeventf(pf->cl->logctx,
-              "Forwarded connection refused by remote%s%s",
-              errtext ? ": " : "", errtext ? errtext : "");
-}
-
-/* ----------------------------------------------------------------------
- * Code to manage the complete set of currently active port
- * forwardings, and update it from Conf.
- */
-
-struct PortFwdRecord {
-    enum { DESTROY, KEEP, CREATE } status;
-    int type;
-    unsigned sport, dport;
-    char *saddr, *daddr;
-    char *sserv, *dserv;
-    struct ssh_rportfwd *remote;
-    int addressfamily;
-    struct PortListener *local;
-};
-
-static int pfr_cmp(void *av, void *bv)
-{
-    PortFwdRecord *a = (PortFwdRecord *) av;
-    PortFwdRecord *b = (PortFwdRecord *) bv;
-    int i;
-    if (a->type > b->type)
-        return +1;
-    if (a->type < b->type)
-        return -1;
-    if (a->addressfamily > b->addressfamily)
-        return +1;
-    if (a->addressfamily < b->addressfamily)
-        return -1;
-    if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
-        return i < 0 ? -1 : +1;
-    if (a->sport > b->sport)
-        return +1;
-    if (a->sport < b->sport)
-        return -1;
-    if (a->type != 'D') {
-        if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
-            return i < 0 ? -1 : +1;
-        if (a->dport > b->dport)
-            return +1;
-        if (a->dport < b->dport)
-            return -1;
-    }
-    return 0;
-}
-
-static void pfr_free(PortFwdRecord *pfr)
-{
-    /* Dispose of any listening socket. */
-    if (pfr->local)
-        pfl_terminate(pfr->local);
-
-    sfree(pfr->saddr);
-    sfree(pfr->daddr);
-    sfree(pfr->sserv);
-    sfree(pfr->dserv);
-    sfree(pfr);
-}
-
-struct PortFwdManager {
-    ConnectionLayer *cl;
-    Conf *conf;
-    tree234 *forwardings;
-};
-
-PortFwdManager *portfwdmgr_new(ConnectionLayer *cl)
-{
-    PortFwdManager *mgr = snew(PortFwdManager);
-
-    mgr->cl = cl;
-    mgr->conf = NULL;
-    mgr->forwardings = newtree234(pfr_cmp);
-
-    return mgr;
-}
-
-void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr)
-{
-    PortFwdRecord *realpfr = del234(mgr->forwardings, pfr);
-    if (realpfr == pfr)
-        pfr_free(pfr);
-}
-
-void portfwdmgr_close_all(PortFwdManager *mgr)
-{
-    PortFwdRecord *pfr;
-
-    while ((pfr = delpos234(mgr->forwardings, 0)) != NULL)
-        pfr_free(pfr);
-}
-
-void portfwdmgr_free(PortFwdManager *mgr)
-{
-    portfwdmgr_close_all(mgr);
-    freetree234(mgr->forwardings);
-    if (mgr->conf)
-        conf_free(mgr->conf);
-    sfree(mgr);
-}
-
-void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)
-{
-    PortFwdRecord *pfr;
-    int i;
-    char *key, *val;
-
-    if (mgr->conf)
-        conf_free(mgr->conf);
-    mgr->conf = conf_copy(conf);
-
-    /*
-     * Go through the existing port forwardings and tag them
-     * with status==DESTROY. Any that we want to keep will be
-     * re-enabled (status==KEEP) as we go through the
-     * configuration and find out which bits are the same as
-     * they were before.
-     */
-    for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++)
-        pfr->status = DESTROY;
-
-    for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
-         val != NULL;
-         val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
-        char *kp, *kp2, *vp, *vp2;
-        char address_family, type;
-        int sport, dport, sserv, dserv;
-        char *sports, *dports, *saddr, *host;
-
-        kp = key;
-
-        address_family = 'A';
-        type = 'L';
-        if (*kp == 'A' || *kp == '4' || *kp == '6')
-            address_family = *kp++;
-        if (*kp == 'L' || *kp == 'R')
-            type = *kp++;
-
-        if ((kp2 = host_strchr(kp, ':')) != NULL) {
-            /*
-             * There's a colon in the middle of the source port
-             * string, which means that the part before it is
-             * actually a source address.
-             */
-            char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp);
-            saddr = host_strduptrim(saddr_tmp);
-            sfree(saddr_tmp);
-            sports = kp2+1;
-        } else {
-            saddr = NULL;
-            sports = kp;
-        }
-        sport = atoi(sports);
-        sserv = 0;
-        if (sport == 0) {
-            sserv = 1;
-            sport = net_service_lookup(sports);
-            if (!sport) {
-                logeventf(mgr->cl->logctx, "Service lookup failed for source"
-                          " port \"%s\"", sports);
-            }
-        }
-
-        if (type == 'L' && !strcmp(val, "D")) {
-            /* dynamic forwarding */
-            host = NULL;
-            dports = NULL;
-            dport = -1;
-            dserv = 0;
-            type = 'D';
-        } else {
-            /* ordinary forwarding */
-            vp = val;
-            vp2 = vp + host_strcspn(vp, ":");
-            host = dupprintf("%.*s", (int)(vp2 - vp), vp);
-            if (*vp2)
-                vp2++;
-            dports = vp2;
-            dport = atoi(dports);
-            dserv = 0;
-            if (dport == 0) {
-                dserv = 1;
-                dport = net_service_lookup(dports);
-                if (!dport) {
-                    logeventf(mgr->cl->logctx,
-                              "Service lookup failed for destination"
-                              " port \"%s\"", dports);
-                }
-            }
-        }
-
-        if (sport && dport) {
-            /* Set up a description of the source port. */
-            pfr = snew(PortFwdRecord);
-            pfr->type = type;
-            pfr->saddr = saddr;
-            pfr->sserv = sserv ? dupstr(sports) : NULL;
-            pfr->sport = sport;
-            pfr->daddr = host;
-            pfr->dserv = dserv ? dupstr(dports) : NULL;
-            pfr->dport = dport;
-            pfr->local = NULL;
-            pfr->remote = NULL;
-            pfr->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :
-                                  address_family == '6' ? ADDRTYPE_IPV6 :
-                                  ADDRTYPE_UNSPEC);
-
-            { // WINSCP
-            PortFwdRecord *existing = add234(mgr->forwardings, pfr);
-            if (existing != pfr) {
-                if (existing->status == DESTROY) {
-                    /*
-                     * We already have a port forwarding up and running
-                     * with precisely these parameters. Hence, no need
-                     * to do anything; simply re-tag the existing one
-                     * as KEEP.
-                     */
-                    existing->status = KEEP;
-                }
-                /*
-                 * Anything else indicates that there was a duplicate
-                 * in our input, which we'll silently ignore.
-                 */
-                pfr_free(pfr);
-            } else {
-                pfr->status = CREATE;
-            }
-            } // WINSCP
-        } else {
-            sfree(saddr);
-            sfree(host);
-        }
-    }
-
-    /*
-     * Now go through and destroy any port forwardings which were
-     * not re-enabled.
-     */
-    for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {
-        if (pfr->status == DESTROY) {
-            char *message;
-
-            message = dupprintf("%s port forwarding from %s%s%d",
-                                pfr->type == 'L' ? "local" :
-                                pfr->type == 'R' ? "remote" : "dynamic",
-                                pfr->saddr ? pfr->saddr : "",
-                                pfr->saddr ? ":" : "",
-                                pfr->sport);
-
-            if (pfr->type != 'D') {
-                char *msg2 = dupprintf("%s to %s:%d", message,
-                                       pfr->daddr, pfr->dport);
-                sfree(message);
-                message = msg2;
-            }
-
-            logeventf(mgr->cl->logctx, "Cancelling %s", message);
-            sfree(message);
-
-            /* pfr->remote or pfr->local may be NULL if setting up a
-             * forwarding failed. */
-            if (pfr->remote) {
-                /*
-                 * Cancel the port forwarding at the server
-                 * end.
-                 *
-                 * Actually closing the listening port on the server
-                 * side may fail - because in SSH-1 there's no message
-                 * in the protocol to request it!
-                 *
-                 * Instead, we simply remove the record of the
-                 * forwarding from our local end, so that any
-                 * connections the server tries to make on it are
-                 * rejected.
-                 */
-                ssh_rportfwd_remove(mgr->cl, pfr->remote);
-                pfr->remote = NULL;
-            } else if (pfr->local) {
-                pfl_terminate(pfr->local);
-                pfr->local = NULL;
-            }
-
-            delpos234(mgr->forwardings, i);
-            pfr_free(pfr);
-            i--;                       /* so we don't skip one in the list */
-        }
-    }
-
-    /*
-     * And finally, set up any new port forwardings (status==CREATE).
-     */
-    for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {
-        if (pfr->status == CREATE) {
-            char *sportdesc, *dportdesc;
-            sportdesc = dupprintf("%s%s%s%s%d%s",
-                                  pfr->saddr ? pfr->saddr : "",
-                                  pfr->saddr ? ":" : "",
-                                  pfr->sserv ? pfr->sserv : "",
-                                  pfr->sserv ? "(" : "",
-                                  pfr->sport,
-                                  pfr->sserv ? ")" : "");
-            if (pfr->type == 'D') {
-                dportdesc = NULL;
-            } else {
-                dportdesc = dupprintf("%s:%s%s%d%s",
-                                      pfr->daddr,
-                                      pfr->dserv ? pfr->dserv : "",
-                                      pfr->dserv ? "(" : "",
-                                      pfr->dport,
-                                      pfr->dserv ? ")" : "");
-            }
-
-            if (pfr->type == 'L') {
-                char *err = pfl_listen(pfr->daddr, pfr->dport,
-                                       pfr->saddr, pfr->sport,
-                                       mgr->cl, conf, &pfr->local,
-                                       pfr->addressfamily);
-
-                logeventf(mgr->cl->logctx,
-                          "Local %sport %s forwarding to %s%s%s",
-                          pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
-                          pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
-                          sportdesc, dportdesc,
-                          err ? " failed: " : "", err ? err : "");
-                if (err)
-                    sfree(err);
-            } else if (pfr->type == 'D') {
-                char *err = pfl_listen(NULL, -1, pfr->saddr, pfr->sport,
-                                       mgr->cl, conf, &pfr->local,
-                                       pfr->addressfamily);
-
-                logeventf(mgr->cl->logctx,
-                          "Local %sport %s SOCKS dynamic forwarding%s%s",
-                          pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
-                          pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
-                          sportdesc,
-                          err ? " failed: " : "", err ? err : "");
-
-                if (err)
-                    sfree(err);
-            } else {
-                const char *shost;
-
-                if (pfr->saddr) {
-                    shost = pfr->saddr;
-                } else if (conf_get_bool(conf, CONF_rport_acceptall)) {
-                    shost = "";
-                } else {
-                    shost = "localhost";
-                }
-
-                pfr->remote = ssh_rportfwd_alloc(
-                    mgr->cl, shost, pfr->sport, pfr->daddr, pfr->dport,
-                    pfr->addressfamily, sportdesc, pfr, NULL);
-
-                if (!pfr->remote) {
-                    logeventf(mgr->cl->logctx,
-                              "Duplicate remote port forwarding to %s:%d",
-                              pfr->daddr, pfr->dport);
-                    pfr_free(pfr);
-                } else {
-                    logeventf(mgr->cl->logctx, "Requesting remote port %s"
-                              " forward to %s", sportdesc, dportdesc);
-                }
-            }
-            sfree(sportdesc);
-            sfree(dportdesc);
-        }
-    }
-}
-
-bool portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port,
-                       const char *keyhost, int keyport, Conf *conf)
-{
-    PortFwdRecord *pfr;
-
-    pfr = snew(PortFwdRecord);
-    pfr->type = 'L';
-    pfr->saddr = host ? dupstr(host) : NULL;
-    pfr->daddr = keyhost ? dupstr(keyhost) : NULL;
-    pfr->sserv = pfr->dserv = NULL;
-    pfr->sport = port;
-    pfr->dport = keyport;
-    pfr->local = NULL;
-    pfr->remote = NULL;
-    pfr->addressfamily = ADDRTYPE_UNSPEC;
-
-    { // WINSCP
-    PortFwdRecord *existing = add234(mgr->forwardings, pfr);
-    if (existing != pfr) {
-        /*
-         * We had this record already. Return failure.
-         */
-        pfr_free(pfr);
-        return false;
-    }
-    } // WINSCP
-
-    { // WINSCP
-    char *err = pfl_listen(keyhost, keyport, host, port,
-                           mgr->cl, conf, &pfr->local, pfr->addressfamily);
-    logeventf(mgr->cl->logctx,
-              "%s on port %s:%d to forward to client%s%s",
-              err ? "Failed to listen" : "Listening", host, port,
-              err ? ": " : "", err ? err : "");
-    if (err) {
-        sfree(err);
-        del234(mgr->forwardings, pfr);
-        pfr_free(pfr);
-        return false;
-    }
-    } // WINSCP
-
-    return true;
-}
-
-bool portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port)
-{
-    PortFwdRecord pfr_key;
-
-    pfr_key.type = 'L';
-    /* Safe to cast the const away here, because it will only be used
-     * by pfr_cmp, which won't write to the string */
-    pfr_key.saddr = pfr_key.daddr = (char *)host;
-    pfr_key.sserv = pfr_key.dserv = NULL;
-    pfr_key.sport = pfr_key.dport = port;
-    pfr_key.local = NULL;
-    pfr_key.remote = NULL;
-    pfr_key.addressfamily = ADDRTYPE_UNSPEC;
-
-    { // WINSCP
-    PortFwdRecord *pfr = del234(mgr->forwardings, &pfr_key);
-
-    if (!pfr)
-        return false;
-
-    logeventf(mgr->cl->logctx, "Closing listening port %s:%d", host, port);
-
-    pfr_free(pfr);
-    } // WINSCP
-    return true;
-}
-
-/*
- * Called when receiving a PORT OPEN from the server to make a
- * connection to a destination host.
- *
- * On success, returns NULL and fills in *pf_ret. On error, returns a
- * dynamically allocated error message string.
- */
-char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,
-                         char *hostname, int port, SshChannel *c,
-                         int addressfamily)
-{
-    SockAddr *addr;
-    const char *err;
-    char *dummy_realhost = NULL;
-    struct PortForwarding *pf;
-
-    /*
-     * Try to find host.
-     */
-    addr = name_lookup(hostname, port, &dummy_realhost, mgr->conf,
-                       addressfamily, NULL, NULL);
-    if ((err = sk_addr_error(addr)) != NULL) {
-        char *err_ret = dupstr(err);
-        sk_addr_free(addr);
-        sfree(dummy_realhost);
-        return err_ret;
-    }
-
-    /*
-     * Open socket.
-     */
-    pf = new_portfwd_state();
-    *chan_ret = &pf->chan;
-    pf->plug.vt = &PortForwarding_plugvt;
-    pf->chan.initial_fixed_window_size = 0;
-    pf->chan.vt = &PortForwarding_channelvt;
-    pf->input_wanted = true;
-    pf->ready = true;
-    pf->c = c;
-    pf->cl = mgr->cl;
-    pf->socks_state = SOCKS_NONE;
-
-    pf->s = new_connection(addr, dummy_realhost, port,
-                           false, true, false, false, &pf->plug, mgr->conf);
-    sfree(dummy_realhost);
-    if ((err = sk_socket_error(pf->s)) != NULL) {
-        char *err_ret = dupstr(err);
-        sk_close(pf->s);
-        free_portfwd_state(pf);
-        *chan_ret = NULL;
-        return err_ret;
-    }
-
-    return NULL;
-}
-
-#ifdef MPEXT
-
-#include "puttyexp.h"
-
-int is_pfwd(Plug * plug)
-{
-  return
-    (plug->vt->closing == pfd_closing) ||
-    (plug->vt->closing == pfl_closing);
-}
-
-Seat * get_pfwd_seat(Plug * plug)
-{
-  LogContext * logctx;
-  if (plug->vt->closing == pfl_closing)
-  {
-    struct PortListener *pl = container_of(plug, struct PortListener, plug);
-    logctx = pl->cl->logctx;
-  }
-  else if (plug->vt->closing == pfd_closing)
-  {
-    struct PortForwarding *pf = container_of(plug, struct PortForwarding, plug);
-    logctx = pf->cl->logctx;
-  }
-  return get_log_seat(logctx);
-}
-
-#endif

+ 0 - 1532
source/putty/proxy.c

@@ -1,1532 +0,0 @@
-/*
- * Network proxy abstraction in PuTTY
- *
- * A proxy layer, if necessary, wedges itself between the network
- * code and the higher level backend.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "putty.h"
-#include "network.h"
-#include "proxy.h"
-
-#define do_proxy_dns(conf) \
-    (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \
-         (conf_get_int(conf, CONF_proxy_dns) == AUTO && \
-              conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4))
-
-/*
- * Call this when proxy negotiation is complete, so that this
- * socket can begin working normally.
- */
-void proxy_activate (ProxySocket *p)
-{
-    size_t output_before, output_after;
-
-    p->state = PROXY_STATE_ACTIVE;
-
-    /* we want to ignore new receive events until we have sent
-     * all of our buffered receive data.
-     */
-    sk_set_frozen(p->sub_socket, true);
-
-    /* how many bytes of output have we buffered? */
-    output_before = bufchain_size(&p->pending_oob_output_data) +
-        bufchain_size(&p->pending_output_data);
-    /* and keep track of how many bytes do not get sent. */
-    output_after = 0;
-
-    /* send buffered OOB writes */
-    while (bufchain_size(&p->pending_oob_output_data) > 0) {
-        ptrlen data = bufchain_prefix(&p->pending_oob_output_data);
-        output_after += sk_write_oob(p->sub_socket, data.ptr, data.len);
-        bufchain_consume(&p->pending_oob_output_data, data.len);
-    }
-
-    /* send buffered normal writes */
-    while (bufchain_size(&p->pending_output_data) > 0) {
-        ptrlen data = bufchain_prefix(&p->pending_output_data);
-        output_after += sk_write(p->sub_socket, data.ptr, data.len);
-        bufchain_consume(&p->pending_output_data, data.len);
-    }
-
-    /* if we managed to send any data, let the higher levels know. */
-    if (output_after < output_before)
-        plug_sent(p->plug, output_after);
-
-    /* if we have a pending EOF to send, send it */
-    if (p->pending_eof) sk_write_eof(p->sub_socket);
-
-    /* if the backend wanted the socket unfrozen, try to unfreeze.
-     * our set_frozen handler will flush buffered receive data before
-     * unfreezing the actual underlying socket.
-     */
-    if (!p->freeze)
-        sk_set_frozen(&p->sock, false);
-}
-
-/* basic proxy socket functions */
-
-static Plug *sk_proxy_plug (Socket *s, Plug *p)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-    Plug *ret = ps->plug;
-    if (p)
-        ps->plug = p;
-    return ret;
-}
-
-static void sk_proxy_close (Socket *s)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-
-    sk_close(ps->sub_socket);
-    sk_addr_free(ps->remote_addr);
-    sfree(ps);
-}
-
-static size_t sk_proxy_write (Socket *s, const void *data, size_t len)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        bufchain_add(&ps->pending_output_data, data, len);
-        return bufchain_size(&ps->pending_output_data);
-    }
-    return sk_write(ps->sub_socket, data, len);
-}
-
-static size_t sk_proxy_write_oob (Socket *s, const void *data, size_t len)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        bufchain_clear(&ps->pending_output_data);
-        bufchain_clear(&ps->pending_oob_output_data);
-        bufchain_add(&ps->pending_oob_output_data, data, len);
-        return len;
-    }
-    return sk_write_oob(ps->sub_socket, data, len);
-}
-
-static void sk_proxy_write_eof (Socket *s)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        ps->pending_eof = true;
-        return;
-    }
-    sk_write_eof(ps->sub_socket);
-}
-
-static void sk_proxy_set_frozen (Socket *s, bool is_frozen)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        ps->freeze = is_frozen;
-        return;
-    }
-
-    /* handle any remaining buffered recv data first */
-    if (bufchain_size(&ps->pending_input_data) > 0) {
-        ps->freeze = is_frozen;
-
-        /* loop while we still have buffered data, and while we are
-         * unfrozen. the plug_receive call in the loop could result
-         * in a call back into this function refreezing the socket,
-         * so we have to check each time.
-         */
-        while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {
-            char databuf[512];
-            ptrlen data = bufchain_prefix(&ps->pending_input_data);
-            if (data.len > lenof(databuf))
-                data.len = lenof(databuf);
-            memcpy(databuf, data.ptr, data.len);
-            bufchain_consume(&ps->pending_input_data, data.len);
-            plug_receive(ps->plug, 0, databuf, data.len);
-        }
-
-        /* if we're still frozen, we'll have to wait for another
-         * call from the backend to finish unbuffering the data.
-         */
-        if (ps->freeze) return;
-    }
-
-    sk_set_frozen(ps->sub_socket, is_frozen);
-}
-
-static const char * sk_proxy_socket_error (Socket *s)
-{
-    ProxySocket *ps = container_of(s, ProxySocket, sock);
-    if (ps->error != NULL || ps->sub_socket == NULL) {
-        return ps->error;
-    }
-    return sk_socket_error(ps->sub_socket);
-}
-
-/* basic proxy plug functions */
-
-static void plug_proxy_log(Plug *plug, PlugLogType type, SockAddr *addr,
-                           int port, const char *error_msg, int error_code)
-{
-    ProxySocket *ps = container_of(plug, ProxySocket, plugimpl);
-
-    plug_log(ps->plug, type, addr, port, error_msg, error_code);
-}
-
-static void plug_proxy_closing (Plug *p, const char *error_msg,
-                                int error_code, bool calling_back)
-{
-    ProxySocket *ps = container_of(p, ProxySocket, plugimpl);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        ps->closing_error_msg = error_msg;
-        ps->closing_error_code = error_code;
-        ps->closing_calling_back = calling_back;
-        ps->negotiate(ps, PROXY_CHANGE_CLOSING);
-    } else {
-        plug_closing(ps->plug, error_msg, error_code, calling_back);
-    }
-}
-
-static void plug_proxy_receive(
-    Plug *p, int urgent, const char *data, size_t len)
-{
-    ProxySocket *ps = container_of(p, ProxySocket, plugimpl);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        /* we will lose the urgentness of this data, but since most,
-         * if not all, of this data will be consumed by the negotiation
-         * process, hopefully it won't affect the protocol above us
-         */
-        bufchain_add(&ps->pending_input_data, data, len);
-        ps->receive_urgent = (urgent != 0);
-        ps->receive_data = data;
-        ps->receive_len = len;
-        ps->negotiate(ps, PROXY_CHANGE_RECEIVE);
-    } else {
-        plug_receive(ps->plug, urgent, data, len);
-    }
-}
-
-static void plug_proxy_sent (Plug *p, size_t bufsize)
-{
-    ProxySocket *ps = container_of(p, ProxySocket, plugimpl);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        ps->negotiate(ps, PROXY_CHANGE_SENT);
-        return;
-    }
-    plug_sent(ps->plug, bufsize);
-}
-
-static int plug_proxy_accepting(Plug *p,
-                                accept_fn_t constructor, accept_ctx_t ctx)
-{
-    ProxySocket *ps = container_of(p, ProxySocket, plugimpl);
-
-    if (ps->state != PROXY_STATE_ACTIVE) {
-        ps->accepting_constructor = constructor;
-        ps->accepting_ctx = ctx;
-        return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);
-    }
-    return plug_accepting(ps->plug, constructor, ctx);
-}
-
-/*
- * This function can accept a NULL pointer as `addr', in which case
- * it will only check the host name.
- */
-static bool proxy_for_destination(SockAddr *addr, const char *hostname,
-                                  int port, Conf *conf)
-{
-    int s = 0, e = 0;
-    char hostip[64];
-    int hostip_len, hostname_len;
-    const char *exclude_list;
-
-    /*
-     * Special local connections such as Unix-domain sockets
-     * unconditionally cannot be proxied, even in proxy-localhost
-     * mode. There just isn't any way to ask any known proxy type for
-     * them.
-     */
-    if (addr && sk_address_is_special_local(addr))
-        return false;                  /* do not proxy */
-
-    /*
-     * Check the host name and IP against the hard-coded
-     * representations of `localhost'.
-     */
-    if (!conf_get_bool(conf, CONF_even_proxy_localhost) &&
-        (sk_hostname_is_local(hostname) ||
-         (addr && sk_address_is_local(addr))))
-        return false;                  /* do not proxy */
-
-    /* we want a string representation of the IP address for comparisons */
-    if (addr) {
-        sk_getaddr(addr, hostip, 64);
-        hostip_len = strlen(hostip);
-    } else
-        hostip_len = 0;                /* placate gcc; shouldn't be required */
-
-    hostname_len = strlen(hostname);
-
-    exclude_list = conf_get_str(conf, CONF_proxy_exclude_list);
-
-    /* now parse the exclude list, and see if either our IP
-     * or hostname matches anything in it.
-     */
-
-    while (exclude_list[s]) {
-        while (exclude_list[s] &&
-               (isspace((unsigned char)exclude_list[s]) ||
-                exclude_list[s] == ',')) s++;
-
-        if (!exclude_list[s]) break;
-
-        e = s;
-
-        while (exclude_list[e] &&
-               (isalnum((unsigned char)exclude_list[e]) ||
-                exclude_list[e] == '-' ||
-                exclude_list[e] == '.' ||
-                exclude_list[e] == '*')) e++;
-
-        if (exclude_list[s] == '*') {
-            /* wildcard at beginning of entry */
-
-            if ((addr && strnicmp(hostip + hostip_len - (e - s - 1),
-                                  exclude_list + s + 1, e - s - 1) == 0) ||
-                strnicmp(hostname + hostname_len - (e - s - 1),
-                         exclude_list + s + 1, e - s - 1) == 0) {
-                /* IP/hostname range excluded. do not use proxy. */
-                return false;
-            }
-        } else if (exclude_list[e-1] == '*') {
-            /* wildcard at end of entry */
-
-            if ((addr && strnicmp(hostip, exclude_list + s, e - s - 1) == 0) ||
-                strnicmp(hostname, exclude_list + s, e - s - 1) == 0) {
-                /* IP/hostname range excluded. do not use proxy. */
-                return false;
-            }
-        } else {
-            /* no wildcard at either end, so let's try an absolute
-             * match (ie. a specific IP)
-             */
-
-            if (addr && strnicmp(hostip, exclude_list + s, e - s) == 0)
-                return false; /* IP/hostname excluded. do not use proxy. */
-            if (strnicmp(hostname, exclude_list + s, e - s) == 0)
-                return false; /* IP/hostname excluded. do not use proxy. */
-        }
-
-        s = e;
-
-        /* Make sure we really have reached the next comma or end-of-string */
-        while (exclude_list[s] &&
-               !isspace((unsigned char)exclude_list[s]) &&
-               exclude_list[s] != ',') s++;
-    }
-
-    /* no matches in the exclude list, so use the proxy */
-    return true;
-}
-
-static char *dns_log_msg(const char *host, int addressfamily,
-                         const char *reason)
-{
-    return dupprintf("Looking up host \"%s\"%s for %s", host,
-                     (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :
-                      addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :
-                      ""), reason);
-}
-
-SockAddr *name_lookup(const char *host, int port, char **canonicalname,
-                     Conf *conf, int addressfamily, LogContext *logctx,
-                     const char *reason)
-{
-    if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
-        do_proxy_dns(conf) &&
-        proxy_for_destination(NULL, host, port, conf)) {
-
-        if (logctx)
-            logeventf(logctx, "Leaving host lookup to proxy of \"%s\""
-                      " (for %s)", host, reason);
-
-        *canonicalname = dupstr(host);
-        return sk_nonamelookup(host);
-    } else {
-        if (logctx)
-            logevent_and_free(
-                logctx, dns_log_msg(host, addressfamily, reason));
-
-        return sk_namelookup(host, canonicalname, addressfamily);
-    }
-}
-
-static const SocketVtable ProxySocket_sockvt = {
-    // WINSCP
-    /*.plug =*/ sk_proxy_plug,
-    /*.close =*/ sk_proxy_close,
-    /*.write =*/ sk_proxy_write,
-    /*.write_oob =*/ sk_proxy_write_oob,
-    /*.write_eof =*/ sk_proxy_write_eof,
-    /*.set_frozen =*/ sk_proxy_set_frozen,
-    /*.socket_error =*/ sk_proxy_socket_error,
-    /*.peer_info =*/ NULL,
-};
-
-static const PlugVtable ProxySocket_plugvt = {
-    // WINSCP
-    /*.log =*/ plug_proxy_log,
-    /*.closing =*/ plug_proxy_closing,
-    /*.receive =*/ plug_proxy_receive,
-    /*.sent =*/ plug_proxy_sent,
-    /*.accepting =*/ plug_proxy_accepting
-};
-
-Socket *new_connection(SockAddr *addr, const char *hostname,
-                       int port, bool privport,
-                       bool oobinline, bool nodelay, bool keepalive,
-                       Plug *plug, Conf *conf)
-{
-    if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
-        proxy_for_destination(addr, hostname, port, conf))
-    {
-        ProxySocket *ret;
-        SockAddr *proxy_addr;
-        char *proxy_canonical_name;
-        const char *proxy_type;
-        Socket *sret;
-        int type;
-
-        if ((sret = platform_new_connection(addr, hostname, port, privport,
-                                            oobinline, nodelay, keepalive,
-                                            plug, conf)) != NULL)
-            return sret;
-
-        ret = snew(ProxySocket);
-        ret->sock.vt = &ProxySocket_sockvt;
-        ret->plugimpl.vt = &ProxySocket_plugvt;
-        ret->conf = conf_copy(conf);
-        ret->plug = plug;
-        ret->remote_addr = addr;       /* will need to be freed on close */
-        ret->remote_port = port;
-
-        ret->error = NULL;
-        ret->pending_eof = false;
-        ret->freeze = false;
-
-        bufchain_init(&ret->pending_input_data);
-        bufchain_init(&ret->pending_output_data);
-        bufchain_init(&ret->pending_oob_output_data);
-
-        ret->sub_socket = NULL;
-        ret->state = PROXY_STATE_NEW;
-        ret->negotiate = NULL;
-
-        type = conf_get_int(conf, CONF_proxy_type);
-        if (type == PROXY_HTTP) {
-            ret->negotiate = proxy_http_negotiate;
-            proxy_type = "HTTP";
-        } else if (type == PROXY_SOCKS4) {
-            ret->negotiate = proxy_socks4_negotiate;
-            proxy_type = "SOCKS 4";
-        } else if (type == PROXY_SOCKS5) {
-            ret->negotiate = proxy_socks5_negotiate;
-            proxy_type = "SOCKS 5";
-        } else if (type == PROXY_TELNET) {
-            ret->negotiate = proxy_telnet_negotiate;
-            proxy_type = "Telnet";
-        } else {
-            ret->error = "Proxy error: Unknown proxy method";
-            return &ret->sock;
-        }
-
-        {
-            char *logmsg = dupprintf("Will use %s proxy at %s:%d to connect"
-                                      " to %s:%d", proxy_type,
-                                      conf_get_str(conf, CONF_proxy_host),
-                                      conf_get_int(conf, CONF_proxy_port),
-                                      hostname, port);
-            plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);
-            sfree(logmsg);
-        }
-
-        {
-            char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host),
-                                       conf_get_int(conf, CONF_addressfamily),
-                                       "proxy");
-            plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);
-            sfree(logmsg);
-        }
-
-        /* look-up proxy */
-        proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),
-                                   &proxy_canonical_name,
-                                   conf_get_int(conf, CONF_addressfamily));
-        if (sk_addr_error(proxy_addr) != NULL) {
-            ret->error = "Proxy error: Unable to resolve proxy host name";
-            sk_addr_free(proxy_addr);
-            return &ret->sock;
-        }
-        sfree(proxy_canonical_name);
-
-        {
-            char addrbuf[256], *logmsg;
-            sk_getaddr(proxy_addr, addrbuf, lenof(addrbuf));
-            logmsg = dupprintf("Connecting to %s proxy at %s port %d",
-                               proxy_type, addrbuf,
-                               conf_get_int(conf, CONF_proxy_port));
-            plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);
-            sfree(logmsg);
-        }
-
-        /* create the actual socket we will be using,
-         * connected to our proxy server and port.
-         */
-        ret->sub_socket = sk_new(proxy_addr,
-                                 conf_get_int(conf, CONF_proxy_port),
-                                 privport, oobinline,
-                                 nodelay, keepalive, &ret->plugimpl,
-                                 #ifdef MPEXT
-                                 conf_get_int(conf, CONF_connect_timeout), conf_get_int(conf, CONF_sndbuf),
-                                 conf_get_str(conf, CONF_srcaddr)
-                                 #endif
-                                 );
-        if (sk_socket_error(ret->sub_socket) != NULL)
-            return &ret->sock;
-
-        /* start the proxy negotiation process... */
-        sk_set_frozen(ret->sub_socket, false);
-        ret->negotiate(ret, PROXY_CHANGE_NEW);
-
-        return &ret->sock;
-    }
-
-    /* no proxy, so just return the direct socket */
-    return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug,
-      #ifdef MPEXT
-      conf_get_int(conf, CONF_connect_timeout), conf_get_int(conf, CONF_sndbuf),
-      conf_get_str(conf, CONF_srcaddr)
-      #endif
-      );
-}
-
-Socket *new_listener(const char *srcaddr, int port, Plug *plug,
-                     bool local_host_only, Conf *conf, int addressfamily)
-{
-    /* TODO: SOCKS (and potentially others) support inbound
-     * TODO: connections via the proxy. support them.
-     */
-
-    return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);
-}
-
-/* ----------------------------------------------------------------------
- * HTTP CONNECT proxy type.
- */
-
-static bool get_line_end(char *data, size_t len, size_t *out)
-{
-    size_t off = 0;
-
-    while (off < len)
-    {
-        if (data[off] == '\n') {
-            /* we have a newline */
-            off++;
-
-            /* is that the only thing on this line? */
-            if (off <= 2) {
-                *out = off;
-                return true;
-            }
-
-            /* if not, then there is the possibility that this header
-             * continues onto the next line, if it starts with a space
-             * or a tab.
-             */
-
-            if (off + 1 < len && data[off+1] != ' ' && data[off+1] != '\t') {
-                *out = off;
-                return true;
-            }
-
-            /* the line does continue, so we have to keep going
-             * until we see an the header's "real" end of line.
-             */
-            off++;
-        }
-
-        off++;
-    }
-
-    return false;
-}
-
-int proxy_http_negotiate (ProxySocket *p, int change)
-{
-    if (p->state == PROXY_STATE_NEW) {
-        /* we are just beginning the proxy negotiate process,
-         * so we'll send off the initial bits of the request.
-         * for this proxy method, it's just a simple HTTP
-         * request
-         */
-        char *buf, dest[512];
-        char *username, *password;
-
-        sk_getaddr(p->remote_addr, dest, lenof(dest));
-
-        buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n",
-                        dest, p->remote_port, dest, p->remote_port);
-        sk_write(p->sub_socket, buf, strlen(buf));
-        sfree(buf);
-
-        username = conf_get_str(p->conf, CONF_proxy_username);
-        password = conf_get_str(p->conf, CONF_proxy_password);
-        if (username[0] || password[0]) {
-            char *buf, *buf2;
-            int i, j, len;
-            buf = dupprintf("%s:%s", username, password);
-            len = strlen(buf);
-            buf2 = snewn(len * 4 / 3 + 100, char);
-            sprintf(buf2, "Proxy-Authorization: Basic ");
-            for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)
-                base64_encode_atom((unsigned char *)(buf+i),
-                                   (len-i > 3 ? 3 : len-i), buf2+j);
-            strcpy(buf2+j, "\r\n");
-            sk_write(p->sub_socket, buf2, strlen(buf2));
-            sfree(buf);
-            sfree(buf2);
-        }
-
-        sk_write(p->sub_socket, "\r\n", 2);
-
-        p->state = 1;
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_CLOSING) {
-        /* if our proxy negotiation process involves closing and opening
-         * new sockets, then we would want to intercept this closing
-         * callback when we were expecting it. if we aren't anticipating
-         * a socket close, then some error must have occurred. we'll
-         * just pass those errors up to the backend.
-         */
-        plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
-                     p->closing_calling_back);
-        return 0; /* ignored */
-    }
-
-    if (change == PROXY_CHANGE_SENT) {
-        /* some (or all) of what we wrote to the proxy was sent.
-         * we don't do anything new, however, until we receive the
-         * proxy's response. we might want to set a timer so we can
-         * timeout the proxy negotiation after a while...
-         */
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_ACCEPTING) {
-        /* we should _never_ see this, as we are using our socket to
-         * connect to a proxy, not accepting inbound connections.
-         * what should we do? close the socket with an appropriate
-         * error message?
-         */
-        return plug_accepting(p->plug,
-                              p->accepting_constructor, p->accepting_ctx);
-    }
-
-    if (change == PROXY_CHANGE_RECEIVE) {
-        /* we have received data from the underlying socket, which
-         * we'll need to parse, process, and respond to appropriately.
-         */
-
-        char *data, *datap;
-        size_t len, eol;
-
-        if (p->state == 1) {
-
-            int min_ver, maj_ver, status;
-
-            /* get the status line */
-            len = bufchain_size(&p->pending_input_data);
-            assert(len > 0);           /* or we wouldn't be here */
-            data = snewn(len+1, char);
-            bufchain_fetch(&p->pending_input_data, data, len);
-            /*
-             * We must NUL-terminate this data, because Windows
-             * sscanf appears to require a NUL at the end of the
-             * string because it strlens it _first_. Sigh.
-             */
-            data[len] = '\0';
-
-            if (!get_line_end(data, len, &eol)) {
-                sfree(data);
-                return 1;
-            }
-
-            status = -1;
-            /* We can't rely on whether the %n incremented the sscanf return */
-            if (sscanf((char *)data, "HTTP/%i.%i %n",
-                       &maj_ver, &min_ver, &status) < 2 || status == -1) {
-                plug_closing(p->plug, "Proxy error: HTTP response was absent",
-                             PROXY_ERROR_GENERAL, 0);
-                sfree(data);
-                return 1;
-            }
-
-            /* remove the status line from the input buffer. */
-            bufchain_consume(&p->pending_input_data, eol);
-            if (data[status] != '2') {
-                /* error */
-                char *buf;
-                data[eol] = '\0';
-                while (eol > status &&
-                       (data[eol-1] == '\r' || data[eol-1] == '\n'))
-                    data[--eol] = '\0';
-                buf = dupprintf("Proxy error: %s", data+status);
-                plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);
-                sfree(buf);
-                sfree(data);
-                return 1;
-            }
-
-            sfree(data);
-
-            p->state = 2;
-        }
-
-        if (p->state == 2) {
-
-            /* get headers. we're done when we get a
-             * header of length 2, (ie. just "\r\n")
-             */
-
-            len = bufchain_size(&p->pending_input_data);
-            assert(len > 0);           /* or we wouldn't be here */
-            data = snewn(len, char);
-            datap = data;
-            bufchain_fetch(&p->pending_input_data, data, len);
-
-            if (!get_line_end(datap, len, &eol)) {
-                sfree(data);
-                return 1;
-            }
-            while (eol > 2) {
-                bufchain_consume(&p->pending_input_data, eol);
-                datap += eol;
-                len   -= eol;
-                if (!get_line_end(datap, len, &eol))
-                    eol = 0;           /* terminate the loop */
-            }
-
-            if (eol == 2) {
-                /* we're done */
-                bufchain_consume(&p->pending_input_data, 2);
-                proxy_activate(p);
-                /* proxy activate will have dealt with
-                 * whatever is left of the buffer */
-                sfree(data);
-                return 1;
-            }
-
-            sfree(data);
-            return 1;
-        }
-    }
-
-    plug_closing(p->plug, "Proxy error: unexpected proxy error",
-                 PROXY_ERROR_UNEXPECTED, 0);
-    return 1;
-}
-
-/* ----------------------------------------------------------------------
- * SOCKS proxy type.
- */
-
-/* SOCKS version 4 */
-int proxy_socks4_negotiate (ProxySocket *p, int change)
-{
-    if (p->state == PROXY_CHANGE_NEW) {
-
-        /* request format:
-         *  version number (1 byte) = 4
-         *  command code (1 byte)
-         *    1 = CONNECT
-         *    2 = BIND
-         *  dest. port (2 bytes) [network order]
-         *  dest. address (4 bytes)
-         *  user ID (variable length, null terminated string)
-         */
-
-        strbuf *command = strbuf_new();
-        char hostname[512];
-        bool write_hostname = false;
-
-        put_byte(command, 4);          /* SOCKS version 4 */
-        put_byte(command, 1);          /* CONNECT command */
-        put_uint16(command, p->remote_port);
-
-        switch (sk_addrtype(p->remote_addr)) {
-          case ADDRTYPE_IPV4: {
-            char addr[4];
-            sk_addrcopy(p->remote_addr, addr);
-            put_data(command, addr, 4);
-            break;
-          }
-          case ADDRTYPE_NAME:
-            sk_getaddr(p->remote_addr, hostname, lenof(hostname));
-            put_uint32(command, 1);
-            write_hostname = true;
-            break;
-          case ADDRTYPE_IPV6:
-            p->error = "Proxy error: SOCKS version 4 does not support IPv6";
-            strbuf_free(command);
-            return 1;
-        }
-
-        put_asciz(command, conf_get_str(p->conf, CONF_proxy_username));
-        if (write_hostname)
-            put_asciz(command, hostname);
-        sk_write(p->sub_socket, command->s, command->len);
-        strbuf_free(command);
-
-        p->state = 1;
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_CLOSING) {
-        /* if our proxy negotiation process involves closing and opening
-         * new sockets, then we would want to intercept this closing
-         * callback when we were expecting it. if we aren't anticipating
-         * a socket close, then some error must have occurred. we'll
-         * just pass those errors up to the backend.
-         */
-        plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
-                     p->closing_calling_back);
-        return 0; /* ignored */
-    }
-
-    if (change == PROXY_CHANGE_SENT) {
-        /* some (or all) of what we wrote to the proxy was sent.
-         * we don't do anything new, however, until we receive the
-         * proxy's response. we might want to set a timer so we can
-         * timeout the proxy negotiation after a while...
-         */
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_ACCEPTING) {
-        /* we should _never_ see this, as we are using our socket to
-         * connect to a proxy, not accepting inbound connections.
-         * what should we do? close the socket with an appropriate
-         * error message?
-         */
-        return plug_accepting(p->plug,
-                              p->accepting_constructor, p->accepting_ctx);
-    }
-
-    if (change == PROXY_CHANGE_RECEIVE) {
-        /* we have received data from the underlying socket, which
-         * we'll need to parse, process, and respond to appropriately.
-         */
-
-        if (p->state == 1) {
-            /* response format:
-             *  version number (1 byte) = 4
-             *  reply code (1 byte)
-             *    90 = request granted
-             *    91 = request rejected or failed
-             *    92 = request rejected due to lack of IDENTD on client
-             *    93 = request rejected due to difference in user ID
-             *         (what we sent vs. what IDENTD said)
-             *  dest. port (2 bytes)
-             *  dest. address (4 bytes)
-             */
-
-            char data[8];
-
-            if (bufchain_size(&p->pending_input_data) < 8)
-                return 1;              /* not got anything yet */
-
-            /* get the response */
-            bufchain_fetch(&p->pending_input_data, data, 8);
-
-            if (data[0] != 0) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy responded with "
-                                      "unexpected reply code version",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-
-            if (data[1] != 90) {
-
-                switch (data[1]) {
-                  case 92:
-                    plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client",
-                                 PROXY_ERROR_GENERAL, 0);
-                    break;
-                  case 93:
-                    plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree",
-                                 PROXY_ERROR_GENERAL, 0);
-                    break;
-                  case 91:
-                  default:
-                    plug_closing(p->plug, "Proxy error: Error while communicating with proxy",
-                                 PROXY_ERROR_GENERAL, 0);
-                    break;
-                }
-
-                return 1;
-            }
-            bufchain_consume(&p->pending_input_data, 8);
-
-            /* we're done */
-            proxy_activate(p);
-            /* proxy activate will have dealt with
-             * whatever is left of the buffer */
-            return 1;
-        }
-    }
-
-    plug_closing(p->plug, "Proxy error: unexpected proxy error",
-                 PROXY_ERROR_UNEXPECTED, 0);
-    return 1;
-}
-
-/* SOCKS version 5 */
-int proxy_socks5_negotiate (ProxySocket *p, int change)
-{
-    if (p->state == PROXY_CHANGE_NEW) {
-
-        /* initial command:
-         *  version number (1 byte) = 5
-         *  number of available authentication methods (1 byte)
-         *  available authentication methods (1 byte * previous value)
-         *    authentication methods:
-         *     0x00 = no authentication
-         *     0x01 = GSSAPI
-         *     0x02 = username/password
-         *     0x03 = CHAP
-         */
-
-        strbuf *command;
-        char *username, *password;
-        int method_count_offset, methods_start;
-
-        command = strbuf_new();
-        put_byte(command, 5);          /* SOCKS version 5 */
-        username = conf_get_str(p->conf, CONF_proxy_username);
-        password = conf_get_str(p->conf, CONF_proxy_password);
-
-        method_count_offset = command->len;
-        put_byte(command, 0);
-        methods_start = command->len;
-
-        put_byte(command, 0x00);       /* no authentication */
-
-        if (username[0] || password[0]) {
-            proxy_socks5_offerencryptedauth(BinarySink_UPCAST(command));
-            put_byte(command, 0x02);    /* username/password */
-        }
-
-        command->u[method_count_offset] = command->len - methods_start;
-
-        sk_write(p->sub_socket, command->s, command->len);
-        strbuf_free(command);
-
-        p->state = 1;
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_CLOSING) {
-        /* if our proxy negotiation process involves closing and opening
-         * new sockets, then we would want to intercept this closing
-         * callback when we were expecting it. if we aren't anticipating
-         * a socket close, then some error must have occurred. we'll
-         * just pass those errors up to the backend.
-         */
-        plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
-                     p->closing_calling_back);
-        return 0; /* ignored */
-    }
-
-    if (change == PROXY_CHANGE_SENT) {
-        /* some (or all) of what we wrote to the proxy was sent.
-         * we don't do anything new, however, until we receive the
-         * proxy's response. we might want to set a timer so we can
-         * timeout the proxy negotiation after a while...
-         */
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_ACCEPTING) {
-        /* we should _never_ see this, as we are using our socket to
-         * connect to a proxy, not accepting inbound connections.
-         * what should we do? close the socket with an appropriate
-         * error message?
-         */
-        return plug_accepting(p->plug,
-                              p->accepting_constructor, p->accepting_ctx);
-    }
-
-    if (change == PROXY_CHANGE_RECEIVE) {
-        /* we have received data from the underlying socket, which
-         * we'll need to parse, process, and respond to appropriately.
-         */
-
-        if (p->state == 1) {
-
-            /* initial response:
-             *  version number (1 byte) = 5
-             *  authentication method (1 byte)
-             *    authentication methods:
-             *     0x00 = no authentication
-             *     0x01 = GSSAPI
-             *     0x02 = username/password
-             *     0x03 = CHAP
-             *     0xff = no acceptable methods
-             */
-            char data[2];
-
-            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);
-
-            if (data[0] != 5) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-
-            if (data[1] == 0x00) p->state = 2; /* no authentication needed */
-            else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */
-            else if (data[1] == 0x02) p->state = 5; /* username/password authentication */
-            else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */
-            else {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-            bufchain_consume(&p->pending_input_data, 2);
-        }
-
-        if (p->state == 7) {
-
-            /* password authentication reply format:
-             *  version number (1 bytes) = 1
-             *  reply code (1 byte)
-             *    0 = succeeded
-             *    >0 = failed
-             */
-            char data[2];
-
-            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);
-
-            if (data[0] != 1) {
-                plug_closing(p->plug, "Proxy error: SOCKS password "
-                             "subnegotiation contained wrong version number",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-
-            if (data[1] != 0) {
-
-                plug_closing(p->plug, "Proxy error: SOCKS proxy refused"
-                             " password authentication",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-
-            bufchain_consume(&p->pending_input_data, 2);
-            p->state = 2;              /* now proceed as authenticated */
-        }
-
-        if (p->state == 8) {
-            int ret;
-            ret = proxy_socks5_handlechap(p);
-            if (ret) return ret;
-        }
-
-        if (p->state == 2) {
-
-            /* request format:
-             *  version number (1 byte) = 5
-             *  command code (1 byte)
-             *    1 = CONNECT
-             *    2 = BIND
-             *    3 = UDP ASSOCIATE
-             *  reserved (1 byte) = 0x00
-             *  address type (1 byte)
-             *    1 = IPv4
-             *    3 = domainname (first byte has length, no terminating null)
-             *    4 = IPv6
-             *  dest. address (variable)
-             *  dest. port (2 bytes) [network order]
-             */
-
-            strbuf *command = strbuf_new();
-            put_byte(command, 5);      /* SOCKS version 5 */
-            put_byte(command, 1);      /* CONNECT command */
-            put_byte(command, 0x00);   /* reserved byte */
-
-            switch (sk_addrtype(p->remote_addr)) {
-              case ADDRTYPE_IPV4:
-                put_byte(command, 1);  /* IPv4 */
-                sk_addrcopy(p->remote_addr, strbuf_append(command, 4));
-                break;
-              case ADDRTYPE_IPV6:
-                put_byte(command, 4);  /* IPv6 */
-                sk_addrcopy(p->remote_addr, strbuf_append(command, 16));
-                break;
-              case ADDRTYPE_NAME: {
-                char hostname[512];
-                put_byte(command, 3);  /* domain name */
-                sk_getaddr(p->remote_addr, hostname, lenof(hostname));
-                if (!put_pstring(command, hostname)) {
-                  p->error = "Proxy error: SOCKS 5 cannot "
-                      "support host names longer than 255 chars";
-                  strbuf_free(command);
-                  return 1;
-                }
-                break;
-              }
-            }
-
-            put_uint16(command, p->remote_port);
-
-            sk_write(p->sub_socket, command->s, command->len);
-
-            strbuf_free(command);
-
-            p->state = 3;
-            return 1;
-        }
-
-        if (p->state == 3) {
-
-            /* reply format:
-             *  version number (1 bytes) = 5
-             *  reply code (1 byte)
-             *    0 = succeeded
-             *    1 = general SOCKS server failure
-             *    2 = connection not allowed by ruleset
-             *    3 = network unreachable
-             *    4 = host unreachable
-             *    5 = connection refused
-             *    6 = TTL expired
-             *    7 = command not supported
-             *    8 = address type not supported
-             * reserved (1 byte) = x00
-             * address type (1 byte)
-             *    1 = IPv4
-             *    3 = domainname (first byte has length, no terminating null)
-             *    4 = IPv6
-             * server bound address (variable)
-             * server bound port (2 bytes) [network order]
-             */
-            char data[5];
-            int len;
-
-            /* First 5 bytes of packet are enough to tell its length. */
-            if (bufchain_size(&p->pending_input_data) < 5)
-                return 1;              /* not got anything yet */
-
-            /* get the response */
-            bufchain_fetch(&p->pending_input_data, data, 5);
-
-            if (data[0] != 5) {
-                plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-
-            if (data[1] != 0) {
-                char buf[256];
-
-                strcpy(buf, "Proxy error: ");
-
-                switch (data[1]) {
-                  case 1: strcat(buf, "General SOCKS server failure"); break;
-                  case 2: strcat(buf, "Connection not allowed by ruleset"); break;
-                  case 3: strcat(buf, "Network unreachable"); break;
-                  case 4: strcat(buf, "Host unreachable"); break;
-                  case 5: strcat(buf, "Connection refused"); break;
-                  case 6: strcat(buf, "TTL expired"); break;
-                  case 7: strcat(buf, "Command not supported"); break;
-                  case 8: strcat(buf, "Address type not supported"); break;
-                  default: sprintf(buf+strlen(buf),
-                                   "Unrecognised SOCKS error code %d",
-                                   data[1]);
-                    break;
-                }
-                plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);
-
-                return 1;
-            }
-
-            /*
-             * Eat the rest of the reply packet.
-             */
-            len = 6;                   /* first 4 bytes, last 2 */
-            switch (data[3]) {
-              case 1: len += 4; break; /* IPv4 address */
-              case 4: len += 16; break;/* IPv6 address */
-              case 3: len += 1+(unsigned char)data[4]; break; /* domain name */
-              default:
-                plug_closing(p->plug, "Proxy error: SOCKS proxy returned "
-                             "unrecognised address format",
-                             PROXY_ERROR_GENERAL, 0);
-                return 1;
-            }
-            if (bufchain_size(&p->pending_input_data) < len)
-                return 1;              /* not got whole reply yet */
-            bufchain_consume(&p->pending_input_data, len);
-
-            /* we're done */
-            proxy_activate(p);
-            return 1;
-        }
-
-        if (p->state == 4) {
-            /* TODO: Handle GSSAPI authentication */
-            plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication",
-                         PROXY_ERROR_GENERAL, 0);
-            return 1;
-        }
-
-        if (p->state == 5) {
-            const char *username = conf_get_str(p->conf, CONF_proxy_username);
-            const char *password = conf_get_str(p->conf, CONF_proxy_password);
-            if (username[0] || password[0]) {
-                strbuf *auth = strbuf_new_nm();
-                put_byte(auth, 1); /* version number of subnegotiation */
-                if (!put_pstring(auth, username)) {
-                    p->error = "Proxy error: SOCKS 5 authentication cannot "
-                        "support usernames longer than 255 chars";
-                    strbuf_free(auth);
-                    return 1;
-                }
-                if (!put_pstring(auth, password)) {
-                    p->error = "Proxy error: SOCKS 5 authentication cannot "
-                        "support passwords longer than 255 chars";
-                    strbuf_free(auth);
-                    return 1;
-                }
-                sk_write(p->sub_socket, auth->s, auth->len);
-                strbuf_free(auth);
-                p->state = 7;
-            } else
-                plug_closing(p->plug, "Proxy error: Server chose "
-                             "username/password authentication but we "
-                             "didn't offer it!",
-                         PROXY_ERROR_GENERAL, 0);
-            return 1;
-        }
-
-        if (p->state == 6) {
-            int ret;
-            ret = proxy_socks5_selectchap(p);
-            if (ret) return ret;
-        }
-
-    }
-
-    plug_closing(p->plug, "Proxy error: Unexpected proxy error",
-                 PROXY_ERROR_UNEXPECTED, 0);
-    return 1;
-}
-
-/* ----------------------------------------------------------------------
- * `Telnet' proxy type.
- *
- * (This is for ad-hoc proxies where you connect to the proxy's
- * telnet port and send a command such as `connect host port'. The
- * command is configurable, since this proxy type is typically not
- * standardised or at all well-defined.)
- */
-
-char *format_telnet_command(SockAddr *addr, int port, Conf *conf)
-{
-    char *fmt = conf_get_str(conf, CONF_proxy_telnet_command);
-    int so = 0, eo = 0;
-    strbuf *buf = strbuf_new();
-
-    /* we need to escape \\, \%, \r, \n, \t, \x??, \0???,
-     * %%, %host, %port, %user, and %pass
-     */
-
-    while (fmt[eo] != 0) {
-
-        /* scan forward until we hit end-of-line,
-         * or an escape character (\ or %) */
-        while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\')
-            eo++;
-
-        /* if we hit eol, break out of our escaping loop */
-        if (fmt[eo] == 0) break;
-
-        /* if there was any unescaped text before the escape
-         * character, send that now */
-        if (eo != so)
-            put_data(buf, fmt + so, eo - so);
-
-        so = eo++;
-
-        /* if the escape character was the last character of
-         * the line, we'll just stop and send it. */
-        if (fmt[eo] == 0) break;
-
-        if (fmt[so] == '\\') {
-
-            /* we recognize \\, \%, \r, \n, \t, \x??.
-             * anything else, we just send unescaped (including the \).
-             */
-
-            switch (fmt[eo]) {
-
-              case '\\':
-                put_byte(buf, '\\');
-                eo++;
-                break;
-
-              case '%':
-                put_byte(buf, '%');
-                eo++;
-                break;
-
-              case 'r':
-                put_byte(buf, '\r');
-                eo++;
-                break;
-
-              case 'n':
-                put_byte(buf, '\n');
-                eo++;
-                break;
-
-              case 't':
-                put_byte(buf, '\t');
-                eo++;
-                break;
-
-              case 'x':
-              case 'X': {
-                /* escaped hexadecimal value (ie. \xff) */
-                unsigned char v = 0;
-                int i = 0;
-
-                for (;;) {
-                  eo++;
-                  if (fmt[eo] >= '0' && fmt[eo] <= '9')
-                      v += fmt[eo] - '0';
-                  else if (fmt[eo] >= 'a' && fmt[eo] <= 'f')
-                      v += fmt[eo] - 'a' + 10;
-                  else if (fmt[eo] >= 'A' && fmt[eo] <= 'F')
-                      v += fmt[eo] - 'A' + 10;
-                  else {
-                    /* non hex character, so we abort and just
-                     * send the whole thing unescaped (including \x)
-                     */
-                    put_byte(buf, '\\');
-                    eo = so + 1;
-                    break;
-                  }
-
-                  /* we only extract two hex characters */
-                  if (i == 1) {
-                    put_byte(buf, v);
-                    eo++;
-                    break;
-                  }
-
-                  i++;
-                  v <<= 4;
-                }
-                break;
-              }
-
-              default:
-                put_data(buf, fmt + so, 2);
-                eo++;
-                break;
-            }
-        } else {
-
-            /* % escape. we recognize %%, %host, %port, %user, %pass.
-             * %proxyhost, %proxyport. Anything else we just send
-             * unescaped (including the %).
-             */
-
-            if (fmt[eo] == '%') {
-                put_byte(buf, '%');
-                eo++;
-            }
-            else if (strnicmp(fmt + eo, "host", 4) == 0) {
-                char dest[512];
-                sk_getaddr(addr, dest, lenof(dest));
-                put_data(buf, dest, strlen(dest));
-                eo += 4;
-            }
-            else if (strnicmp(fmt + eo, "port", 4) == 0) {
-                strbuf_catf(buf, "%d", port);
-                eo += 4;
-            }
-            else if (strnicmp(fmt + eo, "user", 4) == 0) {
-                const char *username = conf_get_str(conf, CONF_proxy_username);
-                put_data(buf, username, strlen(username));
-                eo += 4;
-            }
-            else if (strnicmp(fmt + eo, "pass", 4) == 0) {
-                const char *password = conf_get_str(conf, CONF_proxy_password);
-                put_data(buf, password, strlen(password));
-                eo += 4;
-            }
-            else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) {
-                const char *host = conf_get_str(conf, CONF_proxy_host);
-                put_data(buf, host, strlen(host));
-                eo += 9;
-            }
-            else if (strnicmp(fmt + eo, "proxyport", 9) == 0) {
-                int port = conf_get_int(conf, CONF_proxy_port);
-                strbuf_catf(buf, "%d", port);
-                eo += 9;
-            }
-            else {
-                /* we don't escape this, so send the % now, and
-                 * don't advance eo, so that we'll consider the
-                 * text immediately following the % as unescaped.
-                 */
-                put_byte(buf, '%');
-            }
-        }
-
-        /* resume scanning for additional escapes after this one. */
-        so = eo;
-    }
-
-    /* if there is any unescaped text at the end of the line, send it */
-    if (eo != so) {
-        put_data(buf, fmt + so, eo - so);
-    }
-
-    return strbuf_to_str(buf);
-}
-
-int proxy_telnet_negotiate (ProxySocket *p, int change)
-{
-    if (p->state == PROXY_CHANGE_NEW) {
-        char *formatted_cmd;
-
-        formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,
-                                              p->conf);
-
-        {
-            /*
-             * Re-escape control chars in the command, for logging.
-             */
-            char *reescaped = snewn(4*strlen(formatted_cmd) + 1, char);
-            const char *in;
-            char *out;
-            char *logmsg;
-
-            for (in = formatted_cmd, out = reescaped; *in; in++) {
-                if (*in == '\n') {
-                    *out++ = '\\'; *out++ = 'n';
-                } else if (*in == '\r') {
-                    *out++ = '\\'; *out++ = 'r';
-                } else if (*in == '\t') {
-                    *out++ = '\\'; *out++ = 't';
-                } else if (*in == '\\') {
-                    *out++ = '\\'; *out++ = '\\';
-                } else if ((unsigned)(((unsigned char)*in) - 0x20) <
-                           (0x7F-0x20)) {
-                    *out++ = *in;
-                } else {
-                    out += sprintf(out, "\\x%02X", (unsigned)*in & 0xFF);
-                }
-            }
-            *out = '\0';
-
-            logmsg = dupprintf("Sending Telnet proxy command: %s", reescaped);
-            plug_log(p->plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);
-            sfree(logmsg);
-            sfree(reescaped);
-        }
-
-        sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));
-        sfree(formatted_cmd);
-
-        p->state = 1;
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_CLOSING) {
-        /* if our proxy negotiation process involves closing and opening
-         * new sockets, then we would want to intercept this closing
-         * callback when we were expecting it. if we aren't anticipating
-         * a socket close, then some error must have occurred. we'll
-         * just pass those errors up to the backend.
-         */
-        plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,
-                     p->closing_calling_back);
-        return 0; /* ignored */
-    }
-
-    if (change == PROXY_CHANGE_SENT) {
-        /* some (or all) of what we wrote to the proxy was sent.
-         * we don't do anything new, however, until we receive the
-         * proxy's response. we might want to set a timer so we can
-         * timeout the proxy negotiation after a while...
-         */
-        return 0;
-    }
-
-    if (change == PROXY_CHANGE_ACCEPTING) {
-        /* we should _never_ see this, as we are using our socket to
-         * connect to a proxy, not accepting inbound connections.
-         * what should we do? close the socket with an appropriate
-         * error message?
-         */
-        return plug_accepting(p->plug,
-                              p->accepting_constructor, p->accepting_ctx);
-    }
-
-    if (change == PROXY_CHANGE_RECEIVE) {
-        /* we have received data from the underlying socket, which
-         * we'll need to parse, process, and respond to appropriately.
-         */
-
-        /* we're done */
-        proxy_activate(p);
-        /* proxy activate will have dealt with
-         * whatever is left of the buffer */
-        return 1;
-    }
-
-    plug_closing(p->plug, "Proxy error: Unexpected proxy error",
-                 PROXY_ERROR_UNEXPECTED, 0);
-    return 1;
-}
-
-#ifdef MPEXT
-ProxySocket * get_proxy_plug_socket(Plug * p)
-{
-    return container_of(p, ProxySocket, plugimpl);
-}
-#endif

+ 0 - 115
source/putty/proxy.h

@@ -1,115 +0,0 @@
-/*
- * Network proxy abstraction in PuTTY
- *
- * A proxy layer, if necessary, wedges itself between the
- * network code and the higher level backend.
- *
- * Supported proxies: HTTP CONNECT, generic telnet, SOCKS 4 & 5
- */
-
-#ifndef PUTTY_PROXY_H
-#define PUTTY_PROXY_H
-
-#define PROXY_ERROR_GENERAL 8000
-#define PROXY_ERROR_UNEXPECTED 8001
-
-typedef struct ProxySocket ProxySocket;
-
-struct ProxySocket {
-    const char *error;
-
-    Socket *sub_socket;
-    Plug *plug;
-    SockAddr *remote_addr;
-    int remote_port;
-
-    bufchain pending_output_data;
-    bufchain pending_oob_output_data;
-    bufchain pending_input_data;
-    bool pending_eof;
-
-#define PROXY_STATE_NEW    -1
-#define PROXY_STATE_ACTIVE  0
-
-    int state; /* proxy states greater than 0 are implementation
-                * dependent, but represent various stages/states
-                * of the initialization/setup/negotiation with the
-                * proxy server.
-                */
-    bool freeze; /* should we freeze the underlying socket when
-                  * we are done with the proxy negotiation? this
-                  * simply caches the value of sk_set_frozen calls.
-                  */
-
-#define PROXY_CHANGE_NEW      -1
-#define PROXY_CHANGE_CLOSING   0
-#define PROXY_CHANGE_SENT      1
-#define PROXY_CHANGE_RECEIVE   2
-#define PROXY_CHANGE_ACCEPTING 3
-
-    /* something has changed (a call from the sub socket
-     * layer into our Proxy Plug layer, or we were just
-     * created, etc), so the proxy layer needs to handle
-     * this change (the type of which is the second argument)
-     * and further the proxy negotiation process.
-     */
-
-    int (*negotiate) (ProxySocket * /* this */, int /* change type */);
-
-    /* current arguments of plug handlers
-     * (for use by proxy's negotiate function)
-     */
-
-    /* closing */
-    const char *closing_error_msg;
-    int closing_error_code;
-    bool closing_calling_back;
-
-    /* receive */
-    bool receive_urgent;
-    const char *receive_data;
-    int receive_len;
-
-    /* accepting */
-    accept_fn_t accepting_constructor;
-    accept_ctx_t accepting_ctx;
-
-    /* configuration, used to look up proxy settings */
-    Conf *conf;
-
-    /* CHAP transient data */
-    int chap_num_attributes;
-    int chap_num_attributes_processed;
-    int chap_current_attribute;
-    int chap_current_datalen;
-
-    Socket sock;
-    Plug plugimpl;
-};
-
-extern void proxy_activate (ProxySocket *);
-
-extern int proxy_http_negotiate (ProxySocket *, int);
-extern int proxy_telnet_negotiate (ProxySocket *, int);
-extern int proxy_socks4_negotiate (ProxySocket *, int);
-extern int proxy_socks5_negotiate (ProxySocket *, int);
-
-/*
- * This may be reused by local-command proxies on individual
- * platforms.
- */
-char *format_telnet_command(SockAddr *addr, int port, Conf *conf);
-
-/*
- * These are implemented in cproxy.c or nocproxy.c, depending on
- * whether encrypted proxy authentication is available.
- */
-extern void proxy_socks5_offerencryptedauth(BinarySink *);
-extern int proxy_socks5_handlechap (ProxySocket *);
-extern int proxy_socks5_selectchap(ProxySocket *);
-
-#ifdef MPEXT
-ProxySocket * get_proxy_plug_socket(Plug * p);
-#endif
-
-#endif

文件差异内容过多而无法显示
+ 522 - 125
source/putty/putty.h


+ 7 - 6
source/putty/puttymem.h

@@ -107,18 +107,19 @@ void *safegrowarray(void *array, size_t *size, size_t eltsize,
 
 /*
  * This function is called by the innermost safemalloc/saferealloc
- * functions when allocation fails. Usually it's provided by misc.c
- * which ties it into an application's existing modalfatalbox()
- * system, but standalone test applications can reimplement it some
- * other way if they prefer.
+ * functions when allocation fails. Usually it's provided by an
+ * implementation in utils, which ties it into an application's
+ * existing modalfatalbox() system, but standalone test applications
+ * can reimplement it some other way if they prefer.
  */
 NORETURN void out_of_memory(void);
 
 #ifdef MINEFIELD
 /*
  * Definitions for Minefield, PuTTY's own Windows-specific malloc
- * debugger in the style of Electric Fence. Implemented in winmisc.c,
- * and referred to by the main malloc wrappers in memory.c.
+ * debugger in the style of Electric Fence. Implemented in
+ * windows/utils/minefield.c, and referred to by the main malloc
+ * wrappers in memory.c.
  */
 void *minefield_c_malloc(size_t size);
 void minefield_c_free(void *p);

+ 0 - 18
source/putty/puttyps.h

@@ -1,18 +0,0 @@
-/*
- * Find the platform-specific header for this platform.
- */
-
-#ifndef PUTTY_PUTTYPS_H
-#define PUTTY_PUTTYPS_H
-
-#ifdef _WINDOWS
-
-#include "winstuff.h"
-
-#else
-
-#include "unix.h"
-
-#endif
-
-#endif

+ 10 - 5
source/putty/settings.c

@@ -8,8 +8,8 @@
 #include "putty.h"
 #include "storage.h"
 #ifndef NO_GSSAPI
-#include "sshgssc.h"
-#include "sshgss.h"
+#include "ssh/gssc.h"
+#include "ssh/gss.h"
 #endif
 
 
@@ -49,9 +49,9 @@ static const struct keyvalwhere hknames[] = {
 /*
  * All the terminal modes that we know about for the "TerminalModes"
  * setting. (Also used by config.c for the drop-down list.)
- * This is currently precisely the same as the set in ssh.c, but could
- * in principle differ if other backends started to support tty modes
- * (e.g., the pty backend).
+ * This is currently precisely the same as the set in
+ * ssh/ttymode-list.h, but could in principle differ if other backends
+ * started to support tty modes (e.g., the pty backend).
  * The set of modes in in this array is currently significant for
  * settings migration from old versions; if they change, review the
  * gppmap() invocation for "TerminalModes".
@@ -630,6 +630,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
     write_setting_b(sesskey, "BackspaceIsDelete", conf_get_bool(conf, CONF_bksp_is_delete));
     write_setting_b(sesskey, "RXVTHomeEnd", conf_get_bool(conf, CONF_rxvt_homeend));
     write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type));
+    write_setting_i(sesskey, "ShiftedArrowKeys", conf_get_int(conf, CONF_sharrow_type));
     write_setting_b(sesskey, "NoApplicationKeys", conf_get_bool(conf, CONF_no_applic_k));
     write_setting_b(sesskey, "NoApplicationCursors", conf_get_bool(conf, CONF_no_applic_c));
     write_setting_b(sesskey, "NoMouseReporting", conf_get_bool(conf, CONF_no_mouse_rep));
@@ -769,6 +770,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf)
     write_setting_i(sesskey, "BugOldGex2", 2-conf_get_int(conf, CONF_sshbug_oldgex2));
     write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj));
     write_setting_i(sesskey, "BugChanReq", 2-conf_get_int(conf, CONF_sshbug_chanreq));
+    write_setting_i(sesskey, "BugDropStart", 2-conf_get_int(conf, CONF_sshbug_dropstart));
     write_setting_b(sesskey, "StampUtmp", conf_get_bool(conf, CONF_stamp_utmp));
     write_setting_b(sesskey, "LoginShell", conf_get_bool(conf, CONF_login_shell));
     write_setting_b(sesskey, "ScrollbarOnLeft", conf_get_bool(conf, CONF_scrollbar_on_left));
@@ -1047,6 +1049,8 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
     gppb(sesskey, "BackspaceIsDelete", true, conf, CONF_bksp_is_delete);
     gppb(sesskey, "RXVTHomeEnd", false, conf, CONF_rxvt_homeend);
     gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type);
+    gppi(sesskey, "ShiftedArrowKeys", SHARROW_APPLICATION, conf,
+         CONF_sharrow_type);
     gppb(sesskey, "NoApplicationKeys", false, conf, CONF_no_applic_k);
     gppb(sesskey, "NoApplicationCursors", false, conf, CONF_no_applic_c);
     gppb(sesskey, "NoMouseReporting", false, conf, CONF_no_mouse_rep);
@@ -1246,6 +1250,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf)
     i = gppi_raw(sesskey, "BugOldGex2", 0); conf_set_int(conf, CONF_sshbug_oldgex2, 2-i);
     i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i);
     i = gppi_raw(sesskey, "BugChanReq", 0); conf_set_int(conf, CONF_sshbug_chanreq, 2-i);
+    i = gppi_raw(sesskey, "BugDropStart", 1); conf_set_int(conf, CONF_sshbug_dropstart, 2-i);
     conf_set_bool(conf, CONF_ssh_simple, false);
     gppb(sesskey, "StampUtmp", true, conf, CONF_stamp_utmp);
     gppb(sesskey, "LoginShell", true, conf, CONF_login_shell);

+ 0 - 1325
source/putty/ssh.c

@@ -1,1325 +0,0 @@
-/*
- * SSH backend.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <assert.h>
-#include <limits.h>
-#include <signal.h>
-
-#include "putty.h"
-#include "pageant.h" /* for AGENT_MAX_MSGLEN */
-#include "tree234.h"
-#include "storage.h"
-#include "marshal.h"
-#include "ssh.h"
-#include "sshcr.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshchan.h"
-#ifndef NO_GSSAPI
-#include "sshgssc.h"
-#include "sshgss.h"
-#define MIN_CTXT_LIFETIME 5     /* Avoid rekey with short lifetime (seconds) */
-#define GSS_KEX_CAPABLE (1<<0)  /* Can do GSS KEX */
-#define GSS_CRED_UPDATED (1<<1) /* Cred updated since previous delegation */
-#define GSS_CTXT_EXPIRES (1<<2) /* Context expires before next timer */
-#define GSS_CTXT_MAYFAIL (1<<3) /* Context may expire during handshake */
-#endif
-
-struct Ssh {
-    Socket *s;
-    Seat *seat;
-    Conf *conf;
-
-    struct ssh_version_receiver version_receiver;
-    int remote_bugs;
-
-    Plug plug;
-    Backend backend;
-
-    Ldisc *ldisc;
-    LogContext *logctx;
-
-    /* The last list returned from get_specials. */
-    SessionSpecial *specials;
-
-    bool bare_connection;
-    ssh_sharing_state *connshare;
-    bool attempting_connshare;
-
-#ifndef NO_GSSAPI
-    struct ssh_connection_shared_gss_state gss_state;
-#endif
-
-    char *savedhost;
-    int savedport;
-    char *fullhostname;
-
-    bool fallback_cmd;
-    int exitcode;
-
-    int version;
-    int conn_throttle_count;
-    size_t overall_bufsize;
-    bool throttled_all;
-
-    /*
-     * logically_frozen is true if we're not currently _processing_
-     * data from the SSH socket (e.g. because a higher layer has asked
-     * us not to due to ssh_throttle_conn). socket_frozen is true if
-     * we're not even _reading_ data from the socket (i.e. it should
-     * always match the value we last passed to sk_set_frozen).
-     *
-     * The two differ in that socket_frozen can also become
-     * temporarily true because of a large backlog in the in_raw
-     * bufchain, to force no further plug_receive events until the BPP
-     * input function has had a chance to run. (Some front ends, like
-     * GTK, can persistently call the network and never get round to
-     * the toplevel callbacks.) If we've stopped reading from the
-     * socket for that reason, we absolutely _do_ want to carry on
-     * processing our input bufchain, because that's the only way
-     * it'll ever get cleared!
-     *
-     * ssh_check_frozen() resets socket_frozen, and should be called
-     * whenever either of logically_frozen and the bufchain size
-     * changes.
-     */
-    bool logically_frozen, socket_frozen;
-
-    /* in case we find these out before we have a ConnectionLayer to tell */
-    int term_width, term_height;
-
-    bufchain in_raw, out_raw, user_input;
-    bool pending_close;
-    IdempotentCallback ic_out_raw;
-
-    PacketLogSettings pls;
-    struct DataTransferStats stats;
-
-    BinaryPacketProtocol *bpp;
-
-    /*
-     * base_layer identifies the bottommost packet protocol layer, the
-     * one connected directly to the BPP's packet queues. Any
-     * operation that needs to talk to all layers (e.g. free, or
-     * get_specials) will do it by talking to this, which will
-     * recursively propagate it if necessary.
-     */
-    PacketProtocolLayer *base_layer;
-
-    /*
-     * The ConnectionLayer vtable from our connection layer.
-     */
-    ConnectionLayer *cl;
-
-    /*
-     * A dummy ConnectionLayer that can be used for logging sharing
-     * downstreams that connect before the real one is ready.
-     */
-    ConnectionLayer cl_dummy;
-
-    /*
-     * session_started is false until we initialise the main protocol
-     * layers. So it distinguishes between base_layer==NULL meaning
-     * that the SSH protocol hasn't been set up _yet_, and
-     * base_layer==NULL meaning the SSH protocol has run and finished.
-     * It's also used to mark the point where we stop counting proxy
-     * command diagnostics as pre-session-startup.
-     */
-    bool session_started;
-
-    Pinger *pinger;
-
-    char *deferred_abort_message;
-
-    bool need_random_unref;
-};
-
-
-#define ssh_logevent(params) ( \
-        logevent_and_free((ssh)->logctx, dupprintf params))
-
-static void ssh_shutdown(Ssh *ssh);
-static void ssh_throttle_all(Ssh *ssh, bool enable, size_t bufsize);
-static void ssh_bpp_output_raw_data_callback(void *vctx);
-
-LogContext *ssh_get_logctx(Ssh *ssh)
-{
-    return ssh->logctx;
-}
-
-static void ssh_connect_bpp(Ssh *ssh)
-{
-    ssh->bpp->ssh = ssh;
-    ssh->bpp->in_raw = &ssh->in_raw;
-    ssh->bpp->out_raw = &ssh->out_raw;
-    bufchain_set_callback(ssh->bpp->out_raw, &ssh->ic_out_raw);
-    ssh->bpp->pls = &ssh->pls;
-    ssh->bpp->logctx = ssh->logctx;
-    ssh->bpp->remote_bugs = ssh->remote_bugs;
-}
-
-static void ssh_connect_ppl(Ssh *ssh, PacketProtocolLayer *ppl)
-{
-    ppl->bpp = ssh->bpp;
-    ppl->user_input = &ssh->user_input;
-    ppl->seat = ssh->seat;
-    ppl->ssh = ssh;
-    ppl->logctx = ssh->logctx;
-    ppl->remote_bugs = ssh->remote_bugs;
-}
-
-static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
-                                int major_version)
-{
-    Ssh *ssh = container_of(rcv, Ssh, version_receiver);
-    BinaryPacketProtocol *old_bpp;
-    PacketProtocolLayer *connection_layer;
-
-    ssh->session_started = true;
-
-    /*
-     * We don't support choosing a major protocol version dynamically,
-     * so this should always be the same value we set up in
-     * connect_to_host().
-     */
-    assert(ssh->version == major_version);
-    assert(ssh->version == 2);
-
-    old_bpp = ssh->bpp;
-    ssh->remote_bugs = ssh_verstring_get_bugs(old_bpp);
-
-    if (!ssh->bare_connection) {
-        /*WINSCP if (ssh->version == 2)*/ {
-            PacketProtocolLayer *userauth_layer, *transport_child_layer;
-
-            /*
-             * We use the 'simple' variant of the SSH protocol if
-             * we're asked to, except not if we're also doing
-             * connection-sharing (either tunnelling our packets over
-             * an upstream or expecting to be tunnelled over
-             * ourselves), since then the assumption that we have only
-             * one channel to worry about is not true after all.
-             */
-            bool is_simple =
-                (conf_get_bool(ssh->conf, CONF_ssh_simple) && !ssh->connshare);
-
-            ssh->bpp = ssh2_bpp_new(ssh->logctx, &ssh->stats, false);
-            ssh_connect_bpp(ssh);
-
-#ifndef NO_GSSAPI
-            /* Load and pick the highest GSS library on the preference
-             * list. */
-            if (!ssh->gss_state.libs)
-                ssh->gss_state.libs = ssh_gss_setup(ssh->conf, ssh->logctx); // WINSCP
-            ssh->gss_state.lib = NULL;
-            if (ssh->gss_state.libs->nlibraries > 0) {
-                int i, j;
-                for (i = 0; i < ngsslibs; i++) {
-                    int want_id = conf_get_int_int(ssh->conf,
-                                                   CONF_ssh_gsslist, i);
-                    for (j = 0; j < ssh->gss_state.libs->nlibraries; j++)
-                        if (ssh->gss_state.libs->libraries[j].id == want_id) {
-                            ssh->gss_state.lib =
-                                &ssh->gss_state.libs->libraries[j];
-                            goto got_gsslib;   /* double break */
-                        }
-                }
-              got_gsslib:
-                /*
-                 * We always expect to have found something in
-                 * the above loop: we only came here if there
-                 * was at least one viable GSS library, and the
-                 * preference list should always mention
-                 * everything and only change the order.
-                 */
-                assert(ssh->gss_state.lib);
-            }
-#endif
-
-            connection_layer = ssh2_connection_new(
-                ssh, ssh->connshare, is_simple, ssh->conf,
-                ssh_verstring_get_remote(old_bpp), &ssh->cl);
-            ssh_connect_ppl(ssh, connection_layer);
-
-            if (conf_get_bool(ssh->conf, CONF_ssh_no_userauth)) {
-                userauth_layer = NULL;
-                transport_child_layer = connection_layer;
-            } else {
-                char *username = get_remote_username(ssh->conf);
-
-                userauth_layer = ssh2_userauth_new(
-                    connection_layer, ssh->savedhost, ssh->fullhostname,
-                    conf_get_filename(ssh->conf, CONF_keyfile),
-                    conf_get_bool(ssh->conf, CONF_ssh_show_banner),
-                    conf_get_bool(ssh->conf, CONF_tryagent),
-                    conf_get_bool(ssh->conf, CONF_ssh_no_trivial_userauth),
-                    username,
-                    conf_get_bool(ssh->conf, CONF_change_username),
-                    conf_get_bool(ssh->conf, CONF_try_ki_auth),
-#ifndef NO_GSSAPI
-                    conf_get_bool(ssh->conf, CONF_try_gssapi_auth),
-                    conf_get_bool(ssh->conf, CONF_try_gssapi_kex),
-                    conf_get_bool(ssh->conf, CONF_gssapifwd),
-                    &ssh->gss_state
-#else
-                    false,
-                    false,
-                    false,
-                    NULL
-#endif
-                    ,conf_get_str(ssh->conf, CONF_loghost),
-                    conf_get_bool(ssh->conf, CONF_change_password), // WINSCP
-                    ssh->seat
-                    );
-                ssh_connect_ppl(ssh, userauth_layer);
-                transport_child_layer = userauth_layer;
-
-                sfree(username);
-            }
-
-            ssh->base_layer = ssh2_transport_new(
-                ssh->conf, ssh->savedhost, ssh->savedport,
-                ssh->fullhostname,
-                ssh_verstring_get_local(old_bpp),
-                ssh_verstring_get_remote(old_bpp),
-#ifndef NO_GSSAPI
-                &ssh->gss_state,
-#else
-                NULL,
-#endif
-                &ssh->stats, transport_child_layer, NULL);
-            ssh_connect_ppl(ssh, ssh->base_layer);
-
-            if (userauth_layer)
-                ssh2_userauth_set_transport_layer(userauth_layer,
-                                                  ssh->base_layer);
-
-        } // WINSCP
-
-    } else {
-        ssh->bpp = ssh2_bare_bpp_new(ssh->logctx);
-        ssh_connect_bpp(ssh);
-
-        connection_layer = ssh2_connection_new(
-            ssh, ssh->connshare, false, ssh->conf,
-            ssh_verstring_get_remote(old_bpp), &ssh->cl);
-        ssh_connect_ppl(ssh, connection_layer);
-        ssh->base_layer = connection_layer;
-    }
-
-    /* Connect the base layer - whichever it is - to the BPP, and set
-     * up its selfptr. */
-    ssh->base_layer->selfptr = &ssh->base_layer;
-    ssh_ppl_setup_queues(ssh->base_layer, &ssh->bpp->in_pq, &ssh->bpp->out_pq);
-
-    seat_update_specials_menu(ssh->seat);
-    ssh->pinger = pinger_new(ssh->conf, &ssh->backend);
-
-    queue_idempotent_callback(&ssh->bpp->ic_in_raw);
-    ssh_ppl_process_queue(ssh->base_layer);
-
-    /* Pass in the initial terminal size, if we knew it already. */
-    ssh_terminal_size(ssh->cl, ssh->term_width, ssh->term_height);
-
-    ssh_bpp_free(old_bpp);
-}
-
-void ssh_check_frozen(Ssh *ssh)
-{
-    if (!ssh->s)
-        return;
-
-    { // WINSCP
-    bool prev_frozen = ssh->socket_frozen;
-    ssh->socket_frozen = (ssh->logically_frozen ||
-                          bufchain_size(&ssh->in_raw) > SSH_MAX_BACKLOG);
-    sk_set_frozen(ssh->s, ssh->socket_frozen);
-    if (prev_frozen && !ssh->socket_frozen && ssh->bpp) {
-        /*
-         * If we've just unfrozen, process any SSH connection data
-         * that was stashed in our queue while we were frozen.
-         */
-        queue_idempotent_callback(&ssh->bpp->ic_in_raw);
-    }
-    } // WINSCP
-}
-
-void ssh_conn_processed_data(Ssh *ssh)
-{
-    ssh_check_frozen(ssh);
-}
-
-static void ssh_bpp_output_raw_data_callback(void *vctx)
-{
-    Ssh *ssh = (Ssh *)vctx;
-
-    if (!ssh->s)
-        return;
-
-    while (bufchain_size(&ssh->out_raw) > 0) {
-        size_t backlog;
-
-        ptrlen data = bufchain_prefix(&ssh->out_raw);
-
-        if (ssh->logctx)
-            log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data.ptr, data.len,
-                       0, NULL, NULL, 0, NULL);
-        backlog = sk_write(ssh->s, data.ptr, data.len);
-
-        bufchain_consume(&ssh->out_raw, data.len);
-
-        if (backlog > SSH_MAX_BACKLOG) {
-            ssh_throttle_all(ssh, true, backlog);
-            return;
-        }
-    }
-
-    ssh_check_frozen(ssh);
-
-    if (ssh->pending_close) {
-        sk_close(ssh->s);
-        ssh->s = NULL;
-    }
-}
-
-static void ssh_shutdown_internal(Ssh *ssh)
-{
-    expire_timer_context(ssh);
-
-    if (ssh->connshare) {
-        sharestate_free(ssh->connshare);
-        ssh->connshare = NULL;
-    }
-
-    if (ssh->pinger) {
-        pinger_free(ssh->pinger);
-        ssh->pinger = NULL;
-    }
-
-    /*
-     * We only need to free the base PPL, which will free the others
-     * (if any) transitively.
-     */
-    if (ssh->base_layer) {
-        ssh_ppl_free(ssh->base_layer);
-        ssh->base_layer = NULL;
-    }
-
-    ssh->cl = NULL;
-}
-
-static void ssh_shutdown(Ssh *ssh)
-{
-    ssh_shutdown_internal(ssh);
-
-    if (ssh->bpp) {
-        ssh_bpp_free(ssh->bpp);
-        ssh->bpp = NULL;
-    }
-
-    if (ssh->s) {
-        sk_close(ssh->s);
-        ssh->s = NULL;
-    }
-
-    bufchain_clear(&ssh->in_raw);
-    bufchain_clear(&ssh->out_raw);
-    bufchain_clear(&ssh->user_input);
-}
-
-static void ssh_initiate_connection_close(Ssh *ssh)
-{
-    /* Wind up everything above the BPP. */
-    ssh_shutdown_internal(ssh);
-
-    /* Force any remaining queued SSH packets through the BPP, and
-     * schedule closing the network socket after they go out. */
-    ssh_bpp_handle_output(ssh->bpp);
-    ssh->pending_close = true;
-    queue_idempotent_callback(&ssh->ic_out_raw);
-
-    /* Now we expect the other end to close the connection too in
-     * response, so arrange that we'll receive notification of that
-     * via ssh_remote_eof. */
-    ssh->bpp->expect_close = true;
-}
-
-#define GET_FORMATTED_MSG                       \
-    char *msg;                                  \
-    va_list ap;                                 \
-    va_start(ap, fmt);                          \
-    msg = dupvprintf(fmt, ap);                  \
-    va_end(ap);                                 \
-    ((void)0) /* eat trailing semicolon */
-
-void ssh_remote_error(Ssh *ssh, const char *fmt, ...)
-{
-    if (ssh->base_layer || !ssh->session_started) {
-        GET_FORMATTED_MSG;
-
-        /* Error messages sent by the remote don't count as clean exits */
-        ssh->exitcode = 128;
-
-        /* Close the socket immediately, since the server has already
-         * closed its end (or is about to). */
-        ssh_shutdown(ssh);
-
-        logevent(ssh->logctx, msg);
-        seat_connection_fatal(ssh->seat, "%s", msg);
-        sfree(msg);
-    }
-}
-
-void ssh_remote_eof(Ssh *ssh, const char *fmt, ...)
-{
-    if (ssh->base_layer || !ssh->session_started) {
-        GET_FORMATTED_MSG;
-
-        /* EOF from the remote, if we were expecting it, does count as
-         * a clean exit */
-        ssh->exitcode = 0;
-
-        /* Close the socket immediately, since the server has already
-         * closed its end. */
-        ssh_shutdown(ssh);
-
-        logevent(ssh->logctx, msg);
-        sfree(msg);
-        seat_notify_remote_exit(ssh->seat);
-    } else {
-        /* This is responding to EOF after we've already seen some
-         * other reason for terminating the session. */
-        ssh_shutdown(ssh);
-    }
-}
-
-void ssh_proto_error(Ssh *ssh, const char *fmt, ...)
-{
-    if (ssh->base_layer || !ssh->session_started) {
-        GET_FORMATTED_MSG;
-
-        ssh->exitcode = 128;
-
-        ssh_bpp_queue_disconnect(ssh->bpp, msg,
-                                 SSH2_DISCONNECT_PROTOCOL_ERROR);
-        ssh_initiate_connection_close(ssh);
-
-        logevent(ssh->logctx, msg);
-        seat_connection_fatal(ssh->seat, "%s", msg);
-        sfree(msg);
-    }
-}
-
-void ssh_sw_abort(Ssh *ssh, const char *fmt, ...)
-{
-    if (ssh->base_layer || !ssh->session_started) {
-        GET_FORMATTED_MSG;
-
-        ssh->exitcode = 128;
-
-        ssh_initiate_connection_close(ssh);
-
-        logevent(ssh->logctx, msg);
-        seat_connection_fatal(ssh->seat, "%s", msg);
-        sfree(msg);
-
-        seat_notify_remote_exit(ssh->seat);
-    }
-}
-
-void ssh_user_close(Ssh *ssh, const char *fmt, ...)
-{
-    if (ssh->base_layer || !ssh->session_started) {
-        GET_FORMATTED_MSG;
-
-        /* Closing the connection due to user action, even if the
-         * action is the user aborting during authentication prompts,
-         * does count as a clean exit - except that this is also how
-         * we signal ordinary session termination, in which case we
-         * should use the exit status already sent from the main
-         * session (if any). */
-        if (ssh->exitcode < 0)
-            ssh->exitcode = 0;
-
-        ssh_initiate_connection_close(ssh);
-
-        logevent(ssh->logctx, msg);
-        sfree(msg);
-
-        seat_notify_remote_exit(ssh->seat);
-    }
-}
-
-static void ssh_deferred_abort_callback(void *vctx)
-{
-    Ssh *ssh = (Ssh *)vctx;
-    char *msg = ssh->deferred_abort_message;
-    ssh->deferred_abort_message = NULL;
-    ssh_sw_abort(ssh, "%s", msg);
-    sfree(msg);
-}
-
-void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...)
-{
-    if (!ssh->deferred_abort_message) {
-        GET_FORMATTED_MSG;
-        ssh->deferred_abort_message = msg;
-        queue_toplevel_callback(get_seat_callback_set(ssh->seat), ssh_deferred_abort_callback, ssh);
-    }
-}
-
-static void ssh_socket_log(Plug *plug, PlugLogType type, SockAddr *addr,
-                           int port, const char *error_msg, int error_code)
-{
-    Ssh *ssh = container_of(plug, Ssh, plug);
-
-    /*
-     * While we're attempting connection sharing, don't loudly log
-     * everything that happens. Real TCP connections need to be logged
-     * when we _start_ trying to connect, because it might be ages
-     * before they respond if something goes wrong; but connection
-     * sharing is local and quick to respond, and it's sufficient to
-     * simply wait and see whether it worked afterwards.
-     */
-
-    if (!ssh->attempting_connshare)
-        backend_socket_log(ssh->seat, ssh->logctx, type, addr, port,
-                           error_msg, error_code, ssh->conf,
-                           ssh->session_started);
-}
-
-static void ssh_closing(Plug *plug, const char *error_msg, int error_code,
-                        bool calling_back)
-{
-    Ssh *ssh = container_of(plug, Ssh, plug);
-    if (error_msg) {
-        ssh_remote_error(ssh, "%s", error_msg);
-    } else if (ssh->bpp) {
-        ssh->bpp->input_eof = true;
-        queue_idempotent_callback(&ssh->bpp->ic_in_raw);
-    }
-}
-
-static void ssh_receive(Plug *plug, int urgent, const char *data, size_t len)
-{
-    Ssh *ssh = container_of(plug, Ssh, plug);
-
-    /* Log raw data, if we're in that mode. */
-    if (ssh->logctx)
-        log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, len,
-                   0, NULL, NULL, 0, NULL);
-
-    bufchain_add(&ssh->in_raw, data, len);
-    if (!ssh->logically_frozen && ssh->bpp)
-        queue_idempotent_callback(&ssh->bpp->ic_in_raw);
-
-    ssh_check_frozen(ssh);
-}
-
-static void ssh_sent(Plug *plug, size_t bufsize)
-{
-    Ssh *ssh = container_of(plug, Ssh, plug);
-    /*
-     * If the send backlog on the SSH socket itself clears, we should
-     * unthrottle the whole world if it was throttled. Also trigger an
-     * extra call to the consumer of the BPP's output, to try to send
-     * some more data off its bufchain.
-     */
-    if (bufsize < SSH_MAX_BACKLOG) {
-        ssh_throttle_all(ssh, false, bufsize);
-        queue_idempotent_callback(&ssh->ic_out_raw);
-    }
-}
-
-static void ssh_hostport_setup(const char *host, int port, Conf *conf,
-                               char **savedhost, int *savedport,
-                               char **loghost_ret)
-{
-    char *loghost = conf_get_str(conf, CONF_loghost);
-    if (loghost_ret)
-        *loghost_ret = loghost;
-
-    if (*loghost) {
-        char *tmphost;
-        char *colon;
-
-        tmphost = dupstr(loghost);
-        *savedport = 22;               /* default ssh port */
-
-        /*
-         * A colon suffix on the hostname string also lets us affect
-         * savedport. (Unless there are multiple colons, in which case
-         * we assume this is an unbracketed IPv6 literal.)
-         */
-        colon = host_strrchr(tmphost, ':');
-        if (colon && colon == host_strchr(tmphost, ':')) {
-            *colon++ = '\0';
-            if (*colon)
-                *savedport = atoi(colon);
-        }
-
-        *savedhost = host_strduptrim(tmphost);
-        sfree(tmphost);
-    } else {
-        *savedhost = host_strduptrim(host);
-        if (port < 0)
-            port = 22;                 /* default ssh port */
-        *savedport = port;
-    }
-}
-
-static bool ssh_test_for_upstream(const char *host, int port, Conf *conf)
-{
-    char *savedhost;
-    int savedport;
-    bool ret;
-
-    random_ref(); /* platform may need this to determine share socket name */
-    ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL);
-    ret = ssh_share_test_for_upstream(savedhost, savedport, conf);
-    sfree(savedhost);
-    random_unref();
-
-    return ret;
-}
-
-static char *ssh_close_warn_text(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    if (!ssh->connshare)
-        return NULL;
-    { // WINSCP
-    int ndowns = share_ndownstreams(ssh->connshare);
-    if (ndowns == 0)
-        return NULL;
-    { // WINSCP
-    char *msg = dupprintf("This will also close %d downstream connection%s.",
-                          ndowns, ndowns==1 ? "" : "s");
-    return msg;
-    } // WINSCP
-    } // WINSCP
-}
-
-static const PlugVtable Ssh_plugvt = {
-    // WINSCP
-    /*.log =*/ ssh_socket_log,
-    /*.closing =*/ ssh_closing,
-    /*.receive =*/ ssh_receive,
-    /*.sent =*/ ssh_sent,
-    // NULL
-};
-
-/*
- * Connect to specified host and port.
- * Returns an error message, or NULL on success.
- * Also places the canonical host name into `realhost'. It must be
- * freed by the caller.
- */
-static char *connect_to_host(
-    Ssh *ssh, const char *host, int port, char **realhost,
-    bool nodelay, bool keepalive)
-{
-    SockAddr *addr;
-    const char *err;
-    char *loghost;
-    int addressfamily;
-
-    ssh_hostport_setup(host, port, ssh->conf,
-                       &ssh->savedhost, &ssh->savedport, &loghost);
-
-    ssh->plug.vt = &Ssh_plugvt;
-    
-    #ifdef MPEXT
-    // make sure the field is initialized, in case lookup below fails
-    ssh->fullhostname = NULL;
-    #endif
-
-    /*
-     * Try connection-sharing, in case that means we don't open a
-     * socket after all. ssh_connection_sharing_init will connect to a
-     * previously established upstream if it can, and failing that,
-     * establish a listening socket for _us_ to be the upstream. In
-     * the latter case it will return NULL just as if it had done
-     * nothing, because here we only need to care if we're a
-     * downstream and need to do our connection setup differently.
-     */
-    ssh->connshare = NULL;
-    ssh->attempting_connshare = true;  /* affects socket logging behaviour */
-    ssh->s = ssh_connection_sharing_init(
-        ssh->savedhost, ssh->savedport, ssh->conf, ssh->logctx,
-        &ssh->plug, &ssh->connshare);
-    if (ssh->connshare)
-        ssh_connshare_provide_connlayer(ssh->connshare, &ssh->cl_dummy);
-    ssh->attempting_connshare = false;
-    if (ssh->s != NULL) {
-        /*
-         * We are a downstream.
-         */
-        ssh->bare_connection = true;
-        ssh->fullhostname = NULL;
-        *realhost = dupstr(host);      /* best we can do */
-
-        if (seat_verbose(ssh->seat) || seat_interactive(ssh->seat)) {
-            /* In an interactive session, or in verbose mode, announce
-             * in the console window that we're a sharing downstream,
-             * to avoid confusing users as to why this session doesn't
-             * behave in quite the usual way. */
-            const char *msg =
-                "Reusing a shared connection to this server.\r\n";
-            seat_stderr_pl(ssh->seat, ptrlen_from_asciz(msg));
-        }
-    } else {
-        /*
-         * We're not a downstream, so open a normal socket.
-         */
-
-        /*
-         * Try to find host.
-         */
-        addressfamily = conf_get_int(ssh->conf, CONF_addressfamily);
-        addr = name_lookup(host, port, realhost, ssh->conf, addressfamily,
-                           ssh->logctx, "SSH connection");
-        if ((err = sk_addr_error(addr)) != NULL) {
-            sk_addr_free(addr);
-            return dupstr(err);
-        }
-        ssh->fullhostname = dupstr(*realhost);   /* save in case of GSSAPI */
-
-        ssh->s = new_connection(addr, *realhost, port,
-                                false, true, nodelay, keepalive,
-                                &ssh->plug, ssh->conf);
-        if ((err = sk_socket_error(ssh->s)) != NULL) {
-            ssh->s = NULL;
-            seat_notify_remote_exit(ssh->seat);
-            return dupstr(err);
-        }
-    }
-
-    /*
-     * The SSH version number is always fixed (since we no longer support
-     * fallback between versions), so set it now.
-     */
-        /* SSH-2 only */
-        ssh->version = 2; // WINSCP
-
-    /*
-     * Set up the initial BPP that will do the version string
-     * exchange, and get it started so that it can send the outgoing
-     * version string early if it wants to.
-     */
-    ssh->version_receiver.got_ssh_version = ssh_got_ssh_version;
-    ssh->bpp = ssh_verstring_new(
-        ssh->conf, ssh->logctx, ssh->bare_connection,
-        ssh->version == 1 ? "1.5" : "2.0", &ssh->version_receiver,
-        false, ""); // WINSCP (impl_name provided in sshver already)
-    ssh_connect_bpp(ssh);
-    queue_idempotent_callback(&ssh->bpp->ic_in_raw);
-
-    /*
-     * loghost, if configured, overrides realhost.
-     */
-    if (*loghost) {
-        sfree(*realhost);
-        *realhost = dupstr(loghost);
-    }
-
-    return NULL;
-}
-
-/*
- * Throttle or unthrottle the SSH connection.
- */
-void ssh_throttle_conn(Ssh *ssh, int adjust)
-{
-    int old_count = ssh->conn_throttle_count;
-    bool frozen;
-
-    ssh->conn_throttle_count += adjust;
-    assert(ssh->conn_throttle_count >= 0);
-
-    if (ssh->conn_throttle_count && !old_count) {
-        frozen = true;
-    } else if (!ssh->conn_throttle_count && old_count) {
-        frozen = false;
-    } else {
-        return;                /* don't change current frozen state */
-    }
-
-    ssh->logically_frozen = frozen;
-    ssh_check_frozen(ssh);
-}
-
-/*
- * Throttle or unthrottle _all_ local data streams (for when sends
- * on the SSH connection itself back up).
- */
-static void ssh_throttle_all(Ssh *ssh, bool enable, size_t bufsize)
-{
-    if (enable == ssh->throttled_all)
-        return;
-    ssh->throttled_all = enable;
-    ssh->overall_bufsize = bufsize;
-
-    ssh_throttle_all_channels(ssh->cl, enable);
-}
-
-static void ssh_cache_conf_values(Ssh *ssh)
-{
-    ssh->pls.omit_passwords = conf_get_bool(ssh->conf, CONF_logomitpass);
-    ssh->pls.omit_data = conf_get_bool(ssh->conf, CONF_logomitdata);
-}
-
-bool ssh_is_bare(Ssh *ssh)
-{
-    return ssh->backend.vt->protocol == PROT_SSHCONN;
-}
-
-/* Dummy connlayer must provide ssh_sharing_no_more_downstreams,
- * because it might be called early due to plink -shareexists */
-static void dummy_sharing_no_more_downstreams(ConnectionLayer *cl) {}
-static const ConnectionLayerVtable dummy_connlayer_vtable = {
-    // WINSCP
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    /*.sharing_no_more_downstreams =*/ dummy_sharing_no_more_downstreams,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
-/*
- * Called to set up the connection.
- *
- * Returns an error message, or NULL on success.
- */
-static char *ssh_init(const BackendVtable *vt, Seat *seat,
-                      Backend **backend_handle, LogContext *logctx,
-                      Conf *conf, const char *host, int port,
-                      char **realhost, bool nodelay, bool keepalive)
-{
-    Ssh *ssh;
-
-    ssh = snew(Ssh);
-    memset(ssh, 0, sizeof(Ssh));
-
-    ssh->conf = conf_copy(conf);
-    ssh_cache_conf_values(ssh);
-    ssh->exitcode = -1;
-    ssh->pls.kctx = SSH2_PKTCTX_NOKEX;
-    ssh->pls.actx = SSH2_PKTCTX_NOAUTH;
-    bufchain_init(&ssh->in_raw);
-    bufchain_init(&ssh->out_raw);
-    bufchain_init(&ssh->user_input);
-    ssh->ic_out_raw.fn = ssh_bpp_output_raw_data_callback;
-    ssh->ic_out_raw.ctx = ssh;
-    ssh->ic_out_raw.set = get_seat_callback_set(seat);
-
-    ssh->term_width = conf_get_int(ssh->conf, CONF_width);
-    ssh->term_height = conf_get_int(ssh->conf, CONF_height);
-
-    ssh->backend.vt = vt;
-    *backend_handle = &ssh->backend;
-
-    ssh->bare_connection = (vt->protocol == PROT_SSHCONN);
-
-    ssh->seat = seat;
-    ssh->cl_dummy.vt = &dummy_connlayer_vtable;
-    ssh->cl_dummy.logctx = ssh->logctx = logctx;
-
-    random_ref(); /* do this now - may be needed by sharing setup code */
-    ssh->need_random_unref = true;
-
-    { // WINSCP
-    char *conn_err = connect_to_host(
-        ssh, host, port, realhost, nodelay, keepalive);
-    if (conn_err) {
-        /* Call random_unref now instead of waiting until the caller
-         * frees this useless Ssh object, in case the caller is
-         * impatient and just exits without bothering, in which case
-         * the random seed won't be re-saved. */
-        ssh->need_random_unref = false;
-        random_unref();
-        return conn_err;
-    }
-    } // WINSCP
-
-    return NULL;
-}
-
-static void ssh_free(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    bool need_random_unref;
-
-    ssh_shutdown(ssh);
-
-    conf_free(ssh->conf);
-    if (ssh->connshare)
-        sharestate_free(ssh->connshare);
-    sfree(ssh->savedhost);
-    sfree(ssh->fullhostname);
-    sfree(ssh->specials);
-
-#ifndef NO_GSSAPI
-    if (ssh->gss_state.srv_name)
-        ssh->gss_state.lib->release_name(
-            ssh->gss_state.lib, &ssh->gss_state.srv_name);
-    if (ssh->gss_state.ctx != NULL)
-        ssh->gss_state.lib->release_cred(
-            ssh->gss_state.lib, &ssh->gss_state.ctx);
-    if (ssh->gss_state.libs)
-        ssh_gss_cleanup(ssh->gss_state.libs);
-#endif
-
-    sfree(ssh->deferred_abort_message);
-
-    delete_callbacks_for_context(get_seat_callback_set(ssh->seat), ssh); /* likely to catch ic_out_raw */ // WINSCP (seat)
-
-    need_random_unref = ssh->need_random_unref;
-    sfree(ssh);
-
-    if (need_random_unref)
-        random_unref();
-}
-
-/*
- * Reconfigure the SSH backend.
- */
-static void ssh_reconfig(Backend *be, Conf *conf)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-
-    if (ssh->pinger)
-        pinger_reconfig(ssh->pinger, ssh->conf, conf);
-
-    ssh_ppl_reconfigure(ssh->base_layer, conf);
-
-    conf_free(ssh->conf);
-    ssh->conf = conf_copy(conf);
-    ssh_cache_conf_values(ssh);
-}
-
-/*
- * Called to send data down the SSH connection.
- */
-static size_t ssh_send(Backend *be, const char *buf, size_t len)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-
-    if (ssh == NULL || ssh->s == NULL)
-        return 0;
-
-    bufchain_add(&ssh->user_input, buf, len);
-    if (ssh->base_layer)
-        ssh_ppl_got_user_input(ssh->base_layer);
-
-    return backend_sendbuffer(&ssh->backend);
-}
-
-/*
- * Called to query the current amount of buffered stdin data.
- */
-static size_t ssh_sendbuffer(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    size_t backlog;
-
-    if (!ssh || !ssh->s || !ssh->cl)
-        return 0;
-
-    backlog = ssh_stdin_backlog(ssh->cl);
-
-    #ifndef WINSCP
-    // This throttles WinSCP unnecessarily, as it uses its own throttling mechanism.
-    // This value is used only by WinSCP, never directly by PuTTY code.
-    if (ssh->base_layer)
-        backlog += ssh_ppl_queued_data_size(ssh->base_layer);
-    #endif
-
-    /*
-     * If the SSH socket itself has backed up, add the total backup
-     * size on that to any individual buffer on the stdin channel.
-     */
-    if (ssh->throttled_all)
-        backlog += ssh->overall_bufsize;
-
-    return backlog;
-}
-
-/*
- * Called to set the size of the window from SSH's POV.
- */
-static void ssh_size(Backend *be, int width, int height)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-
-    ssh->term_width = width;
-    ssh->term_height = height;
-    if (ssh->cl)
-        ssh_terminal_size(ssh->cl, ssh->term_width, ssh->term_height);
-}
-
-struct ssh_add_special_ctx {
-    SessionSpecial *specials;
-    size_t nspecials, specials_size;
-};
-
-static void ssh_add_special(void *vctx, const char *text,
-                            SessionSpecialCode code, int arg)
-{
-    struct ssh_add_special_ctx *ctx = (struct ssh_add_special_ctx *)vctx;
-    SessionSpecial *spec;
-
-    sgrowarray(ctx->specials, ctx->specials_size, ctx->nspecials);
-    spec = &ctx->specials[ctx->nspecials++];
-    spec->name = text;
-    spec->code = code;
-    spec->arg = arg;
-}
-
-/*
- * Return a list of the special codes that make sense in this
- * protocol.
- */
-static const SessionSpecial *ssh_get_specials(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-
-    /*
-     * Ask all our active protocol layers what specials they've got,
-     * and amalgamate the list into one combined one.
-     */
-
-    struct ssh_add_special_ctx ctx[1];
-
-    ctx->specials = NULL;
-    ctx->nspecials = ctx->specials_size = 0;
-
-    if (ssh->base_layer)
-        ssh_ppl_get_specials(ssh->base_layer, ssh_add_special, ctx);
-
-    if (ctx->specials) {
-        /* If the list is non-empty, terminate it with a SS_EXITMENU. */
-        ssh_add_special(ctx, NULL, SS_EXITMENU, 0);
-    }
-
-    sfree(ssh->specials);
-    ssh->specials = ctx->specials;
-    return ssh->specials;
-}
-
-/*
- * Send special codes.
- */
-static void ssh_special(Backend *be, SessionSpecialCode code, int arg)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-
-    if (ssh->base_layer)
-        ssh_ppl_special_cmd(ssh->base_layer, code, arg);
-}
-
-/*
- * This is called when the seat's output channel manages to clear some
- * backlog.
- */
-static void ssh_unthrottle(Backend *be, size_t bufsize)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-
-    if (ssh->cl)
-        ssh_stdout_unthrottle(ssh->cl, bufsize);
-}
-
-static bool ssh_connected(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    return ssh->s != NULL;
-}
-
-static bool ssh_sendok(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    return ssh->base_layer && ssh_ppl_want_user_input(ssh->base_layer);
-}
-
-void ssh_ldisc_update(Ssh *ssh)
-{
-    /* Called when the connection layer wants to propagate an update
-     * to the line discipline options */
-    if (ssh->ldisc)
-        ldisc_echoedit_update(ssh->ldisc);
-}
-
-static bool ssh_ldisc(Backend *be, int option)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    return ssh->cl ? ssh_ldisc_option(ssh->cl, option) : false;
-}
-
-static void ssh_provide_ldisc(Backend *be, Ldisc *ldisc)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    ssh->ldisc = ldisc;
-}
-
-void ssh_got_exitcode(Ssh *ssh, int exitcode)
-{
-    ssh->exitcode = exitcode;
-}
-
-static int ssh_return_exitcode(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    if (ssh->s && (!ssh->session_started || ssh->base_layer))
-        return -1;
-    else
-        return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX);
-}
-
-/*
- * cfg_info for SSH is the protocol running in this session.
- * (1 or 2 for the full SSH-1 or SSH-2 protocol; -1 for the bare
- * SSH-2 connection protocol, i.e. a downstream; 0 for not-decided-yet.)
- */
-static int ssh_cfg_info(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    if (ssh->version == 0)
-        return 0; /* don't know yet */
-    else if (ssh->bare_connection)
-        return -1;
-    else
-        return ssh->version;
-}
-
-/*
- * Gross hack: pscp will try to start SFTP but fall back to scp1 if
- * that fails. This variable is the means by which scp.c can reach
- * into the SSH code and find out which one it got.
- */
-extern bool ssh_fallback_cmd(Backend *be)
-{
-    Ssh *ssh = container_of(be, Ssh, backend);
-    return ssh->fallback_cmd;
-}
-
-void ssh_got_fallback_cmd(Ssh *ssh)
-{
-    ssh->fallback_cmd = true;
-}
-
-const BackendVtable ssh_backend = {
-    // WINSCP
-    /*.init =*/ ssh_init,
-    /*.free =*/ ssh_free,
-    /*.reconfig =*/ ssh_reconfig,
-    /*.send =*/ ssh_send,
-    /*.sendbuffer =*/ ssh_sendbuffer,
-    /*.size =*/ ssh_size,
-    /*.special =*/ ssh_special,
-    /*.get_specials =*/ ssh_get_specials,
-    /*.connected =*/ ssh_connected,
-    /*.exitcode =*/ ssh_return_exitcode,
-    /*.sendok =*/ ssh_sendok,
-    /*.ldisc_option_state =*/ ssh_ldisc,
-    /*.provide_ldisc =*/ ssh_provide_ldisc,
-    /*.unthrottle =*/ ssh_unthrottle,
-    /*.cfg_info =*/ ssh_cfg_info,
-    /*.test_for_upstream =*/ ssh_test_for_upstream,
-    /*.close_warn_text =*/ ssh_close_warn_text,
-    /*.id =*/ "ssh",
-    /*.displayname =*/ "SSH",
-    /*.protocol =*/ PROT_SSH,
-    /*.default_port =*/ 22,
-    0, 0, 0, // WINSCP
-};
-
-const BackendVtable sshconn_backend = {
-    // WINSCP
-    /*.init =*/ ssh_init,
-    /*.free =*/ ssh_free,
-    /*.reconfig =*/ ssh_reconfig,
-    /*.send =*/ ssh_send,
-    /*.sendbuffer =*/ ssh_sendbuffer,
-    /*.size =*/ ssh_size,
-    /*.special =*/ ssh_special,
-    /*.get_specials =*/ ssh_get_specials,
-    /*.connected =*/ ssh_connected,
-    /*.exitcode =*/ ssh_return_exitcode,
-    /*.sendok =*/ ssh_sendok,
-    /*.ldisc_option_state =*/ ssh_ldisc,
-    /*.provide_ldisc =*/ ssh_provide_ldisc,
-    /*.unthrottle =*/ ssh_unthrottle,
-    /*.cfg_info =*/ ssh_cfg_info,
-    /*.test_for_upstream =*/ ssh_test_for_upstream,
-    /*.close_warn_text =*/ ssh_close_warn_text,
-    /*.id =*/ "ssh-connection",
-    /*.displayname =*/ "Bare ssh-connection",
-    /*.protocol =*/ PROT_SSHCONN,
-    0, 0, 0, 0, // WINSCP
-};
-
-#ifdef MPEXT
-
-#include "puttyexp.h"
-
-int is_ssh(Plug * plug)
-{
-  return plug->vt->closing == ssh_closing;
-}
-
-int get_ssh_version(Backend * be)
-{
-  Ssh * ssh = container_of(be, Ssh, backend);
-  return ssh->version;
-}
-
-Seat * get_ssh_seat(Plug * plug)
-{
-  return container_of(plug, Ssh, plug)->seat;
-}
-
-const ssh_cipher * get_cscipher(Backend * be)
-{
-  Ssh * ssh = container_of(be, Ssh, backend);
-  return ssh2_bpp_get_cscipher(ssh->bpp);
-}
-
-const ssh_cipher * get_sccipher(Backend * be)
-{
-  Ssh * ssh = container_of(be, Ssh, backend);
-  return ssh2_bpp_get_sccipher(ssh->bpp);
-}
-
-const struct ssh_compressor * get_cscomp(Backend * be)
-{
-  Ssh * ssh = container_of(be, Ssh, backend);
-  return ssh2_bpp_get_cscomp(ssh->bpp);
-}
-
-const struct ssh_decompressor * get_sccomp(Backend * be)
-{
-  Ssh * ssh = container_of(be, Ssh, backend);
-  return ssh2_bpp_get_sccomp(ssh->bpp);
-}
-
-unsigned int winscp_query(Backend * be, int query)
-{
-  Ssh * ssh = container_of(be, Ssh, backend);
-  if ((ssh->base_layer != NULL) && (ssh->base_layer->vt->winscp_query != NULL))
-  {
-    return ssh_ppl_winscp_query(ssh->base_layer, query);
-  }
-  else
-  {
-    return 0;
-  }
-}
-
-void md5checksum(const char * buffer, int len, unsigned char output[16])
-{
-  hash_simple(&ssh_md5, make_ptrlen(buffer, len), output);
-}
-#endif

+ 61 - 33
source/putty/ssh.h

@@ -305,9 +305,15 @@ struct ConnectionLayerVtable {
      * subsequent channel-opens). */
     void (*enable_x_fwd)(ConnectionLayer *cl);
 
-    /* Communicate to the connection layer whether the main session
-     * channel currently wants user input. */
+    /* Communicate / query whether the main session channel currently
+     * wants user input. The set function is called by mainchan; the
+     * query function is called by the top-level ssh.c. */
     void (*set_wants_user_input)(ConnectionLayer *cl, bool wanted);
+    bool (*get_wants_user_input)(ConnectionLayer *cl);
+
+    /* Notify the connection layer that more data has been added to
+     * the user input queue. */
+    void (*got_user_input)(ConnectionLayer *cl);
 };
 
 struct ConnectionLayer {
@@ -377,6 +383,10 @@ static inline void ssh_enable_x_fwd(ConnectionLayer *cl)
 { cl->vt->enable_x_fwd(cl); }
 static inline void ssh_set_wants_user_input(ConnectionLayer *cl, bool wanted)
 { cl->vt->set_wants_user_input(cl, wanted); }
+static inline bool ssh_get_wants_user_input(ConnectionLayer *cl)
+{ return cl->vt->get_wants_user_input(cl); }
+static inline void ssh_got_user_input(ConnectionLayer *cl)
+{ cl->vt->got_user_input(cl); }
 
 /* Exports from portfwd.c */
 PortFwdManager *portfwdmgr_new(ConnectionLayer *cl);
@@ -403,11 +413,13 @@ LogContext *ssh_get_logctx(Ssh *ssh);
 void ssh_throttle_conn(Ssh *ssh, int adjust);
 void ssh_got_exitcode(Ssh *ssh, int status);
 void ssh_ldisc_update(Ssh *ssh);
+void ssh_check_sendok(Ssh *ssh);
 void ssh_got_fallback_cmd(Ssh *ssh);
 bool ssh_is_bare(Ssh *ssh);
 
 /* Communications back to ssh.c from the BPP */
 void ssh_conn_processed_data(Ssh *ssh);
+void ssh_sendbuffer_changed(Ssh *ssh);
 void ssh_check_frozen(Ssh *ssh);
 
 /* Functions to abort the connection, for various reasons. */
@@ -417,6 +429,7 @@ void ssh_proto_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void ssh_user_close(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_spr_close(Ssh *ssh, SeatPromptResult spr, const char *context);
 
 /* Bit positions in the SSH-1 cipher protocol word */
 #define SSH1_CIPHER_IDEA        1
@@ -452,7 +465,7 @@ struct RSAKey {
     ssh_key sshk;
 };
 
-struct dss_key {
+struct dsa_key {
     mp_int *p, *q, *g, *y, *x;
     ssh_key sshk;
 };
@@ -622,7 +635,7 @@ mp_int *ssh_ecdhkex_getkey(ecdh_key *key, ptrlen remoteKey);
 /*
  * Helper function for k generation in DSA, reused in ECDSA
  */
-mp_int *dss_gen_k(const char *id_string,
+mp_int *dsa_gen_k(const char *id_string,
                      mp_int *modulus, mp_int *private_key,
                      unsigned char *digest, int digest_len);
 #endif
@@ -746,7 +759,7 @@ static inline const char *ssh2_mac_text_name(ssh2_mac *m)
 static inline const ssh2_macalg *ssh2_mac_alg(ssh2_mac *m)
 { return m->vt; }
 
-/* Centralised 'methods' for ssh2_mac, defined in sshmac.c. These run
+/* Centralised 'methods' for ssh2_mac, defined in mac.c. These run
  * the MAC in a specifically SSH-2 style, i.e. taking account of a
  * packet sequence number as well as the data to be authenticated. */
 bool ssh2_mac_verresult(ssh2_mac *, const void *);
@@ -983,22 +996,28 @@ extern const ssh_cipheralg ssh_3des_ssh2;
 extern const ssh_cipheralg ssh_des;
 extern const ssh_cipheralg ssh_des_sshcom_ssh2;
 extern const ssh_cipheralg ssh_aes256_sdctr;
-extern const ssh_cipheralg ssh_aes256_sdctr_hw;
+extern const ssh_cipheralg ssh_aes256_sdctr_ni;
+extern const ssh_cipheralg ssh_aes256_sdctr_neon;
 extern const ssh_cipheralg ssh_aes256_sdctr_sw;
 extern const ssh_cipheralg ssh_aes256_cbc;
-extern const ssh_cipheralg ssh_aes256_cbc_hw;
+extern const ssh_cipheralg ssh_aes256_cbc_ni;
+extern const ssh_cipheralg ssh_aes256_cbc_neon;
 extern const ssh_cipheralg ssh_aes256_cbc_sw;
 extern const ssh_cipheralg ssh_aes192_sdctr;
-extern const ssh_cipheralg ssh_aes192_sdctr_hw;
+extern const ssh_cipheralg ssh_aes192_sdctr_ni;
+extern const ssh_cipheralg ssh_aes192_sdctr_neon;
 extern const ssh_cipheralg ssh_aes192_sdctr_sw;
 extern const ssh_cipheralg ssh_aes192_cbc;
-extern const ssh_cipheralg ssh_aes192_cbc_hw;
+extern const ssh_cipheralg ssh_aes192_cbc_ni;
+extern const ssh_cipheralg ssh_aes192_cbc_neon;
 extern const ssh_cipheralg ssh_aes192_cbc_sw;
 extern const ssh_cipheralg ssh_aes128_sdctr;
-extern const ssh_cipheralg ssh_aes128_sdctr_hw;
+extern const ssh_cipheralg ssh_aes128_sdctr_ni;
+extern const ssh_cipheralg ssh_aes128_sdctr_neon;
 extern const ssh_cipheralg ssh_aes128_sdctr_sw;
 extern const ssh_cipheralg ssh_aes128_cbc;
-extern const ssh_cipheralg ssh_aes128_cbc_hw;
+extern const ssh_cipheralg ssh_aes128_cbc_ni;
+extern const ssh_cipheralg ssh_aes128_cbc_neon;
 extern const ssh_cipheralg ssh_aes128_cbc_sw;
 extern const ssh_cipheralg ssh_blowfish_ssh2_ctr;
 extern const ssh_cipheralg ssh_blowfish_ssh2;
@@ -1013,16 +1032,18 @@ extern const ssh2_ciphers ssh2_arcfour;
 extern const ssh2_ciphers ssh2_ccp;
 extern const ssh_hashalg ssh_md5;
 extern const ssh_hashalg ssh_sha1;
-extern const ssh_hashalg ssh_sha1_hw;
+extern const ssh_hashalg ssh_sha1_ni;
+extern const ssh_hashalg ssh_sha1_neon;
 extern const ssh_hashalg ssh_sha1_sw;
 extern const ssh_hashalg ssh_sha256;
-extern const ssh_hashalg ssh_sha256_hw;
+extern const ssh_hashalg ssh_sha256_ni;
+extern const ssh_hashalg ssh_sha256_neon;
 extern const ssh_hashalg ssh_sha256_sw;
 extern const ssh_hashalg ssh_sha384;
-extern const ssh_hashalg ssh_sha384_hw;
+extern const ssh_hashalg ssh_sha384_neon;
 extern const ssh_hashalg ssh_sha384_sw;
 extern const ssh_hashalg ssh_sha512;
-extern const ssh_hashalg ssh_sha512_hw;
+extern const ssh_hashalg ssh_sha512_neon;
 extern const ssh_hashalg ssh_sha512_sw;
 extern const ssh_hashalg ssh_sha3_224;
 extern const ssh_hashalg ssh_sha3_256;
@@ -1033,6 +1054,9 @@ extern const ssh_hashalg ssh_blake2b;
 extern const ssh_kexes ssh_diffiehellman_group1;
 extern const ssh_kexes ssh_diffiehellman_group14;
 extern const ssh_kexes ssh_diffiehellman_gex;
+extern const ssh_kex ssh_diffiehellman_group1_sha1;
+extern const ssh_kex ssh_diffiehellman_group14_sha256;
+extern const ssh_kex ssh_diffiehellman_group14_sha1;
 extern const ssh_kexes ssh_gssk5_sha1_kex;
 extern const ssh_kexes ssh_rsa_kex;
 extern const ssh_kex ssh_ec_kex_curve25519;
@@ -1041,7 +1065,7 @@ extern const ssh_kex ssh_ec_kex_nistp256;
 extern const ssh_kex ssh_ec_kex_nistp384;
 extern const ssh_kex ssh_ec_kex_nistp521;
 extern const ssh_kexes ssh_ecdh_kex;
-extern const ssh_keyalg ssh_dss;
+extern const ssh_keyalg ssh_dsa;
 extern const ssh_keyalg ssh_rsa;
 extern const ssh_keyalg ssh_rsa_sha256;
 extern const ssh_keyalg ssh_rsa_sha512;
@@ -1069,10 +1093,10 @@ ssh_hash *blake2b_new_general(unsigned hashlen);
  * itself. If so, then this function should be implemented in each
  * platform subdirectory.
  */
-bool platform_aes_hw_available(void);
-bool platform_sha256_hw_available(void);
-bool platform_sha1_hw_available(void);
-bool platform_sha512_hw_available(void);
+bool platform_aes_neon_available(void);
+bool platform_sha256_neon_available(void);
+bool platform_sha1_neon_available(void);
+bool platform_sha512_neon_available(void);
 
 /*
  * PuTTY version number formatted as an SSH version string.
@@ -1085,13 +1109,13 @@ extern
 
 /*
  * Gross hack: pscp will try to start SFTP but fall back to scp1 if
- * that fails. This variable is the means by which scp.c can reach
+ * that fails. This variable is the means by which pscp.c can reach
  * into the SSH code and find out which one it got.
  */
 extern bool ssh_fallback_cmd(Backend *backend);
 
 /*
- * The PRNG type, defined in sshprng.c. Visible data fields are
+ * The PRNG type, defined in prng.c. Visible data fields are
  * 'savesize', which suggests how many random bytes you should request
  * from a particular PRNG instance to write to putty.rnd, and a
  * BinarySink implementation which you can use to write seed data in
@@ -1100,7 +1124,7 @@ extern bool ssh_fallback_cmd(Backend *backend);
 struct prng {
     size_t savesize;
     BinarySink_IMPLEMENTATION;
-    /* (also there's a surrounding implementation struct in sshprng.c) */
+    /* (also there's a surrounding implementation struct in prng.c) */
 };
 prng *prng_new(const ssh_hashalg *hashalg);
 void prng_free(prng *p);
@@ -1171,10 +1195,6 @@ struct X11FakeAuth {
     ssh_sharing_connstate *share_cs;
     share_channel *share_chan;
 };
-void *x11_make_greeting(int endian, int protomajor, int protominor,
-                        int auth_proto, const void *auth_data, int auth_len,
-                        const char *peer_ip, int peer_port,
-                        int *outlen);
 int x11_authcmp(void *av, void *bv); /* for putting X11FakeAuth in a tree234 */
 /*
  * x11_setup_display() parses the display variable and fills in an
@@ -1204,7 +1224,7 @@ SockAddr *platform_get_x11_unix_address(const char *path, int displaynum);
     /* make up a SockAddr naming the address for displaynum */
 char *platform_get_x_display(void);
     /* allocated local X display string, if any */
-/* Callbacks in x11.c usable _by_ platform X11 functions */
+/* X11-related helper functions in utils */
 /*
  * This function does the job of platform_get_x11_auth, provided
  * it is told where to find a normally formatted .Xauthority file:
@@ -1223,8 +1243,13 @@ void x11_get_auth_from_authfile(struct X11Display *display,
 void x11_format_auth_for_authfile(
     BinarySink *bs, SockAddr *addr, int display_no,
     ptrlen authproto, ptrlen authdata);
+void *x11_make_greeting(int endian, int protomajor, int protominor,
+                        int auth_proto, const void *auth_data, int auth_len,
+                        const char *peer_ip, int peer_port,
+                        int *outlen);
 int x11_identify_auth_proto(ptrlen protoname);
 void *x11_dehexify(ptrlen hex, int *outlen);
+bool x11_parse_ip(const char *addr_string, unsigned long *ip);
 
 Channel *agentf_new(SshChannel *c, Plug *plug); // WINSCP
 
@@ -1233,7 +1258,7 @@ dh_ctx *dh_setup_group(const ssh_kex *kex);
 dh_ctx *dh_setup_gex(mp_int *pval, mp_int *gval);
 int dh_modulus_bit_size(const dh_ctx *ctx);
 void dh_cleanup(dh_ctx *);
-mp_int *dh_create_e(dh_ctx *, int nbits);
+mp_int *dh_create_e(dh_ctx *);
 const char *dh_validate_f(dh_ctx *, mp_int *f);
 mp_int *dh_find_K(dh_ctx *, mp_int *f);
 
@@ -1426,8 +1451,7 @@ void aes256_decrypt_pubkey(const void *key, const void *iv,
 void des_encrypt_xdmauth(const void *key, void *blk, int len);
 void des_decrypt_xdmauth(const void *key, void *blk, int len);
 
-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);
 
 /*
@@ -1640,7 +1664,7 @@ enum {
     /* TTY modes with opcodes defined consistently in the SSH specs. */
     #define TTYMODE_CHAR(name, val, index) SSH_TTYMODE_##name = val,
     #define TTYMODE_FLAG(name, val, field, mask) SSH_TTYMODE_##name = val,
-    #include "sshttymodes.h"
+    #include "ssh/ttymode-list.h"
     #undef TTYMODE_CHAR
     #undef TTYMODE_FLAG
 
@@ -1723,7 +1747,11 @@ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset);
 void add_to_commasep(strbuf *buf, const char *data);
 bool get_commasep_word(ptrlen *list, ptrlen *word);
 
-int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key);
+SeatPromptResult verify_ssh_host_key(
+    InteractionReadySeat iseat, Conf *conf, const char *host, int port,
+    ssh_key *key, const char *keytype, char *keystr, const char *keydisp,
+    char **fingerprints, void (*callback)(void *ctx, SeatPromptResult result),
+    void *ctx);
 
 typedef struct ssh_transient_hostkey_cache ssh_transient_hostkey_cache;
 ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void);

+ 0 - 205
source/putty/ssh2bpp-bare.c

@@ -1,205 +0,0 @@
-/*
- * Trivial binary packet protocol for the 'bare' ssh-connection
- * protocol used in PuTTY's SSH-2 connection sharing system.
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshcr.h"
-
-struct ssh2_bare_bpp_state {
-    int crState;
-    long packetlen, maxlen;
-    unsigned char *data;
-    unsigned long incoming_sequence, outgoing_sequence;
-    PktIn *pktin;
-
-    BinaryPacketProtocol bpp;
-};
-
-static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp);
-static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp);
-static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp);
-static PktOut *ssh2_bare_bpp_new_pktout(int type);
-
-static const BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
-    // WINSCP
-    /*.free =*/ ssh2_bare_bpp_free,
-    /*.handle_input =*/ ssh2_bare_bpp_handle_input,
-    /*.handle_output =*/ ssh2_bare_bpp_handle_output,
-    /*.new_pktout =*/ ssh2_bare_bpp_new_pktout,
-    /*.queue_disconnect =*/ ssh2_bpp_queue_disconnect, /* in sshcommon.c */
-
-    /* packet size limit, per protocol spec in sshshare.c comment */
-    /*.packet_size_limit =*/ 0x4000,
-};
-
-BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx)
-{
-    struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state);
-    memset(s, 0, sizeof(*s));
-    s->bpp.vt = &ssh2_bare_bpp_vtable;
-    s->bpp.logctx = logctx;
-    ssh_bpp_common_setup(&s->bpp);
-    return &s->bpp;
-}
-
-static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bare_bpp_state *s =
-        container_of(bpp, struct ssh2_bare_bpp_state, bpp);
-    sfree(s->pktin);
-    sfree(s);
-}
-
-#define BPP_READ(ptr, len) do                                           \
-    {                                                                   \
-        bool success;                                                   \
-        crMaybeWaitUntilV((success = bufchain_try_fetch_consume(        \
-                               s->bpp.in_raw, ptr, len)) ||             \
-                          s->bpp.input_eof);                            \
-        if (!success)                                                   \
-            goto eof;                                                   \
-        ssh_check_frozen(s->bpp.ssh);                                   \
-    } while (0)
-
-static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bare_bpp_state *s =
-        container_of(bpp, struct ssh2_bare_bpp_state, bpp);
-
-    crBegin(s->crState);
-
-    while (1) {
-        /* Read the length field. */
-        {
-            unsigned char lenbuf[4];
-            BPP_READ(lenbuf, 4);
-            s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf));
-        }
-
-        if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
-            ssh_sw_abort(s->bpp.ssh, "Invalid packet length received");
-            crStopV;
-        }
-
-        /*
-         * Allocate the packet to return, now we know its length.
-         */
-        s->pktin = snew_plus(PktIn, s->packetlen);
-        s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
-        s->pktin->qnode.on_free_queue = false;
-        s->maxlen = 0;
-        s->data = snew_plus_get_aux(s->pktin);
-
-        s->pktin->sequence = s->incoming_sequence++;
-
-        /*
-         * Read the remainder of the packet.
-         */
-        BPP_READ(s->data, s->packetlen);
-
-        /*
-         * The data we just read is precisely the initial type byte
-         * followed by the packet payload.
-         */
-        s->pktin->type = s->data[0];
-        s->data++;
-        s->packetlen--;
-        BinarySource_INIT(s->pktin, s->data, s->packetlen);
-
-        if (s->pktin->type == SSH2_MSG_EXT_INFO) {
-            /*
-             * Mild layer violation: EXT_INFO is not permitted in the
-             * bare ssh-connection protocol. Faulting it here means
-             * that ssh2_common_filter_queue doesn't receive it in the
-             * first place unless it's legal to have sent it.
-             */
-            ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO "
-                            "in bare connection protocol");
-            return;
-        }
-
-        /*
-         * Log incoming packet, possibly omitting sensitive fields.
-         */
-        if (s->bpp.logctx) {
-            logblank_t blanks[MAX_BLANKS];
-            int nblanks = ssh2_censor_packet(
-                s->bpp.pls, s->pktin->type, false,
-                make_ptrlen(s->data, s->packetlen), blanks);
-            log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
-                       ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
-                                     s->pktin->type),
-                       get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
-                       &s->pktin->sequence, 0, NULL);
-        }
-
-        if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) {
-            sfree(s->pktin);
-            s->pktin = NULL;
-            continue;
-        }
-
-        s->pktin->qnode.formal_size = get_avail(s->pktin);
-        pq_push(&s->bpp.in_pq, s->pktin);
-        s->pktin = NULL;
-    }
-
-  eof:
-    if (!s->bpp.expect_close) {
-        ssh_remote_error(s->bpp.ssh,
-                         "Remote side unexpectedly closed network connection");
-    } else {
-        ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
-    }
-    return;  /* avoid touching s now it's been freed */
-
-    crFinishV;
-}
-
-static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type)
-{
-    PktOut *pkt = ssh_new_packet();
-    pkt->length = 4; /* space for packet length */
-    pkt->type = pkt_type;
-    put_byte(pkt, pkt_type);
-    return pkt;
-}
-
-static void ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state *s,
-                                        PktOut *pkt)
-{
-    if (s->bpp.logctx) {
-        ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5);
-        logblank_t blanks[MAX_BLANKS];
-        int nblanks = ssh2_censor_packet(
-            s->bpp.pls, pkt->type, true, pktdata, blanks);
-        log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
-                   ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
-                                 pkt->type),
-                   pktdata.ptr, pktdata.len, nblanks, blanks,
-                   &s->outgoing_sequence,
-                   pkt->downstream_id, pkt->additional_log_text);
-    }
-
-    s->outgoing_sequence++;        /* only for diagnostics, really */
-
-    PUT_32BIT_MSB_FIRST(pkt->data, pkt->length - 4);
-    bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
-}
-
-static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bare_bpp_state *s =
-        container_of(bpp, struct ssh2_bare_bpp_state, bpp);
-    PktOut *pkt;
-
-    while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
-        ssh2_bare_bpp_format_packet(s, pkt);
-        ssh_free_pktout(pkt);
-    }
-}

+ 0 - 1009
source/putty/ssh2bpp.c

@@ -1,1009 +0,0 @@
-/*
- * Binary packet protocol for SSH-2.
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshcr.h"
-
-struct ssh2_bpp_direction {
-    unsigned long sequence;
-    ssh_cipher *cipher;
-    ssh2_mac *mac;
-    bool etm_mode;
-    const ssh_compression_alg *pending_compression;
-};
-
-struct ssh2_bpp_state {
-    int crState;
-    long len, pad, payload, packetlen, maclen, length, maxlen;
-    unsigned char *buf;
-    size_t bufsize;
-    unsigned char *data;
-    unsigned cipherblk;
-    PktIn *pktin;
-    struct DataTransferStats *stats;
-    bool cbc_ignore_workaround;
-
-    struct ssh2_bpp_direction in, out;
-    /* comp and decomp logically belong in the per-direction
-     * substructure, except that they have different types */
-    ssh_decompressor *in_decomp;
-    ssh_compressor *out_comp;
-
-    bool is_server;
-    bool pending_newkeys;
-    bool pending_compression, seen_userauth_success;
-    bool enforce_next_packet_is_userauth_success;
-    unsigned nnewkeys;
-    int prev_type;
-
-    BinaryPacketProtocol bpp;
-};
-
-static void ssh2_bpp_free(BinaryPacketProtocol *bpp);
-static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp);
-static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp);
-static PktOut *ssh2_bpp_new_pktout(int type);
-
-static const BinaryPacketProtocolVtable ssh2_bpp_vtable = {
-    // WINSCP
-    /*.free =*/ ssh2_bpp_free,
-    /*.handle_input =*/ ssh2_bpp_handle_input,
-    /*.handle_output =*/ ssh2_bpp_handle_output,
-    /*.new_pktout =*/ ssh2_bpp_new_pktout,
-    /*.queue_disconnect =*/ ssh2_bpp_queue_disconnect, /* in sshcommon.c */
-    /*.packet_size_limit =*/ 0xFFFFFFFF, /* no special limit for this bpp */
-};
-
-BinaryPacketProtocol *ssh2_bpp_new(
-    LogContext *logctx, struct DataTransferStats *stats, bool is_server)
-{
-    struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
-    memset(s, 0, sizeof(*s));
-    s->bpp.vt = &ssh2_bpp_vtable;
-    s->bpp.logctx = logctx;
-    s->stats = stats;
-    s->is_server = is_server;
-    ssh_bpp_common_setup(&s->bpp);
-    return &s->bpp;
-}
-
-static void ssh2_bpp_free_outgoing_crypto(struct ssh2_bpp_state *s)
-{
-    /*
-     * We must free the MAC before the cipher, because sometimes the
-     * MAC is not actually separately allocated but just a different
-     * facet of the same object as the cipher, in which case
-     * ssh2_mac_free does nothing and ssh_cipher_free does the actual
-     * freeing. So if we freed the cipher first and then tried to
-     * dereference the MAC's vtable pointer to find out how to free
-     * that too, we'd be accessing freed memory.
-     */
-    if (s->out.mac)
-        ssh2_mac_free(s->out.mac);
-    if (s->out.cipher)
-        ssh_cipher_free(s->out.cipher);
-    if (s->out_comp)
-        ssh_compressor_free(s->out_comp);
-}
-
-static void ssh2_bpp_free_incoming_crypto(struct ssh2_bpp_state *s)
-{
-    /* As above, take care to free in.mac before in.cipher */
-    if (s->in.mac)
-        ssh2_mac_free(s->in.mac);
-    if (s->in.cipher)
-        ssh_cipher_free(s->in.cipher);
-    if (s->in_decomp)
-        ssh_decompressor_free(s->in_decomp);
-}
-
-static void ssh2_bpp_free(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp);
-    sfree(s->buf);
-    ssh2_bpp_free_outgoing_crypto(s);
-    ssh2_bpp_free_incoming_crypto(s);
-    sfree(s->pktin);
-    sfree(s);
-}
-
-void ssh2_bpp_new_outgoing_crypto(
-    BinaryPacketProtocol *bpp,
-    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
-    const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
-    const ssh_compression_alg *compression, bool delayed_compression)
-{
-    struct ssh2_bpp_state *s;
-    assert(bpp->vt == &ssh2_bpp_vtable);
-    s = container_of(bpp, struct ssh2_bpp_state, bpp);
-
-    ssh2_bpp_free_outgoing_crypto(s);
-
-    if (cipher) {
-        s->out.cipher = ssh_cipher_new(cipher);
-        ssh_cipher_setkey(s->out.cipher, ckey);
-        ssh_cipher_setiv(s->out.cipher, iv);
-
-        s->cbc_ignore_workaround = (
-            (ssh_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) &&
-            !(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE));
-
-        bpp_logevent("Initialised %s [%s] outbound encryption",
-                     ssh_cipher_alg(s->out.cipher)->text_name, ssh_cipher_alg(s->out.cipher)->ssh2_id);
-    } else {
-        s->out.cipher = NULL;
-        s->cbc_ignore_workaround = false;
-    }
-    s->out.etm_mode = etm_mode;
-    if (mac) {
-        s->out.mac = ssh2_mac_new(mac, s->out.cipher);
-        ssh2_mac_setkey(s->out.mac, make_ptrlen(mac_key, mac->keylen));
-
-        bpp_logevent("Initialised %s outbound MAC algorithm%s%s",
-                     ssh2_mac_text_name(s->out.mac),
-                     etm_mode ? " (in ETM mode)" : "",
-                     (s->out.cipher &&
-                      ssh_cipher_alg(s->out.cipher)->required_mac ?
-                      " (required by cipher)" : ""));
-    } else {
-        s->out.mac = NULL;
-    }
-
-    if (delayed_compression && !s->seen_userauth_success) {
-        s->out.pending_compression = compression;
-        s->out_comp = NULL;
-
-        bpp_logevent("Will enable %s compression after user authentication",
-                     s->out.pending_compression->text_name);
-    } else {
-        s->out.pending_compression = NULL;
-
-        /* 'compression' is always non-NULL, because no compression is
-         * indicated by ssh_comp_none. But this setup call may return a
-         * null out_comp. */
-        s->out_comp = ssh_compressor_new(compression);
-
-        if (s->out_comp)
-            bpp_logevent("Initialised %s compression",
-                         ssh_compressor_alg(s->out_comp)->text_name);
-    }
-}
-
-void ssh2_bpp_new_incoming_crypto(
-    BinaryPacketProtocol *bpp,
-    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
-    const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
-    const ssh_compression_alg *compression, bool delayed_compression)
-{
-    struct ssh2_bpp_state *s;
-    assert(bpp->vt == &ssh2_bpp_vtable);
-    s = container_of(bpp, struct ssh2_bpp_state, bpp);
-
-    ssh2_bpp_free_incoming_crypto(s);
-
-    if (cipher) {
-        s->in.cipher = ssh_cipher_new(cipher);
-        ssh_cipher_setkey(s->in.cipher, ckey);
-        ssh_cipher_setiv(s->in.cipher, iv);
-
-        bpp_logevent("Initialised %s [%s] inbound encryption",
-                     ssh_cipher_alg(s->in.cipher)->text_name, ssh_cipher_alg(s->in.cipher)->ssh2_id);
-    } else {
-        s->in.cipher = NULL;
-    }
-    s->in.etm_mode = etm_mode;
-    if (mac) {
-        s->in.mac = ssh2_mac_new(mac, s->in.cipher);
-        ssh2_mac_setkey(s->in.mac, make_ptrlen(mac_key, mac->keylen));
-
-        bpp_logevent("Initialised %s inbound MAC algorithm%s%s",
-                     ssh2_mac_text_name(s->in.mac),
-                     etm_mode ? " (in ETM mode)" : "",
-                     (s->in.cipher &&
-                      ssh_cipher_alg(s->in.cipher)->required_mac ?
-                      " (required by cipher)" : ""));
-    } else {
-        s->in.mac = NULL;
-    }
-
-    if (delayed_compression && !s->seen_userauth_success) {
-        s->in.pending_compression = compression;
-        s->in_decomp = NULL;
-
-        bpp_logevent("Will enable %s decompression after user authentication",
-                     s->in.pending_compression->text_name);
-    } else {
-        s->in.pending_compression = NULL;
-
-        /* 'compression' is always non-NULL, because no compression is
-         * indicated by ssh_comp_none. But this setup call may return a
-         * null in_decomp. */
-        s->in_decomp = ssh_decompressor_new(compression);
-
-        if (s->in_decomp)
-            bpp_logevent("Initialised %s decompression",
-                         ssh_decompressor_alg(s->in_decomp)->text_name);
-    }
-
-    /* Clear the pending_newkeys flag, so that handle_input below will
-     * start consuming the input data again. */
-    s->pending_newkeys = false;
-
-    /* And schedule a run of handle_input, in case there's already
-     * input data in the queue. */
-    queue_idempotent_callback(&s->bpp.ic_in_raw);
-}
-
-bool ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bpp_state *s;
-    assert(bpp->vt == &ssh2_bpp_vtable);
-    s = container_of(bpp, struct ssh2_bpp_state, bpp);
-
-    return s->pending_compression;
-}
-
-static void ssh2_bpp_enable_pending_compression(struct ssh2_bpp_state *s)
-{
-    BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */
-
-    if (s->in.pending_compression) {
-        s->in_decomp = ssh_decompressor_new(s->in.pending_compression);
-        bpp_logevent("Initialised delayed %s decompression",
-                     ssh_decompressor_alg(s->in_decomp)->text_name);
-        s->in.pending_compression = NULL;
-    }
-    if (s->out.pending_compression) {
-        s->out_comp = ssh_compressor_new(s->out.pending_compression);
-        bpp_logevent("Initialised delayed %s compression",
-                     ssh_compressor_alg(s->out_comp)->text_name);
-        s->out.pending_compression = NULL;
-    }
-}
-
-#define BPP_READ(ptr, len) do                                           \
-    {                                                                   \
-        bool success;                                                   \
-        crMaybeWaitUntilV((success = bufchain_try_fetch_consume(        \
-                               s->bpp.in_raw, ptr, len)) ||             \
-                          s->bpp.input_eof);                            \
-        if (!success)                                                   \
-            goto eof;                                                   \
-        ssh_check_frozen(s->bpp.ssh);                                   \
-    } while (0)
-
-#define userauth_range(pkttype) ((unsigned)((pkttype) - 50) < 20)
-
-static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp);
-
-    crBegin(s->crState);
-
-    while (1) {
-        s->maxlen = 0;
-        s->length = 0;
-        if (s->in.cipher)
-            s->cipherblk = ssh_cipher_alg(s->in.cipher)->blksize;
-        else
-            s->cipherblk = 8;
-        if (s->cipherblk < 8)
-            s->cipherblk = 8;
-        s->maclen = s->in.mac ? ssh2_mac_alg(s->in.mac)->len : 0;
-
-        if (s->in.cipher &&
-            (ssh_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) &&
-            s->in.mac && !s->in.etm_mode) {
-            /*
-             * When dealing with a CBC-mode cipher, we want to avoid the
-             * possibility of an attacker's tweaking the ciphertext stream
-             * so as to cause us to feed the same block to the block
-             * cipher more than once and thus leak information
-             * (VU#958563).  The way we do this is not to take any
-             * decisions on the basis of anything we've decrypted until
-             * we've verified it with a MAC.  That includes the packet
-             * length, so we just read data and check the MAC repeatedly,
-             * and when the MAC passes, see if the length we've got is
-             * plausible.
-             *
-             * This defence is unnecessary in OpenSSH ETM mode, because
-             * the whole point of ETM mode is that the attacker can't
-             * tweak the ciphertext stream at all without the MAC
-             * detecting it before we decrypt anything.
-             */
-
-            /*
-             * Make sure we have buffer space for a maximum-size packet.
-             */
-            unsigned buflimit = OUR_V2_PACKETLIMIT + s->maclen;
-            if (s->bufsize < buflimit) {
-                s->bufsize = buflimit;
-                s->buf = sresize(s->buf, s->bufsize, unsigned char);
-            }
-
-            /* Read an amount corresponding to the MAC. */
-            BPP_READ(s->buf, s->maclen);
-
-            s->packetlen = 0;
-            ssh2_mac_start(s->in.mac);
-            put_uint32(s->in.mac, s->in.sequence);
-
-            for (;;) { /* Once around this loop per cipher block. */
-                /* Read another cipher-block's worth, and tack it on to
-                 * the end. */
-                BPP_READ(s->buf + (s->packetlen + s->maclen), s->cipherblk);
-                /* Decrypt one more block (a little further back in
-                 * the stream). */
-                ssh_cipher_decrypt(s->in.cipher,
-                                   s->buf + s->packetlen, s->cipherblk);
-
-                /* Feed that block to the MAC. */
-                put_data(s->in.mac,
-                         s->buf + s->packetlen, s->cipherblk);
-                s->packetlen += s->cipherblk;
-
-                /* See if that gives us a valid packet. */
-                if (ssh2_mac_verresult(s->in.mac, s->buf + s->packetlen) &&
-                    ((s->len = toint(GET_32BIT_MSB_FIRST(s->buf))) ==
-                     s->packetlen-4))
-                    break;
-                if (s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
-                    ssh_sw_abort(s->bpp.ssh,
-                                 "No valid incoming packet found");
-                    crStopV;
-                }
-            }
-            s->maxlen = s->packetlen + s->maclen;
-
-            /*
-             * Now transfer the data into an output packet.
-             */
-            s->pktin = snew_plus(PktIn, s->maxlen);
-            s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
-            s->pktin->type = 0;
-            s->pktin->qnode.on_free_queue = false;
-            s->data = snew_plus_get_aux(s->pktin);
-            memcpy(s->data, s->buf, s->maxlen);
-        } else if (s->in.mac && s->in.etm_mode) {
-            if (s->bufsize < 4) {
-                s->bufsize = 4;
-                s->buf = sresize(s->buf, s->bufsize, unsigned char);
-            }
-
-            /*
-             * OpenSSH encrypt-then-MAC mode: the packet length is
-             * unencrypted, unless the cipher supports length encryption.
-             */
-            BPP_READ(s->buf, 4);
-
-            /* Cipher supports length decryption, so do it */
-            if (s->in.cipher && (ssh_cipher_alg(s->in.cipher)->flags &
-                                 SSH_CIPHER_SEPARATE_LENGTH)) {
-                /* Keep the packet the same though, so the MAC passes */
-                unsigned char len[4];
-                memcpy(len, s->buf, 4);
-                ssh_cipher_decrypt_length(
-                    s->in.cipher, len, 4, s->in.sequence);
-                s->len = toint(GET_32BIT_MSB_FIRST(len));
-            } else {
-                s->len = toint(GET_32BIT_MSB_FIRST(s->buf));
-            }
-
-            /*
-             * _Completely_ silly lengths should be stomped on before they
-             * do us any more damage.
-             */
-            if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
-                s->len % s->cipherblk != 0) {
-                ssh_sw_abort(s->bpp.ssh,
-                             "Incoming packet length field was garbled");
-                crStopV;
-            }
-
-            /*
-             * So now we can work out the total packet length.
-             */
-            s->packetlen = s->len + 4;
-
-            /*
-             * Allocate the packet to return, now we know its length.
-             */
-            s->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + s->maclen);
-            s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
-            s->pktin->type = 0;
-            s->pktin->qnode.on_free_queue = false;
-            s->data = snew_plus_get_aux(s->pktin);
-            memcpy(s->data, s->buf, 4);
-
-            /*
-             * Read the remainder of the packet.
-             */
-            BPP_READ(s->data + 4, s->packetlen + s->maclen - 4);
-
-            /*
-             * Check the MAC.
-             */
-            if (s->in.mac && !ssh2_mac_verify(
-                    s->in.mac, s->data, s->len + 4, s->in.sequence)) {
-                ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet");
-                crStopV;
-            }
-
-            /* Decrypt everything between the length field and the MAC. */
-            if (s->in.cipher)
-                ssh_cipher_decrypt(
-                    s->in.cipher, s->data + 4, s->packetlen - 4);
-        } else {
-            if (s->bufsize < s->cipherblk) {
-                s->bufsize = s->cipherblk;
-                s->buf = sresize(s->buf, s->bufsize, unsigned char);
-            }
-
-            /*
-             * Acquire and decrypt the first block of the packet. This will
-             * contain the length and padding details.
-             */
-            BPP_READ(s->buf, s->cipherblk);
-
-            if (s->in.cipher)
-                ssh_cipher_decrypt(s->in.cipher, s->buf, s->cipherblk);
-
-            /*
-             * Now get the length figure.
-             */
-            s->len = toint(GET_32BIT_MSB_FIRST(s->buf));
-
-            /*
-             * _Completely_ silly lengths should be stomped on before they
-             * do us any more damage.
-             */
-            if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT ||
-                (s->len + 4) % s->cipherblk != 0) {
-                ssh_sw_abort(s->bpp.ssh,
-                             "Incoming packet was garbled on decryption");
-                crStopV;
-            }
-
-            /*
-             * So now we can work out the total packet length.
-             */
-            s->packetlen = s->len + 4;
-
-            /*
-             * Allocate the packet to return, now we know its length.
-             */
-            s->maxlen = s->packetlen + s->maclen;
-            s->pktin = snew_plus(PktIn, s->maxlen);
-            s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
-            s->pktin->type = 0;
-            s->pktin->qnode.on_free_queue = false;
-            s->data = snew_plus_get_aux(s->pktin);
-            memcpy(s->data, s->buf, s->cipherblk);
-
-            /*
-             * Read and decrypt the remainder of the packet.
-             */
-            BPP_READ(s->data + s->cipherblk,
-                     s->packetlen + s->maclen - s->cipherblk);
-
-            /* Decrypt everything _except_ the MAC. */
-            if (s->in.cipher)
-                ssh_cipher_decrypt(
-                    s->in.cipher,
-                    s->data + s->cipherblk, s->packetlen - s->cipherblk);
-
-            /*
-             * Check the MAC.
-             */
-            if (s->in.mac && !ssh2_mac_verify(
-                    s->in.mac, s->data, s->len + 4, s->in.sequence)) {
-                ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet");
-                crStopV;
-            }
-        }
-        /* Get and sanity-check the amount of random padding. */
-        s->pad = s->data[4];
-        if (s->pad < 4 || s->len - s->pad < 1) {
-            ssh_sw_abort(s->bpp.ssh,
-                         "Invalid padding length on received packet");
-            crStopV;
-        }
-        /*
-         * This enables us to deduce the payload length.
-         */
-        s->payload = s->len - s->pad - 1;
-
-        s->length = s->payload + 5;
-
-        dts_consume(&s->stats->in, s->packetlen);
-
-        s->pktin->sequence = s->in.sequence++;
-
-        s->length = s->packetlen - s->pad;
-        assert(s->length >= 0);
-
-        /*
-         * Decompress packet payload.
-         */
-        {
-            unsigned char *newpayload;
-            int newlen;
-            if (s->in_decomp && ssh_decompressor_decompress(
-                    s->in_decomp, s->data + 5, s->length - 5,
-                    &newpayload, &newlen)) {
-                if (s->maxlen < newlen + 5) {
-                    PktIn *old_pktin = s->pktin;
-
-                    s->maxlen = newlen + 5;
-                    s->pktin = snew_plus(PktIn, s->maxlen);
-                    *s->pktin = *old_pktin; /* structure copy */
-                    s->data = snew_plus_get_aux(s->pktin);
-
-                    smemclr(old_pktin, s->packetlen + s->maclen);
-                    sfree(old_pktin);
-                }
-                s->length = 5 + newlen;
-                memcpy(s->data + 5, newpayload, newlen);
-                sfree(newpayload);
-            }
-        }
-
-        /*
-         * Now we can identify the semantic content of the packet,
-         * and also the initial type byte.
-         */
-        if (s->length <= 5) { /* == 5 we hope, but robustness */
-            /*
-             * RFC 4253 doesn't explicitly say that completely empty
-             * packets with no type byte are forbidden. We handle them
-             * here by giving them a type code larger than 0xFF, which
-             * will be picked up at the next layer and trigger
-             * SSH_MSG_UNIMPLEMENTED.
-             */
-            s->pktin->type = SSH_MSG_NO_TYPE_CODE;
-            s->data += 5;
-            s->length = 0;
-        } else {
-            s->pktin->type = s->data[5];
-            s->data += 6;
-            s->length -= 6;
-        }
-        BinarySource_INIT(s->pktin, s->data, s->length);
-
-        if (s->bpp.logctx) {
-            logblank_t blanks[MAX_BLANKS];
-            int nblanks = ssh2_censor_packet(
-                s->bpp.pls, s->pktin->type, false,
-                make_ptrlen(s->data, s->length), blanks);
-            log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
-                       ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
-                                     s->pktin->type),
-                       s->data, s->length, nblanks, blanks,
-                       &s->pktin->sequence, 0, NULL);
-        }
-
-        if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) {
-            sfree(s->pktin);
-            s->pktin = NULL;
-            continue;
-        }
-
-        s->pktin->qnode.formal_size = get_avail(s->pktin);
-        pq_push(&s->bpp.in_pq, s->pktin);
-
-        {
-            int type = s->pktin->type;
-            int prev_type = s->prev_type;
-            s->prev_type = type;
-            s->pktin = NULL;
-
-            if (s->enforce_next_packet_is_userauth_success) {
-                /* See EXT_INFO handler below */
-                if (type != SSH2_MSG_USERAUTH_SUCCESS) {
-                    ssh_proto_error(s->bpp.ssh,
-                                    "Remote side sent SSH2_MSG_EXT_INFO "
-                                    "not either preceded by NEWKEYS or "
-                                    "followed by USERAUTH_SUCCESS");
-                    return;
-                }
-                s->enforce_next_packet_is_userauth_success = false;
-            }
-
-            if (type == SSH2_MSG_NEWKEYS) {
-                if (s->nnewkeys < 2)
-                    s->nnewkeys++;
-                /*
-                 * Mild layer violation: in this situation we must
-                 * suspend processing of the input byte stream until
-                 * the transport layer has initialised the new keys by
-                 * calling ssh2_bpp_new_incoming_crypto above.
-                 */
-                s->pending_newkeys = true;
-                crWaitUntilV(!s->pending_newkeys);
-                continue;
-            }
-
-            if (type == SSH2_MSG_USERAUTH_SUCCESS && !s->is_server) {
-                /*
-                 * Another one: if we were configured with OpenSSH's
-                 * deferred compression which is triggered on receipt
-                 * of USERAUTH_SUCCESS, then this is the moment to
-                 * turn on compression.
-                 */
-                ssh2_bpp_enable_pending_compression(s);
-
-                /*
-                 * Whether or not we were doing delayed compression in
-                 * _this_ set of crypto parameters, we should set a
-                 * flag indicating that we're now authenticated, so
-                 * that a delayed compression method enabled in any
-                 * future rekey will be treated as un-delayed.
-                 */
-                s->seen_userauth_success = true;
-            }
-
-            if (type == SSH2_MSG_EXT_INFO) {
-                /*
-                 * And another: enforce that an incoming EXT_INFO is
-                 * either the message immediately after the initial
-                 * NEWKEYS, or (if we're the client) the one
-                 * immediately before USERAUTH_SUCCESS.
-                 */
-                if (prev_type == SSH2_MSG_NEWKEYS && s->nnewkeys == 1) {
-                    /* OK - this is right after the first NEWKEYS. */
-                } else if (s->is_server) {
-                    /* We're the server, so they're the client.
-                     * Clients may not send EXT_INFO at _any_ other
-                     * time. */
-                    ssh_proto_error(s->bpp.ssh,
-                                    "Remote side sent SSH2_MSG_EXT_INFO "
-                                    "that was not immediately after the "
-                                    "initial NEWKEYS");
-                    return;
-                } else if (s->nnewkeys > 0 && s->seen_userauth_success) {
-                    /* We're the client, so they're the server. In
-                     * that case they may also send EXT_INFO
-                     * immediately before USERAUTH_SUCCESS. Error out
-                     * immediately if this can't _possibly_ be that
-                     * moment (because we haven't even seen NEWKEYS
-                     * yet, or because we've already seen
-                     * USERAUTH_SUCCESS). */
-                    ssh_proto_error(s->bpp.ssh,
-                                    "Remote side sent SSH2_MSG_EXT_INFO "
-                                    "after USERAUTH_SUCCESS");
-                    return;
-                } else {
-                    /* This _could_ be OK, provided the next packet is
-                     * USERAUTH_SUCCESS. Set a flag to remember to
-                     * fault it if not. */
-                    s->enforce_next_packet_is_userauth_success = true;
-                }
-            }
-
-            if (s->pending_compression && userauth_range(type)) {
-                /*
-                 * Receiving any userauth message at all indicates
-                 * that we're not about to turn on delayed compression
-                 * - either because we just _have_ done, or because
-                 * this message is a USERAUTH_FAILURE or some kind of
-                 * intermediate 'please send more data' continuation
-                 * message. Either way, we turn off the outgoing
-                 * packet blockage for now, and release any queued
-                 * output packets, so that we can make another attempt
-                 * to authenticate. The next userauth packet we send
-                 * will re-block the output direction.
-                 */
-                s->pending_compression = false;
-                queue_idempotent_callback(&s->bpp.ic_out_pq);
-            }
-        }
-    }
-
-  eof:
-    /*
-     * We've seen EOF. But we might have pushed stuff on the outgoing
-     * packet queue first, and that stuff _might_ include a DISCONNECT
-     * message, in which case we'd like to use that as the diagnostic.
-     * So first wait for the queue to have been processed.
-     */
-    crMaybeWaitUntilV(!pq_peek(&s->bpp.in_pq));
-    if (!s->bpp.expect_close) {
-        ssh_remote_error(s->bpp.ssh,
-                         "Remote side unexpectedly closed network connection");
-    } else {
-        ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
-    }
-    return;  /* avoid touching s now it's been freed */
-
-    crFinishV;
-}
-
-static PktOut *ssh2_bpp_new_pktout(int pkt_type)
-{
-    PktOut *pkt = ssh_new_packet();
-    pkt->length = 5; /* space for packet length + padding length */
-    pkt->minlen = 0;
-    pkt->type = pkt_type;
-    put_byte(pkt, pkt_type);
-    pkt->prefix = pkt->length;
-    return pkt;
-}
-
-static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
-{
-    int origlen, cipherblk, maclen, padding, unencrypted_prefix, i;
-
-    if (s->bpp.logctx) {
-        ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
-                                     pkt->length - pkt->prefix);
-        logblank_t blanks[MAX_BLANKS];
-        int nblanks = ssh2_censor_packet(
-            s->bpp.pls, pkt->type, true, pktdata, blanks);
-        log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
-                   ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
-                                 pkt->type),
-                   pktdata.ptr, pktdata.len, nblanks, blanks, &s->out.sequence,
-                   pkt->downstream_id, pkt->additional_log_text);
-    }
-
-    cipherblk = s->out.cipher ? ssh_cipher_alg(s->out.cipher)->blksize : 8;
-    cipherblk = cipherblk < 8 ? 8 : cipherblk;  /* or 8 if blksize < 8 */
-
-    if (s->out_comp) {
-        unsigned char *newpayload;
-        int minlen, newlen;
-
-        /*
-         * Compress packet payload.
-         */
-        minlen = pkt->minlen;
-        if (minlen) {
-            /*
-             * Work out how much compressed data we need (at least) to
-             * make the overall packet length come to pkt->minlen.
-             */
-            if (s->out.mac)
-                minlen -= ssh2_mac_alg(s->out.mac)->len;
-            minlen -= 8;              /* length field + min padding */
-        }
-
-        ssh_compressor_compress(s->out_comp, pkt->data + 5, pkt->length - 5,
-                                &newpayload, &newlen, minlen);
-        pkt->length = 5;
-        put_data(pkt, newpayload, newlen);
-        sfree(newpayload);
-    }
-
-    /*
-     * Add padding. At least four bytes, and must also bring total
-     * length (minus MAC) up to a multiple of the block size.
-     * If pkt->forcepad is set, make sure the packet is at least that size
-     * after padding.
-     */
-    padding = 4;
-    unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0;
-    padding +=
-        (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk)
-        % cipherblk;
-    assert(padding <= 255);
-    maclen = s->out.mac ? ssh2_mac_alg(s->out.mac)->len : 0;
-    origlen = pkt->length;
-    for (i = 0; i < padding; i++)
-        put_byte(pkt, 0);              /* make space for random padding */
-    random_read(pkt->data + origlen, padding);
-    pkt->data[4] = padding;
-    PUT_32BIT_MSB_FIRST(pkt->data, origlen + padding - 4);
-
-    /* Encrypt length if the scheme requires it */
-    if (s->out.cipher &&
-        (ssh_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) {
-        ssh_cipher_encrypt_length(s->out.cipher, pkt->data, 4,
-                                  s->out.sequence);
-    }
-
-    put_padding(pkt, maclen, 0);
-
-    if (s->out.mac && s->out.etm_mode) {
-        /*
-         * OpenSSH-defined encrypt-then-MAC protocol.
-         */
-        if (s->out.cipher)
-            ssh_cipher_encrypt(s->out.cipher,
-                               pkt->data + 4, origlen + padding - 4);
-        ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
-                          s->out.sequence);
-    } else {
-        /*
-         * SSH-2 standard protocol.
-         */
-        if (s->out.mac)
-            ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding,
-                              s->out.sequence);
-        if (s->out.cipher)
-            ssh_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding);
-    }
-
-    s->out.sequence++;       /* whether or not we MACed */
-
-    dts_consume(&s->stats->out, origlen + padding);
-}
-
-static void ssh2_bpp_format_packet(struct ssh2_bpp_state *s, PktOut *pkt)
-{
-    if (pkt->minlen > 0 && !s->out_comp) {
-        /*
-         * If we've been told to pad the packet out to a given minimum
-         * length, but we're not compressing (and hence can't get the
-         * compression to do the padding by pointlessly opening and
-         * closing zlib blocks), then our other strategy is to precede
-         * this message with an SSH_MSG_IGNORE that makes it up to the
-         * right length.
-         *
-         * A third option in principle, and the most obviously
-         * sensible, would be to set the explicit padding field in the
-         * packet to more than its minimum value. Sadly, that turns
-         * out to break some servers (our institutional memory thinks
-         * Cisco in particular) and so we abandoned that idea shortly
-         * after trying it.
-         */
-
-        /*
-         * Calculate the length we expect the real packet to have.
-         */
-        int block, length;
-        PktOut *ignore_pkt;
-
-        block = s->out.cipher ? ssh_cipher_alg(s->out.cipher)->blksize : 0;
-        if (block < 8)
-            block = 8;
-        length = pkt->length;
-        length += 4;       /* minimum 4 byte padding */
-        length += block-1;
-        length -= (length % block);
-        if (s->out.mac)
-            length += ssh2_mac_alg(s->out.mac)->len;
-
-        if (length < pkt->minlen) {
-            /*
-             * We need an ignore message. Calculate its length.
-             */
-            length = pkt->minlen - length;
-
-            /*
-             * And work backwards from that to the length of the
-             * contained string.
-             */
-            if (s->out.mac)
-                length -= ssh2_mac_alg(s->out.mac)->len;
-            length -= 8;               /* length field + min padding */
-            length -= 5;               /* type code + string length prefix */
-
-            if (length < 0)
-                length = 0;
-
-            ignore_pkt = ssh2_bpp_new_pktout(SSH2_MSG_IGNORE);
-            put_uint32(ignore_pkt, length);
-            { // WINSCP
-            size_t origlen = ignore_pkt->length;
-            size_t i; // WINSCP
-            for (i = 0; i < length; i++)
-                put_byte(ignore_pkt, 0);  /* make space for random padding */
-            random_read(ignore_pkt->data + origlen, length);
-            ssh2_bpp_format_packet_inner(s, ignore_pkt);
-            bufchain_add(s->bpp.out_raw, ignore_pkt->data, ignore_pkt->length);
-            ssh_free_pktout(ignore_pkt);
-            } // WINSCP
-        }
-    }
-
-    ssh2_bpp_format_packet_inner(s, pkt);
-    bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
-}
-
-static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp)
-{
-    struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp);
-    PktOut *pkt;
-    int n_userauth;
-
-    /*
-     * Count the userauth packets in the queue.
-     */
-    n_userauth = 0;
-    for (pkt = pq_first(&s->bpp.out_pq); pkt != NULL;
-         pkt = pq_next(&s->bpp.out_pq, pkt))
-        if (userauth_range(pkt->type))
-            n_userauth++;
-
-    if (s->pending_compression && !n_userauth) {
-        /*
-         * We're currently blocked from sending any outgoing packets
-         * until the other end tells us whether we're going to have to
-         * enable compression or not.
-         *
-         * If our end has pushed a userauth packet on the queue, that
-         * must mean it knows that a USERAUTH_SUCCESS is not
-         * immediately forthcoming, so we unblock ourselves and send
-         * up to and including that packet. But in this if statement,
-         * there aren't any, so we're still blocked.
-         */
-        return;
-    }
-
-    if (s->cbc_ignore_workaround) {
-        /*
-         * When using a CBC-mode cipher in SSH-2, it's necessary to
-         * ensure that an attacker can't provide data to be encrypted
-         * using an IV that they know. We ensure this by inserting an
-         * SSH_MSG_IGNORE if the last cipher block of the previous
-         * packet has already been sent to the network (which we
-         * approximate conservatively by checking if it's vanished
-         * from out_raw).
-         */
-        if (bufchain_size(s->bpp.out_raw) <
-            (ssh_cipher_alg(s->out.cipher)->blksize +
-             ssh2_mac_alg(s->out.mac)->len)) {
-            /*
-             * There's less data in out_raw than the MAC size plus the
-             * cipher block size, which means at least one byte of
-             * that cipher block must already have left. Add an
-             * IGNORE.
-             */
-            pkt = ssh_bpp_new_pktout(&s->bpp, SSH2_MSG_IGNORE);
-            put_stringz(pkt, "");
-            ssh2_bpp_format_packet(s, pkt);
-        }
-    }
-
-    while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
-        int type = pkt->type;
-
-        if (userauth_range(type))
-            n_userauth--;
-
-        ssh2_bpp_format_packet(s, pkt);
-        ssh_free_pktout(pkt);
-
-        if (n_userauth == 0 && s->out.pending_compression && !s->is_server) {
-            /*
-             * This is the last userauth packet in the queue, so
-             * unless our side decides to send another one in future,
-             * we have to assume will potentially provoke
-             * USERAUTH_SUCCESS. Block (non-userauth) outgoing packets
-             * until we see the reply.
-             */
-            s->pending_compression = true;
-            return;
-        } else if (type == SSH2_MSG_USERAUTH_SUCCESS && s->is_server) {
-            ssh2_bpp_enable_pending_compression(s);
-        }
-    }
-}
-
-#ifdef MPEXT
-const ssh_cipher * ssh2_bpp_get_cscipher(BinaryPacketProtocol *bpp)
-{
-    return container_of(bpp, struct ssh2_bpp_state, bpp)->out.cipher;
-}
-
-const ssh_cipher * ssh2_bpp_get_sccipher(BinaryPacketProtocol *bpp)
-{
-    return container_of(bpp, struct ssh2_bpp_state, bpp)->in.cipher;
-}
-
-const struct ssh_compressor * ssh2_bpp_get_cscomp(BinaryPacketProtocol *bpp)
-{
-    return container_of(bpp, struct ssh2_bpp_state, bpp)->out_comp;
-}
-
-const struct ssh_decompressor * ssh2_bpp_get_sccomp(BinaryPacketProtocol *bpp)
-{
-    return container_of(bpp, struct ssh2_bpp_state, bpp)->in_decomp;
-}
-
-#endif

+ 0 - 107
source/putty/ssh2censor.c

@@ -1,107 +0,0 @@
-/*
- * Packet-censoring code for SSH-2, used to identify sensitive fields
- * like passwords so that the logging system can avoid writing them
- * into log files.
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-
-int ssh2_censor_packet(
-    const PacketLogSettings *pls, int type, bool sender_is_client,
-    ptrlen pkt, logblank_t *blanks)
-{
-    int nblanks = 0;
-    ptrlen str;
-    BinarySource src[1];
-
-    BinarySource_BARE_INIT_PL(src, pkt);
-
-    if (pls->omit_data &&
-        (type == SSH2_MSG_CHANNEL_DATA ||
-         type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
-        /* "Session data" packets - omit the data string. */
-        get_uint32(src);              /* skip channel id */
-        if (type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
-            get_uint32(src);          /* skip extended data type */
-        str = get_string(src);
-        if (!get_err(src)) {
-            assert(nblanks < MAX_BLANKS);
-            blanks[nblanks].offset = src->pos - str.len;
-            blanks[nblanks].type = PKTLOG_OMIT;
-            blanks[nblanks].len = str.len;
-            nblanks++;
-        }
-    }
-
-    if (sender_is_client && pls->omit_passwords) {
-        if (type == SSH2_MSG_USERAUTH_REQUEST) {
-            /* If this is a password packet, blank the password(s). */
-            get_string(src);              /* username */
-            get_string(src);              /* service name */
-            str = get_string(src);        /* auth method */
-            if (ptrlen_eq_string(str, "password")) {
-                get_bool(src);
-                /* Blank the password field. */
-                str = get_string(src);
-                if (!get_err(src)) {
-                    assert(nblanks < MAX_BLANKS);
-                    blanks[nblanks].offset = src->pos - str.len;
-                    blanks[nblanks].type = PKTLOG_BLANK;
-                    blanks[nblanks].len = str.len;
-                    nblanks++;
-                    /* If there's another password field beyond it
-                     * (change of password), blank that too. */
-                    str = get_string(src);
-                    if (!get_err(src))
-                        blanks[nblanks-1].len =
-                            src->pos - blanks[nblanks].offset;
-                }
-            }
-        } else if (pls->actx == SSH2_PKTCTX_KBDINTER &&
-                   type == SSH2_MSG_USERAUTH_INFO_RESPONSE) {
-            /* If this is a keyboard-interactive response packet,
-             * blank the responses. */
-            get_uint32(src);
-            assert(nblanks < MAX_BLANKS);
-            blanks[nblanks].offset = src->pos;
-            blanks[nblanks].type = PKTLOG_BLANK;
-            do {
-                str = get_string(src);
-            } while (!get_err(src));
-            blanks[nblanks].len = src->pos - blanks[nblanks].offset;
-            nblanks++;
-        } else if (type == SSH2_MSG_CHANNEL_REQUEST) {
-            /*
-             * If this is an X forwarding request packet, blank the
-             * fake auth data.
-             *
-             * Note that while we blank the X authentication data
-             * here, we don't take any special action to blank the
-             * start of an X11 channel, so using MIT-MAGIC-COOKIE-1
-             * and actually opening an X connection without having
-             * session blanking enabled is likely to leak your cookie
-             * into the log.
-             */
-            get_uint32(src);
-            str = get_string(src);
-            if (ptrlen_eq_string(str, "x11-req")) {
-                get_bool(src);
-                get_bool(src);
-                get_string(src);
-                str = get_string(src);
-                if (!get_err(src)) {
-                    assert(nblanks < MAX_BLANKS);
-                    blanks[nblanks].offset = src->pos - str.len;
-                    blanks[nblanks].type = PKTLOG_BLANK;
-                    blanks[nblanks].len = str.len;
-                    nblanks++;
-                }
-            }
-        }
-    }
-
-    return nblanks;
-}

+ 0 - 511
source/putty/ssh2connection-client.c

@@ -1,511 +0,0 @@
-/*
- * Client-specific parts of the SSH-2 connection layer.
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshchan.h"
-#include "sshcr.h"
-#include "ssh2connection.h"
-
-static ChanopenResult chan_open_x11(
-    struct ssh2_connection_state *s, SshChannel *sc,
-    ptrlen peeraddr, int peerport)
-{
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    char *peeraddr_str;
-    Channel *ch;
-
-    ppl_logevent("Received X11 connect request from %.*s:%d",
-                 PTRLEN_PRINTF(peeraddr), peerport);
-
-    if (!s->X11_fwd_enabled && !s->connshare) {
-        CHANOPEN_RETURN_FAILURE(
-            SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
-            ("X11 forwarding is not enabled"));
-    }
-
-    peeraddr_str = peeraddr.ptr ? mkstr(peeraddr) : NULL;
-    ch = x11_new_channel(
-        s->x11authtree, sc, peeraddr_str, peerport, s->connshare != NULL);
-    sfree(peeraddr_str);
-    ppl_logevent("Opened X11 forward channel");
-    CHANOPEN_RETURN_SUCCESS(ch);
-}
-
-static ChanopenResult chan_open_forwarded_tcpip(
-    struct ssh2_connection_state *s, SshChannel *sc,
-    ptrlen fwdaddr, int fwdport, ptrlen peeraddr, int peerport)
-{
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    struct ssh_rportfwd pf, *realpf;
-    Channel *ch;
-    char *err;
-
-    ppl_logevent("Received remote port %.*s:%d open request from %.*s:%d",
-                 PTRLEN_PRINTF(fwdaddr), fwdport,
-                 PTRLEN_PRINTF(peeraddr), peerport);
-
-    pf.shost = mkstr(fwdaddr);
-    pf.sport = fwdport;
-    realpf = find234(s->rportfwds, &pf, NULL);
-    sfree(pf.shost);
-
-    if (realpf == NULL) {
-        CHANOPEN_RETURN_FAILURE(
-            SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
-            ("Remote port is not recognised"));
-    }
-
-    if (realpf->share_ctx) {
-        /*
-         * This port forwarding is on behalf of a connection-sharing
-         * downstream.
-         */
-        CHANOPEN_RETURN_DOWNSTREAM(realpf->share_ctx);
-    }
-
-    err = portfwdmgr_connect(
-        s->portfwdmgr, &ch, realpf->dhost, realpf->dport,
-        sc, realpf->addressfamily);
-    ppl_logevent("Attempting to forward remote port to %s:%d",
-                 realpf->dhost, realpf->dport);
-    if (err != NULL) {
-        ppl_logevent("Port open failed: %s", err);
-        sfree(err);
-        CHANOPEN_RETURN_FAILURE(
-            SSH2_OPEN_CONNECT_FAILED,
-            ("Port open failed"));
-    }
-
-    ppl_logevent("Forwarded port opened successfully");
-    CHANOPEN_RETURN_SUCCESS(ch);
-}
-
-static ChanopenResult chan_open_auth_agent(
-    struct ssh2_connection_state *s, SshChannel *sc)
-{
-    if (!ssh_agent_forwarding_permitted(&s->cl)) {
-        CHANOPEN_RETURN_FAILURE(
-            SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED,
-            ("Agent forwarding is not enabled"));
-    }
-
-    /*
-     * If possible, make a stream-oriented connection to the agent and
-     * set up an ordinary port-forwarding type channel over it.
-     */
-    { // WINSCP
-    Plug *plug;
-    Channel *ch = portfwd_raw_new(&s->cl, &plug, true);
-    Socket *skt = agent_connect(plug);
-
-    if (!sk_socket_error(skt)) {
-        portfwd_raw_setup(ch, skt, sc);
-        CHANOPEN_RETURN_SUCCESS(ch);
-    } else {
-        portfwd_raw_free(ch);
-        /*
-         * Otherwise, fall back to the old-fashioned system of parsing the
-         * forwarded data stream ourselves for message boundaries, and
-         * passing each individual message to the one-off agent_query().
-         */
-        CHANOPEN_RETURN_SUCCESS(agentf_new(sc, plug));
-    }
-    } // WINSCP
-}
-
-ChanopenResult ssh2_connection_parse_channel_open(
-    struct ssh2_connection_state *s, ptrlen type,
-    PktIn *pktin, SshChannel *sc)
-{
-    if (ptrlen_eq_string(type, "x11")) {
-        ptrlen peeraddr = get_string(pktin);
-        int peerport = get_uint32(pktin);
-
-        return chan_open_x11(s, sc, peeraddr, peerport);
-    } else if (ptrlen_eq_string(type, "forwarded-tcpip")) {
-        ptrlen fwdaddr = get_string(pktin);
-        int fwdport = toint(get_uint32(pktin));
-        ptrlen peeraddr = get_string(pktin);
-        int peerport = toint(get_uint32(pktin));
-
-        return chan_open_forwarded_tcpip(
-            s, sc, fwdaddr, fwdport, peeraddr, peerport);
-    } else if (ptrlen_eq_string(type, "[email protected]")) {
-        return chan_open_auth_agent(s, sc);
-    } else {
-        CHANOPEN_RETURN_FAILURE(
-            SSH2_OPEN_UNKNOWN_CHANNEL_TYPE,
-            ("Unsupported channel type requested"));
-    }
-}
-
-bool ssh2_connection_parse_global_request(
-    struct ssh2_connection_state *s, ptrlen type, PktIn *pktin)
-{
-    /*
-     * We don't know of any global requests that an SSH client needs
-     * to honour.
-     */
-    return false;
-}
-
-PktOut *ssh2_portfwd_chanopen(
-    struct ssh2_connection_state *s, struct ssh2_channel *c,
-    const char *hostname, int port,
-    const char *description, const SocketPeerInfo *peerinfo)
-{
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    PktOut *pktout;
-
-    /*
-     * In client mode, this function is called by portfwdmgr in
-     * response to PortListeners that were set up in
-     * portfwdmgr_config, which means that the hostname and port
-     * parameters will indicate the host we want to tell the server to
-     * connect _to_.
-     */
-
-    ppl_logevent("Opening connection to %s:%d for %s",
-                 hostname, port, description);
-
-    pktout = ssh2_chanopen_init(c, "direct-tcpip");
-    {
-        char *trimmed_host = host_strduptrim(hostname);
-        put_stringz(pktout, trimmed_host);
-        sfree(trimmed_host);
-    }
-    put_uint32(pktout, port);
-
-    /*
-     * We make up values for the originator data; partly it's too much
-     * hassle to keep track, and partly I'm not convinced the server
-     * should be told details like that about my local network
-     * configuration. The "originator IP address" is syntactically a
-     * numeric IP address, and some servers (e.g., Tectia) get upset
-     * if it doesn't match this syntax.
-     */
-    put_stringz(pktout, "0.0.0.0");
-    put_uint32(pktout, 0);
-
-    return pktout;
-}
-
-static int ssh2_rportfwd_cmp(void *av, void *bv)
-{
-    struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
-    struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
-    int i;
-    if ( (i = strcmp(a->shost, b->shost)) != 0)
-        return i < 0 ? -1 : +1;
-    if (a->sport > b->sport)
-        return +1;
-    if (a->sport < b->sport)
-        return -1;
-    return 0;
-}
-
-static void ssh2_rportfwd_globreq_response(struct ssh2_connection_state *s,
-                                           PktIn *pktin, void *ctx)
-{
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx;
-
-    if (pktin->type == SSH2_MSG_REQUEST_SUCCESS) {
-        ppl_logevent("Remote port forwarding from %s enabled",
-                     rpf->log_description);
-    } else {
-        ppl_logevent("Remote port forwarding from %s refused",
-                     rpf->log_description);
-
-        { // WINSCP
-        struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf);
-        assert(realpf == rpf);
-        portfwdmgr_close(s->portfwdmgr, rpf->pfr);
-        free_rportfwd(rpf);
-        } // WINSCP
-    }
-}
-
-struct ssh_rportfwd *ssh2_rportfwd_alloc(
-    ConnectionLayer *cl,
-    const char *shost, int sport, const char *dhost, int dport,
-    int addressfamily, const char *log_description, PortFwdRecord *pfr,
-    ssh_sharing_connstate *share_ctx)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd);
-
-    if (!s->rportfwds)
-        s->rportfwds = newtree234(ssh2_rportfwd_cmp);
-
-    rpf->shost = dupstr(shost);
-    rpf->sport = sport;
-    rpf->dhost = dupstr(dhost);
-    rpf->dport = dport;
-    rpf->addressfamily = addressfamily;
-    rpf->log_description = dupstr(log_description);
-    rpf->pfr = pfr;
-    rpf->share_ctx = share_ctx;
-
-    if (add234(s->rportfwds, rpf) != rpf) {
-        free_rportfwd(rpf);
-        return NULL;
-    }
-
-    if (!rpf->share_ctx) {
-        PktOut *pktout = ssh_bpp_new_pktout(
-            s->ppl.bpp, SSH2_MSG_GLOBAL_REQUEST);
-        put_stringz(pktout, "tcpip-forward");
-        put_bool(pktout, true);       /* want reply */
-        put_stringz(pktout, rpf->shost);
-        put_uint32(pktout, rpf->sport);
-        pq_push(s->ppl.out_pq, pktout);
-
-        ssh2_queue_global_request_handler(
-            s, ssh2_rportfwd_globreq_response, rpf);
-    }
-
-    return rpf;
-}
-
-void ssh2_rportfwd_remove(ConnectionLayer *cl, struct ssh_rportfwd *rpf)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    if (rpf->share_ctx) {
-        /*
-         * We don't manufacture a cancel-tcpip-forward message for
-         * remote port forwardings being removed on behalf of a
-         * downstream; we just pass through the one the downstream
-         * sent to us.
-         */
-    } else {
-        PktOut *pktout = ssh_bpp_new_pktout(
-            s->ppl.bpp, SSH2_MSG_GLOBAL_REQUEST);
-        put_stringz(pktout, "cancel-tcpip-forward");
-        put_bool(pktout, false);           /* _don't_ want reply */
-        put_stringz(pktout, rpf->shost);
-        put_uint32(pktout, rpf->sport);
-        pq_push(s->ppl.out_pq, pktout);
-    }
-
-    assert(s->rportfwds);
-    { // WINSCP
-    struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf);
-    assert(realpf == rpf);
-    } // WINSCP
-    free_rportfwd(rpf);
-}
-
-SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    struct ssh2_channel *c = snew(struct ssh2_channel);
-    PktOut *pktout;
-
-    c->connlayer = s;
-    ssh2_channel_init(c);
-    c->halfopen = true;
-    c->chan = chan;
-
-    ppl_logevent("Opening main session channel");
-
-    pktout = ssh2_chanopen_init(c, "session");
-    pq_push(s->ppl.out_pq, pktout);
-
-    return &c->sc;
-}
-
-SshChannel *ssh2_serverside_x11_open(
-    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi)
-{
-    unreachable("Should never be called in the client");
-}
-
-SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
-{
-    unreachable("Should never be called in the client");
-}
-
-static void ssh2_channel_response(
-    struct ssh2_channel *c, PktIn *pkt, void *ctx)
-{
-    /* If pkt==NULL (because this handler has been called in response
-     * to CHANNEL_CLOSE arriving while the request was still
-     * outstanding), we treat that the same as CHANNEL_FAILURE. */
-    chan_request_response(c->chan,
-                          pkt && pkt->type == SSH2_MSG_CHANNEL_SUCCESS);
-}
-
-void ssh2channel_start_shell(SshChannel *sc, bool want_reply)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "shell", want_reply ? ssh2_channel_response : NULL, NULL);
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-void ssh2channel_start_command(
-    SshChannel *sc, bool want_reply, const char *command)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "exec", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_stringz(pktout, command);
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-bool ssh2channel_start_subsystem(
-    SshChannel *sc, bool want_reply, const char *subsystem)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "subsystem", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_stringz(pktout, subsystem);
-    pq_push(s->ppl.out_pq, pktout);
-
-    return true;
-}
-
-void ssh2channel_send_exit_status(SshChannel *sc, int status)
-{
-    unreachable("Should never be called in the client");
-}
-
-void ssh2channel_send_exit_signal(
-    SshChannel *sc, ptrlen signame, bool core_dumped, ptrlen msg)
-{
-    unreachable("Should never be called in the client");
-}
-
-void ssh2channel_send_exit_signal_numeric(
-    SshChannel *sc, int signum, bool core_dumped, ptrlen msg)
-{
-    unreachable("Should never be called in the client");
-}
-
-void ssh2channel_request_x11_forwarding(
-    SshChannel *sc, bool want_reply, const char *authproto,
-    const char *authdata, int screen_number, bool oneshot)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "x11-req", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_bool(pktout, oneshot);
-    put_stringz(pktout, authproto);
-    put_stringz(pktout, authdata);
-    put_uint32(pktout, screen_number);
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-void ssh2channel_request_agent_forwarding(SshChannel *sc, bool want_reply)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "[email protected]",
-        want_reply ? ssh2_channel_response : NULL, NULL);
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-void ssh2channel_request_pty(
-    SshChannel *sc, bool want_reply, Conf *conf, int w, int h)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-    strbuf *modebuf;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "pty-req", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_stringz(pktout, conf_get_str(conf, CONF_termtype));
-    put_uint32(pktout, w);
-    put_uint32(pktout, h);
-    put_uint32(pktout, 0);             /* pixel width */
-    put_uint32(pktout, 0);             /* pixel height */
-    modebuf = strbuf_new();
-    write_ttymodes_to_packet(
-        BinarySink_UPCAST(modebuf), 2,
-        get_ttymodes_from_conf(s->ppl.seat, conf));
-    put_stringsb(pktout, modebuf);
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-bool ssh2channel_send_env_var(
-    SshChannel *sc, bool want_reply, const char *var, const char *value)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "env", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_stringz(pktout, var);
-    put_stringz(pktout, value);
-    pq_push(s->ppl.out_pq, pktout);
-
-    return true;
-}
-
-bool ssh2channel_send_serial_break(SshChannel *sc, bool want_reply, int length)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "break", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_uint32(pktout, length);
-    pq_push(s->ppl.out_pq, pktout);
-
-    return true;
-}
-
-bool ssh2channel_send_signal(
-    SshChannel *sc, bool want_reply, const char *signame)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "signal", want_reply ? ssh2_channel_response : NULL, NULL);
-    put_stringz(pktout, signame);
-    pq_push(s->ppl.out_pq, pktout);
-
-    return true;
-}
-
-void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(c, "window-change", NULL, NULL);
-    put_uint32(pktout, w);
-    put_uint32(pktout, h);
-    put_uint32(pktout, 0);             /* pixel width */
-    put_uint32(pktout, 0);             /* pixel height */
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s)
-{
-    bool success = seat_set_trust_status(s->ppl.seat, false);
-    return (!success && !ssh_is_bare(s->ppl.ssh));
-}

+ 0 - 1794
source/putty/ssh2connection.c

@@ -1,1794 +0,0 @@
-/*
- * Packet protocol layer for the SSH-2 connection protocol (RFC 4254).
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshchan.h"
-#include "sshcr.h"
-#include "ssh2connection.h"
-// WINSCP
-#define queue_toplevel_callback(FN, CTX) queue_toplevel_callback(get_log_callback_set(CTX->cl.logctx), FN, CTX)
-
-static void ssh2_connection_free(PacketProtocolLayer *);
-static void ssh2_connection_process_queue(PacketProtocolLayer *);
-static bool ssh2_connection_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
-static void ssh2_connection_special_cmd(PacketProtocolLayer *ppl,
-                                        SessionSpecialCode code, int arg);
-static bool ssh2_connection_want_user_input(PacketProtocolLayer *ppl);
-static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl);
-static void ssh2_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
-static unsigned int ssh2_connection_winscp_query(PacketProtocolLayer *ppl, int query);
-
-static const PacketProtocolLayerVtable ssh2_connection_vtable = {
-    // WINSCP
-    /*.free =*/ ssh2_connection_free,
-    /*.process_queue =*/ ssh2_connection_process_queue,
-    /*.get_specials =*/ ssh2_connection_get_specials,
-    /*.special_cmd =*/ ssh2_connection_special_cmd,
-    /*.want_user_input =*/ ssh2_connection_want_user_input,
-    /*.got_user_input =*/ ssh2_connection_got_user_input,
-    /*.reconfigure =*/ ssh2_connection_reconfigure,
-    /*.queued_data_size =*/ ssh_ppl_default_queued_data_size,
-    /*.name =*/ "ssh-connection",
-    ssh2_connection_winscp_query,
-};
-
-static SshChannel *ssh2_lportfwd_open(
-    ConnectionLayer *cl, const char *hostname, int port,
-    const char *description, const SocketPeerInfo *pi, Channel *chan);
-static struct X11FakeAuth *ssh2_add_x11_display(
-    ConnectionLayer *cl, int authtype, struct X11Display *x11disp);
-static struct X11FakeAuth *ssh2_add_sharing_x11_display(
-    ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
-    share_channel *share_chan);
-static void ssh2_remove_sharing_x11_display(ConnectionLayer *cl,
-                                            struct X11FakeAuth *auth);
-static void ssh2_send_packet_from_downstream(
-    ConnectionLayer *cl, unsigned id, int type,
-    const void *pkt, int pktlen, const char *additional_log_text);
-static unsigned ssh2_alloc_sharing_channel(
-    ConnectionLayer *cl, ssh_sharing_connstate *connstate);
-static void ssh2_delete_sharing_channel(
-    ConnectionLayer *cl, unsigned localid);
-static void ssh2_sharing_queue_global_request(
-    ConnectionLayer *cl, ssh_sharing_connstate *share_ctx);
-static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl);
-static bool ssh2_agent_forwarding_permitted(ConnectionLayer *cl);
-static void ssh2_terminal_size(ConnectionLayer *cl, int width, int height);
-static void ssh2_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize);
-static size_t ssh2_stdin_backlog(ConnectionLayer *cl);
-static void ssh2_throttle_all_channels(ConnectionLayer *cl, bool throttled);
-static bool ssh2_ldisc_option(ConnectionLayer *cl, int option);
-static void ssh2_set_ldisc_option(ConnectionLayer *cl, int option, bool value);
-static void ssh2_enable_x_fwd(ConnectionLayer *cl);
-static void ssh2_set_wants_user_input(ConnectionLayer *cl, bool wanted);
-
-static const ConnectionLayerVtable ssh2_connlayer_vtable = {
-    // WINSCP
-    /*.rportfwd_alloc =*/ ssh2_rportfwd_alloc,
-    /*.rportfwd_remove =*/ ssh2_rportfwd_remove,
-    /*.lportfwd_open =*/ ssh2_lportfwd_open,
-    /*.session_open =*/ ssh2_session_open,
-    /*.serverside_x11_open =*/ ssh2_serverside_x11_open,
-    /*.serverside_agent_open =*/ ssh2_serverside_agent_open,
-    /*.add_x11_display =*/ ssh2_add_x11_display,
-    /*.add_sharing_x11_display =*/ ssh2_add_sharing_x11_display,
-    /*.remove_sharing_x11_display =*/ ssh2_remove_sharing_x11_display,
-    /*.send_packet_from_downstream =*/ ssh2_send_packet_from_downstream,
-    /*.alloc_sharing_channel =*/ ssh2_alloc_sharing_channel,
-    /*.delete_sharing_channel =*/ ssh2_delete_sharing_channel,
-    /*.sharing_queue_global_request =*/ ssh2_sharing_queue_global_request,
-    /*.sharing_no_more_downstreams =*/ ssh2_sharing_no_more_downstreams,
-    /*.agent_forwarding_permitted =*/ ssh2_agent_forwarding_permitted,
-    /*.terminal_size =*/ ssh2_terminal_size,
-    /*.stdout_unthrottle =*/ ssh2_stdout_unthrottle,
-    /*.stdin_backlog =*/ ssh2_stdin_backlog,
-    /*.throttle_all_channels =*/ ssh2_throttle_all_channels,
-    /*.ldisc_option =*/ ssh2_ldisc_option,
-    /*.set_ldisc_option =*/ ssh2_set_ldisc_option,
-    /*.enable_x_fwd =*/ ssh2_enable_x_fwd,
-    /*.set_wants_user_input =*/ ssh2_set_wants_user_input,
-};
-
-static char *ssh2_channel_open_failure_error_text(PktIn *pktin)
-{
-    static const char *const reasons[] = {
-        NULL,
-        "Administratively prohibited",
-        "Connect failed",
-        "Unknown channel type",
-        "Resource shortage",
-    };
-    unsigned reason_code;
-    const char *reason_code_string;
-    char reason_code_buf[256];
-    ptrlen reason;
-
-    reason_code = get_uint32(pktin);
-    if (reason_code < lenof(reasons) && reasons[reason_code]) {
-        reason_code_string = reasons[reason_code];
-    } else {
-        reason_code_string = reason_code_buf;
-        sprintf(reason_code_buf, "unknown reason code %#x", reason_code);
-    }
-
-    reason = get_string(pktin);
-
-    return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason));
-}
-
-static size_t ssh2channel_write(
-    SshChannel *c, bool is_stderr, const void *buf, size_t len);
-static void ssh2channel_write_eof(SshChannel *c);
-static void ssh2channel_initiate_close(SshChannel *c, const char *err);
-static void ssh2channel_unthrottle(SshChannel *c, size_t bufsize);
-static Conf *ssh2channel_get_conf(SshChannel *c);
-static void ssh2channel_window_override_removed(SshChannel *c);
-static void ssh2channel_x11_sharing_handover(
-    SshChannel *c, ssh_sharing_connstate *share_cs, share_channel *share_chan,
-    const char *peer_addr, int peer_port, int endian,
-    int protomajor, int protominor, const void *initial_data, int initial_len);
-static void ssh2channel_hint_channel_is_simple(SshChannel *c);
-
-static const SshChannelVtable ssh2channel_vtable = {
-    // WINSCP
-    /*.write =*/ ssh2channel_write,
-    /*.write_eof =*/ ssh2channel_write_eof,
-    /*.initiate_close =*/ ssh2channel_initiate_close,
-    /*.unthrottle =*/ ssh2channel_unthrottle,
-    /*.get_conf =*/ ssh2channel_get_conf,
-    /*.window_override_removed =*/ ssh2channel_window_override_removed,
-    /*.x11_sharing_handover =*/ ssh2channel_x11_sharing_handover,
-    /*.send_exit_status =*/ ssh2channel_send_exit_status,
-    /*.send_exit_signal =*/ ssh2channel_send_exit_signal,
-    /*.send_exit_signal_numeric =*/ ssh2channel_send_exit_signal_numeric,
-    /*.request_x11_forwarding =*/ ssh2channel_request_x11_forwarding,
-    /*.request_agent_forwarding =*/ ssh2channel_request_agent_forwarding,
-    /*.request_pty =*/ ssh2channel_request_pty,
-    /*.send_env_var =*/ ssh2channel_send_env_var,
-    /*.start_shell =*/ ssh2channel_start_shell,
-    /*.start_command =*/ ssh2channel_start_command,
-    /*.start_subsystem =*/ ssh2channel_start_subsystem,
-    /*.send_serial_break =*/ ssh2channel_send_serial_break,
-    /*.send_signal =*/ ssh2channel_send_signal,
-    /*.send_terminal_size_change =*/ ssh2channel_send_terminal_size_change,
-    /*.hint_channel_is_simple =*/ ssh2channel_hint_channel_is_simple,
-};
-
-static void ssh2_channel_check_close(struct ssh2_channel *c);
-static void ssh2_channel_try_eof(struct ssh2_channel *c);
-static void ssh2_set_window(struct ssh2_channel *c, int newwin);
-static size_t ssh2_try_send(struct ssh2_channel *c);
-static void ssh2_try_send_and_unthrottle(struct ssh2_channel *c);
-static void ssh2_channel_check_throttle(struct ssh2_channel *c);
-static void ssh2_channel_close_local(struct ssh2_channel *c,
-                                     const char *reason);
-static void ssh2_channel_destroy(struct ssh2_channel *c);
-
-static void ssh2_check_termination(struct ssh2_connection_state *s);
-
-struct outstanding_global_request {
-    gr_handler_fn_t handler;
-    void *ctx;
-    struct outstanding_global_request *next;
-};
-void ssh2_queue_global_request_handler(
-    struct ssh2_connection_state *s, gr_handler_fn_t handler, void *ctx)
-{
-    struct outstanding_global_request *ogr =
-        snew(struct outstanding_global_request);
-    ogr->handler = handler;
-    ogr->ctx = ctx;
-    if (s->globreq_tail)
-        s->globreq_tail->next = ogr;
-    else
-        s->globreq_head = ogr;
-    s->globreq_tail = ogr;
-}
-
-static int ssh2_channelcmp(void *av, void *bv)
-{
-    const struct ssh2_channel *a = (const struct ssh2_channel *) av;
-    const struct ssh2_channel *b = (const struct ssh2_channel *) bv;
-    if (a->localid < b->localid)
-        return -1;
-    if (a->localid > b->localid)
-        return +1;
-    return 0;
-}
-
-static int ssh2_channelfind(void *av, void *bv)
-{
-    const unsigned *a = (const unsigned *) av;
-    const struct ssh2_channel *b = (const struct ssh2_channel *) bv;
-    if (*a < b->localid)
-        return -1;
-    if (*a > b->localid)
-        return +1;
-    return 0;
-}
-
-/*
- * Each channel has a queue of outstanding CHANNEL_REQUESTS and their
- * handlers.
- */
-struct outstanding_channel_request {
-    cr_handler_fn_t handler;
-    void *ctx;
-    struct outstanding_channel_request *next;
-};
-
-static void ssh2_channel_free(struct ssh2_channel *c)
-{
-    bufchain_clear(&c->outbuffer);
-    bufchain_clear(&c->errbuffer);
-    while (c->chanreq_head) {
-        struct outstanding_channel_request *chanreq = c->chanreq_head;
-        c->chanreq_head = c->chanreq_head->next;
-        sfree(chanreq);
-    }
-    if (c->chan) {
-        struct ssh2_connection_state *s = c->connlayer;
-        if (s->mainchan_sc == &c->sc) {
-            s->mainchan = NULL;
-            s->mainchan_sc = NULL;
-        }
-        chan_free(c->chan);
-    }
-    sfree(c);
-}
-
-PacketProtocolLayer *ssh2_connection_new(
-    Ssh *ssh, ssh_sharing_state *connshare, bool is_simple,
-    Conf *conf, const char *peer_verstring, ConnectionLayer **cl_out)
-{
-    struct ssh2_connection_state *s = snew(struct ssh2_connection_state);
-    memset(s, 0, sizeof(*s));
-    s->ppl.vt = &ssh2_connection_vtable;
-
-    s->conf = conf_copy(conf);
-
-    s->ssh_is_simple = is_simple;
-
-    /*
-     * If the ssh_no_shell option is enabled, we disable the usual
-     * termination check, so that we persist even in the absence of
-     * any at all channels (because our purpose is probably to be a
-     * background port forwarder).
-     */
-    s->persistent = conf_get_bool(s->conf, CONF_ssh_no_shell);
-
-    s->connshare = connshare;
-    s->peer_verstring = dupstr(peer_verstring);
-
-    s->channels = newtree234(ssh2_channelcmp);
-
-    s->x11authtree = newtree234(x11_authcmp);
-
-    /* Need to get the log context for s->cl now, because we won't be
-     * helpfully notified when a copy is written into s->ppl by our
-     * owner. */
-    s->cl.vt = &ssh2_connlayer_vtable;
-    s->cl.logctx = ssh_get_logctx(ssh);
-
-    s->portfwdmgr = portfwdmgr_new(&s->cl);
-
-    *cl_out = &s->cl;
-    if (s->connshare)
-        ssh_connshare_provide_connlayer(s->connshare, &s->cl);
-
-    return &s->ppl;
-}
-
-static void ssh2_connection_free(PacketProtocolLayer *ppl)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-    struct X11FakeAuth *auth;
-    struct ssh2_channel *c;
-    struct ssh_rportfwd *rpf;
-
-    sfree(s->peer_verstring);
-
-    conf_free(s->conf);
-
-    while ((c = delpos234(s->channels, 0)) != NULL)
-        ssh2_channel_free(c);
-    freetree234(s->channels);
-
-    while ((auth = delpos234(s->x11authtree, 0)) != NULL) {
-        if (auth->disp)
-            x11_free_display(auth->disp);
-        x11_free_fake_auth(auth);
-    }
-    freetree234(s->x11authtree);
-
-    if (s->rportfwds) {
-        while ((rpf = delpos234(s->rportfwds, 0)) != NULL)
-            free_rportfwd(rpf);
-        freetree234(s->rportfwds);
-    }
-    portfwdmgr_free(s->portfwdmgr);
-
-    if (s->antispoof_prompt)
-        free_prompts(s->antispoof_prompt);
-
-    delete_callbacks_for_context(get_log_callback_set(s->cl.logctx), s);
-
-    sfree(s);
-}
-
-static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s)
-{
-    PktIn *pktin;
-    PktOut *pktout;
-    ptrlen type, data;
-    struct ssh2_channel *c;
-    struct outstanding_channel_request *ocr;
-    unsigned localid, remid, winsize, pktsize, ext_type;
-    bool want_reply, reply_success, expect_halfopen;
-    ChanopenResult chanopen_result;
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-
-    while (1) {
-        if (ssh2_common_filter_queue(&s->ppl))
-        {
-            return true;
-        }
-        if ((pktin = pq_peek(s->ppl.in_pq)) == NULL)
-        {
-            return false;
-        }
-
-        switch (pktin->type) {
-          case SSH2_MSG_GLOBAL_REQUEST:
-            type = get_string(pktin);
-            want_reply = get_bool(pktin);
-
-            reply_success = ssh2_connection_parse_global_request(
-                s, type, pktin);
-
-            if (want_reply) {
-                int type = (reply_success ? SSH2_MSG_REQUEST_SUCCESS :
-                            SSH2_MSG_REQUEST_FAILURE);
-                pktout = ssh_bpp_new_pktout(s->ppl.bpp, type);
-                pq_push(s->ppl.out_pq, pktout);
-            }
-            pq_pop(s->ppl.in_pq);
-            break;
-
-          case SSH2_MSG_REQUEST_SUCCESS:
-          case SSH2_MSG_REQUEST_FAILURE:
-            if (!s->globreq_head) {
-                ssh_proto_error(
-                    s->ppl.ssh,
-                    "Received %s with no outstanding global request",
-                    ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx,
-                                  pktin->type));
-                return true;
-            }
-
-            s->globreq_head->handler(s, pktin, s->globreq_head->ctx);
-            {
-                struct outstanding_global_request *tmp = s->globreq_head;
-                s->globreq_head = s->globreq_head->next;
-                sfree(tmp);
-            }
-
-            pq_pop(s->ppl.in_pq);
-            break;
-
-          case SSH2_MSG_CHANNEL_OPEN:
-            type = get_string(pktin);
-            c = snew(struct ssh2_channel);
-            c->connlayer = s;
-            c->chan = NULL;
-
-            remid = get_uint32(pktin);
-            winsize = get_uint32(pktin);
-            pktsize = get_uint32(pktin);
-
-            chanopen_result = ssh2_connection_parse_channel_open(
-                s, type, pktin, &c->sc);
-
-            if (chanopen_result.outcome == CHANOPEN_RESULT_DOWNSTREAM) {
-                /*
-                 * This channel-open request needs to go to a
-                 * connection-sharing downstream, so abandon our own
-                 * channel-open procedure and just pass the message on
-                 * to sshshare.c.
-                 */
-                share_got_pkt_from_server(
-                    chanopen_result.u.downstream.share_ctx, pktin->type,
-                    BinarySource_UPCAST(pktin)->data,
-                    BinarySource_UPCAST(pktin)->len);
-                sfree(c);
-                break;
-            }
-
-            c->remoteid = remid;
-            c->halfopen = false;
-            if (chanopen_result.outcome == CHANOPEN_RESULT_FAILURE) {
-                pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_CHANNEL_OPEN_FAILURE);
-                put_uint32(pktout, c->remoteid);
-                put_uint32(pktout, chanopen_result.u.failure.reason_code);
-                put_stringz(pktout, chanopen_result.u.failure.wire_message);
-                put_stringz(pktout, "en");      /* language tag */
-                pq_push(s->ppl.out_pq, pktout);
-                ppl_logevent("Rejected channel open: %s",
-                             chanopen_result.u.failure.wire_message);
-                sfree(chanopen_result.u.failure.wire_message);
-                sfree(c);
-            } else {
-                c->chan = chanopen_result.u.success.channel;
-                ssh2_channel_init(c);
-                c->remwindow = winsize;
-                c->remmaxpkt = pktsize;
-                if (c->remmaxpkt > s->ppl.bpp->vt->packet_size_limit)
-                    c->remmaxpkt = s->ppl.bpp->vt->packet_size_limit;
-                if (c->chan->initial_fixed_window_size) {
-                    c->locwindow = c->locmaxwin = c->remlocwin =
-                        c->chan->initial_fixed_window_size;
-                }
-                pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
-                put_uint32(pktout, c->remoteid);
-                put_uint32(pktout, c->localid);
-                put_uint32(pktout, c->locwindow);
-                put_uint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */
-                pq_push(s->ppl.out_pq, pktout);
-            }
-
-            pq_pop(s->ppl.in_pq);
-            break;
-
-          case SSH2_MSG_CHANNEL_DATA:
-          case SSH2_MSG_CHANNEL_EXTENDED_DATA:
-          case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
-          case SSH2_MSG_CHANNEL_REQUEST:
-          case SSH2_MSG_CHANNEL_EOF:
-          case SSH2_MSG_CHANNEL_CLOSE:
-          case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
-          case SSH2_MSG_CHANNEL_OPEN_FAILURE:
-          case SSH2_MSG_CHANNEL_SUCCESS:
-          case SSH2_MSG_CHANNEL_FAILURE:
-            /*
-             * Common preliminary code for all the messages from the
-             * server that cite one of our channel ids: look up that
-             * channel id, check it exists, and if it's for a sharing
-             * downstream, pass it on.
-             */
-            localid = get_uint32(pktin);
-            c = find234(s->channels, &localid, ssh2_channelfind);
-
-            if (c && c->sharectx) {
-                share_got_pkt_from_server(c->sharectx, pktin->type,
-                                          BinarySource_UPCAST(pktin)->data,
-                                          BinarySource_UPCAST(pktin)->len);
-                pq_pop(s->ppl.in_pq);
-                break;
-            }
-
-            expect_halfopen = (
-                pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION ||
-                pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE);
-
-            if (!c || c->halfopen != expect_halfopen) {
-                ssh_proto_error(s->ppl.ssh,
-                                "Received %s for %s channel %u",
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type),
-                                (!c ? "nonexistent" :
-                                 c->halfopen ? "half-open" : "open"),
-                                localid);
-                return true;
-            }
-
-            switch (pktin->type) {
-              case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
-                assert(c->halfopen);
-                c->remoteid = get_uint32(pktin);
-                c->halfopen = false;
-                c->remwindow = get_uint32(pktin);
-                c->remmaxpkt = get_uint32(pktin);
-                if (c->remmaxpkt > s->ppl.bpp->vt->packet_size_limit)
-                    c->remmaxpkt = s->ppl.bpp->vt->packet_size_limit;
-
-                chan_open_confirmation(c->chan);
-
-                /*
-                 * Now that the channel is fully open, it's possible
-                 * in principle to immediately close it. Check whether
-                 * it wants us to!
-                 *
-                 * This can occur if a local socket error occurred
-                 * between us sending out CHANNEL_OPEN and receiving
-                 * OPEN_CONFIRMATION. If that happens, all we can do
-                 * is immediately initiate close proceedings now that
-                 * we know the server's id to put in the close
-                 * message. We'll have handled that in this code by
-                 * having already turned c->chan into a zombie, so its
-                 * want_close method (which ssh2_channel_check_close
-                 * will consult) will already be returning true.
-                 */
-                ssh2_channel_check_close(c);
-
-                if (c->pending_eof)
-                    ssh2_channel_try_eof(c); /* in case we had a pending EOF */
-                break;
-
-              case SSH2_MSG_CHANNEL_OPEN_FAILURE: {
-                assert(c->halfopen);
-
-                { // WINSCP
-                char *err = ssh2_channel_open_failure_error_text(pktin);
-                chan_open_failed(c->chan, err);
-                sfree(err);
-                } // WINSCP
-
-                del234(s->channels, c);
-                ssh2_channel_free(c);
-
-                break;
-              }
-
-              case SSH2_MSG_CHANNEL_DATA:
-              case SSH2_MSG_CHANNEL_EXTENDED_DATA:
-                ext_type = (pktin->type == SSH2_MSG_CHANNEL_DATA ? 0 :
-                            get_uint32(pktin));
-                data = get_string(pktin);
-                if (!get_err(pktin)) {
-                    int bufsize;
-                    c->locwindow -= data.len;
-                    c->remlocwin -= data.len;
-                    if (ext_type != 0 && ext_type != SSH2_EXTENDED_DATA_STDERR)
-                        data.len = 0; /* ignore unknown extended data */
-                    bufsize = chan_send(
-                        c->chan, ext_type == SSH2_EXTENDED_DATA_STDERR,
-                        data.ptr, data.len);
-
-                    /*
-                     * The channel may have turned into a connection-
-                     * shared one as a result of that chan_send, e.g.
-                     * if the data we just provided completed the X11
-                     * auth phase and caused a callback to
-                     * x11_sharing_handover. If so, do nothing
-                     * further.
-                     */
-                    if (c->sharectx)
-                        break;
-
-                    /*
-                     * If it looks like the remote end hit the end of
-                     * its window, and we didn't want it to do that,
-                     * think about using a larger window.
-                     */
-                    if (c->remlocwin <= 0 &&
-                        c->throttle_state == UNTHROTTLED &&
-                        c->locmaxwin < 0x40000000)
-                        c->locmaxwin += OUR_V2_WINSIZE;
-
-                    /*
-                     * If we are not buffering too much data, enlarge
-                     * the window again at the remote side. If we are
-                     * buffering too much, we may still need to adjust
-                     * the window if the server's sent excess data.
-                     */
-                    if (bufsize < c->locmaxwin)
-                        ssh2_set_window(c, c->locmaxwin - bufsize);
-
-                    /*
-                     * If we're either buffering way too much data, or
-                     * if we're buffering anything at all and we're in
-                     * "simple" mode, throttle the whole channel.
-                     */
-                    if ((bufsize > c->locmaxwin ||
-                         (s->ssh_is_simple && bufsize>0)) &&
-                        !c->throttling_conn) {
-                        c->throttling_conn = true;
-                        ssh_throttle_conn(s->ppl.ssh, +1);
-                    }
-                }
-                break;
-
-              case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
-                if (!(c->closes & CLOSES_SENT_EOF)) {
-                    c->remwindow += get_uint32(pktin);
-                    ssh2_try_send_and_unthrottle(c);
-                }
-                break;
-
-              case SSH2_MSG_CHANNEL_REQUEST:
-                type = get_string(pktin);
-                want_reply = get_bool(pktin);
-
-                reply_success = false;
-
-                if (c->closes & CLOSES_SENT_CLOSE) {
-                    /*
-                     * We don't reply to channel requests after we've
-                     * sent CHANNEL_CLOSE for the channel, because our
-                     * reply might cross in the network with the other
-                     * side's CHANNEL_CLOSE and arrive after they have
-                     * wound the channel up completely.
-                     */
-                    want_reply = false;
-                }
-
-                /*
-                 * Try every channel request name we recognise, no
-                 * matter what the channel, and see if the Channel
-                 * instance will accept it.
-                 */
-                if (ptrlen_eq_string(type, "exit-status")) {
-                    int exitcode = toint(get_uint32(pktin));
-                    reply_success = chan_rcvd_exit_status(c->chan, exitcode);
-                } else if (ptrlen_eq_string(type, "exit-signal")) {
-                    ptrlen signame;
-                    int signum;
-                    bool core = false;
-                    ptrlen errmsg;
-                    int format;
-
-                    /*
-                     * ICK: older versions of OpenSSH (e.g. 3.4p1)
-                     * provide an `int' for the signal, despite its
-                     * having been a `string' in the drafts of RFC
-                     * 4254 since at least 2001. (Fixed in session.c
-                     * 1.147.) Try to infer which we can safely parse
-                     * it as.
-                     */
-
-                    size_t startpos = BinarySource_UPCAST(pktin)->pos;
-
-                    for (format = 0; format < 2; format++) {
-                        BinarySource_UPCAST(pktin)->pos = startpos;
-                        BinarySource_UPCAST(pktin)->err = BSE_NO_ERROR;
-
-                        /* placate compiler warnings about unin */
-                        signame = make_ptrlen(NULL, 0);
-                        signum = 0;
-
-                        if (format == 0) /* standard string-based format */
-                            signame = get_string(pktin);
-                        else      /* nonstandard integer format */
-                            signum = toint(get_uint32(pktin));
-
-                        core = get_bool(pktin);
-                        errmsg = get_string(pktin); /* error message */
-                        get_string(pktin);     /* language tag */
-
-                        if (!get_err(pktin) && get_avail(pktin) == 0)
-                            break;             /* successful parse */
-                    }
-
-                    switch (format) {
-                      case 0:
-                        reply_success = chan_rcvd_exit_signal(
-                            c->chan, signame, core, errmsg);
-                        break;
-                      case 1:
-                        reply_success = chan_rcvd_exit_signal_numeric(
-                            c->chan, signum, core, errmsg);
-                        break;
-                      default:
-                        /* Couldn't parse this message in either format */
-                        reply_success = false;
-                        break;
-                    }
-                } else if (ptrlen_eq_string(type, "shell")) {
-                    reply_success = chan_run_shell(c->chan);
-                } else if (ptrlen_eq_string(type, "exec")) {
-                    ptrlen command = get_string(pktin);
-                    reply_success = chan_run_command(c->chan, command);
-                } else if (ptrlen_eq_string(type, "subsystem")) {
-                    ptrlen subsys = get_string(pktin);
-                    reply_success = chan_run_subsystem(c->chan, subsys);
-                } else if (ptrlen_eq_string(type, "x11-req")) {
-                    bool oneshot = get_bool(pktin);
-                    ptrlen authproto = get_string(pktin);
-                    ptrlen authdata = get_string(pktin);
-                    unsigned screen_number = get_uint32(pktin);
-                    reply_success = chan_enable_x11_forwarding(
-                        c->chan, oneshot, authproto, authdata, screen_number);
-                } else if (ptrlen_eq_string(type,
-                                            "[email protected]")) {
-                    reply_success = chan_enable_agent_forwarding(c->chan);
-                } else if (ptrlen_eq_string(type, "pty-req")) {
-                    ptrlen termtype = get_string(pktin);
-                    unsigned width = get_uint32(pktin);
-                    unsigned height = get_uint32(pktin);
-                    unsigned pixwidth = get_uint32(pktin);
-                    unsigned pixheight = get_uint32(pktin);
-                    ptrlen encoded_modes = get_string(pktin);
-                    BinarySource bs_modes[1];
-                    struct ssh_ttymodes modes;
-
-                    BinarySource_BARE_INIT_PL(bs_modes, encoded_modes);
-                    modes = read_ttymodes_from_packet(bs_modes, 2);
-                    if (get_err(bs_modes) || get_avail(bs_modes) > 0) {
-                        ppl_logevent("Unable to decode terminal mode string");
-                        reply_success = false;
-                    } else {
-                        reply_success = chan_allocate_pty(
-                            c->chan, termtype, width, height,
-                            pixwidth, pixheight, modes);
-                    }
-                } else if (ptrlen_eq_string(type, "env")) {
-                    ptrlen var = get_string(pktin);
-                    ptrlen value = get_string(pktin);
-
-                    reply_success = chan_set_env(c->chan, var, value);
-                } else if (ptrlen_eq_string(type, "break")) {
-                    unsigned length = get_uint32(pktin);
-
-                    reply_success = chan_send_break(c->chan, length);
-                } else if (ptrlen_eq_string(type, "signal")) {
-                    ptrlen signame = get_string(pktin);
-
-                    reply_success = chan_send_signal(c->chan, signame);
-                } else if (ptrlen_eq_string(type, "window-change")) {
-                    unsigned width = get_uint32(pktin);
-                    unsigned height = get_uint32(pktin);
-                    unsigned pixwidth = get_uint32(pktin);
-                    unsigned pixheight = get_uint32(pktin);
-                    reply_success = chan_change_window_size(
-                        c->chan, width, height, pixwidth, pixheight);
-                }
-                if (want_reply) {
-                    int type = (reply_success ? SSH2_MSG_CHANNEL_SUCCESS :
-                                SSH2_MSG_CHANNEL_FAILURE);
-                    pktout = ssh_bpp_new_pktout(s->ppl.bpp, type);
-                    put_uint32(pktout, c->remoteid);
-                    pq_push(s->ppl.out_pq, pktout);
-                }
-                break;
-
-              case SSH2_MSG_CHANNEL_SUCCESS:
-              case SSH2_MSG_CHANNEL_FAILURE:
-                ocr = c->chanreq_head;
-                if (!ocr) {
-                    ssh_proto_error(
-                        s->ppl.ssh,
-                        "Received %s for channel %d with no outstanding "
-                        "channel request",
-                        ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                      s->ppl.bpp->pls->actx, pktin->type),
-                        c->localid);
-                    return true;
-                }
-                ocr->handler(c, pktin, ocr->ctx);
-                c->chanreq_head = ocr->next;
-                sfree(ocr);
-                /*
-                 * We may now initiate channel-closing procedures, if
-                 * that CHANNEL_REQUEST was the last thing outstanding
-                 * before we send CHANNEL_CLOSE.
-                 */
-                ssh2_channel_check_close(c);
-                break;
-
-              case SSH2_MSG_CHANNEL_EOF:
-                if (!(c->closes & CLOSES_RCVD_EOF)) {
-                    c->closes |= CLOSES_RCVD_EOF;
-                    chan_send_eof(c->chan);
-                    ssh2_channel_check_close(c);
-                }
-                break;
-
-              case SSH2_MSG_CHANNEL_CLOSE:
-                /*
-                 * When we receive CLOSE on a channel, we assume it
-                 * comes with an implied EOF if we haven't seen EOF
-                 * yet.
-                 */
-                if (!(c->closes & CLOSES_RCVD_EOF)) {
-                    c->closes |= CLOSES_RCVD_EOF;
-                    chan_send_eof(c->chan);
-                }
-
-                if (!(s->ppl.remote_bugs & BUG_SENDS_LATE_REQUEST_REPLY)) {
-                    /*
-                     * It also means we stop expecting to see replies
-                     * to any outstanding channel requests, so clean
-                     * those up too. (ssh_chanreq_init will enforce by
-                     * assertion that we don't subsequently put
-                     * anything back on this list.)
-                     */
-                    while (c->chanreq_head) {
-                        struct outstanding_channel_request *ocr =
-                            c->chanreq_head;
-                        ocr->handler(c, NULL, ocr->ctx);
-                        c->chanreq_head = ocr->next;
-                        sfree(ocr);
-                    }
-                }
-
-                /*
-                 * And we also send an outgoing EOF, if we haven't
-                 * already, on the assumption that CLOSE is a pretty
-                 * forceful announcement that the remote side is doing
-                 * away with the entire channel. (If it had wanted to
-                 * send us EOF and continue receiving data from us, it
-                 * would have just sent CHANNEL_EOF.)
-                 */
-                if (!(c->closes & CLOSES_SENT_EOF)) {
-                    /*
-                     * Abandon any buffered data we still wanted to
-                     * send to this channel. Receiving a CHANNEL_CLOSE
-                     * is an indication that the server really wants
-                     * to get on and _destroy_ this channel, and it
-                     * isn't going to send us any further
-                     * WINDOW_ADJUSTs to permit us to send pending
-                     * stuff.
-                     */
-                    bufchain_clear(&c->outbuffer);
-                    bufchain_clear(&c->errbuffer);
-
-                    /*
-                     * Send outgoing EOF.
-                     */
-                    sshfwd_write_eof(&c->sc);
-
-                    /*
-                     * Make sure we don't read any more from whatever
-                     * our local data source is for this channel.
-                     * (This will pick up on the changes made by
-                     * sshfwd_write_eof.)
-                     */
-                    ssh2_channel_check_throttle(c);
-                }
-
-                /*
-                 * Now process the actual close.
-                 */
-                if (!(c->closes & CLOSES_RCVD_CLOSE)) {
-                    c->closes |= CLOSES_RCVD_CLOSE;
-                    ssh2_channel_check_close(c);
-                }
-
-                break;
-            }
-
-            pq_pop(s->ppl.in_pq);
-            break;
-
-          default:
-            return false;
-        }
-    }
-}
-
-static void ssh2_handle_winadj_response(struct ssh2_channel *c,
-                                        PktIn *pktin, void *ctx)
-{
-    unsigned *sizep = ctx;
-
-    /*
-     * Winadj responses should always be failures. However, at least
-     * one server ("boks_sshd") is known to return SUCCESS for channel
-     * requests it's never heard of, such as "winadj@putty". Raised
-     * with foxt.com as bug 090916-090424, but for the sake of a quiet
-     * life, we don't worry about what kind of response we got.
-     */
-
-    c->remlocwin += *sizep;
-    sfree(sizep);
-    /*
-     * winadj messages are only sent when the window is fully open, so
-     * if we get an ack of one, we know any pending unthrottle is
-     * complete.
-     */
-    if (c->throttle_state == UNTHROTTLING)
-        c->throttle_state = UNTHROTTLED;
-}
-
-static void ssh2_set_window(struct ssh2_channel *c, int newwin)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-
-    /*
-     * Never send WINDOW_ADJUST for a channel that the remote side has
-     * already sent EOF on; there's no point, since it won't be
-     * sending any more data anyway. Ditto if _we've_ already sent
-     * CLOSE.
-     */
-    if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
-        return;
-
-    /*
-     * If the client-side Channel is in an initial setup phase with a
-     * fixed window size, e.g. for an X11 channel when we're still
-     * waiting to see its initial auth and may yet hand it off to a
-     * downstream, don't send any WINDOW_ADJUST either.
-     */
-    if (c->chan->initial_fixed_window_size)
-        return;
-
-    /*
-     * If the remote end has a habit of ignoring maxpkt, limit the
-     * window so that it has no choice (assuming it doesn't ignore the
-     * window as well).
-     */
-    if ((s->ppl.remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)
-        newwin = OUR_V2_MAXPKT;
-
-    /*
-     * Only send a WINDOW_ADJUST if there's significantly more window
-     * available than the other end thinks there is.  This saves us
-     * sending a WINDOW_ADJUST for every character in a shell session.
-     *
-     * "Significant" is arbitrarily defined as half the window size.
-     */
-    if (newwin / 2 >= c->locwindow) {
-        PktOut *pktout;
-        unsigned *up;
-
-        /*
-         * In order to keep track of how much window the client
-         * actually has available, we'd like it to acknowledge each
-         * WINDOW_ADJUST.  We can't do that directly, so we accompany
-         * it with a CHANNEL_REQUEST that has to be acknowledged.
-         *
-         * This is only necessary if we're opening the window wide.
-         * If we're not, then throughput is being constrained by
-         * something other than the maximum window size anyway.
-         */
-        if (newwin == c->locmaxwin &&
-            !(s->ppl.remote_bugs & BUG_CHOKES_ON_WINADJ)) {
-            up = snew(unsigned);
-            *up = newwin - c->locwindow;
-            pktout = ssh2_chanreq_init(c, "[email protected]",
-                                       ssh2_handle_winadj_response, up);
-            pq_push(s->ppl.out_pq, pktout);
-
-            if (c->throttle_state != UNTHROTTLED)
-                c->throttle_state = UNTHROTTLING;
-        } else {
-            /* Pretend the WINDOW_ADJUST was acked immediately. */
-            c->remlocwin = newwin;
-            c->throttle_state = THROTTLED;
-        }
-        pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_WINDOW_ADJUST);
-        put_uint32(pktout, c->remoteid);
-        put_uint32(pktout, newwin - c->locwindow);
-        pq_push(s->ppl.out_pq, pktout);
-        c->locwindow = newwin;
-    }
-}
-
-static PktIn *ssh2_connection_pop(struct ssh2_connection_state *s)
-{
-    ssh2_connection_filter_queue(s);
-    return pq_pop(s->ppl.in_pq);
-}
-
-static void ssh2_connection_process_queue(PacketProtocolLayer *ppl)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-    PktIn *pktin;
-
-    if (ssh2_connection_filter_queue(s)) /* no matter why we were called */
-        return;
-
-    crBegin(s->crState);
-
-    if (s->connshare)
-        share_activate(s->connshare, s->peer_verstring);
-
-    /*
-     * Signal the seat that authentication is done, so that it can
-     * deploy spoofing defences. If it doesn't have any, deploy our
-     * own fallback one.
-     *
-     * We do this here rather than at the end of userauth, because we
-     * might not have gone through userauth at all (if we're a
-     * connection-sharing downstream).
-     */
-    if (ssh2_connection_need_antispoof_prompt(s)) {
-        s->antispoof_prompt = new_prompts();
-        s->antispoof_prompt->to_server = true;
-        s->antispoof_prompt->from_server = false;
-        s->antispoof_prompt->name = dupstr("Authentication successful");
-        add_prompt(
-            s->antispoof_prompt,
-            dupstr("Access granted. Press Return to begin session. "), false);
-        s->antispoof_ret = seat_get_userpass_input(
-            s->ppl.seat, s->antispoof_prompt, NULL);
-        while (1) {
-            while (s->antispoof_ret < 0 &&
-                   bufchain_size(s->ppl.user_input) > 0)
-                s->antispoof_ret = seat_get_userpass_input(
-                    s->ppl.seat, s->antispoof_prompt, s->ppl.user_input);
-
-            if (s->antispoof_ret >= 0)
-                break;
-
-            s->want_user_input = true;
-            crReturnV;
-            s->want_user_input = false;
-        }
-        free_prompts(s->antispoof_prompt);
-        s->antispoof_prompt = NULL;
-    }
-
-    /*
-     * Enable port forwardings.
-     */
-    portfwdmgr_config(s->portfwdmgr, s->conf);
-    s->portfwdmgr_configured = true;
-
-    /*
-     * Create the main session channel, if any.
-     */
-    s->mainchan = mainchan_new(
-        &s->ppl, &s->cl, s->conf, s->term_width, s->term_height,
-        s->ssh_is_simple, &s->mainchan_sc);
-    s->started = true;
-    // WINSCP
-    if (!s->mainchan)
-    {
-        s->ready = true;
-    }
-
-    /*
-     * Transfer data!
-     */
-
-    while (1) {
-        if ((pktin = ssh2_connection_pop(s)) != NULL) {
-
-            /*
-             * _All_ the connection-layer packets we expect to
-             * receive are now handled by the dispatch table.
-             * Anything that reaches here must be bogus.
-             */
-
-            ssh_proto_error(s->ppl.ssh, "Received unexpected connection-layer "
-                            "packet, type %d (%s)", pktin->type,
-                            ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                          s->ppl.bpp->pls->actx,
-                                          pktin->type));
-            return;
-        }
-        crReturnV;
-    }
-
-    crFinishV;
-}
-
-static void ssh2_channel_check_close(struct ssh2_channel *c)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    PktOut *pktout;
-
-    if (c->halfopen) {
-        /*
-         * If we've sent out our own CHANNEL_OPEN but not yet seen
-         * either OPEN_CONFIRMATION or OPEN_FAILURE in response, then
-         * it's too early to be sending close messages of any kind.
-         */
-        return;
-    }
-
-    if (chan_want_close(c->chan, (c->closes & CLOSES_SENT_EOF),
-                        (c->closes & CLOSES_RCVD_EOF)) &&
-        !c->chanreq_head &&
-        !(c->closes & CLOSES_SENT_CLOSE)) {
-        /*
-         * We have both sent and received EOF (or the channel is a
-         * zombie), and we have no outstanding channel requests, which
-         * means the channel is in final wind-up. But we haven't sent
-         * CLOSE, so let's do so now.
-         */
-        pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_CLOSE);
-        put_uint32(pktout, c->remoteid);
-        pq_push(s->ppl.out_pq, pktout);
-        c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
-    }
-
-    if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) {
-        assert(c->chanreq_head == NULL);
-        /*
-         * We have both sent and received CLOSE, which means we're
-         * completely done with the channel.
-         */
-        ssh2_channel_destroy(c);
-    }
-}
-
-static void ssh2_channel_try_eof(struct ssh2_channel *c)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    PktOut *pktout;
-    assert(c->pending_eof);          /* precondition for calling us */
-    if (c->halfopen)
-        return;                 /* can't close: not even opened yet */
-    if (bufchain_size(&c->outbuffer) > 0 || bufchain_size(&c->errbuffer) > 0)
-        return;              /* can't send EOF: pending outgoing data */
-
-    c->pending_eof = false;            /* we're about to send it */
-
-    pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_EOF);
-    put_uint32(pktout, c->remoteid);
-    pq_push(s->ppl.out_pq, pktout);
-    c->closes |= CLOSES_SENT_EOF;
-    ssh2_channel_check_close(c);
-}
-
-/*
- * Attempt to send data on an SSH-2 channel.
- */
-static size_t ssh2_try_send(struct ssh2_channel *c)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    PktOut *pktout;
-    size_t bufsize;
-
-    if (!c->halfopen) {
-        while (c->remwindow > 0 &&
-               (bufchain_size(&c->outbuffer) > 0 ||
-                bufchain_size(&c->errbuffer) > 0)) {
-            bufchain *buf = (bufchain_size(&c->errbuffer) > 0 ?
-                             &c->errbuffer : &c->outbuffer);
-
-            ptrlen data = bufchain_prefix(buf);
-            if (data.len > c->remwindow)
-                data.len = c->remwindow;
-            if (data.len > c->remmaxpkt)
-                data.len = c->remmaxpkt;
-            if (buf == &c->errbuffer) {
-                pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_CHANNEL_EXTENDED_DATA);
-                put_uint32(pktout, c->remoteid);
-                put_uint32(pktout, SSH2_EXTENDED_DATA_STDERR);
-            } else {
-                pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_DATA);
-                put_uint32(pktout, c->remoteid);
-            }
-            put_stringpl(pktout, data);
-            pq_push(s->ppl.out_pq, pktout);
-            bufchain_consume(buf, data.len);
-            c->remwindow -= data.len;
-        }
-    }
-
-    /*
-     * After having sent as much data as we can, return the amount
-     * still buffered.
-     */
-    bufsize = bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer);
-
-    /*
-     * And if there's no data pending but we need to send an EOF, send
-     * it.
-     */
-    if (!bufsize && c->pending_eof)
-        ssh2_channel_try_eof(c);
-
-    return bufsize;
-}
-
-static void ssh2_try_send_and_unthrottle(struct ssh2_channel *c)
-{
-    int bufsize;
-    if (c->closes & CLOSES_SENT_EOF)
-        return;                   /* don't send on channels we've EOFed */
-    bufsize = ssh2_try_send(c);
-    if (bufsize == 0) {
-        c->throttled_by_backlog = false;
-        ssh2_channel_check_throttle(c);
-    }
-}
-
-static void ssh2_channel_check_throttle(struct ssh2_channel *c)
-{
-    /*
-     * We don't want this channel to read further input if this
-     * particular channel has a backed-up SSH window, or if the
-     * outgoing side of the whole SSH connection is currently
-     * throttled, or if this channel already has an outgoing EOF
-     * either sent or pending.
-     */
-    chan_set_input_wanted(c->chan,
-                          !c->throttled_by_backlog &&
-                          !c->connlayer->all_channels_throttled &&
-                          !c->pending_eof &&
-                          !(c->closes & CLOSES_SENT_EOF));
-}
-
-/*
- * Close any local socket and free any local resources associated with
- * a channel.  This converts the channel into a zombie.
- */
-static void ssh2_channel_close_local(struct ssh2_channel *c,
-                                     const char *reason)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    char *msg = NULL;
-
-    if (c->sharectx)
-        return;
-
-    msg = chan_log_close_msg(c->chan);
-
-    if (msg)
-        ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : "");
-
-    sfree(msg);
-
-    chan_free(c->chan);
-    c->chan = zombiechan_new();
-}
-
-static void ssh2_check_termination_callback(void *vctx)
-{
-    struct ssh2_connection_state *s = (struct ssh2_connection_state *)vctx;
-    ssh2_check_termination(s);
-}
-
-static void ssh2_channel_destroy(struct ssh2_channel *c)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-
-    assert(c->chanreq_head == NULL);
-
-    ssh2_channel_close_local(c, NULL);
-    del234(s->channels, c);
-    ssh2_channel_free(c);
-
-    /*
-     * If that was the last channel left open, we might need to
-     * terminate. But we'll be a bit cautious, by doing that in a
-     * toplevel callback, just in case anything on the current call
-     * stack objects to this entire PPL being freed.
-     */
-    queue_toplevel_callback(ssh2_check_termination_callback, s);
-}
-
-static void ssh2_check_termination(struct ssh2_connection_state *s)
-{
-    /*
-     * Decide whether we should terminate the SSH connection now.
-     * Called after a channel or a downstream goes away. The general
-     * policy is that we terminate when none of either is left.
-     */
-
-    if (s->persistent)
-        return;     /* persistent mode: never proactively terminate */
-
-    if (!s->started) {
-        /* At startup, we don't have any channels open because we
-         * haven't got round to opening the main one yet. In that
-         * situation, we don't want to terminate, even if a sharing
-         * connection opens and closes and causes a call to this
-         * function. */
-        return;
-    }
-
-    if (count234(s->channels) == 0 &&
-        !(s->connshare && share_ndownstreams(s->connshare) > 0)) {
-        /*
-         * We used to send SSH_MSG_DISCONNECT here, because I'd
-         * believed that _every_ conforming SSH-2 connection had to
-         * end with a disconnect being sent by at least one side;
-         * apparently I was wrong and it's perfectly OK to
-         * unceremoniously slam the connection shut when you're done,
-         * and indeed OpenSSH feels this is more polite than sending a
-         * DISCONNECT. So now we don't.
-         */
-        ssh_user_close(s->ppl.ssh, "All channels closed");
-        return;
-    }
-}
-
-/*
- * Set up most of a new ssh2_channel. Nulls out sharectx, but leaves
- * chan untouched (since it will sometimes have been filled in before
- * calling this).
- */
-void ssh2_channel_init(struct ssh2_channel *c)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    c->closes = 0;
-    c->pending_eof = false;
-    c->throttling_conn = false;
-    c->throttled_by_backlog = false;
-    c->sharectx = NULL;
-    c->locwindow = c->locmaxwin = c->remlocwin =
-        s->ssh_is_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
-    c->chanreq_head = NULL;
-    c->throttle_state = UNTHROTTLED;
-    bufchain_init(&c->outbuffer);
-    bufchain_init(&c->errbuffer);
-    c->sc.vt = &ssh2channel_vtable;
-    c->sc.cl = &s->cl;
-    c->localid = alloc_channel_id(s->channels, struct ssh2_channel);
-    add234(s->channels, c);
-}
-
-/*
- * Construct the common parts of a CHANNEL_OPEN.
- */
-PktOut *ssh2_chanopen_init(struct ssh2_channel *c, const char *type)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    PktOut *pktout;
-
-    pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_OPEN);
-    put_stringz(pktout, type);
-    put_uint32(pktout, c->localid);
-    put_uint32(pktout, c->locwindow);     /* our window size */
-    put_uint32(pktout, OUR_V2_MAXPKT);    /* our max pkt size */
-    return pktout;
-}
-
-/*
- * Construct the common parts of a CHANNEL_REQUEST.  If handler is not
- * NULL then a reply will be requested and the handler will be called
- * when it arrives.  The returned packet is ready to have any
- * request-specific data added and be sent.  Note that if a handler is
- * provided, it's essential that the request actually be sent.
- *
- * The handler will usually be passed the response packet in pktin. If
- * pktin is NULL, this means that no reply will ever be forthcoming
- * (e.g. because the entire connection is being destroyed, or because
- * the server initiated channel closure before we saw the response)
- * and the handler should free any storage it's holding.
- */
-PktOut *ssh2_chanreq_init(struct ssh2_channel *c, const char *type,
-                          cr_handler_fn_t handler, void *ctx)
-{
-    struct ssh2_connection_state *s = c->connlayer;
-    PktOut *pktout;
-
-    assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE)));
-    pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_REQUEST);
-    put_uint32(pktout, c->remoteid);
-    put_stringz(pktout, type);
-    put_bool(pktout, handler != NULL);
-    if (handler != NULL) {
-        struct outstanding_channel_request *ocr =
-            snew(struct outstanding_channel_request);
-
-        ocr->handler = handler;
-        ocr->ctx = ctx;
-        ocr->next = NULL;
-        if (!c->chanreq_head)
-            c->chanreq_head = ocr;
-        else
-            c->chanreq_tail->next = ocr;
-        c->chanreq_tail = ocr;
-    }
-    return pktout;
-}
-
-static Conf *ssh2channel_get_conf(SshChannel *sc)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-    return s->conf;
-}
-
-static void ssh2channel_write_eof(SshChannel *sc)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-
-    if (c->closes & CLOSES_SENT_EOF)
-        return;
-
-    c->pending_eof = true;
-    ssh2_channel_try_eof(c);
-}
-
-static void ssh2channel_initiate_close(SshChannel *sc, const char *err)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    char *reason;
-
-    reason = err ? dupprintf("due to local error: %s", err) : NULL;
-    ssh2_channel_close_local(c, reason);
-    sfree(reason);
-    c->pending_eof = false;   /* this will confuse a zombie channel */
-
-    ssh2_channel_check_close(c);
-}
-
-static void ssh2channel_unthrottle(SshChannel *sc, size_t bufsize)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-    size_t buflimit;
-
-    buflimit = s->ssh_is_simple ? 0 : c->locmaxwin;
-    if (bufsize < buflimit)
-        ssh2_set_window(c, buflimit - bufsize);
-
-    if (c->throttling_conn && bufsize <= buflimit) {
-        c->throttling_conn = false;
-        ssh_throttle_conn(s->ppl.ssh, -1);
-    }
-}
-
-static size_t ssh2channel_write(
-    SshChannel *sc, bool is_stderr, const void *buf, size_t len)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    assert(!(c->closes & CLOSES_SENT_EOF));
-    bufchain_add(is_stderr ? &c->errbuffer : &c->outbuffer, buf, len);
-    return ssh2_try_send(c);
-}
-
-static void ssh2channel_x11_sharing_handover(
-    SshChannel *sc, ssh_sharing_connstate *share_cs, share_channel *share_chan,
-    const char *peer_addr, int peer_port, int endian,
-    int protomajor, int protominor, const void *initial_data, int initial_len)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    /*
-     * This function is called when we've just discovered that an X
-     * forwarding channel on which we'd been handling the initial auth
-     * ourselves turns out to be destined for a connection-sharing
-     * downstream. So we turn the channel into a sharing one, meaning
-     * that we completely stop tracking windows and buffering data and
-     * just pass more or less unmodified SSH messages back and forth.
-     */
-    c->sharectx = share_cs;
-    share_setup_x11_channel(share_cs, share_chan,
-                            c->localid, c->remoteid, c->remwindow,
-                            c->remmaxpkt, c->locwindow,
-                            peer_addr, peer_port, endian,
-                            protomajor, protominor,
-                            initial_data, initial_len);
-    chan_free(c->chan);
-    c->chan = NULL;
-}
-
-static void ssh2channel_window_override_removed(SshChannel *sc)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    /*
-     * This function is called when a client-side Channel has just
-     * stopped requiring an initial fixed-size window.
-     */
-    assert(!c->chan->initial_fixed_window_size);
-    ssh2_set_window(c, s->ssh_is_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE);
-}
-
-static void ssh2channel_hint_channel_is_simple(SshChannel *sc)
-{
-    struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc);
-    struct ssh2_connection_state *s = c->connlayer;
-
-    PktOut *pktout = ssh2_chanreq_init(
-        c, "[email protected]", NULL, NULL);
-    pq_push(s->ppl.out_pq, pktout);
-}
-
-static SshChannel *ssh2_lportfwd_open(
-    ConnectionLayer *cl, const char *hostname, int port,
-    const char *description, const SocketPeerInfo *pi, Channel *chan)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct ssh2_channel *c = snew(struct ssh2_channel);
-    PktOut *pktout;
-
-    c->connlayer = s;
-    ssh2_channel_init(c);
-    c->halfopen = true;
-    c->chan = chan;
-
-    pktout = ssh2_portfwd_chanopen(s, c, hostname, port, description, pi);
-    pq_push(s->ppl.out_pq, pktout);
-
-    return &c->sc;
-}
-
-static void ssh2_sharing_globreq_response(
-    struct ssh2_connection_state *s, PktIn *pktin, void *ctx)
-{
-    ssh_sharing_connstate *cs = (ssh_sharing_connstate *)ctx;
-    share_got_pkt_from_server(cs, pktin->type,
-                              BinarySource_UPCAST(pktin)->data,
-                              BinarySource_UPCAST(pktin)->len);
-}
-
-static void ssh2_sharing_queue_global_request(
-    ConnectionLayer *cl, ssh_sharing_connstate *cs)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    ssh2_queue_global_request_handler(s, ssh2_sharing_globreq_response, cs);
-}
-
-static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    queue_toplevel_callback(ssh2_check_termination_callback, s);
-}
-
-static struct X11FakeAuth *ssh2_add_x11_display(
-    ConnectionLayer *cl, int authtype, struct X11Display *disp)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct X11FakeAuth *auth = x11_invent_fake_auth(s->x11authtree, authtype);
-    auth->disp = disp;
-    return auth;
-}
-
-static struct X11FakeAuth *ssh2_add_sharing_x11_display(
-    ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
-    share_channel *share_chan)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct X11FakeAuth *auth;
-
-    /*
-     * Make up a new set of fake X11 auth data, and add it to the tree
-     * of currently valid ones with an indication of the sharing
-     * context that it's relevant to.
-     */
-    auth = x11_invent_fake_auth(s->x11authtree, authtype);
-    auth->share_cs = share_cs;
-    auth->share_chan = share_chan;
-
-    return auth;
-}
-
-static void ssh2_remove_sharing_x11_display(
-    ConnectionLayer *cl, struct X11FakeAuth *auth)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    del234(s->x11authtree, auth);
-    x11_free_fake_auth(auth);
-}
-
-static unsigned ssh2_alloc_sharing_channel(
-    ConnectionLayer *cl, ssh_sharing_connstate *connstate)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct ssh2_channel *c = snew(struct ssh2_channel);
-
-    c->connlayer = s;
-    ssh2_channel_init(c);
-    c->chan = NULL;
-    c->sharectx = connstate;
-    return c->localid;
-}
-
-static void ssh2_delete_sharing_channel(ConnectionLayer *cl, unsigned localid)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct ssh2_channel *c = find234(s->channels, &localid, ssh2_channelfind);
-    if (c)
-        ssh2_channel_destroy(c);
-}
-
-static void ssh2_send_packet_from_downstream(
-        ConnectionLayer *cl, unsigned id, int type,
-        const void *data, int datalen, const char *additional_log_text)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    PktOut *pkt = ssh_bpp_new_pktout(s->ppl.bpp, type);
-    pkt->downstream_id = id;
-    pkt->additional_log_text = additional_log_text;
-    put_data(pkt, data, datalen);
-    pq_push(s->ppl.out_pq, pkt);
-}
-
-static bool ssh2_agent_forwarding_permitted(ConnectionLayer *cl)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    return conf_get_bool(s->conf, CONF_agentfwd) && agent_exists();
-}
-
-static bool ssh2_connection_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-    bool toret = false;
-
-    if (s->mainchan) {
-        mainchan_get_specials(s->mainchan, add_special, ctx);
-        toret = true;
-    }
-
-    /*
-     * Don't bother offering IGNORE if we've decided the remote
-     * won't cope with it, since we wouldn't bother sending it if
-     * asked anyway.
-     */
-    if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
-        if (toret)
-            add_special(ctx, NULL, SS_SEP, 0);
-
-        add_special(ctx, "IGNORE message", SS_NOP, 0);
-        toret = true;
-    }
-
-    return toret;
-}
-
-static void ssh2_connection_special_cmd(PacketProtocolLayer *ppl,
-                                        SessionSpecialCode code, int arg)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-    PktOut *pktout;
-
-    if (code == SS_PING || code == SS_NOP) {
-        if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
-            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_IGNORE);
-            put_stringz(pktout, "");
-            pq_push(s->ppl.out_pq, pktout);
-        }
-    } else if (s->mainchan) {
-        mainchan_special_cmd(s->mainchan, code, arg);
-    }
-}
-
-static void ssh2_terminal_size(ConnectionLayer *cl, int width, int height)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    s->term_width = width;
-    s->term_height = height;
-    if (s->mainchan)
-        mainchan_terminal_size(s->mainchan, width, height);
-}
-
-static void ssh2_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    if (s->mainchan)
-        sshfwd_unthrottle(s->mainchan_sc, bufsize);
-}
-
-static size_t ssh2_stdin_backlog(ConnectionLayer *cl)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct ssh2_channel *c;
-
-    if (!s->mainchan)
-        return 0;
-    c = container_of(s->mainchan_sc, struct ssh2_channel, sc);
-    return s->mainchan ?
-        bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer) : 0;
-}
-
-static void ssh2_throttle_all_channels(ConnectionLayer *cl, bool throttled)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-    struct ssh2_channel *c;
-    int i;
-
-    s->all_channels_throttled = throttled;
-
-    for (i = 0; NULL != (c = index234(s->channels, i)); i++)
-        if (!c->sharectx)
-            ssh2_channel_check_throttle(c);
-}
-
-static bool ssh2_ldisc_option(ConnectionLayer *cl, int option)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    return s->ldisc_opts[option];
-}
-
-static void ssh2_set_ldisc_option(ConnectionLayer *cl, int option, bool value)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    s->ldisc_opts[option] = value;
-}
-
-static void ssh2_enable_x_fwd(ConnectionLayer *cl)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    s->X11_fwd_enabled = true;
-}
-
-static void ssh2_set_wants_user_input(ConnectionLayer *cl, bool wanted)
-{
-    struct ssh2_connection_state *s =
-        container_of(cl, struct ssh2_connection_state, cl);
-
-    s->want_user_input = wanted;
-    s->ready = true; // WINSCP
-}
-
-static bool ssh2_connection_want_user_input(PacketProtocolLayer *ppl)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-    return s->want_user_input;
-}
-
-static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-
-    while (s->mainchan && bufchain_size(s->ppl.user_input) > 0) {
-        /*
-         * Add user input to the main channel's buffer.
-         */
-        ptrlen data = bufchain_prefix(s->ppl.user_input);
-        sshfwd_write(s->mainchan_sc, data.ptr, data.len);
-        bufchain_consume(s->ppl.user_input, data.len);
-    }
-}
-
-static void ssh2_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-
-    conf_free(s->conf);
-    s->conf = conf_copy(conf);
-
-    if (s->portfwdmgr_configured)
-        portfwdmgr_config(s->portfwdmgr, s->conf);
-}
-
-#include <puttyexp.h>
-
-static unsigned int ssh2_connection_winscp_query(PacketProtocolLayer *ppl, int query)
-{
-    struct ssh2_connection_state *s =
-        container_of(ppl, struct ssh2_connection_state, ppl);
-
-    if (query == WINSCP_QUERY_REMMAXPKT)
-    {
-        if (!s->mainchan)
-        {
-            return 0;
-        }
-        else
-        {
-            struct ssh2_channel *c = container_of(s->mainchan_sc, struct ssh2_channel, sc);
-            return c->remmaxpkt;
-        }
-    }
-    else if (query == WINSCP_QUERY_MAIN_CHANNEL)
-    {
-        return s->ready;
-    }
-    else
-    {
-        assert(0);
-        return 0;
-    }
-}

+ 0 - 236
source/putty/ssh2connection.h

@@ -1,236 +0,0 @@
-#ifndef PUTTY_SSH2CONNECTION_H
-#define PUTTY_SSH2CONNECTION_H
-
-struct outstanding_channel_request;
-struct outstanding_global_request;
-
-struct ssh2_connection_state {
-    int crState;
-
-    ssh_sharing_state *connshare;
-    char *peer_verstring;
-
-    mainchan *mainchan;
-    SshChannel *mainchan_sc;
-    bool ldisc_opts[LD_N_OPTIONS];
-    int session_attempt, session_status;
-    int term_width, term_height;
-    bool want_user_input;
-    bool ready; // WINSCP
-
-    bool ssh_is_simple;
-    bool persistent;
-    bool started;
-
-    Conf *conf;
-
-    tree234 *channels;                 /* indexed by local id */
-    bool all_channels_throttled;
-
-    bool X11_fwd_enabled;
-    tree234 *x11authtree;
-
-    bool got_pty;
-
-    tree234 *rportfwds;
-    PortFwdManager *portfwdmgr;
-    bool portfwdmgr_configured;
-
-    prompts_t *antispoof_prompt;
-    int antispoof_ret;
-
-    const SftpServerVtable *sftpserver_vt;
-    const SshServerConfig *ssc;
-
-    /*
-     * These store the list of global requests that we're waiting for
-     * replies to. (REQUEST_FAILURE doesn't come with any indication
-     * of what message caused it, so we have to keep track of the
-     * queue ourselves.)
-     */
-    struct outstanding_global_request *globreq_head, *globreq_tail;
-
-    ConnectionLayer cl;
-    PacketProtocolLayer ppl;
-};
-
-typedef void (*gr_handler_fn_t)(struct ssh2_connection_state *s,
-                                PktIn *pktin, void *ctx);
-void ssh2_queue_global_request_handler(
-    struct ssh2_connection_state *s, gr_handler_fn_t handler, void *ctx);
-
-struct ssh2_channel {
-    struct ssh2_connection_state *connlayer;
-
-    unsigned remoteid, localid;
-    int type;
-    /* True if we opened this channel but server hasn't confirmed. */
-    bool halfopen;
-
-    /* Bitmap of whether we've sent/received CHANNEL_EOF and
-     * CHANNEL_CLOSE. */
-#define CLOSES_SENT_EOF    1
-#define CLOSES_SENT_CLOSE  2
-#define CLOSES_RCVD_EOF    4
-#define CLOSES_RCVD_CLOSE  8
-    int closes;
-
-    /*
-     * This flag indicates that an EOF is pending on the outgoing side
-     * of the channel: that is, wherever we're getting the data for
-     * this channel has sent us some data followed by EOF. We can't
-     * actually send the EOF until we've finished sending the data, so
-     * we set this flag instead to remind us to do so once our buffer
-     * is clear.
-     */
-    bool pending_eof;
-
-    /*
-     * True if this channel is causing the underlying connection to be
-     * throttled.
-     */
-    bool throttling_conn;
-
-    /*
-     * True if we currently have backed-up data on the direction of
-     * this channel pointing out of the SSH connection, and therefore
-     * would prefer the 'Channel' implementation not to read further
-     * local input if possible.
-     */
-    bool throttled_by_backlog;
-
-    bufchain outbuffer, errbuffer;
-    unsigned remwindow, remmaxpkt;
-    /* locwindow is signed so we can cope with excess data. */
-    int locwindow, locmaxwin;
-    /*
-     * remlocwin is the amount of local window that we think
-     * the remote end had available to it after it sent the
-     * last data packet or window adjust ack.
-     */
-    int remlocwin;
-
-    /*
-     * These store the list of channel requests that we're waiting for
-     * replies to. (CHANNEL_FAILURE doesn't come with any indication
-     * of what message caused it, so we have to keep track of the
-     * queue ourselves.)
-     */
-    struct outstanding_channel_request *chanreq_head, *chanreq_tail;
-
-    enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;
-
-    ssh_sharing_connstate *sharectx; /* sharing context, if this is a
-                                      * downstream channel */
-    Channel *chan;      /* handle the client side of this channel, if not */
-    SshChannel sc;      /* entry point for chan to talk back to */
-};
-
-typedef void (*cr_handler_fn_t)(struct ssh2_channel *, PktIn *, void *);
-
-void ssh2_channel_init(struct ssh2_channel *c);
-PktOut *ssh2_chanreq_init(struct ssh2_channel *c, const char *type,
-                          cr_handler_fn_t handler, void *ctx);
-
-typedef enum ChanopenOutcome {
-    CHANOPEN_RESULT_FAILURE,
-    CHANOPEN_RESULT_SUCCESS,
-    CHANOPEN_RESULT_DOWNSTREAM,
-} ChanopenOutcome;
-
-typedef struct ChanopenResult {
-    ChanopenOutcome outcome;
-    union {
-        struct {
-            char *wire_message;        /* must be freed by recipient */
-            unsigned reason_code;
-        } failure;
-        struct {
-            Channel *channel;
-        } success;
-        struct {
-            ssh_sharing_connstate *share_ctx;
-        } downstream;
-    } u;
-} ChanopenResult;
-
-PktOut *ssh2_chanopen_init(struct ssh2_channel *c, const char *type);
-
-PktOut *ssh2_portfwd_chanopen(
-    struct ssh2_connection_state *s, struct ssh2_channel *c,
-    const char *hostname, int port,
-    const char *description, const SocketPeerInfo *peerinfo);
-
-struct ssh_rportfwd *ssh2_rportfwd_alloc(
-    ConnectionLayer *cl,
-    const char *shost, int sport, const char *dhost, int dport,
-    int addressfamily, const char *log_description, PortFwdRecord *pfr,
-    ssh_sharing_connstate *share_ctx);
-void ssh2_rportfwd_remove(
-    ConnectionLayer *cl, struct ssh_rportfwd *rpf);
-SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan);
-SshChannel *ssh2_serverside_x11_open(
-    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi);
-SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan);
-
-void ssh2channel_send_exit_status(SshChannel *c, int status);
-void ssh2channel_send_exit_signal(
-    SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg);
-void ssh2channel_send_exit_signal_numeric(
-    SshChannel *c, int signum, bool core_dumped, ptrlen msg);
-void ssh2channel_request_x11_forwarding(
-    SshChannel *c, bool want_reply, const char *authproto,
-    const char *authdata, int screen_number, bool oneshot);
-void ssh2channel_request_agent_forwarding(SshChannel *c, bool want_reply);
-void ssh2channel_request_pty(
-    SshChannel *c, bool want_reply, Conf *conf, int w, int h);
-bool ssh2channel_send_env_var(
-    SshChannel *c, bool want_reply, const char *var, const char *value);
-void ssh2channel_start_shell(SshChannel *c, bool want_reply);
-void ssh2channel_start_command(
-    SshChannel *c, bool want_reply, const char *command);
-bool ssh2channel_start_subsystem(
-    SshChannel *c, bool want_reply, const char *subsystem);
-bool ssh2channel_send_env_var(
-    SshChannel *c, bool want_reply, const char *var, const char *value);
-bool ssh2channel_send_serial_break(
-    SshChannel *c, bool want_reply, int length);
-bool ssh2channel_send_signal(
-    SshChannel *c, bool want_reply, const char *signame);
-void ssh2channel_send_terminal_size_change(SshChannel *c, int w, int h);
-
-#define CHANOPEN_RETURN_FAILURE(code, msgparams) do             \
-    {                                                           \
-        ChanopenResult toret;                                   \
-        toret.outcome = CHANOPEN_RESULT_FAILURE;                \
-        toret.u.failure.reason_code = code;                     \
-        toret.u.failure.wire_message = dupprintf msgparams;     \
-        return toret;                                           \
-    } while (0)
-
-#define CHANOPEN_RETURN_SUCCESS(chan) do                \
-    {                                                   \
-        ChanopenResult toret;                           \
-        toret.outcome = CHANOPEN_RESULT_SUCCESS;        \
-        toret.u.success.channel = chan;                 \
-        return toret;                                   \
-    } while (0)
-
-#define CHANOPEN_RETURN_DOWNSTREAM(shctx) do            \
-    {                                                   \
-        ChanopenResult toret;                           \
-        toret.outcome = CHANOPEN_RESULT_DOWNSTREAM;     \
-        toret.u.downstream.share_ctx = shctx;           \
-        return toret;                                   \
-    } while (0)
-
-ChanopenResult ssh2_connection_parse_channel_open(
-    struct ssh2_connection_state *s, ptrlen type,
-    PktIn *pktin, SshChannel *sc);
-
-bool ssh2_connection_parse_global_request(
-    struct ssh2_connection_state *s, ptrlen type, PktIn *pktin);
-
-bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s);
-
-#endif /* PUTTY_SSH2CONNECTION_H */

+ 0 - 944
source/putty/ssh2kex-client.c

@@ -1,944 +0,0 @@
-/*
- * Client side of key exchange for the SSH-2 transport protocol (RFC 4253).
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshcr.h"
-#include "storage.h"
-#include "ssh2transport.h"
-#include "mpint.h"
-
-/*
- * Another copy of the symbol defined in mpunsafe.c. See the comment
- * there.
- */
-const int deliberate_symbol_clash = 12345;
-
-void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
-{
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    PktIn *pktin;
-    PktOut *pktout;
-
-    crBegin(s->crStateKex);
-
-    if (s->kex_alg->main_type == KEXTYPE_DH) {
-        /*
-         * Work out the number of bits of key we will need from the
-         * key exchange. We start with the maximum key length of
-         * either cipher...
-         */
-        {
-            int csbits, scbits;
-
-            csbits = s->out.cipher ? s->out.cipher->real_keybits : 0;
-            scbits = s->in.cipher ? s->in.cipher->real_keybits : 0;
-            s->nbits = (csbits > scbits ? csbits : scbits);
-        }
-        /* The keys only have hlen-bit entropy, since they're based on
-         * a hash. So cap the key size at hlen bits. */
-        if (s->nbits > s->kex_alg->hash->hlen * 8)
-            s->nbits = s->kex_alg->hash->hlen * 8;
-
-        /*
-         * If we're doing Diffie-Hellman group exchange, start by
-         * requesting a group.
-         */
-        if (dh_is_gex(s->kex_alg)) {
-            ppl_logevent("Doing Diffie-Hellman group exchange");
-            s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGEX;
-            /*
-             * Work out how big a DH group we will need to allow that
-             * much data.
-             */
-            s->pbits = 512 << ((s->nbits - 1) / 64);
-            if (s->pbits < DH_MIN_SIZE)
-                s->pbits = DH_MIN_SIZE;
-            if (s->pbits > DH_MAX_SIZE)
-                s->pbits = DH_MAX_SIZE;
-            if ((s->ppl.remote_bugs & BUG_SSH2_OLDGEX)) {
-                pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
-                put_uint32(pktout, s->pbits);
-            } else {
-                pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_KEX_DH_GEX_REQUEST);
-                put_uint32(pktout, DH_MIN_SIZE);
-                put_uint32(pktout, s->pbits);
-                put_uint32(pktout, DH_MAX_SIZE);
-            }
-            pq_push(s->ppl.out_pq, pktout);
-
-            crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-            if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
-                ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                                "expecting Diffie-Hellman group, type %d (%s)",
-                                pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                *aborted = true;
-                return;
-            }
-            s->p = get_mp_ssh2(pktin);
-            s->g = get_mp_ssh2(pktin);
-            if (get_err(pktin)) {
-                ssh_proto_error(s->ppl.ssh,
-                                "Unable to parse Diffie-Hellman group packet");
-                *aborted = true;
-                return;
-            }
-            s->dh_ctx = dh_setup_gex(s->p, s->g);
-            s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
-            s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
-
-            ppl_logevent("Doing Diffie-Hellman key exchange using %d-bit "
-                         "modulus and hash %s with a server-supplied group",
-                         dh_modulus_bit_size(s->dh_ctx),
-                         ssh_hash_alg(s->exhash)->text_name);
-        } else {
-            s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGROUP;
-            s->dh_ctx = dh_setup_group(s->kex_alg);
-            s->kex_init_value = SSH2_MSG_KEXDH_INIT;
-            s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
-
-            ppl_logevent("Doing Diffie-Hellman key exchange using %d-bit "
-                         "modulus and hash %s with standard group \"%s\"",
-                         dh_modulus_bit_size(s->dh_ctx),
-                         ssh_hash_alg(s->exhash)->text_name,
-                         s->kex_alg->groupname);
-        }
-
-        /*
-         * Now generate and send e for Diffie-Hellman.
-         */
-        seat_set_busy_status(s->ppl.seat, BUSY_CPU);
-        s->e = dh_create_e(s->dh_ctx, s->nbits * 2);
-        pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_init_value);
-        put_mp_ssh2(pktout, s->e);
-        pq_push(s->ppl.out_pq, pktout);
-
-        seat_set_busy_status(s->ppl.seat, BUSY_WAITING);
-        crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-        if (pktin->type != s->kex_reply_value) {
-            ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                            "expecting Diffie-Hellman reply, type %d (%s)",
-                            pktin->type,
-                            ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                          s->ppl.bpp->pls->actx,
-                                          pktin->type));
-            *aborted = true;
-            return;
-        }
-        seat_set_busy_status(s->ppl.seat, BUSY_CPU);
-        s->hostkeydata = get_string(pktin);
-        s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata);
-        s->f = get_mp_ssh2(pktin);
-        s->sigdata = get_string(pktin);
-        if (get_err(pktin)) {
-            ssh_proto_error(s->ppl.ssh,
-                            "Unable to parse Diffie-Hellman reply packet");
-            *aborted = true;
-            return;
-        }
-
-        {
-            const char *err = dh_validate_f(s->dh_ctx, s->f);
-            if (err) {
-                ssh_proto_error(s->ppl.ssh, "Diffie-Hellman reply failed "
-                                "validation: %s", err);
-                *aborted = true;
-                return;
-            }
-        }
-        s->K = dh_find_K(s->dh_ctx, s->f);
-
-        /* We assume everything from now on will be quick, and it might
-         * involve user interaction. */
-        seat_set_busy_status(s->ppl.seat, BUSY_NOT);
-
-        put_stringpl(s->exhash, s->hostkeydata);
-        if (dh_is_gex(s->kex_alg)) {
-            if (!(s->ppl.remote_bugs & BUG_SSH2_OLDGEX))
-                put_uint32(s->exhash, DH_MIN_SIZE);
-            put_uint32(s->exhash, s->pbits);
-            if (!(s->ppl.remote_bugs & BUG_SSH2_OLDGEX))
-                put_uint32(s->exhash, DH_MAX_SIZE);
-            put_mp_ssh2(s->exhash, s->p);
-            put_mp_ssh2(s->exhash, s->g);
-        }
-        put_mp_ssh2(s->exhash, s->e);
-        put_mp_ssh2(s->exhash, s->f);
-
-        dh_cleanup(s->dh_ctx);
-        s->dh_ctx = NULL;
-        mp_free(s->f); s->f = NULL;
-        if (dh_is_gex(s->kex_alg)) {
-            mp_free(s->g); s->g = NULL;
-            mp_free(s->p); s->p = NULL;
-        }
-    } else if (s->kex_alg->main_type == KEXTYPE_ECDH) {
-
-        ppl_logevent("Doing ECDH key exchange with curve %s and hash %s",
-                     ssh_ecdhkex_curve_textname(s->kex_alg),
-                     ssh_hash_alg(s->exhash)->text_name);
-        s->ppl.bpp->pls->kctx = SSH2_PKTCTX_ECDHKEX;
-
-        s->ecdh_key = ssh_ecdhkex_newkey(s->kex_alg);
-        if (!s->ecdh_key) {
-            ssh_sw_abort(s->ppl.ssh, "Unable to generate key for ECDH");
-            *aborted = true;
-            return;
-        }
-
-        pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEX_ECDH_INIT);
-        {
-            strbuf *pubpoint = strbuf_new();
-            ssh_ecdhkex_getpublic(s->ecdh_key, BinarySink_UPCAST(pubpoint));
-            put_stringsb(pktout, pubpoint);
-        }
-
-        pq_push(s->ppl.out_pq, pktout);
-
-        crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-        if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) {
-            ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                            "expecting ECDH reply, type %d (%s)", pktin->type,
-                            ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                          s->ppl.bpp->pls->actx,
-                                          pktin->type));
-            *aborted = true;
-            return;
-        }
-
-        s->hostkeydata = get_string(pktin);
-        put_stringpl(s->exhash, s->hostkeydata);
-        s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata);
-
-        {
-            strbuf *pubpoint = strbuf_new();
-            ssh_ecdhkex_getpublic(s->ecdh_key, BinarySink_UPCAST(pubpoint));
-            put_string(s->exhash, pubpoint->u, pubpoint->len);
-            strbuf_free(pubpoint);
-        }
-
-        {
-            ptrlen keydata = get_string(pktin);
-            put_stringpl(s->exhash, keydata);
-            s->K = ssh_ecdhkex_getkey(s->ecdh_key, keydata);
-            if (!get_err(pktin) && !s->K) {
-                ssh_proto_error(s->ppl.ssh, "Received invalid elliptic curve "
-                                "point in ECDH reply");
-                *aborted = true;
-                return;
-            }
-        }
-
-        s->sigdata = get_string(pktin);
-        if (get_err(pktin)) {
-            ssh_proto_error(s->ppl.ssh, "Unable to parse ECDH reply packet");
-            *aborted = true;
-            return;
-        }
-
-        ssh_ecdhkex_freekey(s->ecdh_key);
-        s->ecdh_key = NULL;
-#ifndef NO_GSSAPI
-    } else if (s->kex_alg->main_type == KEXTYPE_GSS) {
-        ptrlen data;
-
-        s->ppl.bpp->pls->kctx = SSH2_PKTCTX_GSSKEX;
-        s->init_token_sent = false;
-        s->complete_rcvd = false;
-        s->hkey = NULL;
-        s->keystr = NULL;
-
-        /*
-         * Work out the number of bits of key we will need from the
-         * key exchange. We start with the maximum key length of
-         * either cipher...
-         *
-         * This is rote from the KEXTYPE_DH section above.
-         */
-        {
-            int csbits, scbits;
-
-            csbits = s->out.cipher->real_keybits;
-            scbits = s->in.cipher->real_keybits;
-            s->nbits = (csbits > scbits ? csbits : scbits);
-        }
-        /* The keys only have hlen-bit entropy, since they're based on
-         * a hash. So cap the key size at hlen bits. */
-        if (s->nbits > s->kex_alg->hash->hlen * 8)
-            s->nbits = s->kex_alg->hash->hlen * 8;
-
-        if (dh_is_gex(s->kex_alg)) {
-            /*
-             * Work out how big a DH group we will need to allow that
-             * much data.
-             */
-            s->pbits = 512 << ((s->nbits - 1) / 64);
-            ppl_logevent("Doing GSSAPI (with Kerberos V5) Diffie-Hellman "
-                         "group exchange, with minimum %d bits", s->pbits);
-            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXGSS_GROUPREQ);
-            put_uint32(pktout, s->pbits); /* min */
-            put_uint32(pktout, s->pbits); /* preferred */
-            put_uint32(pktout, s->pbits * 2); /* max */
-            pq_push(s->ppl.out_pq, pktout);
-
-            crMaybeWaitUntilV(
-                (pktin = ssh2_transport_pop(s)) != NULL);
-            if (pktin->type != SSH2_MSG_KEXGSS_GROUP) {
-                ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                                "expecting Diffie-Hellman group, type %d (%s)",
-                                pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                *aborted = true;
-                return;
-            }
-            s->p = get_mp_ssh2(pktin);
-            s->g = get_mp_ssh2(pktin);
-            if (get_err(pktin)) {
-                ssh_proto_error(s->ppl.ssh,
-                                "Unable to parse Diffie-Hellman group packet");
-                *aborted = true;
-                return;
-            }
-            s->dh_ctx = dh_setup_gex(s->p, s->g);
-        } else {
-            s->dh_ctx = dh_setup_group(s->kex_alg);
-            ppl_logevent("Using GSSAPI (with Kerberos V5) Diffie-Hellman with"
-                         " standard group \"%s\"", s->kex_alg->groupname);
-        }
-
-        ppl_logevent("Doing GSSAPI (with Kerberos V5) Diffie-Hellman key "
-                     "exchange with hash %s", ssh_hash_alg(s->exhash)->text_name);
-        /* Now generate e for Diffie-Hellman. */
-        seat_set_busy_status(s->ppl.seat, BUSY_CPU);
-        s->e = dh_create_e(s->dh_ctx, s->nbits * 2);
-
-        if (s->shgss->lib->gsslogmsg)
-            ppl_logevent("%s", s->shgss->lib->gsslogmsg);
-
-        /* initial tokens are empty */
-        SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
-        SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
-        SSH_GSS_CLEAR_BUF(&s->mic);
-        s->gss_stat = s->shgss->lib->acquire_cred(
-            s->shgss->lib, &s->shgss->ctx, &s->gss_cred_expiry);
-        if (s->gss_stat != SSH_GSS_OK) {
-            ssh_sw_abort(s->ppl.ssh,
-                         "GSSAPI key exchange failed to initialise");
-            *aborted = true;
-            return;
-        }
-
-        /* now enter the loop */
-        assert(s->shgss->srv_name);
-        do {
-            /*
-             * When acquire_cred yields no useful expiration, go with the
-             * service ticket expiration.
-             */
-            s->gss_stat = s->shgss->lib->init_sec_context(
-                s->shgss->lib, &s->shgss->ctx, s->shgss->srv_name,
-                s->gss_delegate, &s->gss_rcvtok, &s->gss_sndtok,
-                (s->gss_cred_expiry == GSS_NO_EXPIRATION ?
-                 &s->gss_cred_expiry : NULL), NULL);
-            SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
-
-            if (s->gss_stat == SSH_GSS_S_COMPLETE && s->complete_rcvd)
-                break; /* MIC is verified after the loop */
-
-            if (s->gss_stat != SSH_GSS_S_COMPLETE &&
-                s->gss_stat != SSH_GSS_S_CONTINUE_NEEDED) {
-                if (s->shgss->lib->display_status(
-                        s->shgss->lib, s->shgss->ctx,
-                        &s->gss_buf) == SSH_GSS_OK) {
-                    char *err = s->gss_buf.value;
-                    ssh_sw_abort(s->ppl.ssh,
-                                 "GSSAPI key exchange failed to initialise "
-                                 "context: %s", err);
-                    sfree(err);
-                    *aborted = true;
-                    return;
-                }
-            }
-            assert(s->gss_stat == SSH_GSS_S_COMPLETE ||
-                   s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
-
-            if (!s->init_token_sent) {
-                s->init_token_sent = true;
-                pktout = ssh_bpp_new_pktout(s->ppl.bpp,
-                                            SSH2_MSG_KEXGSS_INIT);
-                if (s->gss_sndtok.length == 0) {
-                    ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange failed: "
-                                 "no initial context token");
-                    *aborted = true;
-                    return;
-                }
-                put_string(pktout,
-                           s->gss_sndtok.value, s->gss_sndtok.length);
-                put_mp_ssh2(pktout, s->e);
-                pq_push(s->ppl.out_pq, pktout);
-                s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok);
-                ppl_logevent("GSSAPI key exchange initialised");
-            } else if (s->gss_sndtok.length != 0) {
-                pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_KEXGSS_CONTINUE);
-                put_string(pktout,
-                           s->gss_sndtok.value, s->gss_sndtok.length);
-                pq_push(s->ppl.out_pq, pktout);
-                s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok);
-            }
-
-            if (s->gss_stat == SSH_GSS_S_COMPLETE && s->complete_rcvd)
-                break;
-
-          wait_for_gss_token:
-            crMaybeWaitUntilV(
-                (pktin = ssh2_transport_pop(s)) != NULL);
-            switch (pktin->type) {
-              case SSH2_MSG_KEXGSS_CONTINUE:
-                data = get_string(pktin);
-                s->gss_rcvtok.value = (char *)data.ptr;
-                s->gss_rcvtok.length = data.len;
-                continue;
-              case SSH2_MSG_KEXGSS_COMPLETE:
-                s->complete_rcvd = true;
-                s->f = get_mp_ssh2(pktin);
-                data = get_string(pktin);
-                s->mic.value = (char *)data.ptr;
-                s->mic.length = data.len;
-                /* If there's a final token we loop to consume it */
-                if (get_bool(pktin)) {
-                    data = get_string(pktin);
-                    s->gss_rcvtok.value = (char *)data.ptr;
-                    s->gss_rcvtok.length = data.len;
-                    continue;
-                }
-                break;
-              case SSH2_MSG_KEXGSS_HOSTKEY:
-                s->hostkeydata = get_string(pktin);
-                if (s->hostkey_alg) {
-                    s->hkey = ssh_key_new_pub(s->hostkey_alg,
-                                              s->hostkeydata);
-                    put_stringpl(s->exhash, s->hostkeydata);
-                }
-                /*
-                 * Can't loop as we have no token to pass to
-                 * init_sec_context.
-                 */
-                goto wait_for_gss_token;
-              case SSH2_MSG_KEXGSS_ERROR:
-                /*
-                 * We have no use for the server's major and minor
-                 * status.  The minor status is really only
-                 * meaningful to the server, and with luck the major
-                 * status means something to us (but not really all
-                 * that much).  The string is more meaningful, and
-                 * hopefully the server sends any error tokens, as
-                 * that will produce the most useful information for
-                 * us.
-                 */
-                get_uint32(pktin); /* server's major status */
-                get_uint32(pktin); /* server's minor status */
-                data = get_string(pktin);
-                ppl_logevent("GSSAPI key exchange failed; "
-                             "server's message: %.*s", PTRLEN_PRINTF(data));
-                /* Language tag, but we have no use for it */
-                get_string(pktin);
-                /*
-                 * Wait for an error token, if there is one, or the
-                 * server's disconnect.  The error token, if there
-                 * is one, must follow the SSH2_MSG_KEXGSS_ERROR
-                 * message, per the RFC.
-                 */
-                goto wait_for_gss_token;
-              default:
-                ssh_proto_error(s->ppl.ssh, "Received unexpected packet "
-                                "during GSSAPI key exchange, type %d (%s)",
-                                pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                *aborted = true;
-                return;
-            }
-        } while (s->gss_rcvtok.length ||
-                 s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED ||
-                 !s->complete_rcvd);
-
-        {
-            const char *err = dh_validate_f(s->dh_ctx, s->f);
-            if (err) {
-                ssh_proto_error(s->ppl.ssh, "GSSAPI reply failed "
-                                "validation: %s", err);
-                *aborted = true;
-                return;
-            }
-        }
-        s->K = dh_find_K(s->dh_ctx, s->f);
-
-        /* We assume everything from now on will be quick, and it might
-         * involve user interaction. */
-        seat_set_busy_status(s->ppl.seat, BUSY_NOT);
-
-        if (!s->hkey)
-            put_stringz(s->exhash, "");
-        if (dh_is_gex(s->kex_alg)) {
-            /* min,  preferred, max */
-            put_uint32(s->exhash, s->pbits);
-            put_uint32(s->exhash, s->pbits);
-            put_uint32(s->exhash, s->pbits * 2);
-
-            put_mp_ssh2(s->exhash, s->p);
-            put_mp_ssh2(s->exhash, s->g);
-        }
-        put_mp_ssh2(s->exhash, s->e);
-        put_mp_ssh2(s->exhash, s->f);
-
-        /*
-         * MIC verification is done below, after we compute the hash
-         * used as the MIC input.
-         */
-
-        dh_cleanup(s->dh_ctx);
-        s->dh_ctx = NULL;
-        mp_free(s->f); s->f = NULL;
-        if (dh_is_gex(s->kex_alg)) {
-            mp_free(s->g); s->g = NULL;
-            mp_free(s->p); s->p = NULL;
-        }
-#endif
-    } else {
-        ptrlen rsakeydata;
-
-        assert(s->kex_alg->main_type == KEXTYPE_RSA);
-        ppl_logevent("Doing RSA key exchange with hash %s",
-                     ssh_hash_alg(s->exhash)->text_name);
-        s->ppl.bpp->pls->kctx = SSH2_PKTCTX_RSAKEX;
-        /*
-         * RSA key exchange. First expect a KEXRSA_PUBKEY packet
-         * from the server.
-         */
-        crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-        if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
-            ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                            "expecting RSA public key, type %d (%s)",
-                            pktin->type,
-                            ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                          s->ppl.bpp->pls->actx,
-                                          pktin->type));
-            *aborted = true;
-            return;
-        }
-
-        s->hostkeydata = get_string(pktin);
-        put_stringpl(s->exhash, s->hostkeydata);
-        s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata);
-
-        rsakeydata = get_string(pktin);
-
-        s->rsa_kex_key = ssh_rsakex_newkey(rsakeydata);
-        if (!s->rsa_kex_key) {
-            ssh_proto_error(s->ppl.ssh,
-                            "Unable to parse RSA public key packet");
-            *aborted = true;
-            return;
-        }
-        s->rsa_kex_key_needs_freeing = true;
-
-        put_stringpl(s->exhash, rsakeydata);
-
-        /*
-         * Next, set up a shared secret K, of precisely KLEN -
-         * 2*HLEN - 49 bits, where KLEN is the bit length of the
-         * RSA key modulus and HLEN is the bit length of the hash
-         * we're using.
-         */
-        {
-            int klen = ssh_rsakex_klen(s->rsa_kex_key);
-
-            const struct ssh_rsa_kex_extra *extra =
-                (const struct ssh_rsa_kex_extra *)s->kex_alg->extra;
-            if (klen < extra->minklen) {
-                ssh_proto_error(s->ppl.ssh, "Server sent %d-bit RSA key, "
-                                "less than the minimum size %d for %s "
-                                "key exchange", klen, extra->minklen,
-                                s->kex_alg->name);
-                *aborted = true;
-                return;
-            }
-
-            { // WINSCP
-            int nbits = klen - (2*s->kex_alg->hash->hlen*8 + 49);
-            assert(nbits > 0);
-
-            { // WINSCP
-            strbuf *buf, *outstr;
-
-            mp_int *tmp = mp_random_bits(nbits - 1);
-            s->K = mp_power_2(nbits - 1);
-            mp_add_into(s->K, s->K, tmp);
-            mp_free(tmp);
-
-            /*
-             * Encode this as an mpint.
-             */
-            buf = strbuf_new_nm();
-            put_mp_ssh2(buf, s->K);
-
-            /*
-             * Encrypt it with the given RSA key.
-             */
-            outstr = ssh_rsakex_encrypt(s->rsa_kex_key, s->kex_alg->hash,
-                                        ptrlen_from_strbuf(buf));
-
-            /*
-             * And send it off in a return packet.
-             */
-            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_SECRET);
-            put_stringpl(pktout, ptrlen_from_strbuf(outstr));
-            pq_push(s->ppl.out_pq, pktout);
-
-            put_stringsb(s->exhash, outstr); /* frees outstr */
-
-            strbuf_free(buf);
-            } // WINSCP
-            } // WINSCP
-        }
-
-        ssh_rsakex_freekey(s->rsa_kex_key);
-        s->rsa_kex_key = NULL;
-        s->rsa_kex_key_needs_freeing = false;
-
-        crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-        if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
-            ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                            "expecting RSA kex signature, type %d (%s)",
-                            pktin->type,
-                            ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                          s->ppl.bpp->pls->actx,
-                                          pktin->type));
-            *aborted = true;
-            return;
-        }
-
-        s->sigdata = get_string(pktin);
-        if (get_err(pktin)) {
-            ssh_proto_error(s->ppl.ssh, "Unable to parse RSA kex signature");
-            *aborted = true;
-            return;
-        }
-    }
-
-    ssh2transport_finalise_exhash(s);
-
-#ifndef NO_GSSAPI
-    if (s->kex_alg->main_type == KEXTYPE_GSS) {
-        Ssh_gss_buf gss_buf;
-        SSH_GSS_CLEAR_BUF(&s->gss_buf);
-
-        gss_buf.value = s->exchange_hash;
-        gss_buf.length = s->kex_alg->hash->hlen;
-        s->gss_stat = s->shgss->lib->verify_mic(
-            s->shgss->lib, s->shgss->ctx, &gss_buf, &s->mic);
-        if (s->gss_stat != SSH_GSS_OK) {
-            if (s->shgss->lib->display_status(
-                    s->shgss->lib, s->shgss->ctx, &s->gss_buf) == SSH_GSS_OK) {
-                char *err = s->gss_buf.value;
-                ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange MIC was "
-                             "not valid: %s", err);
-                sfree(err);
-            } else {
-                ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange MIC was "
-                             "not valid");
-            }
-            *aborted = true;
-            return;
-        }
-
-        s->gss_kex_used = true;
-
-        /*-
-         * If this the first KEX, save the GSS context for "gssapi-keyex"
-         * authentication.
-         *
-         * http://tools.ietf.org/html/rfc4462#section-4
-         *
-         * This method may be used only if the initial key exchange was
-         * performed using a GSS-API-based key exchange method defined in
-         * accordance with Section 2.  The GSS-API context used with this
-         * method is always that established during an initial GSS-API-based
-         * key exchange.  Any context established during key exchange for the
-         * purpose of rekeying MUST NOT be used with this method.
-         */
-        if (s->got_session_id) {
-            s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
-        }
-        ppl_logevent("GSSAPI Key Exchange complete!");
-    }
-#endif
-
-    s->dh_ctx = NULL;
-
-    /* In GSS keyex there's no hostkey signature to verify */
-    if (s->kex_alg->main_type != KEXTYPE_GSS) {
-        if (!s->hkey) {
-            ssh_proto_error(s->ppl.ssh, "Server's host key is invalid");
-            *aborted = true;
-            return;
-        }
-
-        if (!ssh_key_verify(
-                s->hkey, s->sigdata,
-                make_ptrlen(s->exchange_hash, s->kex_alg->hash->hlen))) {
-#ifndef FUZZING
-            ssh_proto_error(s->ppl.ssh, "Signature from server's host key "
-                            "is invalid");
-            *aborted = true;
-            return;
-#endif
-        }
-    }
-
-    s->keystr = (s->hkey ? ssh_key_cache_str(s->hkey) : NULL);
-#ifndef NO_GSSAPI
-    if (s->gss_kex_used) {
-        /*
-         * In a GSS-based session, check the host key (if any) against
-         * the transient host key cache.
-         */
-        if (s->kex_alg->main_type == KEXTYPE_GSS) {
-
-            /*
-             * We've just done a GSS key exchange. If it gave us a
-             * host key, store it.
-             */
-            if (s->hkey) {
-                char *fingerprint = ssh2_fingerprint(
-                    s->hkey, SSH_FPTYPE_DEFAULT);
-                ppl_logevent("GSS kex provided fallback host key:");
-                ppl_logevent("%s", fingerprint);
-                sfree(fingerprint);
-
-                ssh_transient_hostkey_cache_add(s->thc, s->hkey);
-            } else if (!ssh_transient_hostkey_cache_non_empty(s->thc)) {
-                /*
-                 * But if it didn't, then we currently have no
-                 * fallback host key to use in subsequent non-GSS
-                 * rekeys. So we should immediately trigger a non-GSS
-                 * rekey of our own, to set one up, before the session
-                 * keys have been used for anything else.
-                 *
-                 * This is similar to the cross-certification done at
-                 * user request in the permanent host key cache, but
-                 * here we do it automatically, once, at session
-                 * startup, and only add the key to the transient
-                 * cache.
-                 */
-                if (s->hostkey_alg) {
-                    s->need_gss_transient_hostkey = true;
-                } else {
-                    /*
-                     * If we negotiated the "null" host key algorithm
-                     * in the key exchange, that's an indication that
-                     * no host key at all is available from the server
-                     * (both because we listed "null" last, and
-                     * because RFC 4462 section 5 says that a server
-                     * MUST NOT offer "null" as a host key algorithm
-                     * unless that is the only algorithm it provides
-                     * at all).
-                     *
-                     * In that case we actually _can't_ perform a
-                     * non-GSSAPI key exchange, so it's pointless to
-                     * attempt one proactively. This is also likely to
-                     * cause trouble later if a rekey is required at a
-                     * moment whne GSS credentials are not available,
-                     * but someone setting up a server in this
-                     * configuration presumably accepts that as a
-                     * consequence.
-                     */
-                    if (!s->warned_about_no_gss_transient_hostkey) {
-                        ppl_logevent("No fallback host key available");
-                        s->warned_about_no_gss_transient_hostkey = true;
-                    }
-                }
-            }
-        } else {
-            /*
-             * We've just done a fallback key exchange, so make
-             * sure the host key it used is in the cache of keys
-             * we previously received in GSS kexes.
-             *
-             * An exception is if this was the non-GSS key exchange we
-             * triggered on purpose to populate the transient cache.
-             */
-            assert(s->hkey);  /* only KEXTYPE_GSS lets this be null */
-            { // WINSCP
-            char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT);
-
-            if (s->need_gss_transient_hostkey) {
-                ppl_logevent("Post-GSS rekey provided fallback host key:");
-                ppl_logevent("%s", fingerprint);
-                ssh_transient_hostkey_cache_add(s->thc, s->hkey);
-                s->need_gss_transient_hostkey = false;
-            } else if (!ssh_transient_hostkey_cache_verify(s->thc, s->hkey)) {
-                ppl_logevent("Non-GSS rekey after initial GSS kex "
-                             "used host key:");
-                ppl_logevent("%s", fingerprint);
-                sfree(fingerprint);
-                ssh_sw_abort(s->ppl.ssh, "Server's host key did not match any "
-                             "used in previous GSS kex");
-                *aborted = true;
-                return;
-            }
-
-            sfree(fingerprint);
-            } // WINSCP
-        }
-    } else
-#endif /* NO_GSSAPI */
-        if (!s->got_session_id) {
-            /*
-             * Make a note of any other host key formats that are available.
-             */
-            {
-                int i, j, nkeys = 0;
-                char *list = NULL;
-                for (i = 0; i < lenof(ssh2_hostkey_algs); i++) {
-                    if (ssh2_hostkey_algs[i].alg == s->hostkey_alg)
-                        continue;
-
-                    for (j = 0; j < s->n_uncert_hostkeys; j++)
-                        if (s->uncert_hostkeys[j] == i)
-                            break;
-
-                    if (j < s->n_uncert_hostkeys) {
-                        char *newlist;
-                        if (list)
-                            newlist = dupprintf(
-                                "%s/%s", list,
-                                ssh2_hostkey_algs[i].alg->ssh_id);
-                        else
-                            newlist = dupprintf(
-                                "%s", ssh2_hostkey_algs[i].alg->ssh_id);
-                        sfree(list);
-                        list = newlist;
-                        nkeys++;
-                    }
-                }
-                if (list) {
-                    ppl_logevent("Server also has %s host key%s, but we "
-                                 "don't know %s", list,
-                                 nkeys > 1 ? "s" : "",
-                                 nkeys > 1 ? "any of them" : "it");
-                    sfree(list);
-                }
-            }
-
-            /*
-             * Authenticate remote host: verify host key. (We've already
-             * checked the signature of the exchange hash.)
-             */
-            { // WINSCP
-            char **fingerprints = ssh2_all_fingerprints(s->hkey);
-            FingerprintType fptype_default =
-                ssh2_pick_default_fingerprint(fingerprints);
-            ppl_logevent("Host key fingerprint is:");
-            ppl_logevent("%s", fingerprints[fptype_default]);
-            /* First check against manually configured host keys. */
-            s->dlgret = verify_ssh_manual_host_key(
-                s->conf, fingerprints, s->hkey);
-            if (s->dlgret == 0) {          /* did not match */
-                ssh2_free_all_fingerprints(fingerprints);
-                ssh_sw_abort(s->ppl.ssh, "Host key did not appear in manually "
-                             "configured list");
-                *aborted = true;
-                return;
-            } else if (s->dlgret < 0) { /* none configured; use standard handling */
-                ssh2_userkey uk; // WINSCP
-                uk.key = s->hkey; // WINSCP
-                uk.comment = NULL; // WINSCP
-                { // WINSCP
-                char *keydisp = ssh2_pubkey_openssh_str(&uk);
-                s->dlgret = seat_verify_ssh_host_key(
-                    s->ppl.seat, s->savedhost, s->savedport,
-                    ssh_key_cache_id(s->hkey), s->keystr, keydisp,
-                    fingerprints, ssh2_transport_dialog_callback, s);
-                sfree(keydisp);
-                ssh2_free_all_fingerprints(fingerprints);
-#ifdef FUZZING
-                s->dlgret = 1;
-#endif
-                crMaybeWaitUntilV(s->dlgret >= 0);
-                if (s->dlgret == 0) {
-                    ssh_user_close(s->ppl.ssh,
-                                   "User aborted at host key verification");
-                    *aborted = true;
-                    return;
-                }
-                } // WINSCP
-            }
-
-            /*
-             * Save this host key, to check against the one presented in
-             * subsequent rekeys.
-             */
-            s->hostkey_str = s->keystr;
-            s->keystr = NULL;
-            } // WINSCP
-        } else if (s->cross_certifying) {
-            assert(s->hkey);
-            assert(ssh_key_alg(s->hkey) == s->cross_certifying);
-
-            { // WINSCP
-            char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT);
-            ppl_logevent("Storing additional host key for this host:");
-            ppl_logevent("%s", fingerprint);
-            sfree(fingerprint);
-
-            store_host_key(s->savedhost, s->savedport,
-                           ssh_key_cache_id(s->hkey), s->keystr);
-            /*
-             * Don't forget to store the new key as the one we'll be
-             * re-checking in future normal rekeys.
-             */
-            s->hostkey_str = s->keystr;
-            s->keystr = NULL;
-            } // WINSCP
-        } else {
-            /*
-             * In a rekey, we never present an interactive host key
-             * verification request to the user. Instead, we simply
-             * enforce that the key we're seeing this time is identical to
-             * the one we saw before.
-             */
-            assert(s->keystr);         /* filled in by prior key exchange */
-            if (strcmp(s->hostkey_str, s->keystr)) {
-#ifndef FUZZING
-                ssh_sw_abort(s->ppl.ssh,
-                             "Host key was different in repeat key exchange");
-                *aborted = true;
-                return;
-#endif
-            }
-        }
-
-    sfree(s->keystr);
-    s->keystr = NULL;
-    if (s->hkey) {
-        ssh_key_free(s->hkey);
-        s->hkey = NULL;
-    }
-
-    crFinishV;
-}

+ 0 - 126
source/putty/ssh2transhk.c

@@ -1,126 +0,0 @@
-/*
- * Data structure managing host keys in sessions based on GSSAPI KEX.
- *
- * In a session we started with a GSSAPI key exchange, the concept of
- * 'host key' has completely different lifetime and security semantics
- * from the usual ones. Per RFC 4462 section 2.1, we assume that any
- * host key delivered to us in the course of a GSSAPI key exchange is
- * _solely_ there to use as a transient fallback within the same
- * session, if at the time of a subsequent rekey the GSS credentials
- * are temporarily invalid and so a non-GSS KEX method has to be used.
- *
- * In particular, in a GSS-based SSH deployment, host keys may not
- * even _be_ persistent identities for the server; it would be
- * legitimate for a server to generate a fresh one routinely if it
- * wanted to, like SSH-1 server keys.
- *
- * So, in this mode, we never touch the persistent host key cache at
- * all, either to check keys against it _or_ to store keys in it.
- * Instead, we maintain an in-memory cache of host keys that have been
- * mentioned in GSS key exchanges within this particular session, and
- * we permit precisely those host keys in non-GSS rekeys.
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-
-struct ssh_transient_hostkey_cache {
-    tree234 *cache;
-};
-
-struct ssh_transient_hostkey_cache_entry {
-    const ssh_keyalg *alg;
-    strbuf *pub_blob;
-};
-
-static int ssh_transient_hostkey_cache_cmp(void *av, void *bv)
-{
-    const struct ssh_transient_hostkey_cache_entry
-        *a = (const struct ssh_transient_hostkey_cache_entry *)av,
-        *b = (const struct ssh_transient_hostkey_cache_entry *)bv;
-    return strcmp(a->alg->ssh_id, b->alg->ssh_id);
-}
-
-static int ssh_transient_hostkey_cache_find(void *av, void *bv)
-{
-    const ssh_keyalg *aalg = (const ssh_keyalg *)av;
-    const struct ssh_transient_hostkey_cache_entry
-        *b = (const struct ssh_transient_hostkey_cache_entry *)bv;
-    return strcmp(aalg->ssh_id, b->alg->ssh_id);
-}
-
-ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void)
-{
-    ssh_transient_hostkey_cache *thc = snew(ssh_transient_hostkey_cache);
-    thc->cache = newtree234(ssh_transient_hostkey_cache_cmp);
-    return thc;
-}
-
-void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc)
-{
-    struct ssh_transient_hostkey_cache_entry *ent;
-    while ((ent = delpos234(thc->cache, 0)) != NULL) {
-        strbuf_free(ent->pub_blob);
-        sfree(ent);
-    }
-    freetree234(thc->cache);
-    sfree(thc);
-}
-
-void ssh_transient_hostkey_cache_add(
-    ssh_transient_hostkey_cache *thc, ssh_key *key)
-{
-    struct ssh_transient_hostkey_cache_entry *ent, *retd;
-
-    if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
-                       ssh_transient_hostkey_cache_find)) != NULL) {
-        del234(thc->cache, ent);
-        strbuf_free(ent->pub_blob);
-        sfree(ent);
-    }
-
-    ent = snew(struct ssh_transient_hostkey_cache_entry);
-    ent->alg = ssh_key_alg(key);
-    ent->pub_blob = strbuf_new();
-    ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob));
-    retd = add234(thc->cache, ent);
-    assert(retd == ent);
-}
-
-bool ssh_transient_hostkey_cache_verify(
-    ssh_transient_hostkey_cache *thc, ssh_key *key)
-{
-    struct ssh_transient_hostkey_cache_entry *ent;
-    bool toret = false;
-
-    if ((ent = find234(thc->cache, (void *)ssh_key_alg(key),
-                       ssh_transient_hostkey_cache_find)) != NULL) {
-        strbuf *this_blob = strbuf_new();
-        ssh_key_public_blob(key, BinarySink_UPCAST(this_blob));
-
-        if (this_blob->len == ent->pub_blob->len &&
-            !memcmp(this_blob->s, ent->pub_blob->s,
-                    this_blob->len))
-            toret = true;
-
-        strbuf_free(this_blob);
-    }
-
-    return toret;
-}
-
-bool ssh_transient_hostkey_cache_has(
-    ssh_transient_hostkey_cache *thc, const ssh_keyalg *alg)
-{
-    struct ssh_transient_hostkey_cache_entry *ent =
-        find234(thc->cache, (void *)alg,
-                ssh_transient_hostkey_cache_find);
-    return ent != NULL;
-}
-
-bool ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache *thc)
-{
-    return count234(thc->cache) > 0;
-}

+ 0 - 2260
source/putty/ssh2transport.c

@@ -1,2260 +0,0 @@
-/*
- * Packet protocol layer for the SSH-2 transport protocol (RFC 4253).
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshcr.h"
-#ifndef WINSCP
-#include "sshserver.h"
-#endif
-#include "storage.h"
-#include "ssh2transport.h"
-#include "mpint.h"
-
-const struct ssh_signkey_with_user_pref_id ssh2_hostkey_algs[] = {
-    #define ARRAYENT_HOSTKEY_ALGORITHM(type, alg) { &alg, type },
-    HOSTKEY_ALGORITHMS(ARRAYENT_HOSTKEY_ALGORITHM)
-};
-
-const static ssh2_macalg *const macs[] = {
-    &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
-};
-const static ssh2_macalg *const buggymacs[] = {
-    &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
-};
-
-static ssh_compressor *ssh_comp_none_init(void)
-{
-    return NULL;
-}
-static void ssh_comp_none_cleanup(ssh_compressor *handle)
-{
-}
-static ssh_decompressor *ssh_decomp_none_init(void)
-{
-    return NULL;
-}
-static void ssh_decomp_none_cleanup(ssh_decompressor *handle)
-{
-}
-static void ssh_comp_none_block(ssh_compressor *handle,
-                                const unsigned char *block, int len,
-                                unsigned char **outblock, int *outlen,
-                                int minlen)
-{
-}
-static bool ssh_decomp_none_block(ssh_decompressor *handle,
-                                  const unsigned char *block, int len,
-                                  unsigned char **outblock, int *outlen)
-{
-    return false;
-}
-static const ssh_compression_alg ssh_comp_none = {
-    // WINSCP
-    /*.name =*/ "none",
-    /*.delayed_name =*/ NULL,
-    /*.compress_new =*/ ssh_comp_none_init,
-    /*.compress_free =*/ ssh_comp_none_cleanup,
-    /*.compress =*/ ssh_comp_none_block,
-    /*.decompress_new =*/ ssh_decomp_none_init,
-    /*.decompress_free =*/ ssh_decomp_none_cleanup,
-    /*.decompress =*/ ssh_decomp_none_block,
-    /*.text_name =*/ NULL,
-};
-const static ssh_compression_alg *const compressions[] = {
-    &ssh_zlib, &ssh_comp_none
-};
-
-static void ssh2_transport_free(PacketProtocolLayer *);
-static void ssh2_transport_process_queue(PacketProtocolLayer *);
-static bool ssh2_transport_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
-static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl,
-                                       SessionSpecialCode code, int arg);
-static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl);
-static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl);
-static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
-static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl);
-
-static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s);
-static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def);
-static void ssh2_transport_higher_layer_packet_callback(void *context);
-static unsigned int ssh2_transport_winscp_query(PacketProtocolLayer *ppl, int query);
-
-static const PacketProtocolLayerVtable ssh2_transport_vtable = {
-    // WINSCP
-    /*.free =*/ ssh2_transport_free,
-    /*.process_queue =*/ ssh2_transport_process_queue,
-    /*.get_specials =*/ ssh2_transport_get_specials,
-    /*.special_cmd =*/ ssh2_transport_special_cmd,
-    /*.want_user_input =*/ ssh2_transport_want_user_input,
-    /*.got_user_input =*/ ssh2_transport_got_user_input,
-    /*.reconfigure =*/ ssh2_transport_reconfigure,
-    /*.queued_data_size =*/ ssh2_transport_queued_data_size,
-    /*.name =*/ NULL, /* no protocol name for this layer */
-    ssh2_transport_winscp_query,
-};
-
-#ifndef NO_GSSAPI
-static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
-                                      bool definitely_rekeying);
-#endif
-
-static bool ssh2_transport_timer_update(struct ssh2_transport_state *s,
-                                        unsigned long rekey_time);
-static int ssh2_transport_confirm_weak_crypto_primitive(
-    struct ssh2_transport_state *s, const char *type, const char *name,
-    const void *alg);
-
-static const char *const kexlist_descr[NKEXLIST] = {
-    "key exchange algorithm",
-    "host key algorithm",
-    "client-to-server cipher",
-    "server-to-client cipher",
-    "client-to-server MAC",
-    "server-to-client MAC",
-    "client-to-server compression method",
-    "server-to-client compression method"
-};
-
-static int weak_algorithm_compare(void *av, void *bv);
-
-PacketProtocolLayer *ssh2_transport_new(
-    Conf *conf, const char *host, int port, const char *fullhostname,
-    const char *client_greeting, const char *server_greeting,
-    struct ssh_connection_shared_gss_state *shgss,
-    struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
-    const SshServerConfig *ssc)
-{
-    struct ssh2_transport_state *s = snew(struct ssh2_transport_state);
-    memset(s, 0, sizeof(*s));
-    s->ppl.vt = &ssh2_transport_vtable;
-
-    s->conf = conf_copy(conf);
-    s->savedhost = dupstr(host);
-    s->savedport = port;
-    s->fullhostname = dupstr(fullhostname);
-    s->shgss = shgss;
-    s->client_greeting = dupstr(client_greeting);
-    s->server_greeting = dupstr(server_greeting);
-    s->stats = stats;
-    s->hostkeyblob = strbuf_new();
-
-    pq_in_init(&s->pq_in_higher, higher_layer->seat); // WINSCP
-    pq_out_init(&s->pq_out_higher, higher_layer->seat); // WINSCP
-    s->pq_out_higher.pqb.ic = &s->ic_pq_out_higher;
-    s->ic_pq_out_higher.fn = ssh2_transport_higher_layer_packet_callback;
-    s->ic_pq_out_higher.ctx = &s->ppl;
-    s->ic_pq_out_higher.set = get_seat_callback_set(higher_layer->seat);
-
-    s->higher_layer = higher_layer;
-    s->higher_layer->selfptr = &s->higher_layer;
-    ssh_ppl_setup_queues(s->higher_layer, &s->pq_in_higher, &s->pq_out_higher);
-
-#ifndef NO_GSSAPI
-    s->gss_cred_expiry = GSS_NO_EXPIRATION;
-    s->shgss->srv_name = GSS_C_NO_NAME;
-    s->shgss->ctx = NULL;
-#endif
-    s->thc = ssh_transient_hostkey_cache_new();
-    s->gss_kex_used = false;
-
-    s->outgoing_kexinit = strbuf_new();
-    s->incoming_kexinit = strbuf_new();
-    if (ssc) {
-        s->ssc = ssc;
-        s->client_kexinit = s->incoming_kexinit;
-        s->server_kexinit = s->outgoing_kexinit;
-        s->cstrans = &s->in;
-        s->sctrans = &s->out;
-        s->out.mkkey_adjust = 1;
-    } else {
-        s->client_kexinit = s->outgoing_kexinit;
-        s->server_kexinit = s->incoming_kexinit;
-        s->cstrans = &s->out;
-        s->sctrans = &s->in;
-        s->in.mkkey_adjust = 1;
-    }
-
-    s->weak_algorithms_consented_to = newtree234(weak_algorithm_compare);
-
-    ssh2_transport_set_max_data_size(s);
-
-    return &s->ppl;
-}
-
-static void ssh2_transport_free(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-
-    /*
-     * As our last act before being freed, move any outgoing packets
-     * off our higher layer's output queue on to our own output queue.
-     * We might be being freed while the SSH connection is still alive
-     * (because we're initiating shutdown from our end), in which case
-     * we don't want those last few packets to get lost.
-     *
-     * (If our owner were to have already destroyed our output pq
-     * before wanting to free us, then it would have to reset our
-     * publicly visible out_pq field to NULL to inhibit this attempt.
-     * But that's not how I expect the shutdown sequence to go in
-     * practice.)
-     */
-    if (s->ppl.out_pq)
-        pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher);
-
-    conf_free(s->conf);
-
-    ssh_ppl_free(s->higher_layer);
-
-    pq_in_clear(&s->pq_in_higher);
-    pq_out_clear(&s->pq_out_higher);
-
-    sfree(s->savedhost);
-    sfree(s->fullhostname);
-    sfree(s->client_greeting);
-    sfree(s->server_greeting);
-    sfree(s->keystr);
-    sfree(s->hostkey_str);
-    strbuf_free(s->hostkeyblob);
-    if (s->hkey && !s->hostkeys) {
-        ssh_key_free(s->hkey);
-        s->hkey = NULL;
-    }
-    if (s->f) mp_free(s->f);
-    if (s->p) mp_free(s->p);
-    if (s->g) mp_free(s->g);
-    if (s->K) mp_free(s->K);
-    if (s->dh_ctx)
-        dh_cleanup(s->dh_ctx);
-    if (s->rsa_kex_key_needs_freeing) {
-        ssh_rsakex_freekey(s->rsa_kex_key);
-        sfree(s->rsa_kex_key);
-    }
-    if (s->ecdh_key)
-        ssh_ecdhkex_freekey(s->ecdh_key);
-    if (s->exhash)
-        ssh_hash_free(s->exhash);
-    strbuf_free(s->outgoing_kexinit);
-    strbuf_free(s->incoming_kexinit);
-    ssh_transient_hostkey_cache_free(s->thc);
-
-    freetree234(s->weak_algorithms_consented_to);
-
-    expire_timer_context(s);
-    sfree(s);
-}
-
-/*
- * SSH-2 key derivation (RFC 4253 section 7.2).
- */
-static void ssh2_mkkey(
-    struct ssh2_transport_state *s, strbuf *out,
-    mp_int *K, unsigned char *H, char chr, int keylen)
-{
-    int hlen = s->kex_alg->hash->hlen;
-    int keylen_padded;
-    unsigned char *key;
-    ssh_hash *h;
-
-    if (keylen == 0)
-        return;
-
-    /*
-     * Round the requested amount of key material up to a multiple of
-     * the length of the hash we're using to make it. This makes life
-     * simpler because then we can just write each hash output block
-     * straight into the output buffer without fiddling about
-     * truncating the last one. Since it's going into a strbuf, and
-     * strbufs are always smemclr()ed on free, there's no need to
-     * worry about leaving extra potentially-sensitive data in memory
-     * that the caller didn't ask for.
-     */
-    keylen_padded = ((keylen + hlen - 1) / hlen) * hlen;
-
-    strbuf_clear(out);
-    key = strbuf_append(out, keylen_padded);
-
-    /* First hlen bytes. */
-    h = ssh_hash_new(s->kex_alg->hash);
-    if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY))
-        put_mp_ssh2(h, K);
-    put_data(h, H, hlen);
-    put_byte(h, chr);
-    put_data(h, s->session_id, s->session_id_len);
-    ssh_hash_digest(h, key);
-
-    /* Subsequent blocks of hlen bytes. */
-    if (keylen_padded > hlen) {
-        int offset;
-
-        ssh_hash_reset(h);
-        if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY))
-            put_mp_ssh2(h, K);
-        put_data(h, H, hlen);
-
-        for (offset = hlen; offset < keylen_padded; offset += hlen) {
-            put_data(h, key + offset - hlen, hlen);
-            ssh_hash_digest_nondestructive(h, key + offset);
-        }
-
-    }
-
-    ssh_hash_free(h);
-}
-
-/*
- * Find a slot in a KEXINIT algorithm list to use for a new algorithm.
- * If the algorithm is already in the list, return a pointer to its
- * entry, otherwise return an entry from the end of the list.
- * This assumes that every time a particular name is passed in, it
- * comes from the same string constant.  If this isn't true, this
- * function may need to be rewritten to use strcmp() instead.
- */
-static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm
-                                                     *list, const char *name)
-{
-    int i;
-
-    for (i = 0; i < MAXKEXLIST; i++)
-        if (list[i].name == NULL || list[i].name == name) {
-            list[i].name = name;
-            return &list[i];
-        }
-
-    unreachable("Should never run out of space in KEXINIT list");
-}
-
-bool ssh2_common_filter_queue(PacketProtocolLayer *ppl)
-{
-    static const char *const ssh2_disconnect_reasons[] = {
-        NULL,
-        "host not allowed to connect",
-        "protocol error",
-        "key exchange failed",
-        "host authentication failed",
-        "MAC error",
-        "compression error",
-        "service not available",
-        "protocol version not supported",
-        "host key not verifiable",
-        "connection lost",
-        "by application",
-        "too many connections",
-        "auth cancelled by user",
-        "no more auth methods available",
-        "illegal user name",
-    };
-
-    PktIn *pktin;
-    ptrlen msg;
-    int reason;
-
-    while ((pktin = pq_peek(ppl->in_pq)) != NULL) {
-        switch (pktin->type) {
-          case SSH2_MSG_DISCONNECT:
-            reason = get_uint32(pktin);
-            msg = get_string(pktin);
-
-            ssh_remote_error(
-                ppl->ssh, "Remote side sent disconnect message\n"
-                "type %d (%s):\n\"%.*s\"", reason,
-                ((reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
-                 ssh2_disconnect_reasons[reason] : "unknown"),
-                PTRLEN_PRINTF(msg));
-            /* don't try to pop the queue, because we've been freed! */
-            return true;               /* indicate that we've been freed */
-
-          case SSH2_MSG_DEBUG:
-            /* XXX maybe we should actually take notice of the return value */
-            get_bool(pktin);
-            msg = get_string(pktin);
-            ppl_logevent("Remote debug message: %.*s", PTRLEN_PRINTF(msg));
-            pq_pop(ppl->in_pq);
-            break;
-
-          case SSH2_MSG_IGNORE:
-            /* Do nothing, because we're ignoring it! Duhh. */
-            pq_pop(ppl->in_pq);
-            break;
-
-          case SSH2_MSG_EXT_INFO: {
-            /*
-             * The BPP enforces that these turn up only at legal
-             * points in the protocol. In particular, it will not pass
-             * an EXT_INFO on to us if it arrives before encryption is
-             * enabled (which is when a MITM could inject one
-             * maliciously).
-             *
-             * However, one of the criteria for legality is that a
-             * server is permitted to send this message immediately
-             * _before_ USERAUTH_SUCCESS. So we may receive this
-             * message not yet knowing whether it's legal to have sent
-             * it - we won't know until the BPP processes the next
-             * packet.
-             *
-             * But that should be OK, because firstly, an
-             * out-of-sequence EXT_INFO that's still within the
-             * encrypted session is only a _protocol_ violation, not
-             * an attack; secondly, any data we set in response to
-             * such an illegal EXT_INFO won't have a chance to affect
-             * the session before the BPP aborts it anyway.
-             */
-            uint32_t nexts = get_uint32(pktin);
-            uint32_t i; // WINSCP
-            for (i = 0; i < nexts && !get_err(pktin); i++) {
-                ptrlen extname = get_string(pktin);
-                ptrlen extvalue = get_string(pktin);
-                if (ptrlen_eq_string(extname, "server-sig-algs")) {
-                    /*
-                     * Server has sent a list of signature algorithms
-                     * it will potentially accept for user
-                     * authentication keys. Check in particular
-                     * whether the RFC 8332 improved versions of
-                     * ssh-rsa are in the list, and set flags in the
-                     * BPP if so.
-                     *
-                     * TODO: another thing we _could_ do here is to
-                     * record a full list of the algorithm identifiers
-                     * we've seen, whether we understand them
-                     * ourselves or not. Then we could use that as a
-                     * pre-filter during userauth, to skip keys in the
-                     * SSH agent if we already know the server can't
-                     * possibly accept them. (Even if the key
-                     * algorithm is one that the agent and the server
-                     * both understand but we do not.)
-                     */
-                    ptrlen algname;
-                    while (get_commasep_word(&extvalue, &algname)) {
-                        if (ptrlen_eq_string(algname, "rsa-sha2-256"))
-                            ppl->bpp->ext_info_rsa_sha256_ok = true;
-                        if (ptrlen_eq_string(algname, "rsa-sha2-512"))
-                            ppl->bpp->ext_info_rsa_sha512_ok = true;
-                    }
-                }
-            }
-            pq_pop(ppl->in_pq);
-            break;
-          }
-
-          default:
-            return false;
-        }
-    }
-
-    return false;
-}
-
-static bool ssh2_transport_filter_queue(struct ssh2_transport_state *s)
-{
-    PktIn *pktin;
-
-    while (1) {
-        if (ssh2_common_filter_queue(&s->ppl))
-            return true;
-        if ((pktin = pq_peek(s->ppl.in_pq)) == NULL)
-            return false;
-
-        /* Pass on packets to the next layer if they're outside
-         * the range reserved for the transport protocol. */
-        if (pktin->type >= 50) {
-            /* ... except that we shouldn't tolerate higher-layer
-             * packets coming from the server before we've seen
-             * the first NEWKEYS. */
-            if (!s->higher_layer_ok) {
-                ssh_proto_error(s->ppl.ssh, "Received premature higher-"
-                                "layer packet, type %d (%s)", pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                return true;
-            }
-
-            pq_pop(s->ppl.in_pq);
-            pq_push(&s->pq_in_higher, pktin);
-        } else {
-            /* Anything else is a transport-layer packet that the main
-             * process_queue coroutine should handle. */
-            return false;
-        }
-    }
-}
-
-PktIn *ssh2_transport_pop(struct ssh2_transport_state *s)
-{
-    if (ssh2_transport_filter_queue(s))
-        return NULL;   /* we've been freed */
-    return pq_pop(s->ppl.in_pq);
-}
-
-static void ssh2_write_kexinit_lists(
-    /*WINSCP*/ Seat * seat, BinarySink *pktout,
-    struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
-    Conf *conf, const SshServerConfig *ssc, int remote_bugs,
-    const char *hk_host, int hk_port, const ssh_keyalg *hk_prev,
-    ssh_transient_hostkey_cache *thc,
-    ssh_key *const *our_hostkeys, int our_nhostkeys,
-    bool first_time, bool can_gssapi_keyex, bool transient_hostkey_mode)
-{
-    int i, j, k;
-    bool warn;
-
-    int n_preferred_kex;
-    const ssh_kexes *preferred_kex[KEX_MAX + 1]; /* +1 for GSSAPI */
-    int n_preferred_hk;
-    int preferred_hk[HK_MAX];
-    int n_preferred_ciphers;
-    const ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
-    const ssh_compression_alg *preferred_comp;
-    const ssh2_macalg *const *maclist;
-    int nmacs;
-
-    struct kexinit_algorithm *alg;
-
-    /*
-     * Set up the preferred key exchange. (NULL => warn below here)
-     */
-    n_preferred_kex = 0;
-    if (can_gssapi_keyex)
-        preferred_kex[n_preferred_kex++] = &ssh_gssk5_sha1_kex;
-    for (i = 0; i < KEX_MAX; i++) {
-        switch (conf_get_int_int(conf, CONF_ssh_kexlist, i)) {
-          case KEX_DHGEX:
-            preferred_kex[n_preferred_kex++] =
-                &ssh_diffiehellman_gex;
-            break;
-          case KEX_DHGROUP14:
-            preferred_kex[n_preferred_kex++] =
-                &ssh_diffiehellman_group14;
-            break;
-          case KEX_DHGROUP1:
-            preferred_kex[n_preferred_kex++] =
-                &ssh_diffiehellman_group1;
-            break;
-          case KEX_RSA:
-            preferred_kex[n_preferred_kex++] =
-                &ssh_rsa_kex;
-            break;
-          case KEX_ECDH:
-            preferred_kex[n_preferred_kex++] =
-                &ssh_ecdh_kex;
-            break;
-          case KEX_WARN:
-            /* Flag for later. Don't bother if it's the last in
-             * the list. */
-            if (i < KEX_MAX - 1) {
-                preferred_kex[n_preferred_kex++] = NULL;
-            }
-            break;
-        }
-    }
-
-    /*
-     * Set up the preferred host key types. These are just the ids
-     * in the enum in putty.h, so 'warn below here' is indicated
-     * by HK_WARN.
-     */
-    n_preferred_hk = 0;
-    for (i = 0; i < HK_MAX; i++) {
-        int id = conf_get_int_int(conf, CONF_ssh_hklist, i);
-        /* As above, don't bother with HK_WARN if it's last in the
-         * list */
-        if (id != HK_WARN || i < HK_MAX - 1)
-            preferred_hk[n_preferred_hk++] = id;
-    }
-
-    /*
-     * Set up the preferred ciphers. (NULL => warn below here)
-     */
-    n_preferred_ciphers = 0;
-    for (i = 0; i < CIPHER_MAX; i++) {
-        switch (conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
-          case CIPHER_BLOWFISH:
-            preferred_ciphers[n_preferred_ciphers++] = &ssh2_blowfish;
-            break;
-          case CIPHER_DES:
-            if (conf_get_bool(conf, CONF_ssh2_des_cbc))
-                preferred_ciphers[n_preferred_ciphers++] = &ssh2_des;
-            break;
-          case CIPHER_3DES:
-            preferred_ciphers[n_preferred_ciphers++] = &ssh2_3des;
-            break;
-          case CIPHER_AES:
-            preferred_ciphers[n_preferred_ciphers++] = &ssh2_aes;
-            break;
-          case CIPHER_ARCFOUR:
-            preferred_ciphers[n_preferred_ciphers++] = &ssh2_arcfour;
-            break;
-          case CIPHER_CHACHA20:
-            preferred_ciphers[n_preferred_ciphers++] = &ssh2_ccp;
-            break;
-          case CIPHER_WARN:
-            /* Flag for later. Don't bother if it's the last in
-             * the list. */
-            if (i < CIPHER_MAX - 1) {
-                preferred_ciphers[n_preferred_ciphers++] = NULL;
-            }
-            break;
-        }
-    }
-
-    /*
-     * Set up preferred compression.
-     */
-    if (conf_get_bool(conf, CONF_compression))
-        preferred_comp = &ssh_zlib;
-    else
-        preferred_comp = &ssh_comp_none;
-
-    for (i = 0; i < NKEXLIST; i++)
-        for (j = 0; j < MAXKEXLIST; j++)
-            kexlists[i][j].name = NULL;
-    /* List key exchange algorithms. */
-    warn = false;
-    for (i = 0; i < n_preferred_kex; i++) {
-        const ssh_kexes *k = preferred_kex[i];
-        if (!k) warn = true;
-        else for (j = 0; j < k->nkexes; j++) {
-                alg = ssh2_kexinit_addalg(kexlists[KEXLIST_KEX],
-                                          k->list[j]->name);
-                alg->u.kex.kex = k->list[j];
-                alg->u.kex.warn = warn;
-            }
-    }
-    /* List server host key algorithms. */
-    if (our_hostkeys) {
-        /*
-         * In server mode, we just list the algorithms that match the
-         * host keys we actually have.
-         */
-        for (i = 0; i < our_nhostkeys; i++) {
-            const ssh_keyalg *keyalg = ssh_key_alg(our_hostkeys[i]);
-
-            alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
-                                      keyalg->ssh_id);
-            alg->u.hk.hostkey = keyalg;
-            alg->u.hk.hkflags = 0;
-            alg->u.hk.warn = false;
-
-            if (keyalg == &ssh_rsa) {
-                alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
-                                          "rsa-sha2-256");
-                alg->u.hk.hostkey = keyalg;
-                alg->u.hk.hkflags = SSH_AGENT_RSA_SHA2_256;
-                alg->u.hk.warn = false;
-
-                alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
-                                          "rsa-sha2-512");
-                alg->u.hk.hostkey = keyalg;
-                alg->u.hk.hkflags = SSH_AGENT_RSA_SHA2_512;
-                alg->u.hk.warn = false;
-            }
-        }
-    } else if (first_time) {
-        /*
-         * In the first key exchange, we list all the algorithms we're
-         * prepared to cope with, but (if configured to) we prefer
-         * those algorithms for which we have a host key for this
-         * host.
-         *
-         * If the host key algorithm is below the warning
-         * threshold, we warn even if we did already have a key
-         * for it, on the basis that if the user has just
-         * reconfigured that host key type to be warned about,
-         * they surely _do_ want to be alerted that a server
-         * they're actually connecting to is using it.
-         */
-        warn = false;
-        for (i = 0; i < n_preferred_hk; i++) {
-            if (preferred_hk[i] == HK_WARN)
-                warn = true;
-            for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
-                if (ssh2_hostkey_algs[j].id != preferred_hk[i])
-                    continue;
-                if (conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys) &&
-                    have_ssh_host_key(seat, hk_host, hk_port, // WINSCP
-                                      ssh2_hostkey_algs[j].alg->cache_id)) {
-                    alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
-                                              ssh2_hostkey_algs[j].alg->ssh_id);
-                    alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg;
-                    alg->u.hk.warn = warn;
-                }
-            }
-        }
-        warn = false;
-        for (i = 0; i < n_preferred_hk; i++) {
-            if (preferred_hk[i] == HK_WARN)
-                warn = true;
-            for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
-                if (ssh2_hostkey_algs[j].id != preferred_hk[i])
-                    continue;
-                alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
-                                          ssh2_hostkey_algs[j].alg->ssh_id);
-                alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg;
-                alg->u.hk.warn = warn;
-            }
-        }
-#ifndef NO_GSSAPI
-    } else if (transient_hostkey_mode) {
-        /*
-         * If we've previously done a GSSAPI KEX, then we list
-         * precisely the algorithms for which a previous GSS key
-         * exchange has delivered us a host key, because we expect
-         * one of exactly those keys to be used in any subsequent
-         * non-GSS-based rekey.
-         *
-         * An exception is if this is the key exchange we
-         * triggered for the purposes of populating that cache -
-         * in which case the cache will currently be empty, which
-         * isn't helpful!
-         */
-        warn = false;
-        for (i = 0; i < n_preferred_hk; i++) {
-            if (preferred_hk[i] == HK_WARN)
-                warn = true;
-            for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
-                if (ssh2_hostkey_algs[j].id != preferred_hk[i])
-                    continue;
-                if (ssh_transient_hostkey_cache_has(
-                        thc, ssh2_hostkey_algs[j].alg)) {
-                    alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
-                                              ssh2_hostkey_algs[j].alg->ssh_id);
-                    alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg;
-                    alg->u.hk.warn = warn;
-                }
-            }
-        }
-#endif
-    } else {
-        /*
-         * In subsequent key exchanges, we list only the host key
-         * algorithm that was selected in the first key exchange,
-         * so that we keep getting the same host key and hence
-         * don't have to interrupt the user's session to ask for
-         * reverification.
-         */
-        assert(hk_prev);
-        alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], hk_prev->ssh_id);
-        alg->u.hk.hostkey = hk_prev;
-        alg->u.hk.warn = false;
-    }
-    if (can_gssapi_keyex) {
-        alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], "null");
-        alg->u.hk.hostkey = NULL;
-    }
-    /* List encryption algorithms (client->server then server->client). */
-    for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
-        warn = false;
-#ifdef FUZZING
-        alg = ssh2_kexinit_addalg(kexlists[k], "none");
-        alg->u.cipher.cipher = NULL;
-        alg->u.cipher.warn = warn;
-#endif /* FUZZING */
-        for (i = 0; i < n_preferred_ciphers; i++) {
-            const ssh2_ciphers *c = preferred_ciphers[i];
-            if (!c) warn = true;
-            else for (j = 0; j < c->nciphers; j++) {
-                    alg = ssh2_kexinit_addalg(kexlists[k],
-                                              c->list[j]->ssh2_id);
-                    alg->u.cipher.cipher = c->list[j];
-                    alg->u.cipher.warn = warn;
-                }
-        }
-    }
-
-    /*
-     * Be prepared to work around the buggy MAC problem.
-     */
-    if (remote_bugs & BUG_SSH2_HMAC) {
-        maclist = buggymacs;
-        nmacs = lenof(buggymacs);
-    } else {
-        maclist = macs;
-        nmacs = lenof(macs);
-    }
-
-    /* List MAC algorithms (client->server then server->client). */
-    for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
-#ifdef FUZZING
-        alg = ssh2_kexinit_addalg(kexlists[j], "none");
-        alg->u.mac.mac = NULL;
-        alg->u.mac.etm = false;
-#endif /* FUZZING */
-        for (i = 0; i < nmacs; i++) {
-            alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->name);
-            alg->u.mac.mac = maclist[i];
-            alg->u.mac.etm = false;
-        }
-        for (i = 0; i < nmacs; i++) {
-            /* For each MAC, there may also be an ETM version,
-             * which we list second. */
-            if (maclist[i]->etm_name) {
-                alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->etm_name);
-                alg->u.mac.mac = maclist[i];
-                alg->u.mac.etm = true;
-            }
-        }
-    }
-
-    /* List client->server compression algorithms,
-     * then server->client compression algorithms. (We use the
-     * same set twice.) */
-    for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
-        assert(lenof(compressions) > 1);
-        /* Prefer non-delayed versions */
-        alg = ssh2_kexinit_addalg(kexlists[j], preferred_comp->name);
-        alg->u.comp.comp = preferred_comp;
-        alg->u.comp.delayed = false;
-        if (preferred_comp->delayed_name) {
-            alg = ssh2_kexinit_addalg(kexlists[j],
-                                      preferred_comp->delayed_name);
-            alg->u.comp.comp = preferred_comp;
-            alg->u.comp.delayed = true;
-        }
-        for (i = 0; i < lenof(compressions); i++) {
-            const ssh_compression_alg *c = compressions[i];
-            alg = ssh2_kexinit_addalg(kexlists[j], c->name);
-            alg->u.comp.comp = c;
-            alg->u.comp.delayed = false;
-            if (c->delayed_name) {
-                alg = ssh2_kexinit_addalg(kexlists[j], c->delayed_name);
-                alg->u.comp.comp = c;
-                alg->u.comp.delayed = true;
-            }
-        }
-    }
-
-    /*
-     * Finally, format the lists into text and write them into the
-     * outgoing KEXINIT packet.
-     */
-    for (i = 0; i < NKEXLIST; i++) {
-        strbuf *list = strbuf_new();
-        #ifndef WINSCP
-        if (ssc && ssc->kex_override[i].ptr) {
-            put_datapl(list, ssc->kex_override[i]);
-        } else {
-        #endif
-            for (j = 0; j < MAXKEXLIST; j++) {
-                if (kexlists[i][j].name == NULL) break;
-                add_to_commasep(list, kexlists[i][j].name);
-            }
-        #ifndef WINSCP
-        }
-        #endif
-        if (i == KEXLIST_KEX && first_time) {
-            if (our_hostkeys)          /* we're the server */
-                add_to_commasep(list, "ext-info-s");
-            else                       /* we're the client */
-                add_to_commasep(list, "ext-info-c");
-        }
-        put_stringsb(pktout, list);
-    }
-    /* List client->server languages. Empty list. */
-    put_stringz(pktout, "");
-    /* List server->client languages. Empty list. */
-    put_stringz(pktout, "");
-}
-
-static bool ssh2_scan_kexinits(
-    ptrlen client_kexinit, ptrlen server_kexinit,
-    struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
-    const ssh_kex **kex_alg, const ssh_keyalg **hostkey_alg,
-    transport_direction *cs, transport_direction *sc,
-    bool *warn_kex, bool *warn_hk, bool *warn_cscipher, bool *warn_sccipher,
-    Ssh *ssh, bool *ignore_guess_cs_packet, bool *ignore_guess_sc_packet,
-    int *n_server_hostkeys, int server_hostkeys[MAXKEXLIST], unsigned *hkflags,
-    bool *can_send_ext_info)
-{
-    BinarySource client[1], server[1];
-    int i;
-    bool guess_correct;
-    ptrlen clists[NKEXLIST], slists[NKEXLIST];
-    const struct kexinit_algorithm *selected[NKEXLIST];
-
-    BinarySource_BARE_INIT_PL(client, client_kexinit);
-    BinarySource_BARE_INIT_PL(server, server_kexinit);
-
-    /* Skip packet type bytes and random cookies. */
-    get_data(client, 1 + 16);
-    get_data(server, 1 + 16);
-
-    guess_correct = true;
-
-    /* Find the matching string in each list, and map it to its
-     * kexinit_algorithm structure. */
-    for (i = 0; i < NKEXLIST; i++) {
-        ptrlen clist, slist, cword, sword, found;
-        bool cfirst, sfirst;
-        int j;
-
-        clists[i] = get_string(client);
-        slists[i] = get_string(server);
-        if (get_err(client) || get_err(server)) {
-            /* Report a better error than the spurious "Couldn't
-             * agree" that we'd generate if we pressed on regardless
-             * and treated the empty get_string() result as genuine */
-            ssh_proto_error(ssh, "KEXINIT packet was incomplete");
-            return false;
-        }
-
-        for (cfirst = true, clist = clists[i];
-             get_commasep_word(&clist, &cword); cfirst = false)
-            for (sfirst = true, slist = slists[i];
-                 get_commasep_word(&slist, &sword); sfirst = false)
-                if (ptrlen_eq_ptrlen(cword, sword)) {
-                    found = cword;
-                    goto found_match;
-                }
-
-        /* No matching string found in the two lists. Delay reporting
-         * a fatal error until below, because sometimes it turns out
-         * not to be fatal. */
-        selected[i] = NULL;
-
-        /*
-         * However, even if a failure to agree on any algorithm at all
-         * is not completely fatal (e.g. because it's the MAC
-         * negotiation for a cipher that comes with a built-in MAC),
-         * it still invalidates the guessed key exchange packet. (RFC
-         * 4253 section 7, not contradicted by OpenSSH's
-         * PROTOCOL.chacha20poly1305 or as far as I can see by their
-         * code.)
-         */
-        guess_correct = false;
-
-        continue;
-
-      found_match:
-
-        selected[i] = NULL;
-        for (j = 0; j < MAXKEXLIST; j++) {
-            if (kexlists[i][j].name &&
-                ptrlen_eq_string(found, kexlists[i][j].name)) {
-                selected[i] = &kexlists[i][j];
-                break;
-            }
-        }
-        if (!selected[i]) {
-            /*
-             * In the client, this should never happen! But in the
-             * server, where we allow manual override on the command
-             * line of the exact KEXINIT strings, it can happen
-             * because the command line contained a typo. So we
-             * produce a reasonably useful message instead of an
-             * assertion failure.
-             */
-            ssh_sw_abort(ssh, "Selected %s \"%.*s\" does not correspond to "
-                         "any supported algorithm",
-                         kexlist_descr[i], PTRLEN_PRINTF(found));
-            return false;
-        }
-
-        /*
-         * If the kex or host key algorithm is not the first one in
-         * both sides' lists, that means the guessed key exchange
-         * packet (if any) is officially wrong.
-         */
-        if ((i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) && !(cfirst || sfirst))
-            guess_correct = false;
-    }
-
-    /*
-     * Skip language strings in both KEXINITs, and read the flags
-     * saying whether a guessed KEX packet follows.
-     */
-    get_string(client);
-    get_string(client);
-    get_string(server);
-    get_string(server);
-    if (ignore_guess_cs_packet)
-        *ignore_guess_cs_packet = get_bool(client) && !guess_correct;
-    if (ignore_guess_sc_packet)
-        *ignore_guess_sc_packet = get_bool(server) && !guess_correct;
-
-    /*
-     * Now transcribe the selected algorithm set into the output data.
-     */
-    for (i = 0; i < NKEXLIST; i++) {
-        const struct kexinit_algorithm *alg;
-
-        /*
-         * If we've already selected a cipher which requires a
-         * particular MAC, then just select that. This is the case in
-         * which it's not a fatal error if the actual MAC string lists
-         * didn't include any matching error.
-         */
-        if (i == KEXLIST_CSMAC && cs->cipher &&
-            cs->cipher->required_mac) {
-            cs->mac = cs->cipher->required_mac;
-            cs->etm_mode = !!(cs->mac->etm_name);
-            continue;
-        }
-        if (i == KEXLIST_SCMAC && sc->cipher &&
-            sc->cipher->required_mac) {
-            sc->mac = sc->cipher->required_mac;
-            sc->etm_mode = !!(sc->mac->etm_name);
-            continue;
-        }
-
-        alg = selected[i];
-        if (!alg) {
-            /*
-             * Otherwise, any match failure _is_ a fatal error.
-             */
-            ssh_sw_abort(ssh, "Couldn't agree a %s (available: %.*s)",
-                         kexlist_descr[i], PTRLEN_PRINTF(slists[i]));
-            return false;
-        }
-
-        switch (i) {
-          case KEXLIST_KEX:
-            *kex_alg = alg->u.kex.kex;
-            *warn_kex = alg->u.kex.warn;
-            break;
-
-          case KEXLIST_HOSTKEY:
-            /*
-             * Ignore an unexpected/inappropriate offer of "null",
-             * we offer "null" when we're willing to use GSS KEX,
-             * but it is only acceptable when GSSKEX is actually
-             * selected.
-             */
-            if (alg->u.hk.hostkey == NULL &&
-                (*kex_alg)->main_type != KEXTYPE_GSS)
-                continue;
-
-            *hostkey_alg = alg->u.hk.hostkey;
-            *hkflags = alg->u.hk.hkflags;
-            *warn_hk = alg->u.hk.warn;
-            break;
-
-          case KEXLIST_CSCIPHER:
-            cs->cipher = alg->u.cipher.cipher;
-            *warn_cscipher = alg->u.cipher.warn;
-            break;
-
-          case KEXLIST_SCCIPHER:
-            sc->cipher = alg->u.cipher.cipher;
-            *warn_sccipher = alg->u.cipher.warn;
-            break;
-
-          case KEXLIST_CSMAC:
-            cs->mac = alg->u.mac.mac;
-            cs->etm_mode = alg->u.mac.etm;
-            break;
-
-          case KEXLIST_SCMAC:
-            sc->mac = alg->u.mac.mac;
-            sc->etm_mode = alg->u.mac.etm;
-            break;
-
-          case KEXLIST_CSCOMP:
-            cs->comp = alg->u.comp.comp;
-            cs->comp_delayed = alg->u.comp.delayed;
-            break;
-
-          case KEXLIST_SCCOMP:
-            sc->comp = alg->u.comp.comp;
-            sc->comp_delayed = alg->u.comp.delayed;
-            break;
-
-          default:
-            unreachable("Bad list index in scan_kexinits");
-        }
-    }
-
-    /*
-     * Check whether the other side advertised support for EXT_INFO.
-     */
-    {
-        ptrlen extinfo_advert =
-            (server_hostkeys ? PTRLEN_LITERAL("ext-info-c") :
-             PTRLEN_LITERAL("ext-info-s"));
-        ptrlen list = (server_hostkeys ? clists[KEXLIST_KEX] :
-                       slists[KEXLIST_KEX]);
-        ptrlen word; // WINSCP
-        for (; get_commasep_word(&list, &word) ;)
-            if (ptrlen_eq_ptrlen(word, extinfo_advert))
-                *can_send_ext_info = true;
-    }
-
-    if (server_hostkeys) {
-        /*
-         * Finally, make an auxiliary pass over the server's host key
-         * list to find all the host key algorithms offered by the
-         * server which we know about at all, whether we selected each
-         * one or not. We return these as a list of indices into the
-         * constant ssh2_hostkey_algs[] array.
-         */
-        *n_server_hostkeys = 0;
-
-        { // WINSCP
-        ptrlen list = slists[KEXLIST_HOSTKEY];
-        ptrlen word; // WINSCP
-        for (; get_commasep_word(&list, &word) ;) {
-            for (i = 0; i < lenof(ssh2_hostkey_algs); i++)
-                if (ptrlen_eq_string(word, ssh2_hostkey_algs[i].alg->ssh_id)) {
-                    server_hostkeys[(*n_server_hostkeys)++] = i;
-                    break;
-                }
-        }
-        } // WINSCP
-    }
-
-    return true;
-}
-
-void ssh2transport_finalise_exhash(struct ssh2_transport_state *s)
-{
-    put_mp_ssh2(s->exhash, s->K);
-    assert(ssh_hash_alg(s->exhash)->hlen <= sizeof(s->exchange_hash));
-    ssh_hash_final(s->exhash, s->exchange_hash);
-    s->exhash = NULL;
-
-#if 0
-    debug("Exchange hash is:\n");
-    dmemdump(s->exchange_hash, s->kex_alg->hash->hlen);
-#endif
-}
-
-static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-    PktIn *pktin;
-    PktOut *pktout;
-
-    /* Filter centrally handled messages off the front of the queue on
-     * every entry to this coroutine, no matter where we're resuming
-     * from, even if we're _not_ looping on pq_pop. That way we can
-     * still proactively handle those messages even if we're waiting
-     * for a user response. */
-    if (ssh2_transport_filter_queue(s))
-        return;   /* we've been freed */
-
-    crBegin(s->crState);
-
-    s->in.cipher = s->out.cipher = NULL;
-    s->in.mac = s->out.mac = NULL;
-    s->in.comp = s->out.comp = NULL;
-
-    s->got_session_id = false;
-    s->need_gss_transient_hostkey = false;
-    s->warned_about_no_gss_transient_hostkey = false;
-
-  begin_key_exchange:
-
-#ifndef NO_GSSAPI
-    if (s->need_gss_transient_hostkey) {
-        /*
-         * This flag indicates a special case in which we must not do
-         * GSS key exchange even if we could. (See comments below,
-         * where the flag was set on the previous key exchange.)
-         */
-        s->can_gssapi_keyex = false;
-    } else if (conf_get_bool(s->conf, CONF_try_gssapi_kex)) {
-        /*
-         * We always check if we have GSS creds before we come up with
-         * the kex algorithm list, otherwise future rekeys will fail
-         * when creds expire. To make this so, this code section must
-         * follow the begin_key_exchange label above, otherwise this
-         * section would execute just once per-connection.
-         *
-         * Update GSS state unless the reason we're here is that a
-         * timer just checked the GSS state and decided that we should
-         * rekey to update delegated credentials. In that case, the
-         * state is "fresh".
-         */
-        if (s->rekey_class != RK_GSS_UPDATE)
-            ssh2_transport_gss_update(s, true);
-
-        /* Do GSSAPI KEX when capable */
-        s->can_gssapi_keyex = s->gss_status & GSS_KEX_CAPABLE;
-
-        /*
-         * But not when failure is likely. [ GSS implementations may
-         * attempt (and fail) to use a ticket that is almost expired
-         * when retrieved from the ccache that actually expires by the
-         * time the server receives it. ]
-         *
-         * Note: The first time always try KEXGSS if we can, failures
-         * will be very rare, and disabling the initial GSS KEX is
-         * worse. Some day GSS libraries will ignore cached tickets
-         * whose lifetime is critically short, and will instead use
-         * fresh ones.
-         */
-        if (!s->got_session_id && (s->gss_status & GSS_CTXT_MAYFAIL) != 0)
-            s->can_gssapi_keyex = false;
-        s->gss_delegate = conf_get_bool(s->conf, CONF_gssapifwd);
-    } else {
-        s->can_gssapi_keyex = false;
-    }
-#endif
-
-    s->ppl.bpp->pls->kctx = SSH2_PKTCTX_NOKEX;
-
-    /*
-     * Construct our KEXINIT packet, in a strbuf so we can refer to it
-     * later.
-     */
-    strbuf_clear(s->client_kexinit);
-    put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT);
-    random_read(strbuf_append(s->outgoing_kexinit, 16), 16);
-    ssh2_write_kexinit_lists(
-        /*WINSCP*/ s->ppl.seat, BinarySink_UPCAST(s->outgoing_kexinit), s->kexlists,
-        s->conf, s->ssc, s->ppl.remote_bugs,
-        s->savedhost, s->savedport, s->hostkey_alg, s->thc,
-        s->hostkeys, s->nhostkeys,
-        !s->got_session_id, s->can_gssapi_keyex,
-        s->gss_kex_used && !s->need_gss_transient_hostkey);
-    /* First KEX packet does _not_ follow, because we're not that brave. */
-    put_bool(s->outgoing_kexinit, false);
-    put_uint32(s->outgoing_kexinit, 0);             /* reserved */
-
-    /*
-     * Send our KEXINIT.
-     */
-    pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXINIT);
-    put_data(pktout, s->outgoing_kexinit->u + 1,
-             s->outgoing_kexinit->len - 1); /* omit initial packet type byte */
-    pq_push(s->ppl.out_pq, pktout);
-
-    /*
-     * Flag that KEX is in progress.
-     */
-    s->kex_in_progress = true;
-
-    /*
-     * Wait for the other side's KEXINIT, and save it.
-     */
-    crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-    if (pktin->type != SSH2_MSG_KEXINIT) {
-        ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                        "expecting KEXINIT, type %d (%s)", pktin->type,
-                        ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                      s->ppl.bpp->pls->actx, pktin->type));
-        return;
-    }
-    strbuf_clear(s->incoming_kexinit);
-    put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT);
-    put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin));
-
-    /*
-     * Work through the two KEXINIT packets in parallel to find the
-     * selected algorithm identifiers.
-     */
-    {
-        int nhk, hks[MAXKEXLIST], i, j;
-
-        if (!ssh2_scan_kexinits(
-                ptrlen_from_strbuf(s->client_kexinit),
-                ptrlen_from_strbuf(s->server_kexinit),
-                s->kexlists, &s->kex_alg, &s->hostkey_alg, s->cstrans,
-                s->sctrans, &s->warn_kex, &s->warn_hk, &s->warn_cscipher,
-                &s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks,
-                &s->hkflags, &s->can_send_ext_info))
-            return; /* false means a fatal error function was called */
-
-        /*
-         * In addition to deciding which host key we're actually going
-         * to use, we should make a list of the host keys offered by
-         * the server which we _don't_ have cached. These will be
-         * offered as cross-certification options by ssh_get_specials.
-         *
-         * We also count the key we're currently using for KEX as one
-         * we've already got, because by the time this menu becomes
-         * visible, it will be.
-         */
-        s->n_uncert_hostkeys = 0;
-
-        for (i = 0; i < nhk; i++) {
-            j = hks[i];
-            if (ssh2_hostkey_algs[j].alg != s->hostkey_alg &&
-                !have_ssh_host_key(s->ppl.seat, s->savedhost, s->savedport, // WINSCP
-                                   ssh2_hostkey_algs[j].alg->cache_id)) {
-                s->uncert_hostkeys[s->n_uncert_hostkeys++] = j;
-            }
-        }
-    }
-
-    if (s->warn_kex) {
-        s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
-            s, "key-exchange algorithm", s->kex_alg->name, s->kex_alg);
-        crMaybeWaitUntilV(s->dlgret >= 0);
-        if (s->dlgret == 0) {
-            ssh_user_close(s->ppl.ssh, "User aborted at kex warning");
-            return;
-        }
-    }
-
-    if (s->warn_hk) {
-        int j, k;
-        char *betteralgs;
-
-        /*
-         * Change warning box wording depending on why we chose a
-         * warning-level host key algorithm. If it's because
-         * that's all we have *cached*, list the host keys we
-         * could usefully cross-certify. Otherwise, use the same
-         * standard wording as any other weak crypto primitive.
-         */
-        betteralgs = NULL;
-        for (j = 0; j < s->n_uncert_hostkeys; j++) {
-            const struct ssh_signkey_with_user_pref_id *hktype =
-                &ssh2_hostkey_algs[s->uncert_hostkeys[j]];
-            bool better = false;
-            for (k = 0; k < HK_MAX; k++) {
-                int id = conf_get_int_int(s->conf, CONF_ssh_hklist, k);
-                if (id == HK_WARN) {
-                    break;
-                } else if (id == hktype->id) {
-                    better = true;
-                    break;
-                }
-            }
-            if (better) {
-                if (betteralgs) {
-                    char *old_ba = betteralgs;
-                    betteralgs = dupcat(betteralgs, ",", hktype->alg->ssh_id);
-                    sfree(old_ba);
-                } else {
-                    betteralgs = dupstr(hktype->alg->ssh_id);
-                }
-            }
-        }
-        if (betteralgs) {
-                /* Use the special warning prompt that lets us provide
-                 * a list of better algorithms */
-                s->dlgret = seat_confirm_weak_cached_hostkey(
-                    s->ppl.seat, s->hostkey_alg->ssh_id, betteralgs,
-                    ssh2_transport_dialog_callback, s);
-            sfree(betteralgs);
-        } else {
-                /* If none exist, use the more general 'weak crypto'
-                 * warning prompt */
-                s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
-                    s, "host key type", s->hostkey_alg->ssh_id,
-                    s->hostkey_alg);
-        }
-        crMaybeWaitUntilV(s->dlgret >= 0);
-        if (s->dlgret == 0) {
-            ssh_user_close(s->ppl.ssh, "User aborted at host key warning");
-            return;
-        }
-    }
-
-    if (s->warn_cscipher) {
-        s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
-            s, "client-to-server cipher", s->out.cipher->ssh2_id,
-            s->out.cipher);
-        crMaybeWaitUntilV(s->dlgret >= 0);
-        if (s->dlgret == 0) {
-            ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
-            return;
-        }
-    }
-
-    if (s->warn_sccipher) {
-        s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
-            s, "server-to-client cipher", s->in.cipher->ssh2_id,
-            s->in.cipher);
-        crMaybeWaitUntilV(s->dlgret >= 0);
-        if (s->dlgret == 0) {
-            ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
-            return;
-        }
-    }
-
-    /*
-     * If the other side has sent an initial key exchange packet that
-     * we must treat as a wrong guess, wait for it, and discard it.
-     */
-    if (s->ignorepkt)
-        crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-
-    /*
-     * Actually perform the key exchange.
-     */
-    s->exhash = ssh_hash_new(s->kex_alg->hash);
-    put_stringz(s->exhash, s->client_greeting);
-    put_stringz(s->exhash, s->server_greeting);
-    put_string(s->exhash, s->client_kexinit->u, s->client_kexinit->len);
-    put_string(s->exhash, s->server_kexinit->u, s->server_kexinit->len);
-    s->crStateKex = 0;
-    while (1) {
-        bool aborted = false;
-        ssh2kex_coroutine(s, &aborted);
-        if (aborted)
-            return;    /* disaster: our entire state has been freed */
-        if (!s->crStateKex)
-            break;     /* kex phase has terminated normally */
-        crReturnV;
-    }
-
-    /*
-     * The exchange hash from the very first key exchange is also
-     * the session id, used in session key construction and
-     * authentication.
-     */
-    if (!s->got_session_id) {
-        assert(sizeof(s->exchange_hash) <= sizeof(s->session_id));
-        memcpy(s->session_id, s->exchange_hash, sizeof(s->exchange_hash));
-        s->session_id_len = s->kex_alg->hash->hlen;
-        assert(s->session_id_len <= sizeof(s->session_id));
-        s->got_session_id = true;
-    }
-
-    /*
-     * Send SSH2_MSG_NEWKEYS.
-     */
-    pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_NEWKEYS);
-    pq_push(s->ppl.out_pq, pktout);
-    /* Start counting down the outgoing-data limit for these cipher keys. */
-    dts_reset(&s->stats->out, s->max_data_size);
-
-    /*
-     * Force the BPP to synchronously marshal all packets up to and
-     * including that NEWKEYS into wire format, before we switch over
-     * to new crypto.
-     */
-    ssh_bpp_handle_output(s->ppl.bpp);
-
-    /*
-     * We've sent outgoing NEWKEYS, so create and initialise outgoing
-     * session keys.
-     */
-    {
-        strbuf *cipher_key = strbuf_new_nm();
-        strbuf *cipher_iv = strbuf_new_nm();
-        strbuf *mac_key = strbuf_new_nm();
-
-        if (s->out.cipher) {
-            ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash,
-                       'A' + s->out.mkkey_adjust, s->out.cipher->blksize);
-            ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash,
-                       'C' + s->out.mkkey_adjust,
-                       s->out.cipher->padded_keybytes);
-        }
-        if (s->out.mac) {
-            ssh2_mkkey(s, mac_key, s->K, s->exchange_hash,
-                       'E' + s->out.mkkey_adjust, s->out.mac->keylen);
-        }
-
-        ssh2_bpp_new_outgoing_crypto(
-            s->ppl.bpp,
-            s->out.cipher, cipher_key->u, cipher_iv->u,
-            s->out.mac, s->out.etm_mode, mac_key->u,
-            s->out.comp, s->out.comp_delayed);
-
-        strbuf_free(cipher_key);
-        strbuf_free(cipher_iv);
-        strbuf_free(mac_key);
-    }
-
-    /*
-     * If that was our first key exchange, this is the moment to send
-     * our EXT_INFO, if we're sending one.
-     */
-    if (!s->post_newkeys_ext_info) {
-        s->post_newkeys_ext_info = true; /* never do this again */
-        if (s->can_send_ext_info) {
-            strbuf *extinfo = strbuf_new();
-            uint32_t n_exts = 0;
-
-            if (s->ssc) {
-                /* Server->client EXT_INFO lists our supported user
-                 * key algorithms. */
-                n_exts++;
-                put_stringz(extinfo, "server-sig-algs");
-                { // WINSCP
-                strbuf *list = strbuf_new();
-                size_t i; // WINSCP
-                for (i = 0; i < n_keyalgs; i++)
-                    add_to_commasep(list, all_keyalgs[i]->ssh_id);
-                put_stringsb(extinfo, list);
-                }  // WINSCP
-            } else {
-                /* Client->server EXT_INFO is currently not sent, but here's
-                 * where we should put things in it if we ever want to. */
-            }
-
-            /* Only send EXT_INFO if it's non-empty */
-            if (n_exts) {
-                pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_EXT_INFO);
-                put_uint32(pktout, n_exts);
-                put_datapl(pktout, ptrlen_from_strbuf(extinfo));
-                pq_push(s->ppl.out_pq, pktout);
-            }
-
-            strbuf_free(extinfo);
-        }
-    }
-
-    /*
-     * Now our end of the key exchange is complete, we can send all
-     * our queued higher-layer packets. Transfer the whole of the next
-     * layer's outgoing queue on to our own.
-     */
-    pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher);
-
-    /*
-     * Expect SSH2_MSG_NEWKEYS from server.
-     */
-    crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-    if (pktin->type != SSH2_MSG_NEWKEYS) {
-        ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                        "expecting SSH_MSG_NEWKEYS, type %d (%s)",
-                        pktin->type,
-                        ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                      s->ppl.bpp->pls->actx,
-                                      pktin->type));
-        return;
-    }
-    /* Start counting down the incoming-data limit for these cipher keys. */
-    dts_reset(&s->stats->in, s->max_data_size);
-
-    /*
-     * We've seen incoming NEWKEYS, so create and initialise
-     * incoming session keys.
-     */
-    {
-        strbuf *cipher_key = strbuf_new_nm();
-        strbuf *cipher_iv = strbuf_new_nm();
-        strbuf *mac_key = strbuf_new_nm();
-
-        if (s->in.cipher) {
-            ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash,
-                       'A' + s->in.mkkey_adjust, s->in.cipher->blksize);
-            ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash,
-                       'C' + s->in.mkkey_adjust,
-                       s->in.cipher->padded_keybytes);
-        }
-        if (s->in.mac) {
-            ssh2_mkkey(s, mac_key, s->K, s->exchange_hash,
-                       'E' + s->in.mkkey_adjust, s->in.mac->keylen);
-        }
-
-        ssh2_bpp_new_incoming_crypto(
-            s->ppl.bpp,
-            s->in.cipher, cipher_key->u, cipher_iv->u,
-            s->in.mac, s->in.etm_mode, mac_key->u,
-            s->in.comp, s->in.comp_delayed);
-
-        strbuf_free(cipher_key);
-        strbuf_free(cipher_iv);
-        strbuf_free(mac_key);
-    }
-
-    /*
-     * Free shared secret.
-     */
-    mp_free(s->K); s->K = NULL;
-
-    /*
-     * Update the specials menu to list the remaining uncertified host
-     * keys.
-     */
-    seat_update_specials_menu(s->ppl.seat);
-
-    /*
-     * Key exchange is over. Loop straight back round if we have a
-     * deferred rekey reason.
-     */
-    if (s->deferred_rekey_reason) {
-        ppl_logevent("%s", s->deferred_rekey_reason);
-        pktin = NULL;
-        s->deferred_rekey_reason = NULL;
-        goto begin_key_exchange;
-    }
-
-    /*
-     * Otherwise, schedule a timer for our next rekey.
-     */
-    s->kex_in_progress = false;
-    s->last_rekey = GETTICKCOUNT();
-    (void) ssh2_transport_timer_update(s, 0);
-
-    /*
-     * Now we're encrypting. Get the next-layer protocol started if it
-     * hasn't already, and then sit here waiting for reasons to go
-     * back to the start and do a repeat key exchange. One of those
-     * reasons is that we receive KEXINIT from the other end; the
-     * other is if we find rekey_reason is non-NULL, i.e. we've
-     * decided to initiate a rekey ourselves for some reason.
-     */
-    if (!s->higher_layer_ok) {
-        if (!s->hostkeys) {
-            /* We're the client, so send SERVICE_REQUEST. */
-            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_REQUEST);
-            put_stringz(pktout, s->higher_layer->vt->name);
-            pq_push(s->ppl.out_pq, pktout);
-            crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-            if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) {
-                ssh_sw_abort(s->ppl.ssh, "Server refused request to start "
-                             "'%s' protocol", s->higher_layer->vt->name);
-                return;
-            }
-        } else {
-            ptrlen service_name;
-
-            /* We're the server, so expect SERVICE_REQUEST. */
-            crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
-            if (pktin->type != SSH2_MSG_SERVICE_REQUEST) {
-                ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
-                                "expecting SERVICE_REQUEST, type %d (%s)",
-                                pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                return;
-            }
-            service_name = get_string(pktin);
-            if (!ptrlen_eq_string(service_name, s->higher_layer->vt->name)) {
-                ssh_proto_error(s->ppl.ssh, "Client requested service "
-                                "'%.*s' when we only support '%s'",
-                                PTRLEN_PRINTF(service_name),
-                                s->higher_layer->vt->name);
-                return;
-            }
-
-            pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_ACCEPT);
-            put_stringz(pktout, s->higher_layer->vt->name);
-            pq_push(s->ppl.out_pq, pktout);
-        }
-
-        s->higher_layer_ok = true;
-        queue_idempotent_callback(&s->higher_layer->ic_process_queue);
-    }
-
-    s->rekey_class = RK_NONE;
-    do {
-        crReturnV;
-
-        /* Pass through outgoing packets from the higher layer. */
-        pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher);
-
-        /* Wait for either a KEXINIT, or something setting
-         * s->rekey_class. This call to ssh2_transport_pop also has
-         * the side effect of transferring incoming packets _to_ the
-         * higher layer (via filter_queue). */
-        if ((pktin = ssh2_transport_pop(s)) != NULL) {
-            if (pktin->type != SSH2_MSG_KEXINIT) {
-                ssh_proto_error(s->ppl.ssh, "Received unexpected transport-"
-                                "layer packet outside a key exchange, "
-                                "type %d (%s)", pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                return;
-            }
-            pq_push_front(s->ppl.in_pq, pktin);
-            ppl_logevent("Remote side initiated key re-exchange");
-            s->rekey_class = RK_SERVER;
-        }
-
-        if (s->rekey_class == RK_POST_USERAUTH) {
-            /*
-             * userauth has seen a USERAUTH_SUCCESS. This may be the
-             * moment to do an immediate rekey with different
-             * parameters. But it may not; so here we turn that rekey
-             * class into either RK_NONE or RK_NORMAL.
-             *
-             * Currently the only reason for this is if we've done a
-             * GSS key exchange and don't have anything in our
-             * transient hostkey cache, in which case we should make
-             * an attempt to populate the cache now.
-             */
-            if (s->need_gss_transient_hostkey) {
-                s->rekey_reason = "populating transient host key cache";
-                s->rekey_class = RK_NORMAL;
-            } else {
-                /* No need to rekey at this time. */
-                s->rekey_class = RK_NONE;
-            }
-        }
-
-        if (!s->rekey_class) {
-            /* If we don't yet have any other reason to rekey, check
-             * if we've hit our data limit in either direction. */
-            if (s->stats->in.expired) {
-                s->rekey_reason = "too much data received";
-                s->rekey_class = RK_NORMAL;
-            } else if (s->stats->out.expired) {
-                s->rekey_reason = "too much data sent";
-                s->rekey_class = RK_NORMAL;
-            }
-        }
-
-        if (s->rekey_class != RK_NONE && s->rekey_class != RK_SERVER) {
-            /*
-             * Special case: if the server bug is set that doesn't
-             * allow rekeying, we give a different log message and
-             * continue waiting. (If such a server _initiates_ a
-             * rekey, we process it anyway!)
-             */
-            if ((s->ppl.remote_bugs & BUG_SSH2_REKEY)) {
-                ppl_logevent("Remote bug prevents key re-exchange (%s)",
-                             s->rekey_reason);
-                /* Reset the counters, so that at least this message doesn't
-                 * hit the event log _too_ often. */
-                dts_reset(&s->stats->in, s->max_data_size);
-                dts_reset(&s->stats->out, s->max_data_size);
-                (void) ssh2_transport_timer_update(s, 0);
-                s->rekey_class = RK_NONE;
-            } else {
-                ppl_logevent("Initiating key re-exchange (%s)",
-                             s->rekey_reason);
-            }
-        }
-    } while (s->rekey_class == RK_NONE);
-
-    /* Once we exit the above loop, we really are rekeying. */
-    goto begin_key_exchange;
-
-    crFinishV;
-}
-
-static void ssh2_transport_higher_layer_packet_callback(void *context)
-{
-    PacketProtocolLayer *ppl = (PacketProtocolLayer *)context;
-    ssh_ppl_process_queue(ppl);
-}
-
-static void ssh2_transport_timer(void *ctx, unsigned long now)
-{
-    struct ssh2_transport_state *s = (struct ssh2_transport_state *)ctx;
-    unsigned long mins;
-    unsigned long ticks;
-
-    // WINSCP: our WINSCP_QUERY_TIMER implementation of schedule_timer
-    //  does not guarantee the `now` to be exactly as scheduled
-    if (s->kex_in_progress || now < s->next_rekey)
-        return;
-
-    mins = sanitise_rekey_time(conf_get_int(s->conf, CONF_ssh_rekey_time), 60);
-    if (mins == 0)
-        return;
-
-    /* Rekey if enough time has elapsed */
-    ticks = mins * 60 * TICKSPERSEC;
-    if (now - s->last_rekey > ticks - 30*TICKSPERSEC) {
-        s->rekey_reason = "timeout";
-        s->rekey_class = RK_NORMAL;
-        queue_idempotent_callback(&s->ppl.ic_process_queue);
-        return;
-    }
-
-#ifndef NO_GSSAPI
-    /*
-     * Rekey now if we have a new cred or context expires this cycle,
-     * but not if this is unsafe.
-     */
-    if (conf_get_int(s->conf, CONF_gssapirekey)) {
-        ssh2_transport_gss_update(s, false);
-        if ((s->gss_status & GSS_KEX_CAPABLE) != 0 &&
-            (s->gss_status & GSS_CTXT_MAYFAIL) == 0 &&
-            (s->gss_status & (GSS_CRED_UPDATED|GSS_CTXT_EXPIRES)) != 0) {
-            s->rekey_reason = "GSS credentials updated";
-            s->rekey_class = RK_GSS_UPDATE;
-            queue_idempotent_callback(&s->ppl.ic_process_queue);
-            return;
-        }
-    }
-#endif
-
-    /* Try again later. */
-    (void) ssh2_transport_timer_update(s, 0);
-}
-
-/*
- * The rekey_time is zero except when re-configuring.
- *
- * We either schedule the next timer and return false, or return true
- * to run the callback now, which will call us again to re-schedule on
- * completion.
- */
-static bool ssh2_transport_timer_update(struct ssh2_transport_state *s,
-                                        unsigned long rekey_time)
-{
-    unsigned long mins;
-    unsigned long ticks;
-
-    mins = sanitise_rekey_time(conf_get_int(s->conf, CONF_ssh_rekey_time), 60);
-    ticks = mins * 60 * TICKSPERSEC;
-
-    /* Handle change from previous setting */
-    if (rekey_time != 0 && rekey_time != mins) {
-        unsigned long next;
-        unsigned long now = GETTICKCOUNT();
-
-        mins = rekey_time;
-        ticks = mins * 60 * TICKSPERSEC;
-        next = s->last_rekey + ticks;
-
-        /* If overdue, caller will rekey synchronously now */
-        if (now - s->last_rekey > ticks)
-            return true;
-        ticks = next - now;
-    }
-
-#ifndef NO_GSSAPI
-    if (s->gss_kex_used) {
-        /*
-         * If we've used GSSAPI key exchange, then we should
-         * periodically check whether we need to do another one to
-         * pass new credentials to the server.
-         */
-        unsigned long gssmins;
-
-        /* Check cascade conditions more frequently if configured */
-        gssmins = sanitise_rekey_time(
-            conf_get_int(s->conf, CONF_gssapirekey), GSS_DEF_REKEY_MINS);
-        if (gssmins > 0) {
-            if (gssmins < mins)
-                ticks = (mins = gssmins) * 60 * TICKSPERSEC;
-
-            if ((s->gss_status & GSS_KEX_CAPABLE) != 0) {
-                /*
-                 * Run next timer even sooner if it would otherwise be
-                 * too close to the context expiration time
-                 */
-                if ((s->gss_status & GSS_CTXT_EXPIRES) == 0 &&
-                    s->gss_ctxt_lifetime - mins * 60 < 2 * MIN_CTXT_LIFETIME)
-                    ticks -= 2 * MIN_CTXT_LIFETIME * TICKSPERSEC;
-            }
-        }
-    }
-#endif
-
-    /* Schedule the next timer */
-    s->next_rekey = schedule_timer(ticks, ssh2_transport_timer, s);
-    return false;
-}
-
-void ssh2_transport_dialog_callback(void *loginv, int ret)
-{
-    struct ssh2_transport_state *s = (struct ssh2_transport_state *)loginv;
-    s->dlgret = ret;
-    ssh_ppl_process_queue(&s->ppl);
-}
-
-#ifndef NO_GSSAPI
-/*
- * This is called at the beginning of each SSH rekey to determine
- * whether we are GSS capable, and if we did GSS key exchange, and are
- * delegating credentials, it is also called periodically to determine
- * whether we should rekey in order to delegate (more) fresh
- * credentials. This is called "credential cascading".
- *
- * On Windows, with SSPI, we may not get the credential expiration, as
- * Windows automatically renews from cached passwords, so the
- * credential effectively never expires. Since we still want to
- * cascade when the local TGT is updated, we use the expiration of a
- * newly obtained context as a proxy for the expiration of the TGT.
- */
-static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
-                                      bool definitely_rekeying)
-{
-    PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    int gss_stat;
-    time_t gss_cred_expiry;
-    unsigned long mins;
-    Ssh_gss_buf gss_sndtok;
-    Ssh_gss_buf gss_rcvtok;
-    Ssh_gss_ctx gss_ctx;
-
-    s->gss_status = 0;
-
-    /*
-     * Nothing to do if no GSSAPI libraries are configured or GSSAPI
-     * auth is not enabled.
-     */
-    if (s->shgss->libs->nlibraries == 0)
-        return;
-    if (!conf_get_bool(s->conf, CONF_try_gssapi_auth) &&
-        !conf_get_bool(s->conf, CONF_try_gssapi_kex))
-        return;
-
-    /* Import server name and cache it */
-    if (s->shgss->srv_name == GSS_C_NO_NAME) {
-        gss_stat = s->shgss->lib->import_name(
-            s->shgss->lib, s->fullhostname, &s->shgss->srv_name);
-        if (gss_stat != SSH_GSS_OK) {
-            if (gss_stat == SSH_GSS_BAD_HOST_NAME)
-                ppl_logevent("GSSAPI import name failed - Bad service name;"
-                             " won't use GSS key exchange");
-            else
-                ppl_logevent("GSSAPI import name failed;"
-                             " won't use GSS key exchange");
-            return;
-        }
-    }
-
-    /*
-     * Do we (still) have credentials? Capture the credential
-     * expiration when available
-     */
-    gss_stat = s->shgss->lib->acquire_cred(
-        s->shgss->lib, &gss_ctx, &gss_cred_expiry);
-    if (gss_stat != SSH_GSS_OK)
-        return;
-
-    SSH_GSS_CLEAR_BUF(&gss_sndtok);
-    SSH_GSS_CLEAR_BUF(&gss_rcvtok);
-
-    /*
-     * When acquire_cred yields no useful expiration, get a proxy for
-     * the cred expiration from the context expiration.
-     */
-    gss_stat = s->shgss->lib->init_sec_context(
-        s->shgss->lib, &gss_ctx, s->shgss->srv_name,
-        0 /* don't delegate */, &gss_rcvtok, &gss_sndtok,
-        (gss_cred_expiry == GSS_NO_EXPIRATION ? &gss_cred_expiry : NULL),
-        &s->gss_ctxt_lifetime);
-
-    /* This context was for testing only. */
-    if (gss_ctx)
-        s->shgss->lib->release_cred(s->shgss->lib, &gss_ctx);
-
-    if (gss_stat != SSH_GSS_OK &&
-        gss_stat != SSH_GSS_S_CONTINUE_NEEDED) {
-        /*
-         * No point in verbosely interrupting the user to tell them we
-         * couldn't get GSS credentials, if this was only a check
-         * between key exchanges to see if fresh ones were available.
-         * When we do do a rekey, this message (if displayed) will
-         * appear among the standard rekey blurb, but when we're not,
-         * it shouldn't pop up all the time regardless.
-         */
-        if (definitely_rekeying)
-            ppl_logevent("No GSSAPI security context available");
-
-        return;
-    }
-
-    if (gss_sndtok.length)
-        s->shgss->lib->free_tok(s->shgss->lib, &gss_sndtok);
-
-    s->gss_status |= GSS_KEX_CAPABLE;
-
-    /*
-     * When rekeying to cascade, avoding doing this too close to the
-     * context expiration time, since the key exchange might fail.
-     */
-    if (s->gss_ctxt_lifetime < MIN_CTXT_LIFETIME)
-        s->gss_status |= GSS_CTXT_MAYFAIL;
-
-    /*
-     * If we're not delegating credentials, rekeying is not used to
-     * refresh them. We must avoid setting GSS_CRED_UPDATED or
-     * GSS_CTXT_EXPIRES when credential delegation is disabled.
-     */
-    if (!conf_get_bool(s->conf, CONF_gssapifwd))
-        return;
-
-    if (s->gss_cred_expiry != GSS_NO_EXPIRATION &&
-        difftime(gss_cred_expiry, s->gss_cred_expiry) > 0)
-        s->gss_status |= GSS_CRED_UPDATED;
-
-    mins = sanitise_rekey_time(
-        conf_get_int(s->conf, CONF_gssapirekey), GSS_DEF_REKEY_MINS);
-    if (mins > 0 && s->gss_ctxt_lifetime <= mins * 60)
-        s->gss_status |= GSS_CTXT_EXPIRES;
-}
-#endif /* NO_GSSAPI */
-
-ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s;
-
-    assert(ppl->vt == &ssh2_transport_vtable);
-    s = container_of(ppl, struct ssh2_transport_state, ppl);
-
-    assert(s->got_session_id);
-    return make_ptrlen(s->session_id, s->session_id_len);
-}
-
-void ssh2_transport_notify_auth_done(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s;
-
-    assert(ppl->vt == &ssh2_transport_vtable);
-    s = container_of(ppl, struct ssh2_transport_state, ppl);
-
-    s->rekey_reason = NULL;            /* will be filled in later */
-    s->rekey_class = RK_POST_USERAUTH;
-    queue_idempotent_callback(&s->ppl.ic_process_queue);
-}
-
-static bool ssh2_transport_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-    bool need_separator = false;
-    bool toret = false;
-
-    if (ssh_ppl_get_specials(s->higher_layer, add_special, ctx)) {
-        need_separator = true;
-        toret = true;
-    }
-
-    /*
-     * Don't bother offering rekey-based specials if we've decided the
-     * remote won't cope with it, since we wouldn't bother sending it
-     * if asked anyway.
-     */
-    if (!(s->ppl.remote_bugs & BUG_SSH2_REKEY)) {
-        if (need_separator) {
-            add_special(ctx, NULL, SS_SEP, 0);
-            need_separator = false;
-        }
-
-        add_special(ctx, "Repeat key exchange", SS_REKEY, 0);
-        toret = true;
-
-        if (s->n_uncert_hostkeys) {
-            int i;
-
-            add_special(ctx, NULL, SS_SEP, 0);
-            add_special(ctx, "Cache new host key type", SS_SUBMENU, 0);
-            for (i = 0; i < s->n_uncert_hostkeys; i++) {
-                const ssh_keyalg *alg =
-                    ssh2_hostkey_algs[s->uncert_hostkeys[i]].alg;
-
-                add_special(ctx, alg->ssh_id, SS_XCERT, s->uncert_hostkeys[i]);
-            }
-            add_special(ctx, NULL, SS_EXITMENU, 0);
-        }
-    }
-
-    return toret;
-}
-
-static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl,
-                                       SessionSpecialCode code, int arg)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-
-    if (code == SS_REKEY) {
-        if (!s->kex_in_progress) {
-            s->rekey_reason = "at user request";
-            s->rekey_class = RK_NORMAL;
-            queue_idempotent_callback(&s->ppl.ic_process_queue);
-        }
-    } else if (code == SS_XCERT) {
-        if (!s->kex_in_progress) {
-            s->cross_certifying = s->hostkey_alg = ssh2_hostkey_algs[arg].alg;
-            s->rekey_reason = "cross-certifying new host key";
-            s->rekey_class = RK_NORMAL;
-            queue_idempotent_callback(&s->ppl.ic_process_queue);
-        }
-    } else {
-        /* Send everything else to the next layer up. This includes
-         * SS_PING/SS_NOP, which we _could_ handle here - but it's
-         * better to put them in the connection layer, so they'll
-         * still work in bare connection mode. */
-        ssh_ppl_special_cmd(s->higher_layer, code, arg);
-    }
-}
-
-/* Safely convert rekey_time to unsigned long minutes */
-static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def)
-{
-    if (rekey_time < 0 || rekey_time > MAX_TICK_MINS)
-        rekey_time = def;
-    return (unsigned long)rekey_time;
-}
-
-static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s)
-{
-    s->max_data_size = parse_blocksize(
-        conf_get_str(s->conf, CONF_ssh_rekey_data));
-}
-
-static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
-{
-    struct ssh2_transport_state *s;
-    const char *rekey_reason = NULL;
-    bool rekey_mandatory = false;
-    unsigned long old_max_data_size, rekey_time;
-    int i;
-
-    assert(ppl->vt == &ssh2_transport_vtable);
-    s = container_of(ppl, struct ssh2_transport_state, ppl);
-
-    rekey_time = sanitise_rekey_time(
-        conf_get_int(conf, CONF_ssh_rekey_time), 60);
-    if (ssh2_transport_timer_update(s, rekey_time))
-        rekey_reason = "timeout shortened";
-
-    old_max_data_size = s->max_data_size;
-    ssh2_transport_set_max_data_size(s);
-    if (old_max_data_size != s->max_data_size &&
-        s->max_data_size != 0) {
-        if (s->max_data_size < old_max_data_size) {
-            unsigned long diff = old_max_data_size - s->max_data_size;
-
-            dts_consume(&s->stats->out, diff);
-            dts_consume(&s->stats->in, diff);
-            if (s->stats->out.expired || s->stats->in.expired)
-                rekey_reason = "data limit lowered";
-        } else {
-            unsigned long diff = s->max_data_size - old_max_data_size;
-            if (s->stats->out.running)
-                s->stats->out.remaining += diff;
-            if (s->stats->in.running)
-                s->stats->in.remaining += diff;
-        }
-    }
-
-    if (conf_get_bool(s->conf, CONF_compression) !=
-        conf_get_bool(conf, CONF_compression)) {
-        rekey_reason = "compression setting changed";
-        rekey_mandatory = true;
-    }
-
-    for (i = 0; i < CIPHER_MAX; i++)
-        if (conf_get_int_int(s->conf, CONF_ssh_cipherlist, i) !=
-            conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
-        rekey_reason = "cipher settings changed";
-        rekey_mandatory = true;
-    }
-    if (conf_get_bool(s->conf, CONF_ssh2_des_cbc) !=
-        conf_get_bool(conf, CONF_ssh2_des_cbc)) {
-        rekey_reason = "cipher settings changed";
-        rekey_mandatory = true;
-    }
-
-    conf_free(s->conf);
-    s->conf = conf_copy(conf);
-
-    if (rekey_reason) {
-        if (!s->kex_in_progress && !ssh2_bpp_rekey_inadvisable(s->ppl.bpp)) {
-            s->rekey_reason = rekey_reason;
-            s->rekey_class = RK_NORMAL;
-            queue_idempotent_callback(&s->ppl.ic_process_queue);
-        } else if (rekey_mandatory) {
-            s->deferred_rekey_reason = rekey_reason;
-        }
-    }
-
-    /* Also pass the configuration along to our higher layer */
-    ssh_ppl_reconfigure(s->higher_layer, conf);
-}
-
-static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-
-    /* Just delegate this to the higher layer */
-    return ssh_ppl_want_user_input(s->higher_layer);
-}
-
-static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-
-    /* Just delegate this to the higher layer */
-    ssh_ppl_got_user_input(s->higher_layer);
-}
-
-static int weak_algorithm_compare(void *av, void *bv)
-{
-    uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv;
-    return a < b ? -1 : a > b ? +1 : 0;
-}
-
-/*
- * Wrapper on seat_confirm_weak_crypto_primitive(), which uses the
- * tree234 s->weak_algorithms_consented_to to ensure we ask at most
- * once about any given crypto primitive.
- */
-static int ssh2_transport_confirm_weak_crypto_primitive(
-    struct ssh2_transport_state *s, const char *type, const char *name,
-    const void *alg)
-{
-    if (find234(s->weak_algorithms_consented_to, (void *)alg, NULL))
-        return 1;
-    add234(s->weak_algorithms_consented_to, (void *)alg);
-
-    return seat_confirm_weak_crypto_primitive(
-        s->ppl.seat, type, name, ssh2_transport_dialog_callback, s);
-}
-
-static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-
-    return (ssh_ppl_default_queued_data_size(ppl) +
-            ssh_ppl_queued_data_size(s->higher_layer));
-}
-
-#include "puttyexp.h"
-
-static unsigned int ssh2_transport_winscp_query(PacketProtocolLayer *ppl, int query)
-{
-    struct ssh2_transport_state *s =
-        container_of(ppl, struct ssh2_transport_state, ppl);
-    if (query == WINSCP_QUERY_TIMER)
-    {
-        ssh2_transport_timer(s, GETTICKCOUNT());
-        return 1;
-    }
-    else if (s->higher_layer->vt->winscp_query != NULL)
-    {
-        return ssh_ppl_winscp_query(s->higher_layer, query);
-    }
-    else
-    {
-        return 0;
-    }
-}
-
-void call_ssh_timer(Backend * be)
-{
-    // TODO
-}
-
-// WINSCP
-void get_hostkey_algs(int * count, cp_ssh_keyalg * SignKeys)
-{
-    int i;
-    assert(lenof(ssh2_hostkey_algs) <= *count);
-    *count = lenof(ssh2_hostkey_algs);
-    for (i = 0; i < *count; i++)
-    {
-        *(SignKeys + i) = ssh2_hostkey_algs[i].alg;
-    }
-}
-
-// WINSCP
-void get_macs(int * count, const struct ssh2_macalg *** amacs)
-{
-    *amacs = macs;
-    *count = lenof(macs);
-}
-
-int have_any_ssh2_hostkey(Seat * seat, const char * host, int port)
-{
-    int j;
-    for (j = 0; j < lenof(ssh2_hostkey_algs); j++)
-    {
-        if (have_ssh_host_key(seat, host, port, ssh2_hostkey_algs[j].alg->cache_id))
-        {
-            return 1;
-        }
-    }
-    return 0;
-}

+ 0 - 246
source/putty/ssh2transport.h

@@ -1,246 +0,0 @@
-/*
- * Header connecting the pieces of the SSH-2 transport layer.
- */
-
-#ifndef PUTTY_SSH2TRANSPORT_H
-#define PUTTY_SSH2TRANSPORT_H
-
-#ifndef NO_GSSAPI
-#include "sshgssc.h"
-#include "sshgss.h"
-#define MIN_CTXT_LIFETIME 5     /* Avoid rekey with short lifetime (seconds) */
-#define GSS_KEX_CAPABLE (1<<0)  /* Can do GSS KEX */
-#define GSS_CRED_UPDATED (1<<1) /* Cred updated since previous delegation */
-#define GSS_CTXT_EXPIRES (1<<2) /* Context expires before next timer */
-#define GSS_CTXT_MAYFAIL (1<<3) /* Context may expire during handshake */
-#endif
-
-#define DH_MIN_SIZE 1024
-#define DH_MAX_SIZE 8192
-
-#define MAXKEXLIST 16
-struct kexinit_algorithm {
-    const char *name;
-    union {
-        struct {
-            const ssh_kex *kex;
-            bool warn;
-        } kex;
-        struct {
-            const ssh_keyalg *hostkey;
-            unsigned hkflags;
-            bool warn;
-        } hk;
-        struct {
-            const ssh_cipheralg *cipher;
-            bool warn;
-        } cipher;
-        struct {
-            const ssh2_macalg *mac;
-            bool etm;
-        } mac;
-        struct {
-            const ssh_compression_alg *comp;
-            bool delayed;
-        } comp;
-    } u;
-};
-
-#define HOSTKEY_ALGORITHMS(X)                   \
-    X(HK_ED25519, ssh_ecdsa_ed25519)            \
-    X(HK_ED448, ssh_ecdsa_ed448)                \
-    X(HK_ECDSA, ssh_ecdsa_nistp256)             \
-    X(HK_ECDSA, ssh_ecdsa_nistp384)             \
-    X(HK_ECDSA, ssh_ecdsa_nistp521)             \
-    /* Changed order to match WinSCP default preference list for SshHostKeyList() */ \
-    X(HK_RSA, ssh_rsa_sha512)                   \
-    X(HK_RSA, ssh_rsa_sha256)                   \
-    X(HK_RSA, ssh_rsa)                          \
-    X(HK_DSA, ssh_dss)                          \
-    /* end of list */
-#define COUNT_HOSTKEY_ALGORITHM(type, alg) +1
-#define N_HOSTKEY_ALGORITHMS (0 HOSTKEY_ALGORITHMS(COUNT_HOSTKEY_ALGORITHM))
-
-struct ssh_signkey_with_user_pref_id {
-    const ssh_keyalg *alg;
-    int id;
-};
-extern const struct ssh_signkey_with_user_pref_id
-    ssh2_hostkey_algs[N_HOSTKEY_ALGORITHMS];
-
-/*
- * Enumeration of high-level classes of reason why we might need to do
- * a repeat key exchange. A full detailed reason in human-readable
- * string form for the Event Log is also provided, but this enum type
- * is used to discriminate between classes of reason that the code
- * needs to treat differently.
- *
- * RK_NONE == 0 is the value indicating that no rekey is currently
- * needed at all. RK_INITIAL indicates that we haven't even done the
- * _first_ key exchange yet. RK_SERVER indicates that we're rekeying
- * because the server asked for it, not because we decided it
- * ourselves. RK_NORMAL is the usual case. RK_GSS_UPDATE indicates
- * that we're rekeying because we've just got new GSSAPI credentials
- * (hence there's no point in doing a preliminary check for new GSS
- * creds, because we already know the answer); RK_POST_USERAUTH
- * indicates that _if_ we're going to need a post-userauth immediate
- * rekey for any reason, this is the moment to do it.
- *
- * So RK_POST_USERAUTH only tells the transport layer to _consider_
- * rekeying, not to definitely do it. Also, that one enum value is
- * special in that the user-readable reason text is passed in to the
- * transport layer as NULL, whereas fills in the reason text after it
- * decides whether it needs a rekey at all. In the other cases,
- * rekey_reason is passed in to the at the same time as rekey_class.
- */
-typedef enum RekeyClass {
-    RK_NONE = 0,
-    RK_INITIAL,
-    RK_SERVER,
-    RK_NORMAL,
-    RK_POST_USERAUTH,
-    RK_GSS_UPDATE
-} RekeyClass;
-
-typedef struct transport_direction {
-    const ssh_cipheralg *cipher;
-    const ssh2_macalg *mac;
-    bool etm_mode;
-    const ssh_compression_alg *comp;
-    bool comp_delayed;
-    int mkkey_adjust;
-} transport_direction;
-
-struct ssh2_transport_state {
-    int crState, crStateKex;
-
-    PacketProtocolLayer *higher_layer;
-    PktInQueue pq_in_higher;
-    PktOutQueue pq_out_higher;
-    IdempotentCallback ic_pq_out_higher;
-
-    Conf *conf;
-    char *savedhost;
-    int savedport;
-    const char *rekey_reason;
-    enum RekeyClass rekey_class;
-
-    unsigned long max_data_size;
-
-    const ssh_kex *kex_alg;
-    const ssh_keyalg *hostkey_alg;
-    char *hostkey_str; /* string representation, for easy checking in rekeys */
-    unsigned char session_id[MAX_HASH_LEN];
-    int session_id_len;
-    int dh_min_size, dh_max_size;
-    bool dh_got_size_bounds;
-    dh_ctx *dh_ctx;
-    ssh_hash *exhash;
-
-    struct DataTransferStats *stats;
-
-    const SshServerConfig *ssc;
-
-    char *client_greeting, *server_greeting;
-
-    bool kex_in_progress;
-    unsigned long next_rekey, last_rekey;
-    const char *deferred_rekey_reason;
-    bool higher_layer_ok;
-
-    /*
-     * Fully qualified host name, which we need if doing GSSAPI.
-     */
-    char *fullhostname;
-
-    /* shgss is outside the ifdef on purpose to keep APIs simple. If
-     * NO_GSSAPI is not defined, then it's just an opaque structure
-     * tag and the pointer will be NULL. */
-    struct ssh_connection_shared_gss_state *shgss;
-#ifndef NO_GSSAPI
-    int gss_status;
-    time_t gss_cred_expiry;             /* Re-delegate if newer */
-    unsigned long gss_ctxt_lifetime;    /* Re-delegate when short */
-#endif
-    ssh_transient_hostkey_cache *thc;
-
-    bool gss_kex_used;
-
-    int nbits, pbits;
-    bool warn_kex, warn_hk, warn_cscipher, warn_sccipher;
-    mp_int *p, *g, *e, *f, *K;
-    strbuf *outgoing_kexinit, *incoming_kexinit;
-    strbuf *client_kexinit, *server_kexinit; /* aliases to the above */
-    int kex_init_value, kex_reply_value;
-    transport_direction in, out, *cstrans, *sctrans;
-    ptrlen hostkeydata, sigdata;
-    strbuf *hostkeyblob;
-    char *keystr;
-    ssh_key *hkey;                     /* actual host key */
-    unsigned hkflags;                  /* signing flags, used in server */
-    RSAKey *rsa_kex_key;             /* for RSA kex */
-    bool rsa_kex_key_needs_freeing;
-    ecdh_key *ecdh_key;                     /* for ECDH kex */
-    unsigned char exchange_hash[MAX_HASH_LEN];
-    bool can_gssapi_keyex;
-    bool need_gss_transient_hostkey;
-    bool warned_about_no_gss_transient_hostkey;
-    bool got_session_id;
-    bool can_send_ext_info, post_newkeys_ext_info;
-    int dlgret;
-    bool guessok;
-    bool ignorepkt;
-    struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST];
-#ifndef NO_GSSAPI
-    Ssh_gss_buf gss_buf;
-    Ssh_gss_buf gss_rcvtok, gss_sndtok;
-    Ssh_gss_stat gss_stat;
-    Ssh_gss_buf mic;
-    bool init_token_sent;
-    bool complete_rcvd;
-    bool gss_delegate;
-#endif
-
-    /* List of crypto primitives below the warning threshold that the
-     * user has already clicked OK to, so that we don't keep asking
-     * about them again during rekeys. This directly stores pointers
-     * to the algorithm vtables, compared by pointer value (which is
-     * not a determinism hazard, because we're only using it as a
-     * set). */
-    tree234 *weak_algorithms_consented_to;
-
-    /*
-     * List of host key algorithms for which we _don't_ have a stored
-     * host key. These are indices into the main hostkey_algs[] array
-     */
-    int uncert_hostkeys[N_HOSTKEY_ALGORITHMS];
-    int n_uncert_hostkeys;
-
-    /*
-     * Indicate that the current rekey is intended to finish with a
-     * newly cross-certified host key. To double-check that we
-     * certified the right one, we set this to point to the host key
-     * algorithm we expect it to be.
-     */
-    const ssh_keyalg *cross_certifying;
-
-    ssh_key *const *hostkeys;
-    int nhostkeys;
-
-    PacketProtocolLayer ppl;
-};
-
-/* Helpers shared between transport and kex */
-PktIn *ssh2_transport_pop(struct ssh2_transport_state *s);
-void ssh2_transport_dialog_callback(void *, int);
-
-/* Provided by transport for use in kex */
-void ssh2transport_finalise_exhash(struct ssh2_transport_state *s);
-
-/* Provided by kex for use in transport. Must set the 'aborted' flag
- * if it throws a connection-terminating error, so that the caller
- * won't have to check that by looking inside its state parameter
- * which might already have been freed. */
-void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted);
-
-#endif /* PUTTY_SSH2TRANSPORT_H */

+ 0 - 2035
source/putty/ssh2userauth.c

@@ -1,2035 +0,0 @@
-/*
- * Packet protocol layer for the client side of the SSH-2 userauth
- * protocol (RFC 4252).
- */
-
-#include <assert.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshcr.h"
-
-#ifndef NO_GSSAPI
-#include "sshgssc.h"
-#include "sshgss.h"
-#endif
-
-#define BANNER_LIMIT 131072
-
-typedef struct agent_key {
-    strbuf *blob, *comment;
-    ptrlen algorithm;
-} agent_key;
-
-struct ssh2_userauth_state {
-    int crState;
-
-    PacketProtocolLayer *transport_layer, *successor_layer;
-    Filename *keyfile;
-    bool show_banner, tryagent, notrivialauth, change_username;
-    char *hostname, *fullhostname;
-    char *default_username;
-    bool try_ki_auth, try_gssapi_auth, try_gssapi_kex_auth, gssapi_fwd;
-    char *loghost; // WINSCP
-    bool change_password; // WINSCP
-
-    ptrlen session_id;
-    enum {
-        AUTH_TYPE_NONE,
-        AUTH_TYPE_PUBLICKEY,
-        AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
-        AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
-        AUTH_TYPE_PASSWORD,
-        AUTH_TYPE_GSSAPI,      /* always QUIET */
-        AUTH_TYPE_KEYBOARD_INTERACTIVE,
-        AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
-    } type;
-    bool need_pw, can_pubkey, can_passwd, can_keyb_inter;
-    int userpass_ret;
-    bool tried_pubkey_config, done_agent;
-    struct ssh_connection_shared_gss_state *shgss;
-#ifndef NO_GSSAPI
-    bool can_gssapi;
-    bool can_gssapi_keyex_auth;
-    bool tried_gssapi;
-    bool tried_gssapi_keyex_auth;
-    time_t gss_cred_expiry;
-    Ssh_gss_buf gss_buf;
-    Ssh_gss_buf gss_rcvtok, gss_sndtok;
-    Ssh_gss_stat gss_stat;
-#endif
-    bool suppress_wait_for_response_packet;
-    strbuf *last_methods_string;
-    bool kbd_inter_refused;
-    prompts_t *cur_prompt;
-    uint32_t num_prompts;
-    const char *username;
-    char *locally_allocated_username;
-    char *password;
-    bool got_username;
-    strbuf *publickey_blob;
-    bool privatekey_available, privatekey_encrypted;
-    char *publickey_algorithm;
-    char *publickey_comment;
-    void *agent_response_to_free;
-    ptrlen agent_response;
-    BinarySource asrc[1];          /* for reading SSH agent response */
-    size_t agent_keys_len;
-    agent_key *agent_keys;
-    size_t agent_key_index, agent_key_limit;
-    ptrlen agent_keyalg;
-    unsigned signflags;
-    int len;
-    PktOut *pktout;
-    bool want_user_input;
-    bool is_trivial_auth;
-
-    agent_pending_query *auth_agent_query;
-    bufchain banner;
-    bufchain_sink banner_bs;
-    StripCtrlChars *banner_scc;
-    bool banner_scc_initialised;
-
-    StripCtrlChars *ki_scc;
-    bool ki_scc_initialised;
-    bool ki_printed_header;
-
-    Seat *seat; // WINSCP
-
-    PacketProtocolLayer ppl;
-};
-
-static void ssh2_userauth_free(PacketProtocolLayer *);
-static void ssh2_userauth_process_queue(PacketProtocolLayer *);
-static bool ssh2_userauth_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
-static void ssh2_userauth_special_cmd(PacketProtocolLayer *ppl,
-                                      SessionSpecialCode code, int arg);
-static bool ssh2_userauth_want_user_input(PacketProtocolLayer *ppl);
-static void ssh2_userauth_got_user_input(PacketProtocolLayer *ppl);
-static void ssh2_userauth_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
-
-static void ssh2_userauth_agent_query(struct ssh2_userauth_state *, strbuf *);
-static void ssh2_userauth_agent_callback(void *, void *, int);
-static void ssh2_userauth_add_sigblob(
-    struct ssh2_userauth_state *s, PktOut *pkt, ptrlen pkblob, ptrlen sigblob);
-static void ssh2_userauth_add_session_id(
-    struct ssh2_userauth_state *s, strbuf *sigdata);
-#ifndef NO_GSSAPI
-static PktOut *ssh2_userauth_gss_packet(
-    struct ssh2_userauth_state *s, const char *authtype);
-#endif
-static void ssh2_userauth_antispoof_msg(
-    struct ssh2_userauth_state *s, const char *msg);
-
-static const PacketProtocolLayerVtable ssh2_userauth_vtable = {
-    // WINSCP
-    /*.free =*/ ssh2_userauth_free,
-    /*.process_queue =*/ ssh2_userauth_process_queue,
-    /*.get_specials =*/ ssh2_userauth_get_specials,
-    /*.special_cmd =*/ ssh2_userauth_special_cmd,
-    /*.want_user_input =*/ ssh2_userauth_want_user_input,
-    /*.got_user_input =*/ ssh2_userauth_got_user_input,
-    /*.reconfigure =*/ ssh2_userauth_reconfigure,
-    /*.queued_data_size =*/ ssh_ppl_default_queued_data_size,
-    /*.name =*/ "ssh-userauth",
-    NULL, // WINSCP
-};
-
-PacketProtocolLayer *ssh2_userauth_new(
-    PacketProtocolLayer *successor_layer,
-    const char *hostname, const char *fullhostname,
-    Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth,
-    const char *default_username, bool change_username,
-    bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth,
-    bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss,
-    const char * loghost, bool change_password, Seat *seat) // WINSCP
-{
-    struct ssh2_userauth_state *s = snew(struct ssh2_userauth_state);
-    memset(s, 0, sizeof(*s));
-    s->seat = seat;
-    s->ppl.vt = &ssh2_userauth_vtable;
-
-    s->successor_layer = successor_layer;
-    s->hostname = dupstr(hostname);
-    s->fullhostname = dupstr(fullhostname);
-    s->keyfile = filename_copy(keyfile);
-    s->show_banner = show_banner;
-    s->tryagent = tryagent;
-    s->notrivialauth = notrivialauth;
-    s->default_username = dupstr(default_username);
-    s->change_username = change_username;
-    s->try_ki_auth = try_ki_auth;
-    s->try_gssapi_auth = try_gssapi_auth;
-    s->try_gssapi_kex_auth = try_gssapi_kex_auth;
-    s->gssapi_fwd = gssapi_fwd;
-    s->shgss = shgss;
-    s->last_methods_string = strbuf_new();
-    s->is_trivial_auth = true;
-    s->loghost = dupstr(loghost); // WINSCP
-    s->change_password = change_password;
-    bufchain_init(&s->banner);
-    bufchain_sink_init(&s->banner_bs, &s->banner);
-
-    return &s->ppl;
-}
-
-void ssh2_userauth_set_transport_layer(PacketProtocolLayer *userauth,
-                                       PacketProtocolLayer *transport)
-{
-    struct ssh2_userauth_state *s =
-        container_of(userauth, struct ssh2_userauth_state, ppl);
-    s->transport_layer = transport;
-}
-
-static void ssh2_userauth_free(PacketProtocolLayer *ppl)
-{
-    struct ssh2_userauth_state *s =
-        container_of(ppl, struct ssh2_userauth_state, ppl);
-    bufchain_clear(&s->banner);
-
-    if (s->successor_layer)
-        ssh_ppl_free(s->successor_layer);
-
-    if (s->agent_keys) {
-        size_t i; // WINSCP
-        for (i = 0; i < s->agent_keys_len; i++) {
-            strbuf_free(s->agent_keys[i].blob);
-            strbuf_free(s->agent_keys[i].comment);
-        }
-        sfree(s->agent_keys);
-    }
-    sfree(s->agent_response_to_free);
-    if (s->auth_agent_query)
-        agent_cancel_query(s->auth_agent_query);
-    filename_free(s->keyfile);
-    sfree(s->default_username);
-    sfree(s->locally_allocated_username);
-    sfree(s->hostname);
-    sfree(s->fullhostname);
-    sfree(s->publickey_comment);
-    sfree(s->publickey_algorithm);
-    if (s->publickey_blob)
-        strbuf_free(s->publickey_blob);
-    strbuf_free(s->last_methods_string);
-    sfree(s->loghost);
-    if (s->banner_scc)
-        stripctrl_free(s->banner_scc);
-    if (s->ki_scc)
-        stripctrl_free(s->ki_scc);
-    sfree(s);
-}
-
-static void ssh2_userauth_filter_queue(struct ssh2_userauth_state *s)
-{
-    PktIn *pktin;
-    ptrlen string;
-
-    while ((pktin = pq_peek(s->ppl.in_pq)) != NULL) {
-        switch (pktin->type) {
-          case SSH2_MSG_USERAUTH_BANNER:
-            if (!s->show_banner) {
-                pq_pop(s->ppl.in_pq);
-                break;
-            }
-
-            string = get_string(pktin);
-            if (string.len > BANNER_LIMIT - bufchain_size(&s->banner))
-                string.len = BANNER_LIMIT - bufchain_size(&s->banner);
-            if (!s->banner_scc_initialised) {
-                s->banner_scc = seat_stripctrl_new(
-                    s->ppl.seat, BinarySink_UPCAST(&s->banner_bs), SIC_BANNER);
-                if (s->banner_scc)
-                    stripctrl_enable_line_limiting(s->banner_scc);
-                s->banner_scc_initialised = true;
-            }
-            if (s->banner_scc)
-                put_datapl(s->banner_scc, string);
-            else
-                put_datapl(&s->banner_bs, string);
-            pq_pop(s->ppl.in_pq);
-            break;
-
-          default:
-            return;
-        }
-    }
-}
-
-static PktIn *ssh2_userauth_pop(struct ssh2_userauth_state *s)
-{
-    ssh2_userauth_filter_queue(s);
-    return pq_pop(s->ppl.in_pq);
-}
-
-static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
-{
-    struct ssh2_userauth_state *s =
-        container_of(ppl, struct ssh2_userauth_state, ppl);
-    PktIn *pktin;
-
-    ssh2_userauth_filter_queue(s);     /* no matter why we were called */
-
-    crBegin(s->crState);
-
-#ifndef NO_GSSAPI
-    s->tried_gssapi = false;
-    s->tried_gssapi_keyex_auth = false;
-#endif
-
-    /*
-     * Misc one-time setup for authentication.
-     */
-    s->publickey_blob = NULL;
-    s->session_id = ssh2_transport_get_session_id(s->transport_layer);
-
-    /*
-     * Load the public half of any configured public key file for
-     * later use.
-     */
-    if (!filename_is_null(s->keyfile)) {
-        int keytype;
-        ppl_logevent(WINSCP_BOM "Reading key file \"%s\"",
-                     filename_to_str(s->keyfile));
-        keytype = key_type(s->keyfile);
-        if (keytype == SSH_KEYTYPE_SSH2 ||
-            keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
-            keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
-            const char *error;
-            s->publickey_blob = strbuf_new();
-            if (ppk_loadpub_f(s->keyfile, &s->publickey_algorithm,
-                              BinarySink_UPCAST(s->publickey_blob),
-                              &s->publickey_comment, &error)) {
-                s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2);
-                if (!s->privatekey_available)
-                    ppl_logevent("Key file contains public key only");
-                s->privatekey_encrypted = ppk_encrypted_f(s->keyfile, NULL);
-            } else {
-                ppl_logevent("Unable to load key (%s)", error);
-                ppl_printf(WINSCP_BOM "Unable to load key file \"%s\" (%s)\r\n",
-                           filename_to_str(s->keyfile), error);
-                strbuf_free(s->publickey_blob);
-                s->publickey_blob = NULL;
-            }
-        } else {
-            ppl_logevent("Unable to use this key file (%s)",
-                         key_type_to_str(keytype));
-            ppl_printf(WINSCP_BOM "Unable to use key file \"%s\" (%s)\r\n",
-                       filename_to_str(s->keyfile),
-                       key_type_to_str(keytype));
-            s->publickey_blob = NULL;
-        }
-    }
-
-    /*
-     * Find out about any keys Pageant has (but if there's a public
-     * key configured, filter out all others).
-     */
-    if (s->tryagent && agent_exists()) {
-        ppl_logevent("Pageant is running. Requesting keys.");
-
-        /* Request the keys held by the agent. */
-        {
-            strbuf *request = strbuf_new_for_agent_query();
-            put_byte(request, SSH2_AGENTC_REQUEST_IDENTITIES);
-            ssh2_userauth_agent_query(s, request);
-            strbuf_free(request);
-            crWaitUntilV(!s->auth_agent_query);
-        }
-        BinarySource_BARE_INIT_PL(s->asrc, s->agent_response);
-
-        get_uint32(s->asrc); /* skip length field */
-        if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) {
-            size_t nkeys = get_uint32(s->asrc);
-            size_t origpos = s->asrc->pos;
-
-            /*
-             * Check that the agent response is well formed.
-             */
-            { // WINSCP
-            size_t i; // WINSCP
-            for (i = 0; i < nkeys; i++) {
-                get_string(s->asrc);   /* blob */
-                get_string(s->asrc);   /* comment */
-                if (get_err(s->asrc)) {
-                    ppl_logevent("Pageant's response was truncated");
-                    goto done_agent_query;
-                }
-            }
-            } // WINSCP
-
-            /*
-             * Copy the list of public-key blobs out of the Pageant
-             * response.
-             */
-            BinarySource_REWIND_TO(s->asrc, origpos);
-            s->agent_keys_len = nkeys;
-            s->agent_keys = snewn(s->agent_keys_len, agent_key);
-            { // WINSCP
-            size_t i; // WINSCP
-            for (i = 0; i < nkeys; i++) {
-                s->agent_keys[i].blob = strbuf_new();
-                put_datapl(s->agent_keys[i].blob, get_string(s->asrc));
-                s->agent_keys[i].comment = strbuf_new();
-                put_datapl(s->agent_keys[i].comment, get_string(s->asrc));
-
-                { // WINSCP
-                /* Also, extract the algorithm string from the start
-                 * of the public-key blob. */
-                BinarySource src[1];
-                BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
-                    s->agent_keys[i].blob));
-                s->agent_keys[i].algorithm = get_string(src);
-                } // WINSCP
-            }
-            } // WINSCP
-
-            ppl_logevent("Pageant has %"SIZEu" SSH-2 keys", nkeys);
-
-            if (s->publickey_blob) {
-                /*
-                 * If we've been given a specific public key blob,
-                 * filter the list of keys to try from the agent down
-                 * to only that one, or none if it's not there.
-                 */
-                ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob);
-                size_t i;
-
-                for (i = 0; i < nkeys; i++) {
-                    if (ptrlen_eq_ptrlen(our_blob, ptrlen_from_strbuf(
-                                             s->agent_keys[i].blob)))
-                        break;
-                }
-
-                if (i < nkeys) {
-                    ppl_logevent("Pageant key #%"SIZEu" matches "
-                                 "configured key file", i);
-                    s->agent_key_index = i;
-                    s->agent_key_limit = i+1;
-                } else {
-                    ppl_logevent("Configured key file not in Pageant");
-                    s->agent_key_index = 0;
-                    s->agent_key_limit = 0;
-                }
-            } else {
-                /*
-                 * Otherwise, try them all.
-                 */
-                s->agent_key_index = 0;
-                s->agent_key_limit = nkeys;
-            }
-        } else {
-            ppl_logevent("Failed to get reply from Pageant");
-        }
-      done_agent_query:;
-    }
-
-    /*
-     * We repeat this whole loop, including the username prompt,
-     * until we manage a successful authentication. If the user
-     * types the wrong _password_, they can be sent back to the
-     * beginning to try another username, if this is configured on.
-     * (If they specify a username in the config, they are never
-     * asked, even if they do give a wrong password.)
-     *
-     * I think this best serves the needs of
-     *
-     *  - the people who have no configuration, no keys, and just
-     *    want to try repeated (username,password) pairs until they
-     *    type both correctly
-     *
-     *  - people who have keys and configuration but occasionally
-     *    need to fall back to passwords
-     *
-     *  - people with a key held in Pageant, who might not have
-     *    logged in to a particular machine before; so they want to
-     *    type a username, and then _either_ their key will be
-     *    accepted, _or_ they will type a password. If they mistype
-     *    the username they will want to be able to get back and
-     *    retype it!
-     */
-    s->got_username = false;
-    while (1) {
-        /*
-         * Get a username.
-         */
-        if (s->got_username && !s->change_username) {
-            /*
-             * We got a username last time round this loop, and
-             * with change_username turned off we don't try to get
-             * it again.
-             */
-        } else if ((s->username = s->default_username) == NULL) {
-            s->cur_prompt = new_prompts();
-            s->cur_prompt->to_server = true;
-            s->cur_prompt->from_server = false;
-            s->cur_prompt->name = dupstr("SSH login name");
-            add_prompt(s->cur_prompt, dupstr("login as: "), true);
-            s->userpass_ret = seat_get_userpass_input(
-                s->ppl.seat, s->cur_prompt, NULL);
-            while (1) {
-                while (s->userpass_ret < 0 &&
-                       bufchain_size(s->ppl.user_input) > 0)
-                    s->userpass_ret = seat_get_userpass_input(
-                        s->ppl.seat, s->cur_prompt, s->ppl.user_input);
-
-                if (s->userpass_ret >= 0)
-                    break;
-
-                s->want_user_input = true;
-                crReturnV;
-                s->want_user_input = false;
-            }
-            if (!s->userpass_ret) {
-                /*
-                 * seat_get_userpass_input() failed to get a username.
-                 * Terminate.
-                 */
-                free_prompts(s->cur_prompt);
-                ssh_user_close(s->ppl.ssh, "No username provided");
-                return;
-            }
-            sfree(s->locally_allocated_username); /* for change_username */
-            s->username = s->locally_allocated_username =
-                prompt_get_result(s->cur_prompt->prompts[0]);
-            free_prompts(s->cur_prompt);
-        } else {
-            if (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat))
-                ppl_printf(WINSCP_BOM "Using username \"%s\".\r\n", s->username);
-        }
-        s->got_username = true;
-
-        /*
-         * Send an authentication request using method "none": (a)
-         * just in case it succeeds, and (b) so that we know what
-         * authentication methods we can usefully try next.
-         */
-        s->ppl.bpp->pls->actx = SSH2_PKTCTX_NOAUTH;
-
-        s->pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-        put_stringz(s->pktout, s->username);
-        put_stringz(s->pktout, s->successor_layer->vt->name);
-        put_stringz(s->pktout, "none");    /* method */
-        pq_push(s->ppl.out_pq, s->pktout);
-        s->type = AUTH_TYPE_NONE;
-
-        s->tried_pubkey_config = false;
-        s->kbd_inter_refused = false;
-        s->done_agent = false;
-
-        while (1) {
-            /*
-             * Wait for the result of the last authentication request,
-             * unless the request terminated for some reason on our
-             * own side.
-             */
-            if (s->suppress_wait_for_response_packet) {
-                pktin = NULL;
-                s->suppress_wait_for_response_packet = false;
-            } else {
-                crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-            }
-
-            /*
-             * Now is a convenient point to spew any banner material
-             * that we've accumulated. (This should ensure that when
-             * we exit the auth loop, we haven't any left to deal
-             * with.)
-             *
-             * Don't show the banner if we're operating in non-verbose
-             * non-interactive mode. (It's probably a script, which
-             * means nobody will read the banner _anyway_, and
-             * moreover the printing of the banner will screw up
-             * processing on the output of (say) plink.)
-             *
-             * The banner data has been sanitised already by this
-             * point, but we still need to precede and follow it with
-             * anti-spoofing header lines.
-             */
-            if (bufchain_size(&s->banner) &&
-                (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat))) {
-                if (s->banner_scc) {
-                    ssh2_userauth_antispoof_msg(
-                        s, "Pre-authentication banner message from server:");
-                    seat_set_trust_status(s->ppl.seat, false);
-                }
-
-                { // WINSCP
-                bool mid_line = false;
-                while (bufchain_size(&s->banner) > 0) {
-                    ptrlen data = bufchain_prefix(&s->banner);
-                    seat_stderr_pl(s->ppl.seat, data);
-                    display_banner(s->ppl.seat, data.ptr, data.len); // WINSCP
-                    mid_line =
-                        (((const char *)data.ptr)[data.len-1] != '\n');
-                    bufchain_consume(&s->banner, data.len);
-                }
-                bufchain_clear(&s->banner);
-
-                if (mid_line)
-                    seat_stderr_pl(s->ppl.seat, PTRLEN_LITERAL("\r\n"));
-
-                if (s->banner_scc) {
-                    seat_set_trust_status(s->ppl.seat, true);
-                    ssh2_userauth_antispoof_msg(
-                        s, "End of banner message from server");
-                }
-                } // WINSCP
-            }
-
-            if (pktin && pktin->type == SSH2_MSG_USERAUTH_SUCCESS) {
-                ppl_logevent("Access granted");
-                goto userauth_success;
-            }
-
-            if (pktin && pktin->type != SSH2_MSG_USERAUTH_FAILURE &&
-                s->type != AUTH_TYPE_GSSAPI) {
-                ssh_proto_error(s->ppl.ssh, "Received unexpected packet "
-                                "in response to authentication request, "
-                                "type %d (%s)", pktin->type,
-                                ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                              s->ppl.bpp->pls->actx,
-                                              pktin->type));
-                return;
-            }
-
-            /*
-             * OK, we're now sitting on a USERAUTH_FAILURE message, so
-             * we can look at the string in it and know what we can
-             * helpfully try next.
-             */
-            if (pktin && pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
-                ptrlen methods = get_string(pktin);
-                bool partial_success = get_bool(pktin);
-
-                if (!partial_success) {
-                    /*
-                     * We have received an unequivocal Access
-                     * Denied. This can translate to a variety of
-                     * messages, or no message at all.
-                     *
-                     * For forms of authentication which are attempted
-                     * implicitly, by which I mean without printing
-                     * anything in the window indicating that we're
-                     * trying them, we should never print 'Access
-                     * denied'.
-                     *
-                     * If we do print a message saying that we're
-                     * attempting some kind of authentication, it's OK
-                     * to print a followup message saying it failed -
-                     * but the message may sometimes be more specific
-                     * than simply 'Access denied'.
-                     *
-                     * Additionally, if we'd just tried password
-                     * authentication, we should break out of this
-                     * whole loop so as to go back to the username
-                     * prompt (iff we're configured to allow
-                     * username change attempts).
-                     */
-                    if (s->type == AUTH_TYPE_NONE) {
-                        /* do nothing */
-                    } else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||
-                               s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
-                        if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
-                            ppl_printf("Server refused our key\r\n");
-                        ppl_logevent("Server refused our key");
-                    } else if (s->type == AUTH_TYPE_PUBLICKEY) {
-                        /* This _shouldn't_ happen except by a
-                         * protocol bug causing client and server to
-                         * disagree on what is a correct signature. */
-                        ppl_printf("Server refused public-key signature"
-                                   " despite accepting key!\r\n");
-                        ppl_logevent("Server refused public-key signature"
-                                     " despite accepting key!");
-                    } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
-                        /* quiet, so no ppl_printf */
-                        ppl_logevent("Server refused keyboard-interactive "
-                                     "authentication");
-                    } else if (s->type==AUTH_TYPE_GSSAPI) {
-                        /* always quiet, so no ppl_printf */
-                        /* also, the code down in the GSSAPI block has
-                         * already logged this in the Event Log */
-                    } else if (s->type == AUTH_TYPE_KEYBOARD_INTERACTIVE) {
-                        ppl_logevent("Keyboard-interactive authentication "
-                                     "failed");
-                        ppl_printf("Access denied\r\n");
-                    } else {
-                        assert(s->type == AUTH_TYPE_PASSWORD);
-                        ppl_logevent("Password authentication failed");
-                        ppl_printf("Access denied\r\n");
-
-                        if (s->change_username) {
-                            /* XXX perhaps we should allow
-                             * keyboard-interactive to do this too? */
-                            goto try_new_username;
-                        }
-                    }
-                } else {
-                    ppl_printf("Further authentication required\r\n");
-                    ppl_logevent("Further authentication required");
-                }
-
-                /*
-                 * Save the methods string for use in error messages.
-                 */
-                strbuf_clear(s->last_methods_string);
-                put_datapl(s->last_methods_string, methods);
-#ifdef WINSCP
-                ppl_logevent("Server offered these authentication methods: %s", s->last_methods_string->s);
-#endif
-
-                /*
-                 * Scan it for method identifiers we know about.
-                 */
-                { // WINSCP
-                bool srv_pubkey = false, srv_passwd = false;
-                bool srv_keyb_inter = false;
-#ifndef NO_GSSAPI
-                bool srv_gssapi = false, srv_gssapi_keyex_auth = false;
-#endif
-
-                ptrlen method; // WINSCP
-                for (; get_commasep_word(&methods, &method) ;) {
-                    if (ptrlen_eq_string(method, "publickey"))
-                        srv_pubkey = true;
-                    else if (ptrlen_eq_string(method, "password"))
-                        srv_passwd = true;
-                    else if (ptrlen_eq_string(method, "keyboard-interactive"))
-                        srv_keyb_inter = true;
-#ifndef NO_GSSAPI
-                    else if (ptrlen_eq_string(method, "gssapi-with-mic"))
-                        srv_gssapi = true;
-                    else if (ptrlen_eq_string(method, "gssapi-keyex"))
-                        srv_gssapi_keyex_auth = true;
-#endif
-                }
-
-                /*
-                 * And combine those flags with our own configuration
-                 * and context to set the main can_foo variables.
-                 */
-                s->can_pubkey = srv_pubkey;
-                s->can_passwd = srv_passwd;
-                s->can_keyb_inter = s->try_ki_auth && srv_keyb_inter;
-#ifndef NO_GSSAPI
-                s->can_gssapi = s->try_gssapi_auth && srv_gssapi &&
-                    s->shgss->libs->nlibraries > 0;
-                s->can_gssapi_keyex_auth = s->try_gssapi_kex_auth &&
-                    srv_gssapi_keyex_auth &&
-                    s->shgss->libs->nlibraries > 0 && s->shgss->ctx;
-#endif
-                } // WINSCP
-            }
-
-            s->ppl.bpp->pls->actx = SSH2_PKTCTX_NOAUTH;
-
-#ifndef NO_GSSAPI
-            if (s->can_gssapi_keyex_auth && !s->tried_gssapi_keyex_auth) {
-
-                /* gssapi-keyex authentication */
-
-                s->type = AUTH_TYPE_GSSAPI;
-                s->tried_gssapi_keyex_auth = true;
-                s->ppl.bpp->pls->actx = SSH2_PKTCTX_GSSAPI;
-
-                if (s->shgss->lib->gsslogmsg)
-                    ppl_logevent("%s", s->shgss->lib->gsslogmsg);
-
-                ppl_logevent("Trying gssapi-keyex...");
-                s->pktout = ssh2_userauth_gss_packet(s, "gssapi-keyex");
-                pq_push(s->ppl.out_pq, s->pktout);
-                s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
-                s->shgss->ctx = NULL;
-
-                continue;
-            } else
-#endif /* NO_GSSAPI */
-
-            if (s->can_pubkey && !s->done_agent &&
-                s->agent_key_index < s->agent_key_limit) {
-
-                /*
-                 * Attempt public-key authentication using a key from Pageant.
-                 */
-                s->agent_keyalg = s->agent_keys[s->agent_key_index].algorithm;
-                s->signflags = 0;
-                if (ptrlen_eq_string(s->agent_keyalg, "ssh-rsa")) {
-                    /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family,
-                     * if the server has announced support for them. */
-                    if (s->ppl.bpp->ext_info_rsa_sha512_ok) {
-                        s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-512");
-                        s->signflags = SSH_AGENT_RSA_SHA2_512;
-                    } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) {
-                        s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-256");
-                        s->signflags = SSH_AGENT_RSA_SHA2_256;
-                    }
-                }
-
-                s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY;
-
-                ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index);
-
-                /* See if server will accept it */
-                s->pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                put_stringz(s->pktout, s->username);
-                put_stringz(s->pktout, s->successor_layer->vt->name);
-                put_stringz(s->pktout, "publickey");
-                                                    /* method */
-                put_bool(s->pktout, false); /* no signature included */
-                put_stringpl(s->pktout, s->agent_keyalg);
-                put_stringpl(s->pktout, ptrlen_from_strbuf(
-                            s->agent_keys[s->agent_key_index].blob));
-                pq_push(s->ppl.out_pq, s->pktout);
-                s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
-
-                crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-                if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
-
-                    /* Offer of key refused, presumably via
-                     * USERAUTH_FAILURE. Requeue for the next iteration. */
-                    pq_push_front(s->ppl.in_pq, pktin);
-
-                } else {
-                    strbuf *agentreq, *sigdata;
-                    ptrlen comment = ptrlen_from_strbuf(
-                        s->agent_keys[s->agent_key_index].comment);
-
-                    if (seat_verbose(s->ppl.seat))
-                        ppl_printf("Authenticating with public key "
-                                   "\"%.*s\" from agent\r\n",
-                                   PTRLEN_PRINTF(comment));
-
-                    /*
-                     * Server is willing to accept the key.
-                     * Construct a SIGN_REQUEST.
-                     */
-                    s->pktout = ssh_bpp_new_pktout(
-                        s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                    put_stringz(s->pktout, s->username);
-                    put_stringz(s->pktout, s->successor_layer->vt->name);
-                    put_stringz(s->pktout, "publickey");
-                                                        /* method */
-                    put_bool(s->pktout, true);  /* signature included */
-                    put_stringpl(s->pktout, s->agent_keyalg);
-                    put_stringpl(s->pktout, ptrlen_from_strbuf(
-                            s->agent_keys[s->agent_key_index].blob));
-
-                    /* Ask agent for signature. */
-                    agentreq = strbuf_new_for_agent_query();
-                    put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST);
-                    put_stringpl(agentreq, ptrlen_from_strbuf(
-                            s->agent_keys[s->agent_key_index].blob));
-                    /* Now the data to be signed... */
-                    sigdata = strbuf_new();
-                    ssh2_userauth_add_session_id(s, sigdata);
-                    put_data(sigdata, s->pktout->data + 5,
-                             s->pktout->length - 5);
-                    put_stringsb(agentreq, sigdata);
-                    /* And finally the flags word. */
-                    put_uint32(agentreq, s->signflags);
-                    ssh2_userauth_agent_query(s, agentreq);
-                    strbuf_free(agentreq);
-                    crWaitUntilV(!s->auth_agent_query);
-
-                    if (s->agent_response.ptr) {
-                        ptrlen sigblob;
-                        BinarySource src[1];
-                        BinarySource_BARE_INIT(src, s->agent_response.ptr,
-                                               s->agent_response.len);
-                        get_uint32(src); /* skip length field */
-                        if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE &&
-                            (sigblob = get_string(src), !get_err(src))) {
-                            ppl_logevent("Sending Pageant's response");
-                            ssh2_userauth_add_sigblob(
-                                s, s->pktout,
-                                ptrlen_from_strbuf(
-                                    s->agent_keys[s->agent_key_index].blob),
-                                sigblob);
-                            pq_push(s->ppl.out_pq, s->pktout);
-                            s->type = AUTH_TYPE_PUBLICKEY;
-                            s->is_trivial_auth = false;
-                        } else {
-                            ppl_logevent("Pageant refused signing request");
-                            ppl_printf("Pageant failed to "
-                                       "provide a signature\r\n");
-                            s->suppress_wait_for_response_packet = true;
-                            ssh_free_pktout(s->pktout);
-                        }
-                    } else {
-                        ppl_logevent("Pageant failed to respond to "
-                                     "signing request");
-                        ppl_printf("Pageant failed to "
-                                   "respond to signing request\r\n");
-                        s->suppress_wait_for_response_packet = true;
-                        ssh_free_pktout(s->pktout);
-                    }
-                }
-
-                /* Do we have any keys left to try? */
-                if (++s->agent_key_index >= s->agent_key_limit)
-                    s->done_agent = true;
-
-            } else if (s->can_pubkey && s->publickey_blob &&
-                       s->privatekey_available && !s->tried_pubkey_config) {
-
-                ssh2_userkey *key;   /* not live over crReturn */
-                char *passphrase;           /* not live over crReturn */
-
-                s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY;
-
-                s->tried_pubkey_config = true;
-
-                /*
-                 * Try the public key supplied in the configuration.
-                 *
-                 * First, try to upgrade its algorithm.
-                 */
-                if (!strcmp(s->publickey_algorithm, "ssh-rsa")) {
-                    /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family,
-                     * if the server has announced support for them. */
-                    if (s->ppl.bpp->ext_info_rsa_sha512_ok) {
-                        sfree(s->publickey_algorithm);
-                        s->publickey_algorithm = dupstr("rsa-sha2-512");
-                        s->signflags = SSH_AGENT_RSA_SHA2_512;
-                    } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) {
-                        sfree(s->publickey_algorithm);
-                        s->publickey_algorithm = dupstr("rsa-sha2-256");
-                        s->signflags = SSH_AGENT_RSA_SHA2_256;
-                    }
-                }
-
-                /*
-                 * Offer the public blob to see if the server is willing to
-                 * accept it.
-                 */
-                s->pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                put_stringz(s->pktout, s->username);
-                put_stringz(s->pktout, s->successor_layer->vt->name);
-                put_stringz(s->pktout, "publickey");    /* method */
-                put_bool(s->pktout, false);
-                                                /* no signature included */
-                put_stringz(s->pktout, s->publickey_algorithm);
-                put_string(s->pktout, s->publickey_blob->s,
-                           s->publickey_blob->len);
-                pq_push(s->ppl.out_pq, s->pktout);
-                ppl_logevent("Offered public key");
-
-                crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-                if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) {
-                    /* Key refused. Give up. */
-                    pq_push_front(s->ppl.in_pq, pktin);
-                    s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
-                    continue; /* process this new message */
-                }
-                ppl_logevent("Offer of public key accepted");
-
-                /*
-                 * Actually attempt a serious authentication using
-                 * the key.
-                 */
-                if (seat_verbose(s->ppl.seat))
-                    ppl_printf("Authenticating with public key \"%s\"\r\n",
-                               s->publickey_comment);
-
-                key = NULL;
-                while (!key) {
-                    const char *error;  /* not live over crReturn */
-                    if (s->privatekey_encrypted) {
-                        /*
-                         * Get a passphrase from the user.
-                         */
-                        s->cur_prompt = new_prompts();
-                        s->cur_prompt->to_server = false;
-                        s->cur_prompt->from_server = false;
-                        s->cur_prompt->name = dupstr("SSH key passphrase");
-                        add_prompt(s->cur_prompt,
-                                   dupprintf("Passphrase for key \"%s\": ",
-                                             s->publickey_comment),
-                                   false);
-                        s->userpass_ret = seat_get_userpass_input(
-                            s->ppl.seat, s->cur_prompt, NULL);
-                        while (1) {
-                            while (s->userpass_ret < 0 &&
-                                   bufchain_size(s->ppl.user_input) > 0)
-                                s->userpass_ret = seat_get_userpass_input(
-                                    s->ppl.seat, s->cur_prompt,
-                                    s->ppl.user_input);
-
-                            if (s->userpass_ret >= 0)
-                                break;
-
-                            s->want_user_input = true;
-                            crReturnV;
-                            s->want_user_input = false;
-                        }
-                        if (!s->userpass_ret) {
-                            /* Failed to get a passphrase. Terminate. */
-                            free_prompts(s->cur_prompt);
-                            ssh_bpp_queue_disconnect(
-                                s->ppl.bpp, "Unable to authenticate",
-                                SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
-                            ssh_user_close(s->ppl.ssh, "User aborted at "
-                                           "passphrase prompt");
-                            return;
-                        }
-                        passphrase =
-                            prompt_get_result(s->cur_prompt->prompts[0]);
-                        free_prompts(s->cur_prompt);
-                    } else {
-                        passphrase = NULL; /* no passphrase needed */
-                    }
-
-                    /*
-                     * Try decrypting the key.
-                     */
-                    key = ppk_load_f(s->keyfile, passphrase, &error);
-                    if (passphrase) {
-                        /* burn the evidence */
-                        smemclr(passphrase, strlen(passphrase));
-                        sfree(passphrase);
-                    }
-                    if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
-                        if (passphrase &&
-                            (key == SSH2_WRONG_PASSPHRASE)) {
-                            ppl_printf("Wrong passphrase\r\n");
-                            key = NULL;
-                            /* and loop again */
-                        } else {
-                            ppl_printf("Unable to load private key (%s)\r\n",
-                                       error);
-                            key = NULL;
-                            s->suppress_wait_for_response_packet = true;
-                            break; /* try something else */
-                        }
-                    } else {
-                        /* FIXME: if we ever support variable signature
-                         * flags, this is somewhere they'll need to be
-                         * put */
-                        char *invalid = ssh_key_invalid(key->key, 0);
-                        if (invalid) {
-                            ppl_printf("Cannot use this private key (%s)\r\n",
-                                       invalid);
-                            ssh_key_free(key->key);
-                            sfree(key->comment);
-                            sfree(key);
-                            sfree(invalid);
-                            key = NULL;
-                            s->suppress_wait_for_response_packet = true;
-                            break; /* try something else */
-                        }
-                    }
-                }
-
-                if (key) {
-                    strbuf *pkblob, *sigdata, *sigblob;
-
-                    /*
-                     * We have loaded the private key and the server
-                     * has announced that it's willing to accept it.
-                     * Hallelujah. Generate a signature and send it.
-                     */
-                    s->pktout = ssh_bpp_new_pktout(
-                        s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                    put_stringz(s->pktout, s->username);
-                    put_stringz(s->pktout, s->successor_layer->vt->name);
-                    put_stringz(s->pktout, "publickey"); /* method */
-                    put_bool(s->pktout, true); /* signature follows */
-                    put_stringz(s->pktout, s->publickey_algorithm);
-                    pkblob = strbuf_new();
-                    ssh_key_public_blob(key->key, BinarySink_UPCAST(pkblob));
-                    put_string(s->pktout, pkblob->s, pkblob->len);
-
-                    /*
-                     * The data to be signed is:
-                     *
-                     *   string  session-id
-                     *
-                     * followed by everything so far placed in the
-                     * outgoing packet.
-                     */
-                    sigdata = strbuf_new();
-                    ssh2_userauth_add_session_id(s, sigdata);
-                    put_data(sigdata, s->pktout->data + 5,
-                             s->pktout->length - 5);
-                    sigblob = strbuf_new();
-                    ssh_key_sign(key->key, ptrlen_from_strbuf(sigdata),
-                                 s->signflags, BinarySink_UPCAST(sigblob));
-                    strbuf_free(sigdata);
-                    ssh2_userauth_add_sigblob(
-                        s, s->pktout, ptrlen_from_strbuf(pkblob),
-                        ptrlen_from_strbuf(sigblob));
-                    strbuf_free(pkblob);
-                    strbuf_free(sigblob);
-
-                    pq_push(s->ppl.out_pq, s->pktout);
-                    ppl_logevent("Sent public key signature");
-                    s->type = AUTH_TYPE_PUBLICKEY;
-                    ssh_key_free(key->key);
-                    sfree(key->comment);
-                    sfree(key);
-                    s->is_trivial_auth = false;
-                }
-
-#ifndef NO_GSSAPI
-            } else if (s->can_gssapi && !s->tried_gssapi) {
-
-                /* gssapi-with-mic authentication */
-
-                ptrlen data;
-
-                s->type = AUTH_TYPE_GSSAPI;
-                s->tried_gssapi = true;
-                s->ppl.bpp->pls->actx = SSH2_PKTCTX_GSSAPI;
-
-                if (s->shgss->lib->gsslogmsg)
-                    ppl_logevent("%s", s->shgss->lib->gsslogmsg);
-
-                /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
-                ppl_logevent("Trying gssapi-with-mic...");
-                s->pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                put_stringz(s->pktout, s->username);
-                put_stringz(s->pktout, s->successor_layer->vt->name);
-                put_stringz(s->pktout, "gssapi-with-mic");
-                ppl_logevent("Attempting GSSAPI authentication");
-
-                /* add mechanism info */
-                s->shgss->lib->indicate_mech(s->shgss->lib, &s->gss_buf);
-
-                /* number of GSSAPI mechanisms */
-                put_uint32(s->pktout, 1);
-
-                /* length of OID + 2 */
-                put_uint32(s->pktout, s->gss_buf.length + 2);
-                put_byte(s->pktout, SSH2_GSS_OIDTYPE);
-
-                /* length of OID */
-                put_byte(s->pktout, s->gss_buf.length);
-
-                put_data(s->pktout, s->gss_buf.value, s->gss_buf.length);
-                pq_push(s->ppl.out_pq, s->pktout);
-                crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-                if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) {
-                    ppl_logevent("GSSAPI authentication request refused");
-                    pq_push_front(s->ppl.in_pq, pktin);
-                    continue;
-                }
-
-                /* check returned packet ... */
-
-                data = get_string(pktin);
-                s->gss_rcvtok.value = (char *)data.ptr;
-                s->gss_rcvtok.length = data.len;
-                if (s->gss_rcvtok.length != s->gss_buf.length + 2 ||
-                    ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE ||
-                    ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length ||
-                    memcmp((char *)s->gss_rcvtok.value + 2,
-                           s->gss_buf.value,s->gss_buf.length) ) {
-                    ppl_logevent("GSSAPI authentication - wrong response "
-                                 "from server");
-                    continue;
-                }
-
-                /* Import server name if not cached from KEX */
-                if (s->shgss->srv_name == GSS_C_NO_NAME) {
-                    // WINSCP
-                    const char * fullhostname = s->fullhostname;
-                    if (s->loghost[0] != '\0')
-                    {
-                        fullhostname = s->loghost;
-                    }
-                    // /WINSCP
-                    s->gss_stat = s->shgss->lib->import_name(
-                        s->shgss->lib, fullhostname, &s->shgss->srv_name); // WINSCP
-                    if (s->gss_stat != SSH_GSS_OK) {
-                        if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
-                            ppl_logevent("GSSAPI import name failed -"
-                                         " Bad service name");
-                        else
-                            ppl_logevent("GSSAPI import name failed");
-                        continue;
-                    }
-                }
-
-                /* Allocate our gss_ctx */
-                s->gss_stat = s->shgss->lib->acquire_cred(
-                    s->shgss->lib, &s->shgss->ctx, NULL);
-                if (s->gss_stat != SSH_GSS_OK) {
-                    ppl_logevent("GSSAPI authentication failed to get "
-                                 "credentials");
-                    /* The failure was on our side, so the server
-                     * won't be sending a response packet indicating
-                     * failure. Avoid waiting for it next time round
-                     * the loop. */
-                    s->suppress_wait_for_response_packet = true;
-                    continue;
-                }
-
-                /* initial tokens are empty */
-                SSH_GSS_CLEAR_BUF(&s->gss_rcvtok);
-                SSH_GSS_CLEAR_BUF(&s->gss_sndtok);
-
-                /* now enter the loop */
-                do {
-                    /*
-                     * When acquire_cred yields no useful expiration, go with
-                     * the service ticket expiration.
-                     */
-                    s->gss_stat = s->shgss->lib->init_sec_context
-                        (s->shgss->lib,
-                         &s->shgss->ctx,
-                         s->shgss->srv_name,
-                         s->gssapi_fwd,
-                         &s->gss_rcvtok,
-                         &s->gss_sndtok,
-                         NULL,
-                         NULL);
-
-                    if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
-                        s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
-                        ppl_logevent("GSSAPI authentication initialisation "
-                                     "failed");
-
-                        if (s->shgss->lib->display_status(s->shgss->lib,
-                                s->shgss->ctx, &s->gss_buf) == SSH_GSS_OK) {
-                            ppl_logevent("%s", (char *)s->gss_buf.value);
-                            sfree(s->gss_buf.value);
-                        }
-
-                        pq_push_front(s->ppl.in_pq, pktin);
-                        break;
-                    }
-                    ppl_logevent("GSSAPI authentication initialised");
-
-                    /*
-                     * Client and server now exchange tokens until GSSAPI
-                     * no longer says CONTINUE_NEEDED
-                     */
-                    if (s->gss_sndtok.length != 0) {
-                        s->is_trivial_auth = false;
-                        s->pktout =
-                            ssh_bpp_new_pktout(
-                                s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
-                        put_string(s->pktout,
-                                   s->gss_sndtok.value, s->gss_sndtok.length);
-                        pq_push(s->ppl.out_pq, s->pktout);
-                        s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok);
-                    }
-
-                    if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
-                        crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-
-                        if (pktin->type == SSH2_MSG_USERAUTH_GSSAPI_ERRTOK) {
-                            /*
-                             * Per RFC 4462 section 3.9, this packet
-                             * type MUST immediately precede an
-                             * ordinary USERAUTH_FAILURE.
-                             *
-                             * We currently don't know how to do
-                             * anything with the GSSAPI error token
-                             * contained in this packet, so we ignore
-                             * it and just wait for the following
-                             * FAILURE.
-                             */
-                            crMaybeWaitUntilV(
-                                (pktin = ssh2_userauth_pop(s)) != NULL);
-                            if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) {
-                                ssh_proto_error(
-                                    s->ppl.ssh, "Received unexpected packet "
-                                    "after SSH_MSG_USERAUTH_GSSAPI_ERRTOK "
-                                    "(expected SSH_MSG_USERAUTH_FAILURE): "
-                                    "type %d (%s)", pktin->type,
-                                    ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                                  s->ppl.bpp->pls->actx,
-                                                  pktin->type));
-                                return;
-                            }
-                        }
-
-                        if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
-                            ppl_logevent("GSSAPI authentication failed");
-                            s->gss_stat = SSH_GSS_FAILURE;
-                            pq_push_front(s->ppl.in_pq, pktin);
-                            break;
-                        } else if (pktin->type !=
-                                   SSH2_MSG_USERAUTH_GSSAPI_TOKEN) {
-                            ppl_logevent("GSSAPI authentication -"
-                                         " bad server response");
-                            s->gss_stat = SSH_GSS_FAILURE;
-                            break;
-                        }
-                        data = get_string(pktin);
-                        s->gss_rcvtok.value = (char *)data.ptr;
-                        s->gss_rcvtok.length = data.len;
-                    }
-                } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
-
-                if (s->gss_stat != SSH_GSS_OK) {
-                    s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
-                    continue;
-                }
-                ppl_logevent("GSSAPI authentication loop finished OK");
-
-                /* Now send the MIC */
-
-                s->pktout = ssh2_userauth_gss_packet(s, "gssapi-with-mic");
-                pq_push(s->ppl.out_pq, s->pktout);
-
-                s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx);
-                continue;
-#endif
-            } else if (s->can_keyb_inter && !s->kbd_inter_refused) {
-
-                /*
-                 * Keyboard-interactive authentication.
-                 */
-
-                s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-
-                s->ppl.bpp->pls->actx = SSH2_PKTCTX_KBDINTER;
-
-                s->pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                put_stringz(s->pktout, s->username);
-                put_stringz(s->pktout, s->successor_layer->vt->name);
-                put_stringz(s->pktout, "keyboard-interactive");
-                                                        /* method */
-                put_stringz(s->pktout, "");     /* lang */
-                put_stringz(s->pktout, "");     /* submethods */
-                pq_push(s->ppl.out_pq, s->pktout);
-
-                ppl_logevent("Attempting keyboard-interactive authentication");
-
-                if (!s->ki_scc_initialised) {
-                    s->ki_scc = seat_stripctrl_new(
-                        s->ppl.seat, NULL, SIC_KI_PROMPTS);
-                    if (s->ki_scc)
-                        stripctrl_enable_line_limiting(s->ki_scc);
-                    s->ki_scc_initialised = true;
-                }
-
-                crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-                if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
-                    /* Server is not willing to do keyboard-interactive
-                     * at all (or, bizarrely but legally, accepts the
-                     * user without actually issuing any prompts).
-                     * Give up on it entirely. */
-                    pq_push_front(s->ppl.in_pq, pktin);
-                    s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
-                    s->kbd_inter_refused = true; /* don't try it again */
-                    continue;
-                }
-
-                s->ki_printed_header = false;
-
-                /*
-                 * Loop while the server continues to send INFO_REQUESTs.
-                 */
-                while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
-                    ptrlen name, inst;
-                    strbuf *sb;
-
-                    /*
-                     * We've got a fresh USERAUTH_INFO_REQUEST.
-                     * Get the preamble and start building a prompt.
-                     */
-                    name = get_string(pktin);
-                    inst = get_string(pktin);
-                    get_string(pktin); /* skip language tag */
-                    s->cur_prompt = new_prompts();
-                    s->cur_prompt->to_server = true;
-                    s->cur_prompt->from_server = true;
-
-                    /*
-                     * Get any prompt(s) from the packet.
-                     */
-                    s->num_prompts = get_uint32(pktin);
-                    { // WINSCP
-                    uint32_t i;
-                    for (i = 0; i < s->num_prompts; i++) {
-                        s->is_trivial_auth = false;
-                        { // WINSCP
-                        ptrlen prompt = get_string(pktin);
-                        bool echo = get_bool(pktin);
-
-                        if (get_err(pktin)) {
-                            ssh_proto_error(
-                                s->ppl.ssh, "Server sent truncated "
-                                "SSH_MSG_USERAUTH_INFO_REQUEST packet");
-                            return;
-                        }
-
-                        sb = strbuf_new();
-                        if (!prompt.len) {
-                            put_datapl(sb, PTRLEN_LITERAL(
-                                "<server failed to send prompt>: "));
-                        } else if (s->ki_scc) {
-                            stripctrl_retarget(
-                                s->ki_scc, BinarySink_UPCAST(sb));
-                            put_datapl(s->ki_scc, prompt);
-                            stripctrl_retarget(s->ki_scc, NULL);
-                        } else {
-                            put_datapl(sb, prompt);
-                        }
-                        add_prompt(s->cur_prompt, strbuf_to_str(sb), echo);
-                        } // WINSCP
-                    }
-                    } // WINSCP
-
-                    /*
-                     * Make the header strings. This includes the
-                     * 'name' (optional dialog-box title) and
-                     * 'instruction' from the server.
-                     *
-                     * First, display our disambiguating header line
-                     * if this is the first time round the loop -
-                     * _unless_ the server has sent a completely empty
-                     * k-i packet with no prompts _or_ text, which
-                     * apparently some do. In that situation there's
-                     * no need to alert the user that the following
-                     * text is server- supplied, because, well, _what_
-                     * text?
-                     *
-                     * We also only do this if we got a stripctrl,
-                     * because if we didn't, that suggests this is all
-                     * being done via dialog boxes anyway.
-                     */
-                    if (!s->ki_printed_header && s->ki_scc &&
-                        (s->num_prompts || name.len || inst.len)) {
-                        ssh2_userauth_antispoof_msg(
-                            s, "Keyboard-interactive authentication "
-                            "prompts from server:");
-                        s->ki_printed_header = true;
-                        seat_set_trust_status(s->ppl.seat, false);
-                    }
-
-                    sb = strbuf_new();
-                    if (name.len) {
-                        put_datapl(sb, PTRLEN_LITERAL("SSH server: ")); // WINSCP
-                        if (s->ki_scc) {
-                            stripctrl_retarget(s->ki_scc,
-                                               BinarySink_UPCAST(sb));
-                            put_datapl(s->ki_scc, name);
-                            stripctrl_retarget(s->ki_scc, NULL);
-                        } else {
-                            put_datapl(sb, name);
-                        }
-                        s->cur_prompt->name_reqd = true;
-                    } else {
-                        put_datapl(sb, PTRLEN_LITERAL(
-                            "SSH server authentication"));
-                        s->cur_prompt->name_reqd = false;
-                    }
-                    s->cur_prompt->name = strbuf_to_str(sb);
-
-                    sb = strbuf_new();
-                    if (inst.len) {
-                        if (s->ki_scc) {
-                            stripctrl_retarget(s->ki_scc,
-                                               BinarySink_UPCAST(sb));
-                            put_datapl(s->ki_scc, inst);
-                            stripctrl_retarget(s->ki_scc, NULL);
-                        } else {
-                            put_datapl(sb, inst);
-                        }
-                        s->cur_prompt->instr_reqd = true;
-                    } else {
-                        s->cur_prompt->instr_reqd = false;
-                    }
-                    if (sb->len)
-                        s->cur_prompt->instruction = strbuf_to_str(sb);
-                    else
-                        strbuf_free(sb);
-
-                    /*
-                     * Our prompts_t is fully constructed now. Get the
-                     * user's response(s).
-                     */
-                    s->userpass_ret = seat_get_userpass_input(
-                        s->ppl.seat, s->cur_prompt, NULL);
-                    while (1) {
-                        while (s->userpass_ret < 0 &&
-                               bufchain_size(s->ppl.user_input) > 0)
-                            s->userpass_ret = seat_get_userpass_input(
-                                s->ppl.seat, s->cur_prompt, s->ppl.user_input);
-
-                        if (s->userpass_ret >= 0)
-                            break;
-
-                        s->want_user_input = true;
-                        crReturnV;
-                        s->want_user_input = false;
-                    }
-                    if (!s->userpass_ret) {
-                        /*
-                         * Failed to get responses. Terminate.
-                         */
-                        free_prompts(s->cur_prompt);
-                        ssh_bpp_queue_disconnect(
-                            s->ppl.bpp, "Unable to authenticate",
-                            SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
-                        ssh_user_close(s->ppl.ssh, "User aborted during "
-                                       "keyboard-interactive authentication");
-                        return;
-                    }
-
-                    /*
-                     * Send the response(s) to the server.
-                     */
-                    s->pktout = ssh_bpp_new_pktout(
-                        s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
-                    put_uint32(s->pktout, s->num_prompts);
-                    { // WINSCP
-                    uint32_t i; // WINSCP
-                    for (i = 0; i < s->num_prompts; i++) {
-                        put_stringz(s->pktout, prompt_get_result_ref(
-                                        s->cur_prompt->prompts[i]));
-                    }
-                    } // WINSCP
-                    s->pktout->minlen = 256;
-                    pq_push(s->ppl.out_pq, s->pktout);
-
-                    /*
-                     * Free the prompts structure from this iteration.
-                     * If there's another, a new one will be allocated
-                     * when we return to the top of this while loop.
-                     */
-                    free_prompts(s->cur_prompt);
-
-                    /*
-                     * Get the next packet in case it's another
-                     * INFO_REQUEST.
-                     */
-                    crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-
-                }
-
-                /*
-                 * Print our trailer line, if we printed a header.
-                 */
-                if (s->ki_printed_header) {
-                    seat_set_trust_status(s->ppl.seat, true);
-                    ssh2_userauth_antispoof_msg(
-                        s, "End of keyboard-interactive prompts from server");
-                }
-
-                /*
-                 * We should have SUCCESS or FAILURE now.
-                 */
-                pq_push_front(s->ppl.in_pq, pktin);
-
-            } else if (s->can_passwd) {
-                s->is_trivial_auth = false;
-                { // WINSCP
-                /*
-                 * Plain old password authentication.
-                 */
-                bool changereq_first_time; /* not live over crReturn */
-
-                s->ppl.bpp->pls->actx = SSH2_PKTCTX_PASSWORD;
-
-                // WINSCP
-                if (s->change_password)
-                {
-                    s->password = dupstr("");
-                    s->type = AUTH_TYPE_PASSWORD;
-                }
-                else
-                {
-                // no indentation to ease merges
-                // /WINSCP
-                s->cur_prompt = new_prompts();
-                s->cur_prompt->to_server = true;
-                s->cur_prompt->from_server = false;
-                s->cur_prompt->name = dupstr("SSH password");
-                add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
-                                                    s->username, s->hostname),
-                           false);
-
-                s->userpass_ret = seat_get_userpass_input(
-                    s->ppl.seat, s->cur_prompt, NULL);
-                while (1) {
-                    while (s->userpass_ret < 0 &&
-                           bufchain_size(s->ppl.user_input) > 0)
-                        s->userpass_ret = seat_get_userpass_input(
-                            s->ppl.seat, s->cur_prompt, s->ppl.user_input);
-
-                    if (s->userpass_ret >= 0)
-                        break;
-
-                    s->want_user_input = true;
-                    crReturnV;
-                    s->want_user_input = false;
-                }
-                if (!s->userpass_ret) {
-                    /*
-                     * Failed to get responses. Terminate.
-                     */
-                    free_prompts(s->cur_prompt);
-                    ssh_bpp_queue_disconnect(
-                        s->ppl.bpp, "Unable to authenticate",
-                        SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
-                    ssh_user_close(s->ppl.ssh, "User aborted during password "
-                                   "authentication");
-                    return;
-                }
-                /*
-                 * Squirrel away the password. (We may need it later if
-                 * asked to change it.)
-                 */
-                s->password = prompt_get_result(s->cur_prompt->prompts[0]);
-                free_prompts(s->cur_prompt);
-
-                /*
-                 * Send the password packet.
-                 *
-                 * We pad out the password packet to 256 bytes to make
-                 * it harder for an attacker to find the length of the
-                 * user's password.
-                 *
-                 * Anyone using a password longer than 256 bytes
-                 * probably doesn't have much to worry about from
-                 * people who find out how long their password is!
-                 */
-                s->pktout = ssh_bpp_new_pktout(
-                    s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                put_stringz(s->pktout, s->username);
-                put_stringz(s->pktout, s->successor_layer->vt->name);
-                put_stringz(s->pktout, "password");
-                put_bool(s->pktout, false);
-                put_stringz(s->pktout, s->password);
-                s->pktout->minlen = 256;
-                pq_push(s->ppl.out_pq, s->pktout);
-                ppl_logevent("Sent password");
-                s->type = AUTH_TYPE_PASSWORD;
-
-                /*
-                 * Wait for next packet, in case it's a password change
-                 * request.
-                 */
-                crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-                } // WINSCP
-                changereq_first_time = true;
-
-                while ((pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) ||
-                       s->change_password) { // WINSCP
-
-                    /*
-                     * We're being asked for a new password
-                     * (perhaps not for the first time).
-                     * Loop until the server accepts it.
-                     */
-
-                    bool got_new = false; /* not live over crReturn */
-                    ptrlen prompt;  /* not live over crReturn */
-
-                    if (!s->change_password) // WINSCP
-                    {
-                        const char *msg;
-                        if (changereq_first_time)
-                            msg = "Server requested password change";
-                        else
-                            msg = "Server rejected new password";
-                        ppl_logevent("%s", msg);
-                        ppl_printf("%s\r\n", msg);
-                    }
-
-                    s->change_password = false; // WINSCP
-
-                    prompt = get_string(pktin);
-
-                    s->cur_prompt = new_prompts();
-                    s->cur_prompt->to_server = true;
-                    s->cur_prompt->from_server = false;
-                    s->cur_prompt->name = dupstr("New SSH password");
-                    s->cur_prompt->instruction = mkstr(prompt);
-                    s->cur_prompt->instr_reqd = true;
-                    /*
-                     * There's no explicit requirement in the protocol
-                     * for the "old" passwords in the original and
-                     * password-change messages to be the same, and
-                     * apparently some Cisco kit supports password change
-                     * by the user entering a blank password originally
-                     * and the real password subsequently, so,
-                     * reluctantly, we prompt for the old password again.
-                     *
-                     * (On the other hand, some servers don't even bother
-                     * to check this field.)
-                     */
-                    add_prompt(s->cur_prompt,
-                               dupstr("Current password (blank for previously entered password): "),
-                               false);
-                    add_prompt(s->cur_prompt, dupstr("Enter new password: "),
-                               false);
-                    add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
-                               false);
-
-                    /*
-                     * Loop until the user manages to enter the same
-                     * password twice.
-                     */
-                    while (!got_new) {
-                        s->userpass_ret = seat_get_userpass_input(
-                            s->ppl.seat, s->cur_prompt, NULL);
-                        while (1) {
-                            while (s->userpass_ret < 0 &&
-                                   bufchain_size(s->ppl.user_input) > 0)
-                                s->userpass_ret = seat_get_userpass_input(
-                                    s->ppl.seat, s->cur_prompt,
-                                    s->ppl.user_input);
-
-                            if (s->userpass_ret >= 0)
-                                break;
-
-                            s->want_user_input = true;
-                            crReturnV;
-                            s->want_user_input = false;
-                        }
-                        if (!s->userpass_ret) {
-                            /*
-                             * Failed to get responses. Terminate.
-                             */
-                            /* burn the evidence */
-                            free_prompts(s->cur_prompt);
-                            smemclr(s->password, strlen(s->password));
-                            sfree(s->password);
-                            ssh_bpp_queue_disconnect(
-                                s->ppl.bpp, "Unable to authenticate",
-                                SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER);
-                            ssh_user_close(s->ppl.ssh, "User aborted during "
-                                           "password changing");
-                            return;
-                        }
-
-                        /*
-                         * If the user specified a new original password
-                         * (IYSWIM), overwrite any previously specified
-                         * one.
-                         * (A side effect is that the user doesn't have to
-                         * re-enter it if they louse up the new password.)
-                         */
-                        if (s->cur_prompt->prompts[0]->result->s[0]) {
-                            smemclr(s->password, strlen(s->password));
-                                /* burn the evidence */
-                            sfree(s->password);
-                            s->password = prompt_get_result(
-                                s->cur_prompt->prompts[0]);
-                        }
-
-                        /*
-                         * Check the two new passwords match.
-                         */
-                        got_new = !strcmp(
-                            prompt_get_result_ref(s->cur_prompt->prompts[1]),
-                            prompt_get_result_ref(s->cur_prompt->prompts[2]));
-                        if (!got_new)
-                            /* They don't. Silly user. */
-                            ppl_printf("Passwords do not match\r\n");
-
-                    }
-
-                    /*
-                     * Send the new password (along with the old one).
-                     * (see above for padding rationale)
-                     */
-                    s->pktout = ssh_bpp_new_pktout(
-                        s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-                    put_stringz(s->pktout, s->username);
-                    put_stringz(s->pktout, s->successor_layer->vt->name);
-                    put_stringz(s->pktout, "password");
-                    put_bool(s->pktout, true);
-                    put_stringz(s->pktout, s->password);
-                    put_stringz(s->pktout, prompt_get_result_ref(
-                                    s->cur_prompt->prompts[1]));
-                    free_prompts(s->cur_prompt);
-                    s->pktout->minlen = 256;
-                    pq_push(s->ppl.out_pq, s->pktout);
-                    ppl_logevent("Sent new password");
-
-                    /*
-                     * Now see what the server has to say about it.
-                     * (If it's CHANGEREQ again, it's not happy with the
-                     * new password.)
-                     */
-                    crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL);
-                    changereq_first_time = false;
-
-                }
-
-                /*
-                 * We need to reexamine the current pktin at the top
-                 * of the loop. Either:
-                 *  - we weren't asked to change password at all, in
-                 *    which case it's a SUCCESS or FAILURE with the
-                 *    usual meaning
-                 *  - we sent a new password, and the server was
-                 *    either OK with it (SUCCESS or FAILURE w/partial
-                 *    success) or unhappy with the _old_ password
-                 *    (FAILURE w/o partial success)
-                 * In any of these cases, we go back to the top of
-                 * the loop and start again.
-                 */
-                pq_push_front(s->ppl.in_pq, pktin);
-
-                /*
-                 * We don't need the old password any more, in any
-                 * case. Burn the evidence.
-                 */
-                smemclr(s->password, strlen(s->password));
-                sfree(s->password);
-                } // WINSCP
-
-            } else {
-                ssh_bpp_queue_disconnect(
-                    s->ppl.bpp,
-                    "No supported authentication methods available",
-                    SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE);
-                ssh_sw_abort(s->ppl.ssh, "No supported authentication methods "
-                             "available (server sent: %s)",
-                             s->last_methods_string->s);
-                return;
-            }
-
-        }
-      try_new_username:;
-    }
-
-  userauth_success:
-    if (s->notrivialauth && s->is_trivial_auth) {
-        ssh_proto_error(s->ppl.ssh, "Authentication was trivial! "
-                        "Abandoning session as specified in configuration.");
-        return;
-    }
-
-    /*
-     * We've just received USERAUTH_SUCCESS, and we haven't sent
-     * any packets since. Signal the transport layer to consider
-     * doing an immediate rekey, if it has any reason to want to.
-     */
-    ssh2_transport_notify_auth_done(s->transport_layer);
-
-    /*
-     * Finally, hand over to our successor layer, and return
-     * immediately without reaching the crFinishV: ssh_ppl_replace
-     * will have freed us, so crFinishV's zeroing-out of crState would
-     * be a use-after-free bug.
-     */
-    {
-        PacketProtocolLayer *successor = s->successor_layer;
-        s->successor_layer = NULL;     /* avoid freeing it ourself */
-        ssh_ppl_replace(&s->ppl, successor);
-        return;   /* we've just freed s, so avoid even touching s->crState */
-    }
-
-    crFinishV;
-}
-
-static void ssh2_userauth_add_session_id(
-    struct ssh2_userauth_state *s, strbuf *sigdata)
-{
-    if (s->ppl.remote_bugs & BUG_SSH2_PK_SESSIONID) {
-        put_datapl(sigdata, s->session_id);
-    } else {
-        put_stringpl(sigdata, s->session_id);
-    }
-}
-
-static void ssh2_userauth_agent_query(
-    struct ssh2_userauth_state *s, strbuf *req)
-{
-    void *response;
-    int response_len;
-
-    sfree(s->agent_response_to_free);
-    s->agent_response_to_free = NULL;
-
-    s->auth_agent_query = agent_query(req, &response, &response_len,
-                                      ssh2_userauth_agent_callback, s, get_seat_callback_set(s->seat)); // WINSCP
-    if (!s->auth_agent_query)
-        ssh2_userauth_agent_callback(s, response, response_len);
-}
-
-static void ssh2_userauth_agent_callback(void *uav, void *reply, int replylen)
-{
-    struct ssh2_userauth_state *s = (struct ssh2_userauth_state *)uav;
-
-    s->auth_agent_query = NULL;
-    s->agent_response_to_free = reply;
-    s->agent_response = make_ptrlen(reply, replylen);
-
-    queue_idempotent_callback(&s->ppl.ic_process_queue);
-}
-
-/*
- * Helper function to add an SSH-2 signature blob to a packet. Expects
- * to be shown the public key blob as well as the signature blob.
- * Normally just appends the sig blob unmodified as a string, except
- * that it optionally breaks it open and fiddle with it to work around
- * BUG_SSH2_RSA_PADDING.
- */
-static void ssh2_userauth_add_sigblob(
-    struct ssh2_userauth_state *s, PktOut *pkt, ptrlen pkblob, ptrlen sigblob)
-{
-    BinarySource pk[1], sig[1];
-    BinarySource_BARE_INIT_PL(pk, pkblob);
-    BinarySource_BARE_INIT_PL(sig, sigblob);
-
-    /* dmemdump(pkblob, pkblob_len); */
-    /* dmemdump(sigblob, sigblob_len); */
-
-    /*
-     * See if this is in fact an ssh-rsa signature and a buggy
-     * server; otherwise we can just do this the easy way.
-     */
-    if ((s->ppl.remote_bugs & BUG_SSH2_RSA_PADDING) &&
-        ptrlen_eq_string(get_string(pk), "ssh-rsa") &&
-        ptrlen_eq_string(get_string(sig), "ssh-rsa")) {
-        ptrlen mod_mp, sig_mp;
-        size_t sig_prefix_len;
-
-        /*
-         * Find the modulus and signature integers.
-         */
-        get_string(pk);                /* skip over exponent */
-        mod_mp = get_string(pk);       /* remember modulus */
-        sig_prefix_len = sig->pos;
-        sig_mp = get_string(sig);
-        if (get_err(pk) || get_err(sig))
-            goto give_up;
-
-        /*
-         * Find the byte length of the modulus, not counting leading
-         * zeroes.
-         */
-        while (mod_mp.len > 0 && *(const char *)mod_mp.ptr == 0) {
-            mod_mp.len--;
-            mod_mp.ptr = (const char *)mod_mp.ptr + 1;
-        }
-
-        /* debug("modulus length is %d\n", len); */
-        /* debug("signature length is %d\n", siglen); */
-
-        if (mod_mp.len > sig_mp.len) {
-            strbuf *substr = strbuf_new();
-            put_data(substr, sigblob.ptr, sig_prefix_len);
-            put_uint32(substr, mod_mp.len);
-            put_padding(substr, mod_mp.len - sig_mp.len, 0);
-            put_datapl(substr, sig_mp);
-            put_stringsb(pkt, substr);
-            return;
-        }
-
-        /* Otherwise fall through and do it the easy way. We also come
-         * here as a fallback if we discover above that the key blob
-         * is misformatted in some way. */
-      give_up:;
-    }
-
-    put_stringpl(pkt, sigblob);
-}
-
-#ifndef NO_GSSAPI
-static PktOut *ssh2_userauth_gss_packet(
-    struct ssh2_userauth_state *s, const char *authtype)
-{
-    strbuf *sb;
-    PktOut *p;
-    Ssh_gss_buf buf;
-    Ssh_gss_buf mic;
-
-    /*
-     * The mic is computed over the session id + intended
-     * USERAUTH_REQUEST packet.
-     */
-    sb = strbuf_new();
-    put_stringpl(sb, s->session_id);
-    put_byte(sb, SSH2_MSG_USERAUTH_REQUEST);
-    put_stringz(sb, s->username);
-    put_stringz(sb, s->successor_layer->vt->name);
-    put_stringz(sb, authtype);
-
-    /* Compute the mic */
-    buf.value = sb->s;
-    buf.length = sb->len;
-    s->shgss->lib->get_mic(s->shgss->lib, s->shgss->ctx, &buf, &mic);
-    strbuf_free(sb);
-
-    /* Now we can build the real packet */
-    if (strcmp(authtype, "gssapi-with-mic") == 0) {
-        p = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_MIC);
-    } else {
-        p = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST);
-        put_stringz(p, s->username);
-        put_stringz(p, s->successor_layer->vt->name);
-        put_stringz(p, authtype);
-    }
-    put_string(p, mic.value, mic.length);
-
-    return p;
-}
-#endif
-
-static bool ssh2_userauth_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
-{
-    /* No specials provided by this layer. */
-    return false;
-}
-
-static void ssh2_userauth_special_cmd(PacketProtocolLayer *ppl,
-                                      SessionSpecialCode code, int arg)
-{
-    /* No specials provided by this layer. */
-}
-
-static bool ssh2_userauth_want_user_input(PacketProtocolLayer *ppl)
-{
-    struct ssh2_userauth_state *s =
-        container_of(ppl, struct ssh2_userauth_state, ppl);
-    return s->want_user_input;
-}
-
-static void ssh2_userauth_got_user_input(PacketProtocolLayer *ppl)
-{
-    struct ssh2_userauth_state *s =
-        container_of(ppl, struct ssh2_userauth_state, ppl);
-    if (s->want_user_input)
-        queue_idempotent_callback(&s->ppl.ic_process_queue);
-}
-
-static void ssh2_userauth_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
-{
-    struct ssh2_userauth_state *s =
-        container_of(ppl, struct ssh2_userauth_state, ppl);
-    ssh_ppl_reconfigure(s->successor_layer, conf);
-}
-
-static void ssh2_userauth_antispoof_msg(
-    struct ssh2_userauth_state *s, const char *msg)
-{
-    strbuf *sb = strbuf_new();
-    if (seat_set_trust_status(s->ppl.seat, true)) {
-        /*
-         * If the seat can directly indicate that this message is
-         * generated by the client, then we can just use the message
-         * unmodified as an unspoofable header.
-         */
-        put_datapl(sb, ptrlen_from_asciz(msg));
-    } else {
-        /*
-         * Otherwise, add enough padding around it that the server
-         * wouldn't be able to mimic it within our line-length
-         * constraint.
-         */
-        strbuf_catf(sb, "-- %s ", msg);
-        while (sb->len < 78)
-            put_byte(sb, '-');
-    }
-    put_datapl(sb, PTRLEN_LITERAL("\r\n"));
-    seat_stderr_pl(s->ppl.seat, ptrlen_from_strbuf(sb));
-    strbuf_free(sb);
-}

+ 0 - 2009
source/putty/sshaes.c

@@ -1,2009 +0,0 @@
-/*
- * sshaes.c - implementation of AES
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "ssh.h"
-#include "mpint_i.h"               /* we reuse the BignumInt system */
-
-/*
- * Start by deciding whether we can support hardware AES at all.
- */
-#define HW_AES_NONE 0
-#define HW_AES_NI 1
-#define HW_AES_NEON 2
-
-#ifdef _FORCE_AES_NI
-#   define HW_AES HW_AES_NI
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<wmmintrin.h>) &&       \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_AES HW_AES_NI
-#   endif
-#elif defined(__GNUC__)
-#    if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) && \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_AES HW_AES_NI
-#    endif
-#elif defined (_MSC_VER)
-#   if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729
-#      define HW_AES HW_AES_NI
-#   endif
-#endif
-
-#define HW_AES HW_AES_NI // WINSCP
-
-#ifdef _FORCE_AES_NEON
-#   define HW_AES HW_AES_NEON
-#elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-    /* Arm can potentially support both endiannesses, but this code
-     * hasn't been tested on anything but little. If anyone wants to
-     * run big-endian, they'll need to fix it first. */
-#elif defined __ARM_FEATURE_CRYPTO
-    /* If the Arm crypto extension is available already, we can
-     * support NEON AES without having to enable anything by hand */
-#   define HW_AES HW_AES_NEON
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<arm_neon.h>) &&       \
-    (defined(__aarch64__))
-        /* clang can enable the crypto extension in AArch64 using
-         * __attribute__((target)) */
-#       define HW_AES HW_AES_NEON
-#       define USE_CLANG_ATTR_TARGET_AARCH64
-#   endif
-#elif defined _MSC_VER
-#   if defined _M_ARM64
-#       define HW_AES HW_AES_NEON
-        /* 64-bit Visual Studio uses the header <arm64_neon.h> in place
-         * of the standard <arm_neon.h> */
-#       define USE_ARM64_NEON_H
-#   elif defined _M_ARM
-#       define HW_AES HW_AES_NEON
-        /* 32-bit Visual Studio uses the right header name, but requires
-         * this #define to enable a set of intrinsic definitions that
-         * do not omit one of the parameters for vaes[ed]q_u8 */
-#       define _ARM_USE_NEW_NEON_INTRINSICS
-#   endif
-#endif
-
-#if defined _FORCE_SOFTWARE_AES || !defined HW_AES
-#   undef HW_AES
-#   define HW_AES HW_AES_NONE
-#endif
-
-#if HW_AES == HW_AES_NI
-#define HW_NAME_SUFFIX " (AES-NI accelerated)"
-#elif HW_AES == HW_AES_NEON
-#define HW_NAME_SUFFIX " (NEON accelerated)"
-#else
-#define HW_NAME_SUFFIX " (!NONEXISTENT ACCELERATED VERSION!)"
-#endif
-
-/*
- * Vtable collection for AES. For each SSH-level cipher id (i.e.
- * combination of key length and cipher mode), we provide three
- * vtables: one for the pure software implementation, one using
- * hardware acceleration (if available), and a top-level one which is
- * never actually instantiated, and only contains a new() method whose
- * job is to decide which of the other two to return an actual
- * instance of.
- */
-
-static ssh_cipher *aes_select(const ssh_cipheralg *alg);
-static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg);
-static void aes_sw_free(ssh_cipher *);
-static void aes_sw_setiv_cbc(ssh_cipher *, const void *iv);
-static void aes_sw_setiv_sdctr(ssh_cipher *, const void *iv);
-static void aes_sw_setkey(ssh_cipher *, const void *key);
-/*WINSCP static*/ ssh_cipher *aes_hw_new(const ssh_cipheralg *alg);
-/*WINSCP static*/ void aes_hw_free(ssh_cipher *);
-/*WINSCP static*/ void aes_hw_setiv_cbc(ssh_cipher *, const void *iv);
-/*WINSCP static*/ void aes_hw_setiv_sdctr(ssh_cipher *, const void *iv);
-/*WINSCP static*/ void aes_hw_setkey(ssh_cipher *, const void *key);
-
-#ifndef WINSCP_VS
-struct aes_extra {
-    const ssh_cipheralg *sw, *hw;
-};
-
-#define VTABLES_INNER(cid, pid, bits, name, encsuffix,                  \
-                      decsuffix, setivsuffix, flagsval)                 \
-    /*WINSCP static*/ void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    /*WINSCP static*/ void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_sw = {                              \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_sw_new,                                              \
-        /*.free =*/ aes_sw_free,                                            \
-        /*.setiv =*/ aes_sw_##setivsuffix,                                  \
-        /*.setkey =*/ aes_sw_setkey,                                        \
-        /*.encrypt =*/ cid##_sw##encsuffix,                                 \
-        /*.decrypt =*/ cid##_sw##decsuffix,                                 \
-        NULL, NULL, /*WINSCP*/                                              \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name " (unaccelerated)",                           \
-        NULL, NULL, /*WINSCP*/                                              \
-    };                                                                  \
-                                                                        \
-    /*WINSCP static*/ void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len);  \
-    /*WINSCP static*/ void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len);  \
-    const ssh_cipheralg ssh_##cid##_hw = {                              \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_hw_new,                                              \
-        /*.free =*/ aes_hw_free,                                            \
-        /*.setiv =*/ aes_hw_##setivsuffix,                                  \
-        /*.setkey =*/ aes_hw_setkey,                                        \
-        /*.encrypt =*/ cid##_hw##encsuffix,                                 \
-        /*.decrypt =*/ cid##_hw##decsuffix,                                 \
-        NULL, NULL, /*WINSCP*/                                              \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name HW_NAME_SUFFIX,                               \
-        NULL, NULL, /*WINSCP*/                                              \
-    };                                                                  \
-                                                                        \
-    static const struct aes_extra extra_##cid = {                       \
-        &ssh_##cid##_sw, &ssh_##cid##_hw };                             \
-                                                                        \
-    const ssh_cipheralg ssh_##cid = {                                   \
-        /*WINSCP*/                                                          \
-        /*.new =*/ aes_select,                                              \
-        NULL, NULL, NULL, NULL, NULL, NULL, NULL,                           \
-        /*.ssh2_id =*/ pid,                                                 \
-        /*.blksize =*/ 16,                                                  \
-        /*.real_keybits =*/ bits,                                           \
-        /*.padded_keybytes =*/ bits/8,                                      \
-        /*.flags =*/ flagsval,                                              \
-        /*.text_name =*/ name " (dummy selector vtable)",                   \
-        NULL,                                                               \
-        /*.extra =*/ &extra_##cid                                           \
-    };                                                                  \
-
-#define VTABLES(keylen)                                                 \
-    VTABLES_INNER(aes ## keylen ## _cbc, "aes" #keylen "-cbc",          \
-                  keylen, "AES-" #keylen " CBC", _encrypt, _decrypt,    \
-                  setiv_cbc, SSH_CIPHER_IS_CBC)                         \
-    VTABLES_INNER(aes ## keylen ## _sdctr, "aes" #keylen "-ctr",        \
-                  keylen, "AES-" #keylen " SDCTR",,, setiv_sdctr, 0)
-
-VTABLES(128)
-VTABLES(192)
-VTABLES(256)
-
-static const ssh_cipheralg ssh_rijndael_lysator = {
-    /* Same as aes256_cbc, but with a different protocol ID */
-    // WINSCP
-    /*.new =*/ aes_select,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "[email protected]",
-    /*.blksize =*/ 16,
-    /*.real_keybits =*/ 256,
-    /*.padded_keybytes =*/ 256/8,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "AES-256 CBC (dummy selector vtable)",
-    NULL, // WINSCP
-    /*.extra =*/ &extra_aes256_cbc,
-};
-
-static const ssh_cipheralg *const aes_list[] = {
-    &ssh_aes256_sdctr,
-    &ssh_aes256_cbc,
-    &ssh_rijndael_lysator,
-    &ssh_aes192_sdctr,
-    &ssh_aes192_cbc,
-    &ssh_aes128_sdctr,
-    &ssh_aes128_cbc,
-};
-
-const ssh2_ciphers ssh2_aes = { lenof(aes_list), aes_list };
-#endif
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-/*WINSCP static*/ bool aes_hw_available(void);
-
-/*
- * The top-level selection function, caching the results of
- * aes_hw_available() so it only has to run once.
- */
-static bool aes_hw_available_cached(void)
-{
-    static bool initialised = false;
-    static bool hw_available;
-    if (!initialised) {
-        hw_available = aes_hw_available();
-        initialised = true;
-    }
-    return hw_available;
-}
-
-#ifndef WINSCP_VS
-static ssh_cipher *aes_select(const ssh_cipheralg *alg)
-{
-    const struct aes_extra *extra = (const struct aes_extra *)alg->extra;
-    const ssh_cipheralg *real_alg =
-        aes_hw_available_cached() ? extra->hw : extra->sw;
-
-    return ssh_cipher_new(real_alg);
-}
-#endif
-
-/* ----------------------------------------------------------------------
- * Definitions likely to be helpful to multiple implementations.
- */
-
-#define REP2(x) x x
-#define REP4(x) REP2(REP2(x))
-#define REP8(x) REP2(REP4(x))
-#define REP9(x) REP8(x) x
-#define REP11(x) REP8(x) REP2(x) x
-#define REP13(x) REP8(x) REP4(x) x
-
-static const uint8_t key_setup_round_constants[] = {
-    /* The first few powers of X in GF(2^8), used during key setup.
-     * This can safely be a lookup table without side channel risks,
-     * because key setup iterates through it once in a standard way
-     * regardless of the key. */
-    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
-};
-
-#define MAXROUNDKEYS 15
-
-/* ----------------------------------------------------------------------
- * Software implementation of AES.
- *
- * This implementation uses a bit-sliced representation. Instead of
- * the obvious approach of storing the cipher state so that each byte
- * (or field element, or entry in the cipher matrix) occupies 8
- * contiguous bits in a machine integer somewhere, we organise the
- * cipher state as an array of 8 integers, in such a way that each
- * logical byte of the cipher state occupies one bit in each integer,
- * all at the same position. This allows us to do parallel logic on
- * all bytes of the state by doing bitwise operations between the 8
- * integers; in particular, the S-box (SubBytes) lookup is done this
- * way, which takes about 110 operations - but for those 110 bitwise
- * ops you get 64 S-box lookups, not just one.
- */
-
-#define SLICE_PARALLELISM (BIGNUM_INT_BYTES / 2)
-
-#ifdef WINSCP_VS
-
-#ifdef BITSLICED_DEBUG
-/* Dump function that undoes the bitslicing transform, so you can see
- * the logical data represented by a set of slice words. */
-static inline void dumpslices_uint16_t(
-    const char *prefix, const uint16_t slices[8])
-{
-    printf("%-30s", prefix);
-    for (unsigned byte = 0; byte < 16; byte++) {
-        unsigned byteval = 0;
-        for (unsigned bit = 0; bit < 8; bit++)
-            byteval |= (1 & (slices[bit] >> byte)) << bit;
-        printf("%02x", byteval);
-    }
-    printf("\n");
-}
-
-static inline void dumpslices_BignumInt(
-    const char *prefix, const BignumInt slices[8])
-{
-    printf("%-30s", prefix);
-    for (unsigned iter = 0; iter < SLICE_PARALLELISM; iter++) {
-        for (unsigned byte = 0; byte < 16; byte++) {
-            unsigned byteval = 0;
-            for (unsigned bit = 0; bit < 8; bit++)
-                byteval |= (1 & (slices[bit] >> (iter*16+byte))) << bit;
-            printf("%02x", byteval);
-        }
-        if (iter+1 < SLICE_PARALLELISM)
-            printf(" ");
-    }
-    printf("\n");
-}
-#else
-#define dumpslices_uintN_t(prefix, slices) ((void)0)
-#define dumpslices_BignumInt(prefix, slices) ((void)0)
-#endif
-
-/* -----
- * Bit-slicing transformation: convert between an array of 16 uint8_t
- * and an array of 8 uint16_t, so as to interchange the bit index
- * within each element and the element index within the array. (That
- * is, bit j of input[i] == bit i of output[j].
- */
-
-#define SWAPWORDS(shift) do                                     \
-    {                                                           \
-        uint64_t mask = ~(uint64_t)0 / ((1ULL << shift) + 1);   \
-        uint64_t diff = ((i0 >> shift) ^ i1) & mask;            \
-        i0 ^= diff << shift;                                    \
-        i1 ^= diff;                                             \
-    } while (0)
-
-#define SWAPINWORD(i, bigshift, smallshift) do                  \
-    {                                                           \
-        uint64_t mask = ~(uint64_t)0;                           \
-        mask /= ((1ULL << bigshift) + 1);                       \
-        mask /= ((1ULL << smallshift) + 1);                     \
-        mask <<= smallshift;                                    \
-        unsigned shift = bigshift - smallshift;                 \
-        uint64_t diff = ((i >> shift) ^ i) & mask;              \
-        i ^= diff ^ (diff << shift);                            \
-    } while (0)
-
-#define TO_BITSLICES(slices, bytes, uintN_t, assign_op, shift) do       \
-    {                                                                   \
-        uint64_t i0 = GET_64BIT_LSB_FIRST(bytes);                       \
-        uint64_t i1 = GET_64BIT_LSB_FIRST(bytes + 8);                   \
-        SWAPINWORD(i0, 8, 1);                                           \
-        SWAPINWORD(i1, 8, 1);                                           \
-        SWAPINWORD(i0, 16, 2);                                          \
-        SWAPINWORD(i1, 16, 2);                                          \
-        SWAPINWORD(i0, 32, 4);                                          \
-        SWAPINWORD(i1, 32, 4);                                          \
-        SWAPWORDS(8);                                                   \
-        slices[0] assign_op (uintN_t)((i0 >>  0) & 0xFFFF) << (shift);  \
-        slices[2] assign_op (uintN_t)((i0 >> 16) & 0xFFFF) << (shift);  \
-        slices[4] assign_op (uintN_t)((i0 >> 32) & 0xFFFF) << (shift);  \
-        slices[6] assign_op (uintN_t)((i0 >> 48) & 0xFFFF) << (shift);  \
-        slices[1] assign_op (uintN_t)((i1 >>  0) & 0xFFFF) << (shift);  \
-        slices[3] assign_op (uintN_t)((i1 >> 16) & 0xFFFF) << (shift);  \
-        slices[5] assign_op (uintN_t)((i1 >> 32) & 0xFFFF) << (shift);  \
-        slices[7] assign_op (uintN_t)((i1 >> 48) & 0xFFFF) << (shift);  \
-    } while (0)
-
-#define FROM_BITSLICES(bytes, slices, shift) do                 \
-    {                                                           \
-        uint64_t i1 = ((slices[7] >> (shift)) & 0xFFFF);        \
-        i1 = (i1 << 16) | ((slices[5] >> (shift)) & 0xFFFF);    \
-        i1 = (i1 << 16) | ((slices[3] >> (shift)) & 0xFFFF);    \
-        i1 = (i1 << 16) | ((slices[1] >> (shift)) & 0xFFFF);    \
-        uint64_t i0 = ((slices[6] >> (shift)) & 0xFFFF);        \
-        i0 = (i0 << 16) | ((slices[4] >> (shift)) & 0xFFFF);    \
-        i0 = (i0 << 16) | ((slices[2] >> (shift)) & 0xFFFF);    \
-        i0 = (i0 << 16) | ((slices[0] >> (shift)) & 0xFFFF);    \
-        SWAPWORDS(8);                                           \
-        SWAPINWORD(i0, 32, 4);                                  \
-        SWAPINWORD(i1, 32, 4);                                  \
-        SWAPINWORD(i0, 16, 2);                                  \
-        SWAPINWORD(i1, 16, 2);                                  \
-        SWAPINWORD(i0, 8, 1);                                   \
-        SWAPINWORD(i1, 8, 1);                                   \
-        PUT_64BIT_LSB_FIRST(bytes, i0);                         \
-        PUT_64BIT_LSB_FIRST((bytes) + 8, i1);                   \
-    } while (0)
-
-/* -----
- * Some macros that will be useful repeatedly.
- */
-
-/* Iterate a unary transformation over all 8 slices. */
-#define ITERATE(MACRO, output, input, uintN_t) do       \
-    {                                                   \
-        MACRO(output[0], input[0], uintN_t);            \
-        MACRO(output[1], input[1], uintN_t);            \
-        MACRO(output[2], input[2], uintN_t);            \
-        MACRO(output[3], input[3], uintN_t);            \
-        MACRO(output[4], input[4], uintN_t);            \
-        MACRO(output[5], input[5], uintN_t);            \
-        MACRO(output[6], input[6], uintN_t);            \
-        MACRO(output[7], input[7], uintN_t);            \
-    } while (0)
-
-/* Simply add (i.e. XOR) two whole sets of slices together. */
-#define BITSLICED_ADD(output, lhs, rhs) do      \
-    {                                           \
-        output[0] = lhs[0] ^ rhs[0];            \
-        output[1] = lhs[1] ^ rhs[1];            \
-        output[2] = lhs[2] ^ rhs[2];            \
-        output[3] = lhs[3] ^ rhs[3];            \
-        output[4] = lhs[4] ^ rhs[4];            \
-        output[5] = lhs[5] ^ rhs[5];            \
-        output[6] = lhs[6] ^ rhs[6];            \
-        output[7] = lhs[7] ^ rhs[7];            \
-    } while (0)
-
-/* -----
- * The AES S-box, in pure bitwise logic so that it can be run in
- * parallel on whole words full of bit-sliced field elements.
- *
- * Source: 'A new combinational logic minimization technique with
- * applications to cryptology', https://eprint.iacr.org/2009/191
- *
- * As a minor speed optimisation, I use a modified version of the
- * S-box which omits the additive constant 0x63, i.e. this S-box
- * consists of only the field inversion and linear map components.
- * Instead, the addition of the constant is deferred until after the
- * subsequent ShiftRows and MixColumns stages, so that it happens at
- * the same time as adding the next round key - and then we just make
- * it _part_ of the round key, so it doesn't cost any extra
- * instructions to add.
- *
- * (Obviously adding a constant to each byte commutes with ShiftRows,
- * which only permutes the bytes. It also commutes with MixColumns:
- * that's not quite so obvious, but since the effect of MixColumns is
- * to multiply a constant polynomial M into each column, it is obvious
- * that adding some polynomial K and then multiplying by M is
- * equivalent to multiplying by M and then adding the product KM. And
- * in fact, since the coefficients of M happen to sum to 1, it turns
- * out that KM = K, so we don't even have to change the constant when
- * we move it to the far side of MixColumns.)
- *
- * Of course, one knock-on effect of this is that the use of the S-box
- * *during* key setup has to be corrected by manually adding on the
- * constant afterwards!
- */
-
-/* Initial linear transformation for the forward S-box, from Fig 2 of
- * the paper. */
-#define SBOX_FORWARD_TOP_TRANSFORM(input, uintN_t)      \
-        uintN_t y14 = input[4] ^ input[2];              \
-        uintN_t y13 = input[7] ^ input[1];              \
-        uintN_t y9 = input[7] ^ input[4];               \
-        uintN_t y8 = input[7] ^ input[2];               \
-        uintN_t t0 = input[6] ^ input[5];               \
-        uintN_t y1 = t0 ^ input[0];                     \
-        uintN_t y4 = y1 ^ input[4];                     \
-        uintN_t y12 = y13 ^ y14;                        \
-        uintN_t y2 = y1 ^ input[7];                     \
-        uintN_t y5 = y1 ^ input[1];                     \
-        uintN_t y3 = y5 ^ y8;                           \
-        uintN_t t1 = input[3] ^ y12;                    \
-        uintN_t y15 = t1 ^ input[2];                    \
-        uintN_t y20 = t1 ^ input[6];                    \
-        uintN_t y6 = y15 ^ input[0];                    \
-        uintN_t y10 = y15 ^ t0;                         \
-        uintN_t y11 = y20 ^ y9;                         \
-        uintN_t y7 = input[0] ^ y11;                    \
-        uintN_t y17 = y10 ^ y11;                        \
-        uintN_t y19 = y10 ^ y8;                         \
-        uintN_t y16 = t0 ^ y11;                         \
-        uintN_t y21 = y13 ^ y16;                        \
-        uintN_t y18 = input[7] ^ y16;                   \
-        /* Make a copy of input[0] under a new name, because the core
-         * will refer to it, and in the inverse version of the S-box
-         * the corresponding value will be one of the calculated ones
-         * and not in input[0] itself. */               \
-        uintN_t i0 = input[0];                          \
-        /* end */
-
-/* Core nonlinear component, from Fig 3 of the paper. */
-#define SBOX_CORE(uintN_t)                              \
-        uintN_t t2 = y12 & y15;                         \
-        uintN_t t3 = y3 & y6;                           \
-        uintN_t t4 = t3 ^ t2;                           \
-        uintN_t t5 = y4 & i0;                           \
-        uintN_t t6 = t5 ^ t2;                           \
-        uintN_t t7 = y13 & y16;                         \
-        uintN_t t8 = y5 & y1;                           \
-        uintN_t t9 = t8 ^ t7;                           \
-        uintN_t t10 = y2 & y7;                          \
-        uintN_t t11 = t10 ^ t7;                         \
-        uintN_t t12 = y9 & y11;                         \
-        uintN_t t13 = y14 & y17;                        \
-        uintN_t t14 = t13 ^ t12;                        \
-        uintN_t t15 = y8 & y10;                         \
-        uintN_t t16 = t15 ^ t12;                        \
-        uintN_t t17 = t4 ^ t14;                         \
-        uintN_t t18 = t6 ^ t16;                         \
-        uintN_t t19 = t9 ^ t14;                         \
-        uintN_t t20 = t11 ^ t16;                        \
-        uintN_t t21 = t17 ^ y20;                        \
-        uintN_t t22 = t18 ^ y19;                        \
-        uintN_t t23 = t19 ^ y21;                        \
-        uintN_t t24 = t20 ^ y18;                        \
-        uintN_t t25 = t21 ^ t22;                        \
-        uintN_t t26 = t21 & t23;                        \
-        uintN_t t27 = t24 ^ t26;                        \
-        uintN_t t28 = t25 & t27;                        \
-        uintN_t t29 = t28 ^ t22;                        \
-        uintN_t t30 = t23 ^ t24;                        \
-        uintN_t t31 = t22 ^ t26;                        \
-        uintN_t t32 = t31 & t30;                        \
-        uintN_t t33 = t32 ^ t24;                        \
-        uintN_t t34 = t23 ^ t33;                        \
-        uintN_t t35 = t27 ^ t33;                        \
-        uintN_t t36 = t24 & t35;                        \
-        uintN_t t37 = t36 ^ t34;                        \
-        uintN_t t38 = t27 ^ t36;                        \
-        uintN_t t39 = t29 & t38;                        \
-        uintN_t t40 = t25 ^ t39;                        \
-        uintN_t t41 = t40 ^ t37;                        \
-        uintN_t t42 = t29 ^ t33;                        \
-        uintN_t t43 = t29 ^ t40;                        \
-        uintN_t t44 = t33 ^ t37;                        \
-        uintN_t t45 = t42 ^ t41;                        \
-        uintN_t z0 = t44 & y15;                         \
-        uintN_t z1 = t37 & y6;                          \
-        uintN_t z2 = t33 & i0;                          \
-        uintN_t z3 = t43 & y16;                         \
-        uintN_t z4 = t40 & y1;                          \
-        uintN_t z5 = t29 & y7;                          \
-        uintN_t z6 = t42 & y11;                         \
-        uintN_t z7 = t45 & y17;                         \
-        uintN_t z8 = t41 & y10;                         \
-        uintN_t z9 = t44 & y12;                         \
-        uintN_t z10 = t37 & y3;                         \
-        uintN_t z11 = t33 & y4;                         \
-        uintN_t z12 = t43 & y13;                        \
-        uintN_t z13 = t40 & y5;                         \
-        uintN_t z14 = t29 & y2;                         \
-        uintN_t z15 = t42 & y9;                         \
-        uintN_t z16 = t45 & y14;                        \
-        uintN_t z17 = t41 & y8;                         \
-        /* end */
-
-/* Final linear transformation for the forward S-box, from Fig 4 of
- * the paper. */
-#define SBOX_FORWARD_BOTTOM_TRANSFORM(output, uintN_t)   \
-        uintN_t t46 = z15 ^ z16;                        \
-        uintN_t t47 = z10 ^ z11;                        \
-        uintN_t t48 = z5 ^ z13;                         \
-        uintN_t t49 = z9 ^ z10;                         \
-        uintN_t t50 = z2 ^ z12;                         \
-        uintN_t t51 = z2 ^ z5;                          \
-        uintN_t t52 = z7 ^ z8;                          \
-        uintN_t t53 = z0 ^ z3;                          \
-        uintN_t t54 = z6 ^ z7;                          \
-        uintN_t t55 = z16 ^ z17;                        \
-        uintN_t t56 = z12 ^ t48;                        \
-        uintN_t t57 = t50 ^ t53;                        \
-        uintN_t t58 = z4 ^ t46;                         \
-        uintN_t t59 = z3 ^ t54;                         \
-        uintN_t t60 = t46 ^ t57;                        \
-        uintN_t t61 = z14 ^ t57;                        \
-        uintN_t t62 = t52 ^ t58;                        \
-        uintN_t t63 = t49 ^ t58;                        \
-        uintN_t t64 = z4 ^ t59;                         \
-        uintN_t t65 = t61 ^ t62;                        \
-        uintN_t t66 = z1 ^ t63;                         \
-        output[7] = t59 ^ t63;                          \
-        output[1] = t56 ^ t62;                          \
-        output[0] = t48 ^ t60;                          \
-        uintN_t t67 = t64 ^ t65;                        \
-        output[4] = t53 ^ t66;                          \
-        output[3] = t51 ^ t66;                          \
-        output[2] = t47 ^ t65;                          \
-        output[6] = t64 ^ output[4];                    \
-        output[5] = t55 ^ t67;                          \
-        /* end */
-
-#define BITSLICED_SUBBYTES(output, input, uintN_t) do { \
-        SBOX_FORWARD_TOP_TRANSFORM(input, uintN_t);      \
-        SBOX_CORE(uintN_t);                             \
-        SBOX_FORWARD_BOTTOM_TRANSFORM(output, uintN_t);  \
-    } while (0)
-
-/*
- * Initial and final linear transformations for the backward S-box. I
- * generated these myself, by implementing the linear-transform
- * optimisation algorithm in the paper, and applying it to the
- * matrices calculated by _their_ top and bottom transformations, pre-
- * and post-multiplied as appropriate by the linear map in the inverse
- * S_box.
- */
-#define SBOX_BACKWARD_TOP_TRANSFORM(input, uintN_t)     \
-    uintN_t y5 = input[4] ^ input[6];                   \
-    uintN_t y19 = input[3] ^ input[0];                  \
-    uintN_t itmp8 = y5 ^ input[0];                      \
-    uintN_t y4 = itmp8 ^ input[1];                      \
-    uintN_t y9 = input[4] ^ input[3];                   \
-    uintN_t y2 = y9 ^ y4;                               \
-    uintN_t itmp9 = y2 ^ input[7];                      \
-    uintN_t y1 = y9 ^ input[0];                         \
-    uintN_t y6 = y5 ^ input[7];                         \
-    uintN_t y18 = y9 ^ input[5];                        \
-    uintN_t y7 = y18 ^ y2;                              \
-    uintN_t y16 = y7 ^ y1;                              \
-    uintN_t y21 = y7 ^ input[1];                        \
-    uintN_t y3 = input[4] ^ input[7];                   \
-    uintN_t y13 = y16 ^ y21;                            \
-    uintN_t y8 = input[4] ^ y6;                         \
-    uintN_t y10 = y8 ^ y19;                             \
-    uintN_t y14 = y8 ^ y9;                              \
-    uintN_t y20 = itmp9 ^ input[2];                     \
-    uintN_t y11 = y9 ^ y20;                             \
-    uintN_t i0 = y11 ^ y7;                              \
-    uintN_t y15 = i0 ^ y6;                              \
-    uintN_t y17 = y16 ^ y15;                            \
-    uintN_t y12 = itmp9 ^ input[3];                     \
-    /* end */
-#define SBOX_BACKWARD_BOTTOM_TRANSFORM(output, uintN_t) \
-    uintN_t otmp18 = z15 ^ z6;                          \
-    uintN_t otmp19 = z13 ^ otmp18;                      \
-    uintN_t otmp20 = z12 ^ otmp19;                      \
-    uintN_t otmp21 = z16 ^ otmp20;                      \
-    uintN_t otmp22 = z8 ^ otmp21;                       \
-    uintN_t otmp23 = z0 ^ otmp22;                       \
-    uintN_t otmp24 = otmp22 ^ z3;                       \
-    uintN_t otmp25 = otmp24 ^ z4;                       \
-    uintN_t otmp26 = otmp25 ^ z2;                       \
-    uintN_t otmp27 = z1 ^ otmp26;                       \
-    uintN_t otmp28 = z14 ^ otmp27;                      \
-    uintN_t otmp29 = otmp28 ^ z10;                      \
-    output[4] = z2 ^ otmp23;                            \
-    output[7] = z5 ^ otmp24;                            \
-    uintN_t otmp30 = z11 ^ otmp29;                      \
-    output[5] = z13 ^ otmp30;                           \
-    uintN_t otmp31 = otmp25 ^ z8;                       \
-    output[1] = z7 ^ otmp31;                            \
-    uintN_t otmp32 = z11 ^ z9;                          \
-    uintN_t otmp33 = z17 ^ otmp32;                      \
-    uintN_t otmp34 = otmp30 ^ otmp33;                   \
-    output[0] = z15 ^ otmp33;                           \
-    uintN_t otmp35 = z12 ^ otmp34;                      \
-    output[6] = otmp35 ^ z16;                           \
-    uintN_t otmp36 = z1 ^ otmp23;                       \
-    uintN_t otmp37 = z5 ^ otmp36;                       \
-    output[2] = z4 ^ otmp37;                            \
-    uintN_t otmp38 = z11 ^ output[1];                   \
-    uintN_t otmp39 = z2 ^ otmp38;                       \
-    uintN_t otmp40 = z17 ^ otmp39;                      \
-    uintN_t otmp41 = z0 ^ otmp40;                       \
-    uintN_t otmp42 = z5 ^ otmp41;                       \
-    uintN_t otmp43 = otmp42 ^ z10;                      \
-    uintN_t otmp44 = otmp43 ^ z3;                       \
-    output[3] = otmp44 ^ z16;                           \
-    /* end */
-
-#define BITSLICED_INVSUBBYTES(output, input, uintN_t) do {      \
-        SBOX_BACKWARD_TOP_TRANSFORM(input, uintN_t);             \
-        SBOX_CORE(uintN_t);                                     \
-        SBOX_BACKWARD_BOTTOM_TRANSFORM(output, uintN_t);         \
-    } while (0)
-
-
-/* -----
- * The ShiftRows transformation. This operates independently on each
- * bit slice.
- */
-
-#define SINGLE_BITSLICE_SHIFTROWS(output, input, uintN_t) do            \
-    {                                                                   \
-        uintN_t mask, mask2, mask3, diff, x = (input);                  \
-        /* Rotate rows 2 and 3 by 16 bits */                            \
-        mask = 0x00CC * (((uintN_t)~(uintN_t)0) / 0xFFFF);              \
-        diff = ((x >> 8) ^ x) & mask;                                   \
-        x ^= diff ^ (diff << 8);                                        \
-        /* Rotate rows 1 and 3 by 8 bits */                             \
-        mask  = 0x0AAA * (((uintN_t)~(uintN_t)0) / 0xFFFF);             \
-        mask2 = 0xA000 * (((uintN_t)~(uintN_t)0) / 0xFFFF);             \
-        mask3 = 0x5555 * (((uintN_t)~(uintN_t)0) / 0xFFFF);             \
-        x = ((x >> 4) & mask) | ((x << 12) & mask2) | (x & mask3);      \
-        /* Write output */                                              \
-        (output) = x;                                                   \
-    } while (0)
-
-#define SINGLE_BITSLICE_INVSHIFTROWS(output, input, uintN_t) do         \
-    {                                                                   \
-        uintN_t mask, mask2, mask3, diff, x = (input);                  \
-        /* Rotate rows 2 and 3 by 16 bits */                            \
-        mask = 0x00CC * (((uintN_t)~(uintN_t)0) / 0xFFFF);              \
-        diff = ((x >> 8) ^ x) & mask;                                   \
-        x ^= diff ^ (diff << 8);                                        \
-        /* Rotate rows 1 and 3 by 8 bits, the opposite way to ShiftRows */ \
-        mask  = 0x000A * (((uintN_t)~(uintN_t)0) / 0xFFFF);             \
-        mask2 = 0xAAA0 * (((uintN_t)~(uintN_t)0) / 0xFFFF);             \
-        mask3 = 0x5555 * (((uintN_t)~(uintN_t)0) / 0xFFFF);             \
-        x = ((x >> 12) & mask) | ((x << 4) & mask2) | (x & mask3);      \
-        /* Write output */                                              \
-        (output) = x;                                                   \
-    } while (0)
-
-#define BITSLICED_SHIFTROWS(output, input, uintN_t) do                  \
-    {                                                                   \
-        ITERATE(SINGLE_BITSLICE_SHIFTROWS, output, input, uintN_t);     \
-    } while (0)
-
-#define BITSLICED_INVSHIFTROWS(output, input, uintN_t) do               \
-    {                                                                   \
-        ITERATE(SINGLE_BITSLICE_INVSHIFTROWS, output, input, uintN_t);  \
-    } while (0)
-
-/* -----
- * The MixColumns transformation. This has to operate on all eight bit
- * slices at once, and also passes data back and forth between the
- * bits in an adjacent group of 4 within each slice.
- *
- * Notation: let F = GF(2)[X]/<X^8+X^4+X^3+X+1> be the finite field
- * used in AES, and let R = F[Y]/<Y^4+1> be the ring whose elements
- * represent the possible contents of a column of the matrix. I use X
- * and Y below in those senses, i.e. X is the value in F that
- * represents the byte 0x02, and Y is the value in R that cycles the
- * four bytes around by one if you multiply by it.
- */
-
-/* Multiply every column by Y^3, i.e. cycle it round one place to the
- * right. Operates on one bit slice at a time; you have to wrap it in
- * ITERATE to affect all the data at once. */
-#define BITSLICED_MUL_BY_Y3(output, input, uintN_t) do          \
-    {                                                           \
-        uintN_t mask, mask2, x;                                 \
-        mask  = 0x8 * (((uintN_t)~(uintN_t)0) / 0xF);           \
-        mask2 = 0x7 * (((uintN_t)~(uintN_t)0) / 0xF);           \
-        x = input;                                              \
-        output = ((x << 3) & mask) ^ ((x >> 1) & mask2);        \
-    } while (0)
-
-/* Multiply every column by Y^2. */
-#define BITSLICED_MUL_BY_Y2(output, input, uintN_t) do          \
-    {                                                           \
-        uintN_t mask, mask2, x;                                 \
-        mask  = 0xC * (((uintN_t)~(uintN_t)0) / 0xF);           \
-        mask2 = 0x3 * (((uintN_t)~(uintN_t)0) / 0xF);           \
-        x = input;                                              \
-        output = ((x << 2) & mask) ^ ((x >> 2) & mask2);        \
-    } while (0)
-
-#define BITSLICED_MUL_BY_1_Y3(output, input, uintN_t) do        \
-    {                                                           \
-        uintN_t tmp = input;                                    \
-        BITSLICED_MUL_BY_Y3(tmp, input, uintN_t);               \
-        output = input ^ tmp;                                   \
-    } while (0)
-
-/* Multiply every column by 1+Y^2. */
-#define BITSLICED_MUL_BY_1_Y2(output, input, uintN_t) do        \
-    {                                                           \
-        uintN_t tmp = input;                                    \
-        BITSLICED_MUL_BY_Y2(tmp, input, uintN_t);               \
-        output = input ^ tmp;                                   \
-    } while (0)
-
-/* Multiply every field element by X. This has to feed data between
- * slices, so it does the whole job in one go without needing ITERATE. */
-#define BITSLICED_MUL_BY_X(output, input, uintN_t) do   \
-    {                                                   \
-        uintN_t bit7 = input[7];                        \
-        output[7] = input[6];                           \
-        output[6] = input[5];                           \
-        output[5] = input[4];                           \
-        output[4] = input[3] ^ bit7;                    \
-        output[3] = input[2] ^ bit7;                    \
-        output[2] = input[1];                           \
-        output[1] = input[0] ^ bit7;                    \
-        output[0] =            bit7;                    \
-    } while (0)
-
-/*
- * The MixColumns constant is
- *   M = X + Y + Y^2 + (X+1)Y^3
- * which we construct by rearranging it into
- *   M = 1 + (1+Y^3) [ X + (1+Y^2) ]
- */
-#define BITSLICED_MIXCOLUMNS(output, input, uintN_t) do         \
-    {                                                           \
-        uintN_t a[8], aX[8], b[8];                              \
-        /* a = input * (1+Y^3) */                               \
-        ITERATE(BITSLICED_MUL_BY_1_Y3, a, input, uintN_t);      \
-        /* aX = a * X */                                        \
-        BITSLICED_MUL_BY_X(aX, a, uintN_t);                     \
-        /* b = a * (1+Y^2) = input * (1+Y+Y^2+Y^3) */           \
-        ITERATE(BITSLICED_MUL_BY_1_Y2, b, a, uintN_t);          \
-        /* output = input + aX + b (reusing a as a temp */      \
-        BITSLICED_ADD(a, aX, b);                                \
-        BITSLICED_ADD(output, input, a);                        \
-    } while (0)
-
-/*
- * The InvMixColumns constant, written out longhand, is
- *   I = (X^3+X^2+X) + (X^3+1)Y + (X^3+X^2+1)Y^2 + (X^3+X+1)Y^3
- * We represent this as
- *   I = (X^3+X^2+X+1)(Y^3+Y^2+Y+1) + 1 + X(Y+Y^2) + X^2(Y+Y^3)
- */
-#define BITSLICED_INVMIXCOLUMNS(output, input, uintN_t) do      \
-    {                                                           \
-        /* We need input * X^i for i=1,...,3 */                 \
-        uintN_t X[8], X2[8], X3[8];                             \
-        BITSLICED_MUL_BY_X(X, input, uintN_t);                  \
-        BITSLICED_MUL_BY_X(X2, X, uintN_t);                     \
-        BITSLICED_MUL_BY_X(X3, X2, uintN_t);                    \
-        /* Sum them all and multiply by 1+Y+Y^2+Y^3. */         \
-        uintN_t S[8];                                           \
-        BITSLICED_ADD(S, input, X);                             \
-        BITSLICED_ADD(S, S, X2);                                \
-        BITSLICED_ADD(S, S, X3);                                \
-        ITERATE(BITSLICED_MUL_BY_1_Y3, S, S, uintN_t);          \
-        ITERATE(BITSLICED_MUL_BY_1_Y2, S, S, uintN_t);          \
-        /* Compute the X(Y+Y^2) term. */                        \
-        uintN_t A[8];                                           \
-        ITERATE(BITSLICED_MUL_BY_1_Y3, A, X, uintN_t);          \
-        ITERATE(BITSLICED_MUL_BY_Y2, A, A, uintN_t);            \
-        /* Compute the X^2(Y+Y^3) term. */                      \
-        uintN_t B[8];                                           \
-        ITERATE(BITSLICED_MUL_BY_1_Y2, B, X2, uintN_t);         \
-        ITERATE(BITSLICED_MUL_BY_Y3, B, B, uintN_t);            \
-        /* And add all the pieces together. */                  \
-        BITSLICED_ADD(S, S, input);                             \
-        BITSLICED_ADD(S, S, A);                                 \
-        BITSLICED_ADD(output, S, B);                            \
-    } while (0)
-
-/* -----
- * Put it all together into a cipher round.
- */
-
-/* Dummy macro to get rid of the MixColumns in the final round. */
-#define NO_MIXCOLUMNS(out, in, uintN_t) do {} while (0)
-
-#define ENCRYPT_ROUND_FN(suffix, uintN_t, mixcol_macro)                 \
-    static void aes_sliced_round_e_##suffix(                            \
-        uintN_t output[8], const uintN_t input[8], const uintN_t roundkey[8]) \
-    {                                                                   \
-        BITSLICED_SUBBYTES(output, input, uintN_t);                     \
-        BITSLICED_SHIFTROWS(output, output, uintN_t);                   \
-        mixcol_macro(output, output, uintN_t);                          \
-        BITSLICED_ADD(output, output, roundkey);                        \
-    }
-
-ENCRYPT_ROUND_FN(serial, uint16_t, BITSLICED_MIXCOLUMNS)
-ENCRYPT_ROUND_FN(serial_last, uint16_t, NO_MIXCOLUMNS)
-ENCRYPT_ROUND_FN(parallel, BignumInt, BITSLICED_MIXCOLUMNS)
-ENCRYPT_ROUND_FN(parallel_last, BignumInt, NO_MIXCOLUMNS)
-
-#define DECRYPT_ROUND_FN(suffix, uintN_t, mixcol_macro)                 \
-    static void aes_sliced_round_d_##suffix(                            \
-        uintN_t output[8], const uintN_t input[8], const uintN_t roundkey[8]) \
-    {                                                                   \
-        BITSLICED_ADD(output, input, roundkey);                         \
-        mixcol_macro(output, output, uintN_t);                          \
-        BITSLICED_INVSUBBYTES(output, output, uintN_t);                 \
-        BITSLICED_INVSHIFTROWS(output, output, uintN_t);                \
-    }
-
-#if 0 /* no cipher mode we support requires serial decryption */
-DECRYPT_ROUND_FN(serial, uint16_t, BITSLICED_INVMIXCOLUMNS)
-DECRYPT_ROUND_FN(serial_first, uint16_t, NO_MIXCOLUMNS)
-#endif
-DECRYPT_ROUND_FN(parallel, BignumInt, BITSLICED_INVMIXCOLUMNS)
-DECRYPT_ROUND_FN(parallel_first, BignumInt, NO_MIXCOLUMNS)
-
-#endif // WINSCP_VS
-
-/* -----
- * Key setup function.
- */
-
-typedef struct aes_sliced_key aes_sliced_key;
-struct aes_sliced_key {
-    BignumInt roundkeys_parallel[MAXROUNDKEYS * 8];
-    uint16_t roundkeys_serial[MAXROUNDKEYS * 8];
-    unsigned rounds;
-};
-
-/*WINSCP static*/ void aes_sliced_key_setup(
-    aes_sliced_key *sk, const void *vkey, size_t keybits)
-#ifndef WINSCP_VS
-;
-#else
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    size_t key_words = keybits / 32;
-    sk->rounds = key_words + 6;
-    size_t sched_words = (sk->rounds + 1) * 4;
-
-    unsigned rconpos = 0;
-
-    uint16_t *outslices = sk->roundkeys_serial;
-    unsigned outshift = 0;
-
-    memset(sk->roundkeys_serial, 0, sizeof(sk->roundkeys_serial));
-
-    uint8_t inblk[16];
-    memset(inblk, 0, 16);
-    uint16_t slices[8];
-
-    for (size_t i = 0; i < sched_words; i++) {
-        /*
-         * Prepare a word of round key in the low 4 bits of each
-         * integer in slices[].
-         */
-        if (i < key_words) {
-            memcpy(inblk, key + 4*i, 4);
-            TO_BITSLICES(slices, inblk, uint16_t, =, 0);
-        } else {
-            unsigned wordindex, bitshift;
-            uint16_t *prevslices;
-
-            /* Fetch the (i-1)th key word */
-            wordindex = i-1;
-            bitshift = 4 * (wordindex & 3);
-            prevslices = sk->roundkeys_serial + 8 * (wordindex >> 2);
-            for (size_t i = 0; i < 8; i++)
-                slices[i] = prevslices[i] >> bitshift;
-
-            /* Decide what we're doing in this expansion stage */
-            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) {
-                for (size_t i = 0; i < 8; i++)
-                    slices[i] = ((slices[i] << 3) | (slices[i] >> 1)) & 0xF;
-            }
-
-            if (sub) {
-                /* Apply the SubBytes transform to the key word. But
-                 * here we need to apply the _full_ SubBytes from the
-                 * spec, including the constant which our S-box leaves
-                 * out. */
-                BITSLICED_SUBBYTES(slices, slices, uint16_t);
-                slices[0] ^= 0xFFFF;
-                slices[1] ^= 0xFFFF;
-                slices[5] ^= 0xFFFF;
-                slices[6] ^= 0xFFFF;
-            }
-
-            if (rotate_and_round_constant) {
-                assert(rconpos < lenof(key_setup_round_constants));
-                uint8_t rcon = key_setup_round_constants[rconpos++];
-                for (size_t i = 0; i < 8; i++)
-                    slices[i] ^= 1 & (rcon >> i);
-            }
-
-            /* Combine with the (i-Nk)th key word */
-            wordindex = i - key_words;
-            bitshift = 4 * (wordindex & 3);
-            prevslices = sk->roundkeys_serial + 8 * (wordindex >> 2);
-            for (size_t i = 0; i < 8; i++)
-                slices[i] ^= prevslices[i] >> bitshift;
-        }
-
-        /*
-         * Now copy it into sk.
-         */
-        for (unsigned b = 0; b < 8; b++)
-            outslices[b] |= (slices[b] & 0xF) << outshift;
-        outshift += 4;
-        if (outshift == 16) {
-            outshift = 0;
-            outslices += 8;
-        }
-    }
-
-    smemclr(inblk, sizeof(inblk));
-    smemclr(slices, sizeof(slices));
-
-    /*
-     * Add the S-box constant to every round key after the first one,
-     * compensating for it being left out in the main cipher.
-     */
-    for (size_t i = 8; i < 8 * (sched_words/4); i += 8) {
-        sk->roundkeys_serial[i+0] ^= 0xFFFF;
-        sk->roundkeys_serial[i+1] ^= 0xFFFF;
-        sk->roundkeys_serial[i+5] ^= 0xFFFF;
-        sk->roundkeys_serial[i+6] ^= 0xFFFF;
-    }
-
-    /*
-     * Replicate that set of round keys into larger integers for the
-     * parallel versions of the cipher.
-     */
-    for (size_t i = 0; i < 8 * (sched_words / 4); i++) {
-        sk->roundkeys_parallel[i] = sk->roundkeys_serial[i] *
-            ((BignumInt)~(BignumInt)0 / 0xFFFF);
-    }
-}
-#endif
-
-#ifdef WINSCP_VS
-/* -----
- * The full cipher primitive, including transforming the input and
- * output to/from bit-sliced form.
- */
-
-#define ENCRYPT_FN(suffix, uintN_t, nblocks)                            \
-    static void aes_sliced_e_##suffix(                                  \
-        uint8_t *output, const uint8_t *input, const aes_sliced_key *sk) \
-    {                                                                   \
-        uintN_t state[8];                                               \
-        TO_BITSLICES(state, input, uintN_t, =, 0);                      \
-        for (unsigned i = 1; i < nblocks; i++) {                        \
-            input += 16;                                                \
-            TO_BITSLICES(state, input, uintN_t, |=, i*16);              \
-        }                                                               \
-        const uintN_t *keys = sk->roundkeys_##suffix;                   \
-        BITSLICED_ADD(state, state, keys);                              \
-        keys += 8;                                                      \
-        for (unsigned i = 0; i < sk->rounds-1; i++) {                   \
-            aes_sliced_round_e_##suffix(state, state, keys);            \
-            keys += 8;                                                  \
-        }                                                               \
-        aes_sliced_round_e_##suffix##_last(state, state, keys);         \
-        for (unsigned i = 0; i < nblocks; i++) {                        \
-            FROM_BITSLICES(output, state, i*16);                        \
-            output += 16;                                               \
-        }                                                               \
-    }
-
-#define DECRYPT_FN(suffix, uintN_t, nblocks)                            \
-    static void aes_sliced_d_##suffix(                                  \
-        uint8_t *output, const uint8_t *input, const aes_sliced_key *sk) \
-    {                                                                   \
-        uintN_t state[8];                                               \
-        TO_BITSLICES(state, input, uintN_t, =, 0);                      \
-        for (unsigned i = 1; i < nblocks; i++) {                        \
-            input += 16;                                                \
-            TO_BITSLICES(state, input, uintN_t, |=, i*16);              \
-        }                                                               \
-        const uintN_t *keys = sk->roundkeys_##suffix + 8*sk->rounds;    \
-        aes_sliced_round_d_##suffix##_first(state, state, keys);        \
-        keys -= 8;                                                      \
-        for (unsigned i = 0; i < sk->rounds-1; i++) {                   \
-            aes_sliced_round_d_##suffix(state, state, keys);            \
-            keys -= 8;                                                  \
-        }                                                               \
-        BITSLICED_ADD(state, state, keys);                              \
-        for (unsigned i = 0; i < nblocks; i++) {                        \
-            FROM_BITSLICES(output, state, i*16);                        \
-            output += 16;                                               \
-        }                                                               \
-    }
-
-ENCRYPT_FN(serial, uint16_t, 1)
-#if 0 /* no cipher mode we support requires serial decryption */
-DECRYPT_FN(serial, uint16_t, 1)
-#endif
-ENCRYPT_FN(parallel, BignumInt, SLICE_PARALLELISM)
-DECRYPT_FN(parallel, BignumInt, SLICE_PARALLELISM)
-
-#endif // WINSCP_VS
-
-/* -----
- * The SSH interface and the cipher modes.
- */
-
-#define SDCTR_WORDS (16 / BIGNUM_INT_BYTES)
-
-typedef struct aes_sw_context aes_sw_context;
-struct aes_sw_context {
-    aes_sliced_key sk;
-    union {
-        struct {
-            /* In CBC mode, the IV is just a copy of the last seen
-             * cipher block. */
-            uint8_t prevblk[16];
-        } cbc;
-        struct {
-            /* In SDCTR mode, we keep the counter itself in a form
-             * that's easy to increment. We also use the parallel
-             * version of the core AES function, so we'll encrypt
-             * multiple counter values in one go. That won't align
-             * nicely with the sizes of data we're asked to encrypt,
-             * so we must also store a cache of the last set of
-             * keystream blocks we generated, and our current position
-             * within that cache. */
-            BignumInt counter[SDCTR_WORDS];
-            uint8_t keystream[SLICE_PARALLELISM * 16];
-            uint8_t *keystream_pos;
-        } sdctr;
-    } iv;
-    ssh_cipher ciph;
-};
-
-#ifndef WINSCP_VS
-
-static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg)
-{
-    aes_sw_context *ctx = snew(aes_sw_context);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void aes_sw_free(ssh_cipher *ciph)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void aes_sw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-    aes_sliced_key_setup(&ctx->sk, vkey, ctx->ciph.vt->real_keybits);
-}
-
-static void aes_sw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-    memcpy(ctx->iv.cbc.prevblk, iv, 16);
-}
-
-static void aes_sw_setiv_sdctr(ssh_cipher *ciph, const void *viv)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-    const uint8_t *iv = (const uint8_t *)viv;
-
-    /* Import the initial counter value into the internal representation */
-    unsigned i; // WINSCP
-    for (i = 0; i < SDCTR_WORDS; i++)
-        ctx->iv.sdctr.counter[i] =
-            GET_BIGNUMINT_MSB_FIRST(
-                iv + 16 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES);
-
-    /* Set keystream_pos to indicate that the keystream cache is
-     * currently empty */
-    ctx->iv.sdctr.keystream_pos =
-        ctx->iv.sdctr.keystream + sizeof(ctx->iv.sdctr.keystream);
-}
-
-#endif
-
-typedef void (*aes_sw_fn)(uint32_t v[4], const uint32_t *keysched);
-
-#ifdef WINSCP_VS
-
-static inline void memxor16(void *vout, const void *vlhs, const void *vrhs)
-{
-    uint8_t *out = (uint8_t *)vout;
-    const uint8_t *lhs = (const uint8_t *)vlhs, *rhs = (const uint8_t *)vrhs;
-    uint64_t w;
-
-    w = GET_64BIT_LSB_FIRST(lhs);
-    w ^= GET_64BIT_LSB_FIRST(rhs);
-    PUT_64BIT_LSB_FIRST(out, w);
-    w = GET_64BIT_LSB_FIRST(lhs + 8);
-    w ^= GET_64BIT_LSB_FIRST(rhs + 8);
-    PUT_64BIT_LSB_FIRST(out + 8, w);
-}
-
-static inline void aes_cbc_sw_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-
-    /*
-     * CBC encryption has to be done serially, because the input to
-     * each run of the cipher includes the output from the previous
-     * run.
-     */
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        /*
-         * We use the IV array itself as the location for the
-         * encryption, because there's no reason not to.
-         */
-
-        /* XOR the new plaintext block into the previous cipher block */
-        memxor16(ctx->iv.cbc.prevblk, ctx->iv.cbc.prevblk, blk);
-
-        /* Run the cipher over the result, which leaves it
-         * conveniently already stored in ctx->iv */
-        aes_sliced_e_serial(
-            ctx->iv.cbc.prevblk, ctx->iv.cbc.prevblk, &ctx->sk);
-
-        /* Copy it to the output location */
-        memcpy(blk, ctx->iv.cbc.prevblk, 16);
-    }
-}
-
-static inline void aes_cbc_sw_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-    uint8_t *blk = (uint8_t *)vblk;
-
-    /*
-     * CBC decryption can run in parallel, because all the
-     * _ciphertext_ blocks are already available.
-     */
-
-    size_t blocks_remaining = blklen / 16;
-
-    uint8_t data[SLICE_PARALLELISM * 16];
-    /* Zeroing the data array is probably overcautious, but it avoids
-     * technically undefined behaviour from leaving it uninitialised
-     * if our very first iteration doesn't include enough cipher
-     * blocks to populate it fully */
-    memset(data, 0, sizeof(data));
-
-    while (blocks_remaining > 0) {
-        /* Number of blocks we'll handle in this iteration. If we're
-         * dealing with fewer than the maximum, it doesn't matter -
-         * it's harmless to run the full parallel cipher function
-         * anyway. */
-        size_t blocks = (blocks_remaining < SLICE_PARALLELISM ?
-                         blocks_remaining : SLICE_PARALLELISM);
-
-        /* Parallel-decrypt the input, in a separate array so we still
-         * have the cipher stream available for XORing. */
-        memcpy(data, blk, 16 * blocks);
-        aes_sliced_d_parallel(data, data, &ctx->sk);
-
-        /* Write the output and update the IV */
-        for (size_t i = 0; i < blocks; i++) {
-            uint8_t *decrypted = data + 16*i;
-            uint8_t *output = blk + 16*i;
-
-            memxor16(decrypted, decrypted, ctx->iv.cbc.prevblk);
-            memcpy(ctx->iv.cbc.prevblk, output, 16);
-            memcpy(output, decrypted, 16);
-        }
-
-        /* Advance the input pointer. */
-        blk += 16 * blocks;
-        blocks_remaining -= blocks;
-    }
-
-    smemclr(data, sizeof(data));
-}
-
-static inline void aes_sdctr_sw(
-    ssh_cipher *ciph, void *vblk, int blklen)
-{
-    aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph);
-
-    /*
-     * SDCTR encrypt/decrypt loops round one block at a time XORing
-     * the keystream into the user's data, and periodically has to run
-     * a parallel encryption operation to get more keystream.
-     */
-
-    uint8_t *keystream_end =
-        ctx->iv.sdctr.keystream + sizeof(ctx->iv.sdctr.keystream);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-
-        if (ctx->iv.sdctr.keystream_pos == keystream_end) {
-            /*
-             * Generate some keystream.
-             */
-            for (uint8_t *block = ctx->iv.sdctr.keystream;
-                 block < keystream_end; block += 16) {
-                /* Format the counter value into the buffer. */
-                for (unsigned i = 0; i < SDCTR_WORDS; i++)
-                    PUT_BIGNUMINT_MSB_FIRST(
-                        block + 16 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES,
-                        ctx->iv.sdctr.counter[i]);
-
-                /* Increment the counter. */
-                BignumCarry carry = 1;
-                for (unsigned i = 0; i < SDCTR_WORDS; i++)
-                    BignumADC(ctx->iv.sdctr.counter[i], carry,
-                              ctx->iv.sdctr.counter[i], 0, carry);
-            }
-
-            /* Encrypt all those counter blocks. */
-            aes_sliced_e_parallel(ctx->iv.sdctr.keystream,
-                                  ctx->iv.sdctr.keystream, &ctx->sk);
-
-            /* Reset keystream_pos to the start of the buffer. */
-            ctx->iv.sdctr.keystream_pos = ctx->iv.sdctr.keystream;
-        }
-
-        memxor16(blk, blk, ctx->iv.sdctr.keystream_pos);
-        ctx->iv.sdctr.keystream_pos += 16;
-    }
-}
-
-#define SW_ENC_DEC(len)                                 \
-    /*WINSCP static*/ void aes##len##_cbc_sw_encrypt(              \
-        ssh_cipher *ciph, void *vblk, int blklen)       \
-    { aes_cbc_sw_encrypt(ciph, vblk, blklen); }         \
-    /*WINSCP static*/ void aes##len##_cbc_sw_decrypt(              \
-        ssh_cipher *ciph, void *vblk, int blklen)       \
-    { aes_cbc_sw_decrypt(ciph, vblk, blklen); }         \
-    /*WINSCP static*/ void aes##len##_sdctr_sw(                    \
-        ssh_cipher *ciph, void *vblk, int blklen)       \
-    { aes_sdctr_sw(ciph, vblk, blklen); }
-
-SW_ENC_DEC(128)
-SW_ENC_DEC(192)
-SW_ENC_DEC(256)
-
-#endif
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using x86 AES-NI.
- */
-
-#if HW_AES == HW_AES_NI
-
-#ifdef WINSCP_VS
-
-/*
- * Set target architecture for Clang and GCC
- */
-#if !defined(__clang__) && defined(__GNUC__)
-#    pragma GCC target("aes")
-#    pragma GCC target("sse4.1")
-#endif
-
-#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
-#    define FUNC_ISA __attribute__ ((target("sse4.1,aes")))
-#else
-#    define FUNC_ISA
-#endif
-
-#include <wmmintrin.h>
-#include <smmintrin.h>
-
-#if defined(__clang__) || defined(__GNUC__)
-#include <cpuid.h>
-#define GET_CPU_ID(out) __cpuid(1, (out)[0], (out)[1], (out)[2], (out)[3])
-#else
-#define GET_CPU_ID(out) __cpuid(out, 1)
-#endif
-
-bool aes_hw_available(void)
-{
-    /*
-     * Determine if AES is available on this CPU, by checking that
-     * both AES itself and SSE4.1 are supported.
-     */
-    unsigned int CPUInfo[4];
-    GET_CPU_ID(CPUInfo);
-    return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19));
-}
-
-/*
- * Core AES-NI encrypt/decrypt functions, one per length and direction.
- */
-
-#define NI_CIPHER(len, dir, dirlong, repmacro)                          \
-    static FUNC_ISA inline __m128i aes_ni_##len##_##dir(                \
-        __m128i v, const __m128i *keysched)                             \
-    {                                                                   \
-        v = _mm_xor_si128(v, *keysched++);                              \
-        repmacro(v = _mm_aes##dirlong##_si128(v, *keysched++););        \
-        return _mm_aes##dirlong##last_si128(v, *keysched);              \
-    }
-
-NI_CIPHER(128, e, enc, REP9)
-NI_CIPHER(128, d, dec, REP9)
-NI_CIPHER(192, e, enc, REP11)
-NI_CIPHER(192, d, dec, REP11)
-NI_CIPHER(256, e, enc, REP13)
-NI_CIPHER(256, d, dec, REP13)
-
-/*
- * The main key expansion.
- */
-static FUNC_ISA void aes_ni_key_expand(
-    const unsigned char *key, size_t key_words,
-    __m128i *keysched_e, __m128i *keysched_d)
-{
-    size_t rounds = key_words + 6;
-    size_t sched_words = (rounds + 1) * 4;
-
-    /*
-     * Store the key schedule as 32-bit integers during expansion, so
-     * that it's easy to refer back to individual previous words. We
-     * collect them into the final __m128i form at the end.
-     */
-    uint32_t sched[MAXROUNDKEYS * 4];
-
-    unsigned rconpos = 0;
-
-    for (size_t i = 0; i < sched_words; i++) {
-        if (i < key_words) {
-            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
-        } else {
-            uint32_t temp = sched[i - 1];
-
-            bool rotate_and_round_constant = (i % key_words == 0);
-            bool only_sub = (key_words == 8 && i % 8 == 4);
-
-            if (rotate_and_round_constant) {
-                __m128i v = _mm_setr_epi32(0,temp,0,0);
-                v = _mm_aeskeygenassist_si128(v, 0);
-                temp = _mm_extract_epi32(v, 1);
-
-                assert(rconpos < lenof(key_setup_round_constants));
-                temp ^= key_setup_round_constants[rconpos++];
-            } else if (only_sub) {
-                __m128i v = _mm_setr_epi32(0,temp,0,0);
-                v = _mm_aeskeygenassist_si128(v, 0);
-                temp = _mm_extract_epi32(v, 0);
-            }
-
-            sched[i] = sched[i - key_words] ^ temp;
-        }
-    }
-
-    /*
-     * Combine the key schedule words into __m128i vectors and store
-     * them in the output context.
-     */
-    for (size_t round = 0; round <= rounds; round++)
-        keysched_e[round] = _mm_setr_epi32(
-            sched[4*round  ], sched[4*round+1],
-            sched[4*round+2], sched[4*round+3]);
-
-    smemclr(sched, sizeof(sched));
-
-    /*
-     * Now prepare the modified keys for the inverse cipher.
-     */
-    for (size_t eround = 0; eround <= rounds; eround++) {
-        size_t dround = rounds - eround;
-        __m128i rkey = keysched_e[eround];
-        if (eround && dround)      /* neither first nor last */
-            rkey = _mm_aesimc_si128(rkey);
-        keysched_d[dround] = rkey;
-    }
-}
-
-// WINSCP
-// WORKAROUND
-// Cannot use _mm_setr_epi* - it results in the constant being stored in .rdata segment.
-// objconv reports:
-// Warning 1060: Different alignments specified for same segment, %s. Using highest alignment.rdata
-// Despite that the code crashes.
-// This macro is based on:
-// Based on https://stackoverflow.com/q/35268036/850848
-#define _MM_SETR_EPI8(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \
-    { (char)a0, (char)a1, (char)a2, (char)a3, (char)a4, (char)a5, (char)a6, (char)a7, \
-      (char)a8, (char)a9, (char)aa, (char)ab, (char)ac, (char)ad, (char)ae, (char)af }
-
-/*
- * Auxiliary routine to increment the 128-bit counter used in SDCTR
- * mode.
- */
-static FUNC_ISA inline __m128i aes_ni_sdctr_increment(__m128i v)
-{
-    const __m128i ONE = _MM_SETR_EPI8(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); // WINSCP
-    const __m128i ZERO = _mm_setzero_si128();
-
-    /* Increment the low-order 64 bits of v */
-    v  = _mm_add_epi64(v, ONE);
-    /* Check if they've become zero */
-    __m128i cmp = _mm_cmpeq_epi64(v, ZERO);
-    /* If so, the low half of cmp is all 1s. Pack that into the high
-     * half of addend with zero in the low half. */
-    __m128i addend = _mm_unpacklo_epi64(ZERO, cmp);
-    /* And subtract that from v, which increments the high 64 bits iff
-     * the low 64 wrapped round. */
-    v = _mm_sub_epi64(v, addend);
-
-    return v;
-}
-
-/*
- * Auxiliary routine to reverse the byte order of a vector, so that
- * the SDCTR IV can be made big-endian for feeding to the cipher.
- */
-static FUNC_ISA inline __m128i aes_ni_sdctr_reverse(__m128i v)
-{
-    const __m128i R = _MM_SETR_EPI8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0); // WINSCP
-    v = _mm_shuffle_epi8(
-        v, R); // WINSCP
-    return v;
-}
-
-/*
- * The SSH interface and the cipher modes.
- */
-
-typedef struct aes_ni_context aes_ni_context;
-struct aes_ni_context {
-    __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
-
-    void *pointer_to_free;
-    ssh_cipher ciph;
-};
-
-/*static WINSCP*/ ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    if (!aes_hw_available_cached())
-        return NULL;
-
-    /*
-     * The __m128i variables in the context structure need to be
-     * 16-byte aligned, but not all malloc implementations that this
-     * code has to work with will guarantee to return a 16-byte
-     * aligned pointer. So we over-allocate, manually realign the
-     * pointer ourselves, and store the original one inside the
-     * context so we know how to free it later.
-     */
-    void *allocation = smalloc(sizeof(aes_ni_context) + 15);
-    uintptr_t alloc_address = (uintptr_t)allocation;
-    uintptr_t aligned_address = (alloc_address + 15) & ~15;
-    aes_ni_context *ctx = (aes_ni_context *)aligned_address;
-
-    ctx->ciph.vt = alg;
-    ctx->pointer_to_free = allocation;
-    return &ctx->ciph;
-}
-
-/*static WINSCP*/ void aes_hw_free(ssh_cipher *ciph)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    void *allocation = ctx->pointer_to_free;
-    smemclr(ctx, sizeof(*ctx));
-    sfree(allocation);
-}
-
-/*static WINSCP*/ void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    aes_ni_key_expand(key, ctx->ciph.vt->real_keybits / 32,
-                      ctx->keysched_e, ctx->keysched_d);
-}
-
-/*static WINSCP*/ FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    ctx->iv = _mm_loadu_si128(iv);
-}
-
-/*static WINSCP*/ FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-    __m128i counter = _mm_loadu_si128(iv);
-    ctx->iv = aes_ni_sdctr_reverse(counter);
-}
-
-typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched);
-
-static FUNC_ISA inline void aes_cbc_ni_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i plaintext = _mm_loadu_si128((const __m128i *)blk);
-        __m128i cipher_input = _mm_xor_si128(plaintext, ctx->iv);
-        __m128i ciphertext = encrypt(cipher_input, ctx->keysched_e);
-        _mm_storeu_si128((__m128i *)blk, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_cbc_ni_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i ciphertext = _mm_loadu_si128((const __m128i *)blk);
-        __m128i decrypted = decrypt(ciphertext, ctx->keysched_d);
-        __m128i plaintext = _mm_xor_si128(decrypted, ctx->iv);
-        _mm_storeu_si128((__m128i *)blk, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_sdctr_ni(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt)
-{
-    aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        __m128i counter = aes_ni_sdctr_reverse(ctx->iv);
-        __m128i keystream = encrypt(counter, ctx->keysched_e);
-        __m128i input = _mm_loadu_si128((const __m128i *)blk);
-        __m128i output = _mm_xor_si128(input, keystream);
-        _mm_storeu_si128((__m128i *)blk, output);
-        ctx->iv = aes_ni_sdctr_increment(ctx->iv);
-    }
-}
-
-#define NI_ENC_DEC(len)                                                 \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_cbc_hw_encrypt(          \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); }       \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_cbc_hw_decrypt(          \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); }       \
-    /*static WINSCP*/ FUNC_ISA void aes##len##_sdctr_hw(                \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); }             \
-
-NI_ENC_DEC(128)
-NI_ENC_DEC(192)
-NI_ENC_DEC(256)
-
-#endif // WINSCP_VS
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of AES using Arm NEON.
- */
-
-#elif HW_AES == HW_AES_NEON
-
-/*
- * Manually set the target architecture, if we decided above that we
- * need to.
- */
-#ifdef USE_CLANG_ATTR_TARGET_AARCH64
-/*
- * A spot of cheating: redefine some ACLE feature macros before
- * including arm_neon.h. Otherwise we won't get the AES intrinsics
- * defined by that header, because it will be looking at the settings
- * for the whole translation unit rather than the ones we're going to
- * put on some particular functions using __attribute__((target)).
- */
-#define __ARM_NEON 1
-#define __ARM_FEATURE_CRYPTO 1
-#define __ARM_FEATURE_AES 1
-#define FUNC_ISA __attribute__ ((target("neon,crypto")))
-#endif /* USE_CLANG_ATTR_TARGET_AARCH64 */
-
-#ifndef FUNC_ISA
-#define FUNC_ISA
-#endif
-
-#ifdef USE_ARM64_NEON_H
-#include <arm64_neon.h>
-#else
-#include <arm_neon.h>
-#endif
-
-static bool aes_hw_available(void)
-{
-    /*
-     * For Arm, we delegate to a per-platform AES detection function,
-     * because it has to be implemented by asking the operating system
-     * rather than directly querying the CPU.
-     *
-     * That's because Arm systems commonly have multiple cores that
-     * are not all alike, so any method of querying whether NEON
-     * crypto instructions work on the _current_ CPU - even one as
-     * crude as just trying one and catching the SIGILL - wouldn't
-     * give an answer that you could still rely on the first time the
-     * OS migrated your process to another CPU.
-     */
-    return platform_aes_hw_available();
-}
-
-/*
- * Core NEON encrypt/decrypt functions, one per length and direction.
- */
-
-#define NEON_CIPHER(len, repmacro)                              \
-    static FUNC_ISA inline uint8x16_t aes_neon_##len##_e(       \
-        uint8x16_t v, const uint8x16_t *keysched)               \
-    {                                                           \
-        repmacro(v = vaesmcq_u8(vaeseq_u8(v, *keysched++)););   \
-        v = vaeseq_u8(v, *keysched++);                          \
-        return veorq_u8(v, *keysched);                          \
-    }                                                           \
-    static FUNC_ISA inline uint8x16_t aes_neon_##len##_d(       \
-        uint8x16_t v, const uint8x16_t *keysched)               \
-    {                                                           \
-        repmacro(v = vaesimcq_u8(vaesdq_u8(v, *keysched++)););  \
-        v = vaesdq_u8(v, *keysched++);                          \
-        return veorq_u8(v, *keysched);                          \
-    }
-
-NEON_CIPHER(128, REP9)
-NEON_CIPHER(192, REP11)
-NEON_CIPHER(256, REP13)
-
-/*
- * The main key expansion.
- */
-static FUNC_ISA void aes_neon_key_expand(
-    const unsigned char *key, size_t key_words,
-    uint8x16_t *keysched_e, uint8x16_t *keysched_d)
-{
-    size_t rounds = key_words + 6;
-    size_t sched_words = (rounds + 1) * 4;
-
-    /*
-     * Store the key schedule as 32-bit integers during expansion, so
-     * that it's easy to refer back to individual previous words. We
-     * collect them into the final uint8x16_t form at the end.
-     */
-    uint32_t sched[MAXROUNDKEYS * 4];
-
-    unsigned rconpos = 0;
-
-    for (size_t i = 0; i < sched_words; i++) {
-        if (i < key_words) {
-            sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i);
-        } else {
-            uint32_t temp = sched[i - 1];
-
-            bool rotate_and_round_constant = (i % key_words == 0);
-            bool sub = rotate_and_round_constant ||
-                (key_words == 8 && i % 8 == 4);
-
-            if (rotate_and_round_constant)
-                temp = (temp << 24) | (temp >> 8);
-
-            if (sub) {
-                uint32x4_t v32 = vdupq_n_u32(temp);
-                uint8x16_t v8 = vreinterpretq_u8_u32(v32);
-                v8 = vaeseq_u8(v8, vdupq_n_u8(0));
-                v32 = vreinterpretq_u32_u8(v8);
-                temp = vget_lane_u32(vget_low_u32(v32), 0);
-            }
-
-            if (rotate_and_round_constant) {
-                assert(rconpos < lenof(key_setup_round_constants));
-                temp ^= key_setup_round_constants[rconpos++];
-            }
-
-            sched[i] = sched[i - key_words] ^ temp;
-        }
-    }
-
-    /*
-     * Combine the key schedule words into uint8x16_t vectors and
-     * store them in the output context.
-     */
-    for (size_t round = 0; round <= rounds; round++)
-        keysched_e[round] = vreinterpretq_u8_u32(vld1q_u32(sched + 4*round));
-
-    smemclr(sched, sizeof(sched));
-
-    /*
-     * Now prepare the modified keys for the inverse cipher.
-     */
-    for (size_t eround = 0; eround <= rounds; eround++) {
-        size_t dround = rounds - eround;
-        uint8x16_t rkey = keysched_e[eround];
-        if (eround && dround)      /* neither first nor last */
-            rkey = vaesimcq_u8(rkey);
-        keysched_d[dround] = rkey;
-    }
-}
-
-/*
- * Auxiliary routine to reverse the byte order of a vector, so that
- * the SDCTR IV can be made big-endian for feeding to the cipher.
- *
- * In fact we don't need to reverse the vector _all_ the way; we leave
- * the two lanes in MSW,LSW order, because that makes no difference to
- * the efficiency of the increment. That way we only have to reverse
- * bytes within each lane in this function.
- */
-static FUNC_ISA inline uint8x16_t aes_neon_sdctr_reverse(uint8x16_t v)
-{
-    return vrev64q_u8(v);
-}
-
-/*
- * Auxiliary routine to increment the 128-bit counter used in SDCTR
- * mode. There's no instruction to treat a 128-bit vector as a single
- * long integer, so instead we have to increment the bottom half
- * unconditionally, and the top half if the bottom half started off as
- * all 1s (in which case there was about to be a carry).
- */
-static FUNC_ISA inline uint8x16_t aes_neon_sdctr_increment(uint8x16_t in)
-{
-#ifdef __aarch64__
-    /* There will be a carry if the low 64 bits are all 1s. */
-    uint64x1_t all1 = vcreate_u64(0xFFFFFFFFFFFFFFFF);
-    uint64x1_t carry = vceq_u64(vget_high_u64(vreinterpretq_u64_u8(in)), all1);
-
-    /* Make a word whose bottom half is unconditionally all 1s, and
-     * the top half is 'carry', i.e. all 0s most of the time but all
-     * 1s if we need to increment the top half. Then that word is what
-     * we need to _subtract_ from the input counter. */
-    uint64x2_t subtrahend = vcombine_u64(carry, all1);
-#else
-    /* AArch32 doesn't have comparisons that operate on a 64-bit lane,
-     * so we start by comparing each 32-bit half of the low 64 bits
-     * _separately_ to all-1s. */
-    uint32x2_t all1 = vdup_n_u32(0xFFFFFFFF);
-    uint32x2_t carry = vceq_u32(
-        vget_high_u32(vreinterpretq_u32_u8(in)), all1);
-
-    /* Swap the 32-bit words of the compare output, and AND with the
-     * unswapped version. Now carry is all 1s iff the bottom half of
-     * the input counter was all 1s, and all 0s otherwise. */
-    carry = vand_u32(carry, vrev64_u32(carry));
-
-    /* Now make the vector to subtract in the same way as above. */
-    uint64x2_t subtrahend = vreinterpretq_u64_u32(vcombine_u32(carry, all1));
-#endif
-
-    return vreinterpretq_u8_u64(
-        vsubq_u64(vreinterpretq_u64_u8(in), subtrahend));
-}
-
-/*
- * The SSH interface and the cipher modes.
- */
-
-typedef struct aes_neon_context aes_neon_context;
-struct aes_neon_context {
-    uint8x16_t keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv;
-
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    if (!aes_hw_available_cached())
-        return NULL;
-
-    aes_neon_context *ctx = snew(aes_neon_context);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void aes_hw_free(ssh_cipher *ciph)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    const unsigned char *key = (const unsigned char *)vkey;
-
-    aes_neon_key_expand(key, ctx->ciph.vt->real_keybits / 32,
-                      ctx->keysched_e, ctx->keysched_d);
-}
-
-static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    ctx->iv = vld1q_u8(iv);
-}
-
-static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-    uint8x16_t counter = vld1q_u8(iv);
-    ctx->iv = aes_neon_sdctr_reverse(counter);
-}
-
-typedef uint8x16_t (*aes_neon_fn)(uint8x16_t v, const uint8x16_t *keysched);
-
-static FUNC_ISA inline void aes_cbc_neon_encrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t plaintext = vld1q_u8(blk);
-        uint8x16_t cipher_input = veorq_u8(plaintext, ctx->iv);
-        uint8x16_t ciphertext = encrypt(cipher_input, ctx->keysched_e);
-        vst1q_u8(blk, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_cbc_neon_decrypt(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn decrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t ciphertext = vld1q_u8(blk);
-        uint8x16_t decrypted = decrypt(ciphertext, ctx->keysched_d);
-        uint8x16_t plaintext = veorq_u8(decrypted, ctx->iv);
-        vst1q_u8(blk, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static FUNC_ISA inline void aes_sdctr_neon(
-    ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt)
-{
-    aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph);
-
-    for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen;
-         blk < finish; blk += 16) {
-        uint8x16_t counter = aes_neon_sdctr_reverse(ctx->iv);
-        uint8x16_t keystream = encrypt(counter, ctx->keysched_e);
-        uint8x16_t input = vld1q_u8(blk);
-        uint8x16_t output = veorq_u8(input, keystream);
-        vst1q_u8(blk, output);
-        ctx->iv = aes_neon_sdctr_increment(ctx->iv);
-    }
-}
-
-#define NEON_ENC_DEC(len)                                               \
-    static FUNC_ISA void aes##len##_cbc_hw_encrypt(                     \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_neon_encrypt(ciph, vblk, blklen, aes_neon_##len##_e); }   \
-    static FUNC_ISA void aes##len##_cbc_hw_decrypt(                     \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_cbc_neon_decrypt(ciph, vblk, blklen, aes_neon_##len##_d); }   \
-    static FUNC_ISA void aes##len##_sdctr_hw(                           \
-        ssh_cipher *ciph, void *vblk, int blklen)                       \
-    { aes_sdctr_neon(ciph, vblk, blklen, aes_neon_##len##_e); }         \
-
-NEON_ENC_DEC(128)
-NEON_ENC_DEC(192)
-NEON_ENC_DEC(256)
-
-/* ----------------------------------------------------------------------
- * Stub functions if we have no hardware-accelerated AES. In this
- * case, aes_hw_new returns NULL (though it should also never be
- * selected by aes_select, so the only thing that should even be
- * _able_ to call it is testcrypt). As a result, the remaining vtable
- * functions should never be called at all.
- */
-
-#elif HW_AES == HW_AES_NONE
-
-bool aes_hw_available(void)
-{
-    return false;
-}
-
-static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg)
-{
-    return NULL;
-}
-
-#define STUB_BODY { unreachable("Should never be called"); }
-
-static void aes_hw_free(ssh_cipher *ciph) STUB_BODY
-static void aes_hw_setkey(ssh_cipher *ciph, const void *key) STUB_BODY
-static void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) STUB_BODY
-static void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) STUB_BODY
-#define STUB_ENC_DEC(len)                                       \
-    static void aes##len##_cbc_hw_encrypt(                      \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
-    static void aes##len##_cbc_hw_decrypt(                      \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY     \
-    static void aes##len##_sdctr_hw(                            \
-        ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY
-
-STUB_ENC_DEC(128)
-STUB_ENC_DEC(192)
-STUB_ENC_DEC(256)
-
-#endif /* HW_AES */
-
-#ifndef WINSCP_VS
-
-#ifdef MPEXT
-
-#include "puttyexp.h"
-
-AESContext * aes_make_context()
-{
-  ssh_cipher * cipher = ssh_cipher_new(&ssh_aes256_sdctr);
-  return cipher;
-}
-
-void aes_free_context(AESContext * ctx)
-{
-  ssh_cipher * cipher = (ssh_cipher *)ctx;
-  ssh_cipher_free(cipher);
-}
-
-void aes_iv(AESContext * ctx, const void * iv)
-{
-  ssh_cipher * cipher = (ssh_cipher *)ctx;
-  ssh_cipher_setiv(cipher, iv);
-}
-
-void call_aes_setup(AESContext * ctx, unsigned char * key, int keylen)
-{
-  ssh_cipher * cipher = (ssh_cipher *)ctx;
-  assert(keylen == 32);
-  ssh_cipher_setkey(cipher, key);
-}
-
-void call_aes_sdctr(unsigned char *blk, int len, void *ctx)
-{
-  ssh_cipher * cipher = (ssh_cipher *)ctx;
-  ssh_cipher_encrypt(cipher, blk, len);
-}
-
-#endif
-
-#endif // WINSCP_VS

+ 0 - 147
source/putty/ssharcf.c

@@ -1,147 +0,0 @@
-/*
- * Arcfour (RC4) implementation for PuTTY.
- *
- * Coded from Schneier.
- */
-
-#include <assert.h>
-#include "ssh.h"
-
-typedef struct {
-    unsigned char i, j, s[256];
-    ssh_cipher ciph;
-} ArcfourContext;
-
-static void arcfour_block(void *handle, void *vblk, int len)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    ArcfourContext *ctx = (ArcfourContext *)handle;
-    unsigned k;
-    unsigned char tmp, i, j, *s;
-
-    s = ctx->s;
-    i = ctx->i; j = ctx->j;
-    for (k = 0; (int)k < len; k++) {
-        i  = (i + 1) & 0xff;
-        j  = (j + s[i]) & 0xff;
-        tmp = s[i]; s[i] = s[j]; s[j] = tmp;
-        blk[k] ^= s[(s[i]+s[j]) & 0xff];
-    }
-    ctx->i = i; ctx->j = j;
-}
-
-static void arcfour_setkey(ArcfourContext *ctx, unsigned char const *key,
-                           unsigned keybytes)
-{
-    unsigned char tmp, k[256], *s;
-    unsigned i, j;
-
-    s = ctx->s;
-    assert(keybytes <= 256);
-    ctx->i = ctx->j = 0;
-    for (i = 0; i < 256; i++) {
-        s[i] = i;
-        k[i] = key[i % keybytes];
-    }
-    j = 0;
-    for (i = 0; i < 256; i++) {
-        j = (j + s[i] + k[i]) & 0xff;
-        tmp = s[i]; s[i] = s[j]; s[j] = tmp;
-    }
-}
-
-/* -- Interface with PuTTY -- */
-
-/*
- * We don't implement Arcfour in SSH-1 because it's utterly insecure in
- * several ways.  See CERT Vulnerability Notes VU#25309, VU#665372,
- * and VU#565052.
- *
- * We don't implement the "arcfour" algorithm in SSH-2 because it doesn't
- * stir the cipher state before emitting keystream, and hence is likely
- * to leak data about the key.
- */
-
-static ssh_cipher *arcfour_new(const ssh_cipheralg *alg)
-{
-    ArcfourContext *ctx = snew(ArcfourContext);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void arcfour_free(ssh_cipher *cipher)
-{
-    ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void arcfour_stir(ArcfourContext *ctx)
-{
-    unsigned char *junk = snewn(1536, unsigned char);
-    memset(junk, 0, 1536);
-    arcfour_block(ctx, junk, 1536);
-    smemclr(junk, 1536);
-    sfree(junk);
-}
-
-static void arcfour_ssh2_setiv(ssh_cipher *cipher, const void *key)
-{
-    /* As a pure stream cipher, Arcfour has no IV separate from the key */
-}
-
-static void arcfour_ssh2_setkey(ssh_cipher *cipher, const void *key)
-{
-    ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph);
-    arcfour_setkey(ctx, key, ctx->ciph.vt->padded_keybytes);
-    arcfour_stir(ctx);
-}
-
-static void arcfour_ssh2_block(ssh_cipher *cipher, void *blk, int len)
-{
-    ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph);
-    arcfour_block(ctx, blk, len);
-}
-
-const ssh_cipheralg ssh_arcfour128_ssh2 = {
-    // WINSCP
-    /*.new =*/ arcfour_new,
-    /*.free =*/ arcfour_free,
-    /*.setiv =*/ arcfour_ssh2_setiv,
-    /*.setkey =*/ arcfour_ssh2_setkey,
-    /*.encrypt =*/ arcfour_ssh2_block,
-    /*.decrypt =*/ arcfour_ssh2_block,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "arcfour128",
-    /*.blksize =*/ 1,
-    /*.real_keybits =*/ 128,
-    /*.padded_keybytes =*/ 16,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "Arcfour-128",
-    NULL, NULL, // WINSCP
-};
-
-const ssh_cipheralg ssh_arcfour256_ssh2 = {
-    // WINSCP
-    /*.new =*/ arcfour_new,
-    /*.free =*/ arcfour_free,
-    /*.setiv =*/ arcfour_ssh2_setiv,
-    /*.setkey =*/ arcfour_ssh2_setkey,
-    /*.encrypt =*/ arcfour_ssh2_block,
-    /*.decrypt =*/ arcfour_ssh2_block,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "arcfour256",
-    /*.blksize =*/ 1,
-    /*.real_keybits =*/ 256,
-    /*.padded_keybytes =*/ 32,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "Arcfour-256",
-    NULL, NULL, // WINSCP
-};
-
-static const ssh_cipheralg *const arcfour_list[] = {
-    &ssh_arcfour256_ssh2,
-    &ssh_arcfour128_ssh2,
-};
-
-const ssh2_ciphers ssh2_arcfour = { lenof(arcfour_list), arcfour_list };

+ 0 - 623
source/putty/sshargon2.c

@@ -1,623 +0,0 @@
-/*
- * Implementation of the Argon2 password hash function.
- *
- * My sources for the algorithm description and test vectors (the latter in
- * test/cryptsuite.py) were the reference implementation on Github, and also
- * the Internet-Draft description:
- *
- *   https://github.com/P-H-C/phc-winner-argon2
- *   https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-argon2-13
- */
-
-#include <assert.h>
-
-#ifndef WINSCP_VS
-#include "putty.h"
-#endif
-#include "ssh.h"
-#include "marshal.h"
-
-#ifndef WINSCP_VS
-/* ----------------------------------------------------------------------
- * Argon2 uses data marshalling rules similar to SSH but with 32-bit integers
- * stored little-endian. Start with some local BinarySink routines for storing
- * a uint32 and a string in that fashion.
- */
-
-static void BinarySink_put_uint32_le(BinarySink *bs, unsigned long val)
-{
-    unsigned char data[4];
-    PUT_32BIT_LSB_FIRST(data, val);
-    bs->write(bs, data, sizeof(data));
-}
-
-static void BinarySink_put_stringpl_le(BinarySink *bs, ptrlen pl)
-{
-    /* Check that the string length fits in a uint32, without doing a
-     * potentially implementation-defined shift of more than 31 bits */
-    assert((pl.len >> 31) < 2);
-
-    BinarySink_put_uint32_le(bs, pl.len);
-    bs->write(bs, pl.ptr, pl.len);
-}
-
-#define put_uint32_le(bs, val) \
-    BinarySink_put_uint32_le(BinarySink_UPCAST(bs), val)
-#define put_stringpl_le(bs, val) \
-    BinarySink_put_stringpl_le(BinarySink_UPCAST(bs), val)
-
-/* ----------------------------------------------------------------------
- * Argon2 defines a hash-function family that's an extension of BLAKE2b to
- * generate longer output digests, by repeatedly outputting half of a BLAKE2
- * hash output and then re-hashing the whole thing until there are 64 or fewer
- * bytes left to output. The spec calls this H' (a variant of the original
- * hash it calls H, which is the unmodified BLAKE2b).
- */
-
-static ssh_hash *hprime_new(unsigned length)
-{
-    ssh_hash *h = blake2b_new_general(length > 64 ? 64 : length);
-    put_uint32_le(h, length);
-    return h;
-}
-
-static void hprime_final(ssh_hash *h, unsigned length, void *vout)
-{
-    uint8_t *out = (uint8_t *)vout;
-
-    while (length > 64) {
-        uint8_t hashbuf[64];
-        ssh_hash_final(h, hashbuf);
-
-        memcpy(out, hashbuf, 32);
-        out += 32;
-        length -= 32;
-
-        h = blake2b_new_general(length > 64 ? 64 : length);
-        put_data(h, hashbuf, 64);
-
-        smemclr(hashbuf, sizeof(hashbuf));
-    }
-
-    ssh_hash_final(h, out);
-}
-
-/* Externally visible entry point for the long hash function. This is only
- * used by testcrypt, so it would be overkill to set it up like a proper
- * ssh_hash. */
-strbuf *argon2_long_hash(unsigned length, ptrlen data)
-{
-    ssh_hash *h = hprime_new(length);
-    put_datapl(h, data);
-    { // WINSCP
-    strbuf *out = strbuf_new();
-    hprime_final(h, length, strbuf_append(out, length));
-    return out;
-    } // WINSCP
-}
-#endif
-
-/* ----------------------------------------------------------------------
- * Argon2's own mixing function G, which operates on 1Kb blocks of data.
- *
- * The definition of G in the spec takes two 1Kb blocks as input and produces
- * a 1Kb output block. The first thing that happens to the input blocks is
- * that they get XORed together, and then only the XOR output is used, so you
- * could perfectly well regard G as a 1Kb->1Kb function.
- */
-
-#ifdef WINSCP_VS
-static inline uint64_t ror(uint64_t x, unsigned rotation)
-{
-    #pragma warning(suppress: 4068)
-    #pragma warning(suppress: 4146)
-    unsigned lshift = 63 & -rotation, rshift = 63 & rotation;
-    return (x << lshift) | (x >> rshift);
-}
-
-static inline uint64_t trunc32(uint64_t x)
-{
-    return x & 0xFFFFFFFF;
-}
-
-/* Internal function similar to the BLAKE2b round, which mixes up four 64-bit
- * words */
-static inline void GB(uint64_t *a, uint64_t *b, uint64_t *c, uint64_t *d)
-{
-    *a += *b + 2 * trunc32(*a) * trunc32(*b);
-    *d = ror(*d ^ *a, 32);
-    *c += *d + 2 * trunc32(*c) * trunc32(*d);
-    *b = ror(*b ^ *c, 24);
-    *a += *b + 2 * trunc32(*a) * trunc32(*b);
-    *d = ror(*d ^ *a, 16);
-    *c += *d + 2 * trunc32(*c) * trunc32(*d);
-    *b = ror(*b ^ *c, 63);
-}
-
-/* Higher-level internal function which mixes up sixteen 64-bit words. This is
- * applied to different subsets of the 128 words in a kilobyte block, and the
- * API here is designed to make it easy to apply in the circumstances the spec
- * requires. In every call, the sixteen words form eight pairs adjacent in
- * memory, whose addresses are in arithmetic progression. So the 16 input
- * words are in[0], in[1], in[instep], in[instep+1], ..., in[7*instep],
- * in[7*instep+1], and the 16 output words similarly. */
-static inline void P(uint64_t *out, unsigned outstep,
-                     uint64_t *in, unsigned instep)
-{
-    unsigned i; // WINSCP
-    for (i = 0; i < 8; i++) {
-        out[i*outstep] = in[i*instep];
-        out[i*outstep+1] = in[i*instep+1];
-    }
-
-    GB(out+0*outstep+0, out+2*outstep+0, out+4*outstep+0, out+6*outstep+0);
-    GB(out+0*outstep+1, out+2*outstep+1, out+4*outstep+1, out+6*outstep+1);
-    GB(out+1*outstep+0, out+3*outstep+0, out+5*outstep+0, out+7*outstep+0);
-    GB(out+1*outstep+1, out+3*outstep+1, out+5*outstep+1, out+7*outstep+1);
-
-    GB(out+0*outstep+0, out+2*outstep+1, out+5*outstep+0, out+7*outstep+1);
-    GB(out+0*outstep+1, out+3*outstep+0, out+5*outstep+1, out+6*outstep+0);
-    GB(out+1*outstep+0, out+3*outstep+1, out+4*outstep+0, out+6*outstep+1);
-    GB(out+1*outstep+1, out+2*outstep+0, out+4*outstep+1, out+7*outstep+0);
-}
-#endif
-
-/* The full G function, taking input blocks X and Y. The result of G is most
- * often XORed into an existing output block, so this API is designed with
- * that in mind: the mixing function's output is always XORed into whatever
- * 1Kb of data is already at 'out'. */
-/*static*/ void G_xor(uint8_t *out, const uint8_t *X, const uint8_t *Y)
-#ifndef WINSCP_VS
-;
-#else
-{
-    uint64_t R[128], Q[128], Z[128];
-
-    unsigned i; // WINSCP
-    for (i = 0; i < 128; i++)
-        R[i] = GET_64BIT_LSB_FIRST(X + 8*i) ^ GET_64BIT_LSB_FIRST(Y + 8*i);
-
-    for (i = 0; i < 8; i++) // WINSCP
-        P(Q+16*i, 2, R+16*i, 2);
-
-    for (i = 0; i < 8; i++) // WINSCP
-        P(Z+2*i, 16, Q+2*i, 16);
-
-    for (i = 0; i < 128; i++) // WINSCP
-        PUT_64BIT_LSB_FIRST(out + 8*i,
-                            GET_64BIT_LSB_FIRST(out + 8*i) ^ R[i] ^ Z[i]);
-
-    smemclr(R, sizeof(R));
-    smemclr(Q, sizeof(Q));
-    smemclr(Z, sizeof(Z));
-}
-#endif
-
-struct blk { uint8_t data[1024]; };
-
-#ifndef WINSCP_VS
-void argon2_internal_vs(size_t jstart, size_t SL, size_t q, unsigned slice, bool d_mode, struct blk *B, size_t i, uint32_t p, struct blk * pin2i, size_t pass, size_t mprime, uint32_t t, uint32_t y, struct blk * ptmp2i, struct blk * pout2i);
-/* ----------------------------------------------------------------------
- * The main Argon2 function.
- */
-
-static void argon2_internal(uint32_t p, uint32_t T, uint32_t m, uint32_t t,
-                            uint32_t y, ptrlen P, ptrlen S, ptrlen K, ptrlen X,
-                            uint8_t *out)
-{
-    /*
-     * Start by hashing all the input data together: the four string arguments
-     * (password P, salt S, optional secret key K, optional associated data
-     * X), plus all the parameters for the function's memory and time usage.
-     *
-     * The output of this hash is the sole input to the subsequent mixing
-     * step: Argon2 does not preserve any more entropy from the inputs, it
-     * just makes it extra painful to get the final answer.
-     */
-    uint8_t h0[64];
-    {
-        ssh_hash *h = blake2b_new_general(64);
-        put_uint32_le(h, p);
-        put_uint32_le(h, T);
-        put_uint32_le(h, m);
-        put_uint32_le(h, t);
-        put_uint32_le(h, 0x13);        /* hash function version number */
-        put_uint32_le(h, y);
-        put_stringpl_le(h, P);
-        put_stringpl_le(h, S);
-        put_stringpl_le(h, K);
-        put_stringpl_le(h, X);
-        ssh_hash_final(h, h0);
-    }
-
-    { // WINSCP
-    // WINSCP struct blk { uint8_t data[1024]; };
-
-    /*
-     * Array of 1Kb blocks. The total size is (approximately) m, the
-     * caller-specified parameter for how much memory to use; the blocks are
-     * regarded as a rectangular array of p rows ('lanes') by q columns, where
-     * p is the 'parallelism' input parameter (the lanes can be processed
-     * concurrently up to a point) and q is whatever makes the product pq come
-     * to m.
-     *
-     * Additionally, each row is divided into four equal 'segments', which are
-     * important to the way the algorithm decides which blocks to use as input
-     * to each step of the function.
-     *
-     * The term 'slice' refers to a whole set of vertically aligned segments,
-     * i.e. slice 0 is the whole left quarter of the array, and slice 3 the
-     * whole right quarter.
-     */
-    size_t SL = m / (4*p); /* segment length: # of 1Kb blocks in a segment */
-    size_t q = 4 * SL;     /* width of the array: 4 segments times SL */
-    size_t mprime = q * p; /* total size of the array, approximately m */
-
-    /* Allocate the memory. */
-    struct blk *B = snewn(mprime, struct blk);
-    memset(B, 0, mprime * sizeof(struct blk));
-
-    /*
-     * Initial setup: fill the first two full columns of the array with data
-     * expanded from the starting hash h0. Each block is the result of using
-     * the long-output hash function H' to hash h0 itself plus the block's
-     * coordinates in the array.
-     */
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < p; i++) {
-        ssh_hash *h = hprime_new(1024);
-        put_data(h, h0, 64);
-        put_uint32_le(h, 0);
-        put_uint32_le(h, i);
-        hprime_final(h, 1024, B[i].data);
-    }
-    for (i = 0; i < p; i++) { // WINSCP
-        ssh_hash *h = hprime_new(1024);
-        put_data(h, h0, 64);
-        put_uint32_le(h, 1);
-        put_uint32_le(h, i);
-        hprime_final(h, 1024, B[i+p].data);
-    }
-
-    /*
-     * Declarations for the main loop.
-     *
-     * The basic structure of the main loop is going to involve processing the
-     * array one whole slice (vertically divided quarter) at a time. Usually
-     * we'll write a new value into every single block in the slice, except
-     * 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
-     * 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
-     * data-independent (false). In the hybrid Argon2id mode, we start off
-     * independent, and then once we've mixed things up enough, switch over to
-     * dependent mode to force long serial chains of computation.
-     */
-    { // WINSCP
-    size_t jstart = 2;
-    bool d_mode = (y == 0);
-    struct blk out2i, tmp2i, in2i;
-
-    /* Outermost loop: t whole passes from left to right over the array */
-    size_t pass; // WINSCP
-    for (pass = 0; pass < t; pass++) {
-
-        /* Within that, we process the array in its four main slices */
-        unsigned slice; // WINSCP
-        for (slice = 0; slice < 4; slice++) {
-
-            /* In Argon2id mode, if we're half way through the first pass,
-             * this is the moment to switch d_mode from false to true */
-            if (pass == 0 && slice == 2 && y == 2)
-                d_mode = true;
-
-            /* Loop over every segment in the slice (i.e. every row). So i is
-             * the y-coordinate of each block we process. */
-            { // WINSCP
-            size_t i; // WINSCP
-            for (i = 0; i < p; i++) {
-
-                argon2_internal_vs(jstart, SL, q, slice, d_mode, B, i, p, &in2i, pass, mprime, t, y, &tmp2i, &out2i); // WINSCP
-
-#endif
-#ifdef WINSCP_VS
-void argon2_internal_vs(size_t jstart, size_t SL, size_t q, unsigned slice, bool d_mode, struct blk *B, size_t i, uint32_t p, struct blk * pin2i, size_t pass, size_t mprime, uint32_t t, uint32_t y, struct blk * ptmp2i, struct blk * pout2i)
-{
-#define in2i (*pin2i)
-#define tmp2i (*ptmp2i)
-#define out2i (*pout2i)
-                /* And within that segment, process the blocks from left to
-                 * right, starting at 'jstart' (usually 0, but 2 in the first
-                 * slice). */
-                size_t jpre; // WINSCP
-                for (jpre = jstart; jpre < SL; jpre++) {
-
-                    /* j is the x-coordinate of each block we process, made up
-                     * of the slice number and the index 'jpre' within the
-                     * segment. */
-                    size_t j = slice * SL + jpre;
-
-                    /* jm1 is j-1 (mod q) */
-                    uint32_t jm1 = (j == 0 ? q-1 : j-1);
-
-                    /*
-                     * Construct two 32-bit pseudorandom integers J1 and J2.
-                     * This is the part of the algorithm that varies between
-                     * the data-dependent and independent modes.
-                     */
-                    uint32_t J1, J2;
-                    if (d_mode) {
-                        /*
-                         * Data-dependent: grab the first 64 bits of the block
-                         * to the left of this one.
-                         */
-                        J1 = GET_32BIT_LSB_FIRST(B[i + p * jm1].data);
-                        J2 = GET_32BIT_LSB_FIRST(B[i + p * jm1].data + 4);
-                    } else {
-                        /*
-                         * Data-independent: generate pseudorandom data by
-                         * hashing a sequence of preimage blocks that include
-                         * all our input parameters, plus the coordinates of
-                         * this point in the algorithm (array position and
-                         * pass number) to make all the hash outputs distinct.
-                         *
-                         * The hash we use is G itself, applied twice. So we
-                         * generate 1Kb of data at a time, which is enough for
-                         * 128 (J1,J2) pairs. Hence we only need to do the
-                         * hashing if our index within the segment is a
-                         * multiple of 128, or if we're at the very start of
-                         * the algorithm (in which case we started at 2 rather
-                         * than 0). After that we can just keep picking data
-                         * out of our most recent hash output.
-                         */
-                        if (jpre == jstart || jpre % 128 == 0) {
-                            /*
-                             * Hash preimage is mostly zeroes, with a
-                             * collection of assorted integer values we had
-                             * anyway.
-                             */
-                            memset(in2i.data, 0, sizeof(in2i.data));
-                            PUT_64BIT_LSB_FIRST(in2i.data +  0, pass);
-                            PUT_64BIT_LSB_FIRST(in2i.data +  8, i);
-                            PUT_64BIT_LSB_FIRST(in2i.data + 16, slice);
-                            PUT_64BIT_LSB_FIRST(in2i.data + 24, mprime);
-                            PUT_64BIT_LSB_FIRST(in2i.data + 32, t);
-                            PUT_64BIT_LSB_FIRST(in2i.data + 40, y);
-                            PUT_64BIT_LSB_FIRST(in2i.data + 48, jpre / 128 + 1);
-
-                            /*
-                             * Now apply G twice to generate the hash output
-                             * in out2i.
-                             */
-                            memset(tmp2i.data, 0, sizeof(tmp2i.data));
-                            G_xor(tmp2i.data, tmp2i.data, in2i.data);
-                            memset(out2i.data, 0, sizeof(out2i.data));
-                            G_xor(out2i.data, out2i.data, tmp2i.data);
-                        }
-
-                        /*
-                         * Extract J1 and J2 from the most recent hash output
-                         * (whether we've just computed it or not).
-                         */
-                        J1 = GET_32BIT_LSB_FIRST(
-                            out2i.data + 8 * (jpre % 128));
-                        J2 = GET_32BIT_LSB_FIRST(
-                            out2i.data + 8 * (jpre % 128) + 4);
-                    }
-
-                    /*
-                     * Now convert J1 and J2 into the index of an existing
-                     * block of the array to use as input to this step. This
-                     * is fairly fiddly.
-                     *
-                     * The easy part: the y-coordinate of the input block is
-                     * obtained by reducing J2 mod p, except that at the very
-                     * start of the algorithm (processing the first slice on
-                     * the first pass) we simply use the same y-coordinate as
-                     * our output block.
-                     *
-                     * Note that it's safe to use the ordinary % operator
-                     * here, without any concern for timing side channels: in
-                     * data-independent mode J2 is not correlated to any
-                     * secrets, and in data-dependent mode we're going to be
-                     * giving away side-channel data _anyway_ when we use it
-                     * as an array index (and by assumption we don't care,
-                     * because it's already massively randomised from the real
-                     * inputs).
-                     */
-                    { // WINSCP
-                    uint32_t index_l = (pass == 0 && slice == 0) ? i : J2 % p;
-
-                    /*
-                     * The hard part: which block in this array row do we use?
-                     *
-                     * First, we decide what the possible candidates are. This
-                     * requires some case analysis, and depends on whether the
-                     * array row is the same one we're writing into or not.
-                     *
-                     * If it's not the same row: we can't use any block from
-                     * the current slice (because the segments within a slice
-                     * have to be processable in parallel, so in a concurrent
-                     * implementation those blocks are potentially in the
-                     * process of being overwritten by other threads). But the
-                     * other three slices are fair game, except that in the
-                     * first pass, slices to the right of us won't have had
-                     * any values written into them yet at all.
-                     *
-                     * If it is the same row, we _are_ allowed to use blocks
-                     * from the current slice, but only the ones before our
-                     * current position.
-                     *
-                     * In both cases, we also exclude the individual _column_
-                     * just to the left of the current one. (The block
-                     * immediately to our left is going to be the _other_
-                     * input to G, but the spec also says that we avoid that
-                     * column even in a different row.)
-                     *
-                     * All of this means that we end up choosing from a
-                     * cyclically contiguous interval of blocks within this
-                     * lane, but the start and end points require some thought
-                     * to get them right.
-                     */
-
-                    /* Start position is the beginning of the _next_ slice
-                     * (containing data from the previous pass), unless we're
-                     * on pass 0, where the start position has to be 0. */
-                    uint32_t Wstart = (pass == 0 ? 0 : (slice + 1) % 4 * SL);
-
-                    /* End position splits up by cases. */
-                    uint32_t Wend;
-                    if (index_l == i) {
-                        /* Same lane as output: we can use anything up to (but
-                         * not including) the block immediately left of us. */
-                        Wend = jm1;
-                    } else {
-                        /* Different lane from output: we can use anything up
-                         * to the previous slice boundary, or one less than
-                         * that if we're at the very left edge of our slice
-                         * right now. */
-                        Wend = SL * slice;
-                        if (jpre == 0)
-                            Wend = (Wend + q-1) % q;
-                    }
-
-                    /* Total number of blocks available to choose from */
-                    { // WINSCP
-                    uint32_t Wsize = (Wend + q - Wstart) % q;
-
-                    /* Fiddly computation from the spec that chooses from the
-                     * available blocks, in a deliberately non-uniform
-                     * fashion, using J1 as pseudorandom input data. Output is
-                     * zz which is the index within our contiguous interval. */
-                    uint32_t x = ((uint64_t)J1 * J1) >> 32;
-                    uint32_t y = ((uint64_t)Wsize * x) >> 32;
-                    uint32_t zz = Wsize - 1 - y;
-
-                    /* And index_z is the actual x coordinate of the block we
-                     * want. */
-                    uint32_t index_z = (Wstart + zz) % q;
-
-                    /* Phew! Combine that block with the one immediately to
-                     * our left, and XOR over the top of whatever is already
-                     * in our current output block. */
-                    G_xor(B[i + p * j].data, B[i + p * jm1].data,
-                          B[index_l + p * index_z].data);
-                    } // WINSCP
-                    } // WINSCP
-                }
-#undef in2i
-#undef tmp2i
-#undef out2i
-}
-#endif
-#ifndef WINSCP_VS
-            }
-
-            /* We've finished processing a slice. Reset jstart to 0. It will
-             * onily _not_ have been 0 if this was pass 0 slice 0, in which
-             * case it still had its initial value of 2 to avoid the starting
-             * data. */
-            jstart = 0;
-            } // WINSCP
-        }
-    }
-
-    /*
-     * The main output is all done. Final output works by taking the XOR of
-     * all the blocks in the rightmost column of the array, and then using
-     * that as input to our long hash H'. The output of _that_ is what we
-     * deliver to the caller.
-     */
-
-    { // WINSCP
-    struct blk C = B[p * (q-1)];
-    size_t i; // WINSCP
-    for (i = 1; i < p; i++)
-        memxor(C.data, C.data, B[i + p * (q-1)].data, 1024);
-
-    {
-        ssh_hash *h = hprime_new(T);
-        put_data(h, C.data, 1024);
-        hprime_final(h, T, out);
-    }
-
-    /*
-     * Clean up.
-     */
-    smemclr(out2i.data, sizeof(out2i.data));
-    smemclr(tmp2i.data, sizeof(tmp2i.data));
-    smemclr(in2i.data, sizeof(in2i.data));
-    smemclr(C.data, sizeof(C.data));
-    smemclr(B, mprime * sizeof(struct blk));
-    sfree(B);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-/*
- * Wrapper function that appends to a strbuf (which sshpubk.c will want).
- */
-void argon2(Argon2Flavour flavour, uint32_t mem, uint32_t passes,
-            uint32_t parallel, uint32_t taglen,
-            ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out)
-{
-    argon2_internal(parallel, taglen, mem, passes, flavour,
-                    P, S, K, X, strbuf_append(out, taglen));
-}
-
-/*
- * Wrapper function which dynamically chooses the number of passes to run in
- * order to hit an approximate total amount of CPU time. Writes the result
- * into 'passes'.
- */
-void argon2_choose_passes(
-    Argon2Flavour flavour, uint32_t mem,
-    uint32_t milliseconds, uint32_t *passes,
-    uint32_t parallel, uint32_t taglen,
-    ptrlen P, ptrlen S, ptrlen K, ptrlen X,
-    strbuf *out)
-{
-    unsigned long desired_time = (TICKSPERSEC * milliseconds) / 1000 /*WINSCP*/ * 3;
-
-    /*
-     * We only need the time taken to be approximately right, so we
-     * scale up the number of passes geometrically, which avoids
-     * taking O(t^2) time to find a pass count taking time t.
-     *
-     * Using the Fibonacci numbers is slightly nicer than the obvious
-     * approach of powers of 2, because it's still very easy to
-     * compute, and grows less fast (powers of 1.6 instead of 2), so
-     * you get just a touch more precision.
-     */
-    uint32_t a = 1, b = 1;
-
-    while (true) {
-        unsigned long start_time = GETTICKCOUNT();
-        argon2(flavour, mem, b, parallel, taglen, P, S, K, X, out);
-        { // WINSCP
-        unsigned long ticks = GETTICKCOUNT() - start_time;
-
-        /* But just in case computers get _too_ fast, we have to cap
-         * the growth before it gets past the uint32_t upper bound! So
-         * if computing a+b would overflow, stop here. */
-
-        if (ticks >= desired_time || a > (uint32_t)~b) {
-            *passes = b;
-            return;
-        } else {
-            strbuf_clear(out);
-
-            /* Next Fibonacci number: replace (a, b) with (b, a+b) */
-            b += a;
-            a = b - a;
-        }
-        } // WINSCP
-    }
-}
-#endif

+ 0 - 170
source/putty/sshauxcrypt.c

@@ -1,170 +0,0 @@
-/*
- * sshauxcrypt.c: wrapper functions on crypto primitives for use in
- * other contexts than the main SSH packet protocol, such as
- * encrypting private key files and performing XDM-AUTHORIZATION-1.
- *
- * These all work through the standard cipher/hash/MAC APIs, so they
- * don't need to live in the same actual source files as the ciphers
- * they wrap, and I think it keeps things tidier to have them out of
- * the way here instead.
- */
-
-#include "ssh.h"
-
-static ssh_cipher *aes256_pubkey_cipher(const void *key, const void *iv)
-{
-    /*
-     * PuTTY's own .PPK format for SSH-2 private key files is
-     * encrypted with 256-bit AES in CBC mode.
-     */
-    { // WINSCP
-    ssh_cipher *cipher = ssh_cipher_new(&ssh_aes256_cbc);
-    ssh_cipher_setkey(cipher, key);
-    ssh_cipher_setiv(cipher, iv);
-    return cipher;
-    } // WINSCP
-}
-
-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);
-}
-
-static ssh_cipher *des3_pubkey_cipher(const void *vkey)
-{
-    /*
-     * SSH-1 private key files are encrypted with triple-DES in SSH-1
-     * style (three separate CBC layers), but the same key is used for
-     * the first and third layers.
-     */
-    ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh1);
-    uint8_t keys3[24], iv[8];
-
-    memcpy(keys3, vkey, 16);
-    memcpy(keys3 + 16, vkey, 8);
-    ssh_cipher_setkey(c, keys3);
-    smemclr(keys3, sizeof(keys3));
-
-    memset(iv, 0, 8);
-    ssh_cipher_setiv(c, iv);
-
-    return c;
-}
-
-void des3_decrypt_pubkey(const void *vkey, void *vblk, int len)
-{
-    ssh_cipher *c = des3_pubkey_cipher(vkey);
-    ssh_cipher_decrypt(c, vblk, len);
-    ssh_cipher_free(c);
-}
-
-void des3_encrypt_pubkey(const void *vkey, void *vblk, int len)
-{
-    ssh_cipher *c = des3_pubkey_cipher(vkey);
-    ssh_cipher_encrypt(c, vblk, len);
-    ssh_cipher_free(c);
-}
-
-static ssh_cipher *des3_pubkey_ossh_cipher(const void *vkey, const void *viv)
-{
-    /*
-     * OpenSSH PEM private key files are encrypted with triple-DES in
-     * SSH-2 style (one CBC layer), with three distinct keys, and an
-     * IV also generated from the passphrase.
-     */
-    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);
-}
-
-static ssh_cipher *des_xdmauth_cipher(const void *vkeydata)
-{
-    /*
-     * XDM-AUTHORIZATION-1 uses single-DES, but packs the key into 7
-     * bytes, so here we have to repack it manually into the canonical
-     * form where it occupies 8 bytes each with the low bit unused.
-     */
-    const unsigned char *keydata = (const unsigned char *)vkeydata;
-    unsigned char key[8];
-    int i, nbits, j;
-    unsigned int bits;
-
-    bits = 0;
-    nbits = 0;
-    j = 0;
-    for (i = 0; i < 8; i++) {
-        if (nbits < 7) {
-            bits = (bits << 8) | keydata[j];
-            nbits += 8;
-            j++;
-        }
-        key[i] = (bits >> (nbits - 7)) << 1;
-        bits &= ~(0x7F << (nbits - 7));
-        nbits -= 7;
-    }
-
-    { // WINSCP
-    ssh_cipher *c = ssh_cipher_new(&ssh_des);
-    ssh_cipher_setkey(c, key);
-    smemclr(key, sizeof(key));
-    ssh_cipher_setiv(c, key);
-    return c;
-    } // WINSCP
-}
-
-void des_encrypt_xdmauth(const void *keydata, void *blk, int len)
-{
-    ssh_cipher *c = des_xdmauth_cipher(keydata);
-    ssh_cipher_encrypt(c, blk, len);
-    ssh_cipher_free(c);
-}
-
-void des_decrypt_xdmauth(const void *keydata, void *blk, int len)
-{
-    ssh_cipher *c = des_xdmauth_cipher(keydata);
-    ssh_cipher_decrypt(c, blk, len);
-    ssh_cipher_free(c);
-}
-
-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);
-}
-
-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 - 119
source/putty/sshbcrypt.c

@@ -1,119 +0,0 @@
-/*
- * 'bcrypt' password hash function, for PuTTY's import/export of
- * OpenSSH encrypted private key files.
- *
- * This is not really the same as the original bcrypt; OpenSSH has
- * modified it in various ways, and of course we have to do the same.
- */
-
-#include <stddef.h>
-#include <string.h>
-#include "ssh.h"
-#include "sshblowf.h"
-
-BlowfishContext *bcrypt_setup(const unsigned char *key, int keybytes,
-                              const unsigned char *salt, int saltbytes)
-{
-    int i;
-    BlowfishContext *ctx;
-
-    ctx = blowfish_make_context();
-    blowfish_initkey(ctx);
-    blowfish_expandkey(ctx, key, keybytes, salt, saltbytes);
-
-    /* Original bcrypt replaces this fixed loop count with the
-     * variable cost. OpenSSH instead iterates the whole thing more
-     * than once if it wants extra rounds. */
-    for (i = 0; i < 64; i++) {
-        blowfish_expandkey(ctx, salt, saltbytes, NULL, 0);
-        blowfish_expandkey(ctx, key, keybytes, NULL, 0);
-    }
-
-    return ctx;
-}
-
-void bcrypt_hash(const unsigned char *key, int keybytes,
-                 const unsigned char *salt, int saltbytes,
-                 unsigned char output[32])
-{
-    BlowfishContext *ctx;
-    int i;
-
-    ctx = bcrypt_setup(key, keybytes, salt, saltbytes);
-    /* This was quite a nice starting string until it ran into
-     * little-endian Blowfish :-/ */
-    memcpy(output, "cyxOmorhcitawolBhsiftawSanyDetim", 32);
-    for (i = 0; i < 64; i++) {
-        blowfish_lsb_encrypt_ecb(output, 32, ctx);
-    }
-    blowfish_free_context(ctx);
-}
-
-void bcrypt_genblock(int counter,
-                     const unsigned char hashed_passphrase[64],
-                     const unsigned char *salt, int saltbytes,
-                     unsigned char output[32])
-{
-    unsigned char hashed_salt[64];
-
-    /* Hash the input salt with the counter value optionally suffixed
-     * to get our real 32-byte salt */
-    ssh_hash *h = ssh_hash_new(&ssh_sha512);
-    put_data(h, salt, saltbytes);
-    if (counter)
-        put_uint32(h, counter);
-    ssh_hash_final(h, hashed_salt);
-
-    bcrypt_hash(hashed_passphrase, 64, hashed_salt, 64, output);
-
-    smemclr(&hashed_salt, sizeof(hashed_salt));
-}
-
-void openssh_bcrypt(const char *passphrase,
-                    const unsigned char *salt, int saltbytes,
-                    int rounds, unsigned char *out, int outbytes)
-{
-    unsigned char hashed_passphrase[64];
-    unsigned char block[32], outblock[32];
-    const unsigned char *thissalt;
-    int thissaltbytes;
-    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);
-
-    /* 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
-     * modulus, and we output the key bytes to indices of out[] in the
-     * following order: first the indices that are multiples of the
-     * modulus, then the ones congruent to 1 mod modulus, etc. Each of
-     * those passes consumes exactly one block output from
-     * bcrypt_genblock, so we must pick a modulus large enough that at
-     * most 32 bytes are used in the pass. */
-    modulus = (outbytes + 31) / 32;
-
-    for (residue = 0; residue < modulus; residue++) {
-        /* Our output block of data is the XOR of all blocks generated
-         * by bcrypt in the following loop */
-        memset(outblock, 0, sizeof(outblock));
-
-        thissalt = salt;
-        thissaltbytes = saltbytes;
-        for (round = 0; round < rounds; round++) {
-            bcrypt_genblock(round == 0 ? residue+1 : 0,
-                            hashed_passphrase,
-                            thissalt, thissaltbytes, block);
-            /* Each subsequent bcrypt call reuses the previous one's
-             * output as its salt */
-            thissalt = block;
-            thissaltbytes = 32;
-
-            for (i = 0; i < 32; i++)
-                outblock[i] ^= block[i];
-        }
-
-        for (i = residue, j = 0; i < outbytes; i += modulus, j++)
-            out[i] = outblock[j];
-    }
-    smemclr(&hashed_passphrase, sizeof(hashed_passphrase));
-}

+ 0 - 242
source/putty/sshblake2.c

@@ -1,242 +0,0 @@
-/*
- * BLAKE2 (RFC 7693) implementation for PuTTY.
- *
- * The BLAKE2 hash family includes BLAKE2s, in which the hash state is
- * operated on as a collection of 32-bit integers, and BLAKE2b, based
- * on 64-bit integers. At present this code implements BLAKE2b only.
- */
-
-#include <assert.h>
-#include "ssh.h"
-
-static inline uint64_t ror(uint64_t x, unsigned rotation)
-{
-#pragma option push -w-ngu // WINSCP
-    unsigned lshift = 63 & -rotation, rshift = 63 & rotation;
-#pragma option pop // WINSCP
-    return (x << lshift) | (x >> rshift);
-}
-
-/* RFC 7963 section 2.1 */
-enum { R1 = 32, R2 = 24, R3 = 16, R4 = 63 };
-
-/* RFC 7693 section 2.6 */
-static const uint64_t iv[] = {
-    // WINSCP (ULL)
-    0x6a09e667f3bcc908ULL,                /* floor(2^64 * frac(sqrt(2)))  */
-    0xbb67ae8584caa73bULL,                /* floor(2^64 * frac(sqrt(3)))  */
-    0x3c6ef372fe94f82bULL,                /* floor(2^64 * frac(sqrt(5)))  */
-    0xa54ff53a5f1d36f1ULL,                /* floor(2^64 * frac(sqrt(7)))  */
-    0x510e527fade682d1ULL,                /* floor(2^64 * frac(sqrt(11))) */
-    0x9b05688c2b3e6c1fULL,                /* floor(2^64 * frac(sqrt(13))) */
-    0x1f83d9abfb41bd6bULL,                /* floor(2^64 * frac(sqrt(17))) */
-    0x5be0cd19137e2179ULL,                /* floor(2^64 * frac(sqrt(19))) */
-};
-
-/* RFC 7693 section 2.7 */
-static const unsigned char sigma[][16] = {
-    { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15},
-    {14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3},
-    {11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4},
-    { 7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8},
-    { 9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13},
-    { 2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9},
-    {12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11},
-    {13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10},
-    { 6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5},
-    {10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13,  0},
-    /* This array recycles if you have more than 10 rounds. BLAKE2b
-     * has 12, so we repeat the first two rows again. */
-    { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15},
-    {14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3},
-};
-
-static inline void g_half(uint64_t v[16], unsigned a, unsigned b, unsigned c,
-                          unsigned d, uint64_t x, unsigned r1, unsigned r2)
-{
-    v[a] += v[b] + x;
-    v[d] ^= v[a];
-    v[d] = ror(v[d], r1);
-    v[c] += v[d];
-    v[b] ^= v[c];
-    v[b] = ror(v[b], r2);
-}
-
-static inline void g(uint64_t v[16], unsigned a, unsigned b, unsigned c,
-                     unsigned d, uint64_t x, uint64_t y)
-{
-    g_half(v, a, b, c, d, x, R1, R2);
-    g_half(v, a, b, c, d, y, R3, R4);
-}
-
-static inline void f(uint64_t h[8], uint64_t m[16], uint64_t offset_hi,
-                     uint64_t offset_lo, unsigned final)
-{
-    uint64_t v[16];
-    memcpy(v, h, 8 * sizeof(*v));
-    memcpy(v + 8, iv, 8 * sizeof(*v));
-    v[12] ^= offset_lo;
-    v[13] ^= offset_hi;
-    v[14] ^= -(uint64_t)final;
-    { // WINSCP
-    unsigned round; // WINSCP
-    for (round = 0; round < 12; round++) {
-        const unsigned char *s = sigma[round];
-        g(v,  0,  4,  8, 12, m[s[ 0]], m[s[ 1]]);
-        g(v,  1,  5,  9, 13, m[s[ 2]], m[s[ 3]]);
-        g(v,  2,  6, 10, 14, m[s[ 4]], m[s[ 5]]);
-        g(v,  3,  7, 11, 15, m[s[ 6]], m[s[ 7]]);
-        g(v,  0,  5, 10, 15, m[s[ 8]], m[s[ 9]]);
-        g(v,  1,  6, 11, 12, m[s[10]], m[s[11]]);
-        g(v,  2,  7,  8, 13, m[s[12]], m[s[13]]);
-        g(v,  3,  4,  9, 14, m[s[14]], m[s[15]]);
-    }
-    { // WINSCP
-    unsigned i; // WINSCP
-    for (i = 0; i < 8; i++)
-        h[i] ^= v[i] ^ v[i+8];
-    smemclr(v, sizeof(v));
-    } // WINSCP
-    } // WINSCP
-}
-
-static inline void f_outer(uint64_t h[8], uint8_t blk[128], uint64_t offset_hi,
-                           uint64_t offset_lo, unsigned final)
-{
-    uint64_t m[16];
-    unsigned i; // WINSCP
-    for (i = 0; i < 16; i++)
-        m[i] = GET_64BIT_LSB_FIRST(blk + 8*i);
-    f(h, m, offset_hi, offset_lo, final);
-    smemclr(m, sizeof(m));
-}
-
-typedef struct blake2b {
-    uint64_t h[8];
-    unsigned hashlen;
-
-    uint8_t block[128];
-    size_t used;
-    uint64_t lenhi, lenlo;
-
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} blake2b;
-
-static void blake2b_write(BinarySink *bs, const void *vp, size_t len);
-
-static ssh_hash *blake2b_new_inner(unsigned hashlen)
-{
-    assert(hashlen <= ssh_blake2b.hlen);
-
-    { // WINSCP
-    blake2b *s = snew(blake2b);
-    s->hash.vt = &ssh_blake2b;
-    s->hashlen = hashlen;
-    BinarySink_INIT(s, blake2b_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-    return &s->hash;
-    } // WINSCP
-}
-
-static ssh_hash *blake2b_new(const ssh_hashalg *alg)
-{
-    return blake2b_new_inner(alg->hlen);
-}
-
-ssh_hash *blake2b_new_general(unsigned hashlen)
-{
-    ssh_hash *h = blake2b_new_inner(hashlen);
-    ssh_hash_reset(h);
-    return h;
-}
-
-static void blake2b_reset(ssh_hash *hash)
-{
-    blake2b *s = container_of(hash, blake2b, hash);
-
-    /* Initialise the hash to the standard IV */
-    memcpy(s->h, iv, sizeof(s->h));
-
-    /* XOR in the parameters: secret key length (here always 0) in
-     * byte 1, and hash length in byte 0. */
-    s->h[0] ^= 0x01010000 ^ s->hashlen;
-
-    s->used = 0;
-    s->lenhi = s->lenlo = 0;
-}
-
-static void blake2b_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    blake2b *copy = container_of(hcopy, blake2b, hash);
-    blake2b *orig = container_of(horig, blake2b, hash);
-
-    memcpy(copy, orig, sizeof(*copy));
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-static void blake2b_free(ssh_hash *hash)
-{
-    blake2b *s = container_of(hash, blake2b, hash);
-
-    smemclr(s, sizeof(*s));
-    sfree(s);
-}
-
-static void blake2b_write(BinarySink *bs, const void *vp, size_t len)
-{
-    blake2b *s = BinarySink_DOWNCAST(bs, blake2b);
-    const uint8_t *p = vp;
-
-    while (len > 0) {
-        if (s->used == sizeof(s->block)) {
-            f_outer(s->h, s->block, s->lenhi, s->lenlo, 0);
-            s->used = 0;
-        }
-
-        { // WINSCP
-        size_t chunk = sizeof(s->block) - s->used;
-        if (chunk > len)
-            chunk = len;
-
-        memcpy(s->block + s->used, p, chunk);
-        s->used += chunk;
-        p += chunk;
-        len -= chunk;
-
-        s->lenlo += chunk;
-        s->lenhi += (s->lenlo < chunk);
-        } // WINSCP
-    }
-}
-
-static void blake2b_digest(ssh_hash *hash, uint8_t *digest)
-{
-    blake2b *s = container_of(hash, blake2b, hash);
-
-    memset(s->block + s->used, 0, sizeof(s->block) - s->used);
-    f_outer(s->h, s->block, s->lenhi, s->lenlo, 1);
-
-    { // WINSCP
-    uint8_t hash_pre[128];
-    unsigned i; // WINSCP
-    for (i = 0; i < 8; i++)
-        PUT_64BIT_LSB_FIRST(hash_pre + 8*i, s->h[i]);
-    memcpy(digest, hash_pre, s->hashlen);
-    smemclr(hash_pre, sizeof(hash_pre));
-    } // WINSCP
-}
-
-const ssh_hashalg ssh_blake2b = {
-    // WINSCP
-    /*.new =*/ blake2b_new,
-    /*.reset =*/ blake2b_reset,
-    /*.copyfrom =*/ blake2b_copyfrom,
-    /*.digest =*/ blake2b_digest,
-    /*.free =*/ blake2b_free,
-    /*.hlen =*/ 64,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_BARE("BLAKE2b-64"),
-    NULL, // WINSCP
-};

+ 0 - 708
source/putty/sshblowf.c

@@ -1,708 +0,0 @@
-/*
- * Blowfish implementation for PuTTY.
- *
- * Coded from scratch from the algorithm description.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include "ssh.h"
-#include "sshblowf.h"
-
-struct BlowfishContext {
-    uint32_t S0[256], S1[256], S2[256], S3[256], P[18];
-    uint32_t iv0, iv1;                 /* for CBC mode */
-};
-
-/*
- * The Blowfish init data: hex digits of the fractional part of pi.
- * (ie pi as a hex fraction is 3.243F6A8885A308D3...)
- *
- * If you have Simon Tatham's 'spigot' exact real calculator
- * available, or any other method of generating 8336 fractional hex
- * digits of pi on standard output, you can regenerate these tables
- * exactly as below using the following Perl script (adjusting the
- * first line or two if your pi-generator is not spigot).
-
-open my $spig, "spigot -n -B16 -d8336 pi |";
-read $spig, $ignore, 2; # throw away the leading "3."
-for my $name ("parray", "sbox0".."sbox3") {
-    print "static const uint32_t ${name}[] = {\n";
-    my $len = $name eq "parray" ? 18 : 256;
-    for my $i (1..$len) {
-        read $spig, $word, 8;
-        printf "%s0x%s,", ($i%6==1 ? "    " : " "), uc $word;
-        print "\n" if ($i == $len || $i%6 == 0);
-    }
-    print "};\n\n";
-}
-close $spig;
-
- */
-static const uint32_t parray[] = {
-    0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
-    0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
-    0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B,
-};
-
-static const uint32_t sbox0[] = {
-    0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96,
-    0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
-    0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658,
-    0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
-    0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E,
-    0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
-    0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6,
-    0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
-    0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C,
-    0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
-    0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1,
-    0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
-    0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A,
-    0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
-    0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176,
-    0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
-    0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706,
-    0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
-    0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B,
-    0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
-    0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C,
-    0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
-    0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A,
-    0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
-    0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760,
-    0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
-    0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8,
-    0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
-    0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33,
-    0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
-    0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0,
-    0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
-    0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777,
-    0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
-    0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705,
-    0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
-    0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E,
-    0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
-    0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9,
-    0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
-    0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F,
-    0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
-    0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A,
-};
-
-static const uint32_t sbox1[] = {
-    0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D,
-    0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
-    0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65,
-    0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
-    0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9,
-    0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
-    0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D,
-    0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
-    0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC,
-    0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
-    0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908,
-    0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
-    0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124,
-    0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
-    0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908,
-    0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
-    0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B,
-    0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
-    0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA,
-    0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
-    0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D,
-    0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
-    0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5,
-    0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
-    0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96,
-    0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
-    0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA,
-    0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
-    0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77,
-    0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
-    0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054,
-    0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
-    0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA,
-    0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
-    0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646,
-    0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
-    0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA,
-    0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
-    0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E,
-    0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
-    0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD,
-    0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
-    0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7,
-};
-
-static const uint32_t sbox2[] = {
-    0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7,
-    0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
-    0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF,
-    0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
-    0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4,
-    0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
-    0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC,
-    0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
-    0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332,
-    0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
-    0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58,
-    0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
-    0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22,
-    0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
-    0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60,
-    0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
-    0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99,
-    0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
-    0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74,
-    0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
-    0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3,
-    0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
-    0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979,
-    0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
-    0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA,
-    0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
-    0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086,
-    0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
-    0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24,
-    0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
-    0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84,
-    0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
-    0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09,
-    0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
-    0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE,
-    0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
-    0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0,
-    0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
-    0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188,
-    0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
-    0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8,
-    0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
-    0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0,
-};
-
-static const uint32_t sbox3[] = {
-    0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742,
-    0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
-    0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79,
-    0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
-    0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A,
-    0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
-    0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1,
-    0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
-    0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797,
-    0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
-    0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6,
-    0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
-    0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA,
-    0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
-    0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5,
-    0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
-    0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE,
-    0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
-    0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD,
-    0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
-    0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB,
-    0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
-    0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC,
-    0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
-    0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC,
-    0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
-    0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A,
-    0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
-    0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A,
-    0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
-    0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B,
-    0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
-    0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E,
-    0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
-    0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623,
-    0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
-    0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A,
-    0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
-    0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3,
-    0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
-    0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C,
-    0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
-    0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6,
-};
-
-#define Fprime(a,b,c,d) ( ( (S0[a] + S1[b]) ^ S2[c] ) + S3[d] )
-#define F(x) Fprime( ((x>>24)&0xFF), ((x>>16)&0xFF), ((x>>8)&0xFF), (x&0xFF) )
-#define ROUND(n) ( xL ^= P[n], t = xL, xL = F(xL) ^ xR, xR = t )
-
-static void blowfish_encrypt(uint32_t xL, uint32_t xR, uint32_t *output,
-                             BlowfishContext * ctx)
-{
-    uint32_t *S0 = ctx->S0;
-    uint32_t *S1 = ctx->S1;
-    uint32_t *S2 = ctx->S2;
-    uint32_t *S3 = ctx->S3;
-    uint32_t *P = ctx->P;
-    uint32_t t;
-
-    ROUND(0);
-    ROUND(1);
-    ROUND(2);
-    ROUND(3);
-    ROUND(4);
-    ROUND(5);
-    ROUND(6);
-    ROUND(7);
-    ROUND(8);
-    ROUND(9);
-    ROUND(10);
-    ROUND(11);
-    ROUND(12);
-    ROUND(13);
-    ROUND(14);
-    ROUND(15);
-    xL ^= P[16];
-    xR ^= P[17];
-
-    output[0] = xR;
-    output[1] = xL;
-}
-
-static void blowfish_decrypt(uint32_t xL, uint32_t xR, uint32_t *output,
-                             BlowfishContext * ctx)
-{
-    uint32_t *S0 = ctx->S0;
-    uint32_t *S1 = ctx->S1;
-    uint32_t *S2 = ctx->S2;
-    uint32_t *S3 = ctx->S3;
-    uint32_t *P = ctx->P;
-    uint32_t t;
-
-    ROUND(17);
-    ROUND(16);
-    ROUND(15);
-    ROUND(14);
-    ROUND(13);
-    ROUND(12);
-    ROUND(11);
-    ROUND(10);
-    ROUND(9);
-    ROUND(8);
-    ROUND(7);
-    ROUND(6);
-    ROUND(5);
-    ROUND(4);
-    ROUND(3);
-    ROUND(2);
-    xL ^= P[1];
-    xR ^= P[0];
-
-    output[0] = xR;
-    output[1] = xL;
-}
-
-static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len,
-                                     BlowfishContext * ctx)
-{
-    uint32_t xL, xR, out[2], iv0, iv1;
-
-    assert((len & 7) == 0);
-
-    iv0 = ctx->iv0;
-    iv1 = ctx->iv1;
-
-    while (len > 0) {
-        xL = GET_32BIT_LSB_FIRST(blk);
-        xR = GET_32BIT_LSB_FIRST(blk + 4);
-        iv0 ^= xL;
-        iv1 ^= xR;
-        blowfish_encrypt(iv0, iv1, out, ctx);
-        iv0 = out[0];
-        iv1 = out[1];
-        PUT_32BIT_LSB_FIRST(blk, iv0);
-        PUT_32BIT_LSB_FIRST(blk + 4, iv1);
-        blk += 8;
-        len -= 8;
-    }
-
-    ctx->iv0 = iv0;
-    ctx->iv1 = iv1;
-}
-
-void blowfish_lsb_encrypt_ecb(void *vblk, int len, BlowfishContext * ctx)
-{
-    unsigned char *blk = (unsigned char *)vblk;
-    uint32_t xL, xR, out[2];
-
-    assert((len & 7) == 0);
-
-    while (len > 0) {
-        xL = GET_32BIT_LSB_FIRST(blk);
-        xR = GET_32BIT_LSB_FIRST(blk + 4);
-        blowfish_encrypt(xL, xR, out, ctx);
-        PUT_32BIT_LSB_FIRST(blk, out[0]);
-        PUT_32BIT_LSB_FIRST(blk + 4, out[1]);
-        blk += 8;
-        len -= 8;
-    }
-}
-
-static void blowfish_lsb_decrypt_cbc(unsigned char *blk, int len,
-                                     BlowfishContext * ctx)
-{
-    uint32_t xL, xR, out[2], iv0, iv1;
-
-    assert((len & 7) == 0);
-
-    iv0 = ctx->iv0;
-    iv1 = ctx->iv1;
-
-    while (len > 0) {
-        xL = GET_32BIT_LSB_FIRST(blk);
-        xR = GET_32BIT_LSB_FIRST(blk + 4);
-        blowfish_decrypt(xL, xR, out, ctx);
-        iv0 ^= out[0];
-        iv1 ^= out[1];
-        PUT_32BIT_LSB_FIRST(blk, iv0);
-        PUT_32BIT_LSB_FIRST(blk + 4, iv1);
-        iv0 = xL;
-        iv1 = xR;
-        blk += 8;
-        len -= 8;
-    }
-
-    ctx->iv0 = iv0;
-    ctx->iv1 = iv1;
-}
-
-static void blowfish_msb_encrypt_cbc(unsigned char *blk, int len,
-                                     BlowfishContext * ctx)
-{
-    uint32_t xL, xR, out[2], iv0, iv1;
-
-    assert((len & 7) == 0);
-
-    iv0 = ctx->iv0;
-    iv1 = ctx->iv1;
-
-    while (len > 0) {
-        xL = GET_32BIT_MSB_FIRST(blk);
-        xR = GET_32BIT_MSB_FIRST(blk + 4);
-        iv0 ^= xL;
-        iv1 ^= xR;
-        blowfish_encrypt(iv0, iv1, out, ctx);
-        iv0 = out[0];
-        iv1 = out[1];
-        PUT_32BIT_MSB_FIRST(blk, iv0);
-        PUT_32BIT_MSB_FIRST(blk + 4, iv1);
-        blk += 8;
-        len -= 8;
-    }
-
-    ctx->iv0 = iv0;
-    ctx->iv1 = iv1;
-}
-
-static void blowfish_msb_decrypt_cbc(unsigned char *blk, int len,
-                                     BlowfishContext * ctx)
-{
-    uint32_t xL, xR, out[2], iv0, iv1;
-
-    assert((len & 7) == 0);
-
-    iv0 = ctx->iv0;
-    iv1 = ctx->iv1;
-
-    while (len > 0) {
-        xL = GET_32BIT_MSB_FIRST(blk);
-        xR = GET_32BIT_MSB_FIRST(blk + 4);
-        blowfish_decrypt(xL, xR, out, ctx);
-        iv0 ^= out[0];
-        iv1 ^= out[1];
-        PUT_32BIT_MSB_FIRST(blk, iv0);
-        PUT_32BIT_MSB_FIRST(blk + 4, iv1);
-        iv0 = xL;
-        iv1 = xR;
-        blk += 8;
-        len -= 8;
-    }
-
-    ctx->iv0 = iv0;
-    ctx->iv1 = iv1;
-}
-
-static void blowfish_msb_sdctr(unsigned char *blk, int len,
-                                     BlowfishContext * ctx)
-{
-    uint32_t b[2], iv0, iv1, tmp;
-
-    assert((len & 7) == 0);
-
-    iv0 = ctx->iv0;
-    iv1 = ctx->iv1;
-
-    while (len > 0) {
-        blowfish_encrypt(iv0, iv1, b, ctx);
-        tmp = GET_32BIT_MSB_FIRST(blk);
-        PUT_32BIT_MSB_FIRST(blk, tmp ^ b[0]);
-        tmp = GET_32BIT_MSB_FIRST(blk + 4);
-        PUT_32BIT_MSB_FIRST(blk + 4, tmp ^ b[1]);
-        if ((iv1 = (iv1 + 1) & 0xffffffff) == 0)
-            iv0 = (iv0 + 1) & 0xffffffff;
-        blk += 8;
-        len -= 8;
-    }
-
-    ctx->iv0 = iv0;
-    ctx->iv1 = iv1;
-}
-
-void blowfish_initkey(BlowfishContext *ctx)
-{
-    int i;
-
-    for (i = 0; i < 18; i++) {
-        ctx->P[i] = parray[i];
-    }
-
-    for (i = 0; i < 256; i++) {
-        ctx->S0[i] = sbox0[i];
-        ctx->S1[i] = sbox1[i];
-        ctx->S2[i] = sbox2[i];
-        ctx->S3[i] = sbox3[i];
-    }
-}
-
-void blowfish_expandkey(BlowfishContext * ctx,
-                        const void *vkey, short keybytes,
-                        const void *vsalt, short saltbytes)
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-    const unsigned char *salt = (const unsigned char *)vsalt;
-    uint32_t *S0 = ctx->S0;
-    uint32_t *S1 = ctx->S1;
-    uint32_t *S2 = ctx->S2;
-    uint32_t *S3 = ctx->S3;
-    uint32_t *P = ctx->P;
-    uint32_t str[2];
-    int i, j;
-    int saltpos;
-    unsigned char dummysalt[1];
-
-    saltpos = 0;
-    if (!salt) {
-        saltbytes = 1;
-        salt = dummysalt;
-        dummysalt[0] = 0;
-    }
-
-    for (i = 0; i < 18; i++) {
-        P[i] ^=
-            ((uint32_t) (unsigned char) (key[(i * 4 + 0) % keybytes])) << 24;
-        P[i] ^=
-            ((uint32_t) (unsigned char) (key[(i * 4 + 1) % keybytes])) << 16;
-        P[i] ^=
-            ((uint32_t) (unsigned char) (key[(i * 4 + 2) % keybytes])) << 8;
-        P[i] ^= ((uint32_t) (unsigned char) (key[(i * 4 + 3) % keybytes]));
-    }
-
-    str[0] = str[1] = 0;
-
-    for (i = 0; i < 18; i += 2) {
-        for (j = 0; j < 8; j++)
-            str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
-
-        blowfish_encrypt(str[0], str[1], str, ctx);
-        P[i] = str[0];
-        P[i + 1] = str[1];
-    }
-
-    for (i = 0; i < 256; i += 2) {
-        for (j = 0; j < 8; j++)
-            str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
-        blowfish_encrypt(str[0], str[1], str, ctx);
-        S0[i] = str[0];
-        S0[i + 1] = str[1];
-    }
-    for (i = 0; i < 256; i += 2) {
-        for (j = 0; j < 8; j++)
-            str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
-        blowfish_encrypt(str[0], str[1], str, ctx);
-        S1[i] = str[0];
-        S1[i + 1] = str[1];
-    }
-    for (i = 0; i < 256; i += 2) {
-        for (j = 0; j < 8; j++)
-            str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
-        blowfish_encrypt(str[0], str[1], str, ctx);
-        S2[i] = str[0];
-        S2[i + 1] = str[1];
-    }
-    for (i = 0; i < 256; i += 2) {
-        for (j = 0; j < 8; j++)
-            str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4));
-        blowfish_encrypt(str[0], str[1], str, ctx);
-        S3[i] = str[0];
-        S3[i + 1] = str[1];
-    }
-}
-
-static void blowfish_setkey(BlowfishContext *ctx,
-                            const unsigned char *key, short keybytes)
-{
-    blowfish_initkey(ctx);
-    blowfish_expandkey(ctx, key, keybytes, NULL, 0);
-}
-
-/* -- Interface with PuTTY -- */
-
-#define SSH1_SESSION_KEY_LENGTH 32
-
-BlowfishContext *blowfish_make_context(void)
-{
-    return snew(BlowfishContext);
-}
-
-void blowfish_free_context(BlowfishContext *ctx)
-{
-    sfree(ctx);
-}
-
-static void blowfish_iv_be(BlowfishContext *ctx, const void *viv)
-{
-    const unsigned char *iv = (const unsigned char *)viv;
-    ctx->iv0 = GET_32BIT_MSB_FIRST(iv);
-    ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4);
-}
-
-static void blowfish_iv_le(BlowfishContext *ctx, const void *viv)
-{
-    const unsigned char *iv = (const unsigned char *)viv;
-    ctx->iv0 = GET_32BIT_LSB_FIRST(iv);
-    ctx->iv1 = GET_32BIT_LSB_FIRST(iv + 4);
-}
-
-struct blowfish_ctx {
-    BlowfishContext context;
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *blowfish_new(const ssh_cipheralg *alg)
-{
-    struct blowfish_ctx *ctx = snew(struct blowfish_ctx);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void blowfish_free(ssh_cipher *cipher)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void blowfish_ssh_setkey(ssh_cipher *cipher, const void *key)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_setkey(&ctx->context, key, ctx->ciph.vt->padded_keybytes);
-}
-
-static void blowfish_ssh1_setiv(ssh_cipher *cipher, const void *iv)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_iv_le(&ctx->context, iv);
-}
-
-static void blowfish_ssh2_setiv(ssh_cipher *cipher, const void *iv)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_iv_be(&ctx->context, iv);
-}
-
-static void blowfish_ssh1_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_lsb_encrypt_cbc(blk, len, &ctx->context);
-}
-
-static void blowfish_ssh1_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_lsb_decrypt_cbc(blk, len, &ctx->context);
-}
-
-static void blowfish_ssh2_encrypt_blk(ssh_cipher *cipher, void *blk, int len)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_msb_encrypt_cbc(blk, len, &ctx->context);
-}
-
-static void blowfish_ssh2_decrypt_blk(ssh_cipher *cipher, void *blk, int len)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_msb_decrypt_cbc(blk, len, &ctx->context);
-}
-
-static void blowfish_ssh2_sdctr(ssh_cipher *cipher, void *blk, int len)
-{
-    struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph);
-    blowfish_msb_sdctr(blk, len, &ctx->context);
-}
-
-const ssh_cipheralg ssh_blowfish_ssh1 = {
-    // WINSCP
-    /*.new =*/ blowfish_new,
-    /*.free =*/ blowfish_free,
-    /*.setiv =*/ blowfish_ssh1_setiv,
-    /*.setkey =*/ blowfish_ssh_setkey,
-    /*.encrypt =*/ blowfish_ssh1_encrypt_blk,
-    /*.decrypt =*/ blowfish_ssh1_decrypt_blk,
-    NULL, NULL, NULL, // WINSCP
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 128,
-    /*.padded_keybytes =*/ SSH1_SESSION_KEY_LENGTH,
-    /*.flags =*/ SSH_CIPHER_IS_CBC,
-    /*.text_name =*/ "Blowfish-256 CBC",
-    NULL, NULL, // WINSCP
-};
-
-const ssh_cipheralg ssh_blowfish_ssh2 = {
-    // WINSCP
-    /*.new =*/ blowfish_new,
-    /*.free =*/ blowfish_free,
-    /*.setiv =*/ blowfish_ssh2_setiv,
-    /*.setkey =*/ blowfish_ssh_setkey,
-    /*.encrypt =*/ blowfish_ssh2_encrypt_blk,
-    /*.decrypt =*/ blowfish_ssh2_decrypt_blk,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "blowfish-cbc",
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 128,
-    /*.padded_keybytes =*/ 16,
-    /*.flags =*/ SSH_CIPHER_IS_CBC,
-    /*.text_name =*/ "Blowfish-128 CBC",
-    NULL, NULL, // WINSCP
-};
-
-const ssh_cipheralg ssh_blowfish_ssh2_ctr = {
-    // WINSCP
-    /*.new =*/ blowfish_new,
-    /*.free =*/ blowfish_free,
-    /*.setiv =*/ blowfish_ssh2_setiv,
-    /*.setkey =*/ blowfish_ssh_setkey,
-    /*.encrypt =*/ blowfish_ssh2_sdctr,
-    /*.decrypt =*/ blowfish_ssh2_sdctr,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "blowfish-ctr",
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 256,
-    /*.padded_keybytes =*/ 32,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "Blowfish-256 SDCTR",
-    NULL, NULL, // WINSCP
-};
-
-static const ssh_cipheralg *const blowfish_list[] = {
-    &ssh_blowfish_ssh2_ctr,
-    &ssh_blowfish_ssh2
-};
-
-const ssh2_ciphers ssh2_blowfish = { lenof(blowfish_list), blowfish_list };

+ 0 - 14
source/putty/sshblowf.h

@@ -1,14 +0,0 @@
-/*
- * Header file shared between sshblowf.c and sshbcrypt.c. Exposes the
- * internal Blowfish routines needed by bcrypt.
- */
-
-typedef struct BlowfishContext BlowfishContext;
-
-BlowfishContext *blowfish_make_context(void);
-void blowfish_free_context(BlowfishContext *ctx);
-void blowfish_initkey(BlowfishContext *ctx);
-void blowfish_expandkey(BlowfishContext *ctx,
-                        const void *key, short keybytes,
-                        const void *salt, short saltbytes);
-void blowfish_lsb_encrypt_ecb(void *blk, int len, BlowfishContext *ctx);

+ 0 - 186
source/putty/sshbpp.h

@@ -1,186 +0,0 @@
-/*
- * Abstraction of the binary packet protocols used in SSH.
- */
-
-#ifndef PUTTY_SSHBPP_H
-#define PUTTY_SSHBPP_H
-
-typedef struct BinaryPacketProtocolVtable BinaryPacketProtocolVtable;
-
-struct BinaryPacketProtocolVtable {
-    void (*free)(BinaryPacketProtocol *);
-    void (*handle_input)(BinaryPacketProtocol *);
-    void (*handle_output)(BinaryPacketProtocol *);
-    PktOut *(*new_pktout)(int type);
-    void (*queue_disconnect)(BinaryPacketProtocol *,
-                             const char *msg, int category);
-    uint32_t packet_size_limit;
-};
-
-struct BinaryPacketProtocol {
-    const struct BinaryPacketProtocolVtable *vt;
-    bufchain *in_raw, *out_raw;
-    bool input_eof;   /* set this if in_raw will never be added to again */
-    PktInQueue in_pq;
-    PktOutQueue out_pq;
-    PacketLogSettings *pls;
-    LogContext *logctx;
-    Ssh *ssh;
-
-    /* ic_in_raw is filled in by the BPP (probably by calling
-     * ssh_bpp_common_setup). The BPP's owner triggers it when data is
-     * added to in_raw, and also when the BPP is newly created. */
-    IdempotentCallback ic_in_raw;
-
-    /* ic_out_pq is entirely internal to the BPP itself; it's used as
-     * the callback on out_pq. */
-    IdempotentCallback ic_out_pq;
-
-    /* Information that all packet layers sharing this BPP will
-     * potentially be interested in. */
-    int remote_bugs;
-    bool ext_info_rsa_sha256_ok, ext_info_rsa_sha512_ok;
-
-    /* Set this if remote connection closure should not generate an
-     * error message (either because it's not to be treated as an
-     * error at all, or because some other error message has already
-     * been emitted). */
-    bool expect_close;
-};
-
-static inline void ssh_bpp_handle_input(BinaryPacketProtocol *bpp)
-{ bpp->vt->handle_input(bpp); }
-static inline void ssh_bpp_handle_output(BinaryPacketProtocol *bpp)
-{ bpp->vt->handle_output(bpp); }
-static inline PktOut *ssh_bpp_new_pktout(BinaryPacketProtocol *bpp, int type)
-{ return bpp->vt->new_pktout(type); }
-static inline void ssh_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
-                                            const char *msg, int category)
-{ bpp->vt->queue_disconnect(bpp, msg, category); }
-
-/* ssh_bpp_free is more than just a macro wrapper on the vtable; it
- * does centralised parts of the freeing too. */
-void ssh_bpp_free(BinaryPacketProtocol *bpp);
-
-BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx);
-void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
-                         const ssh_cipheralg *cipher,
-                         const void *session_key);
-/* This is only called from outside the BPP in server mode; in client
- * mode the BPP detects compression start time automatically by
- * snooping message types */
-void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
-
-/* Helper routine which does common BPP initialisation, e.g. setting
- * up in_pq and out_pq, and initialising input_consumer. */
-void ssh_bpp_common_setup(BinaryPacketProtocol *);
-
-/* Common helper functions between the SSH-2 full and bare BPPs */
-void ssh2_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
-                               const char *msg, int category);
-bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin);
-
-/* Convenience macro for BPPs to send formatted strings to the Event
- * Log. Assumes a function parameter called 'bpp' is in scope. */
-#define bpp_logevent(...) ( \
-    logevent_and_free((bpp)->logctx, dupprintf(__VA_ARGS__)))
-
-/*
- * Structure that tracks how much data is sent and received, for
- * purposes of triggering an SSH-2 rekey when either one gets over a
- * configured limit. In each direction, the flag 'running' indicates
- * that we haven't hit the limit yet, and 'remaining' tracks how much
- * longer until we do. The function dts_consume() subtracts a given
- * amount from the counter in a particular direction, and sets
- * 'expired' if the limit has been hit.
- *
- * The limit is sticky: once 'running' has flipped to false,
- * 'remaining' is no longer decremented, so it shouldn't dangerously
- * wrap round.
- */
-struct DataTransferStatsDirection {
-    bool running, expired;
-    unsigned long remaining;
-};
-struct DataTransferStats {
-    struct DataTransferStatsDirection in, out;
-};
-static inline void dts_consume(struct DataTransferStatsDirection *s,
-                               unsigned long size_consumed)
-{
-    if (s->running) {
-        if (s->remaining <= size_consumed) {
-            s->running = false;
-            s->expired = true;
-        } else {
-            s->remaining -= size_consumed;
-        }
-    }
-}
-static inline void dts_reset(struct DataTransferStatsDirection *s,
-                             unsigned long starting_size)
-{
-    s->expired = false;
-    s->remaining = starting_size;
-    /*
-     * The semantics of setting CONF_ssh_rekey_data to zero are to
-     * disable data-volume based rekeying completely. So if the
-     * starting size is actually zero, we don't set 'running' to true
-     * in the first place, which means we won't ever set the expired
-     * flag.
-     */
-    s->running = (starting_size != 0);
-}
-
-BinaryPacketProtocol *ssh2_bpp_new(
-    LogContext *logctx, struct DataTransferStats *stats, bool is_server);
-void ssh2_bpp_new_outgoing_crypto(
-    BinaryPacketProtocol *bpp,
-    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
-    const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
-    const ssh_compression_alg *compression, bool delayed_compression);
-void ssh2_bpp_new_incoming_crypto(
-    BinaryPacketProtocol *bpp,
-    const ssh_cipheralg *cipher, const void *ckey, const void *iv,
-    const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
-    const ssh_compression_alg *compression, bool delayed_compression);
-
-/*
- * A query method specific to the interface between ssh2transport and
- * ssh2bpp. If true, it indicates that we're potentially in the
- * race-condition-prone part of delayed compression setup and so
- * asynchronous outgoing transport-layer packets are currently not
- * being sent, which means in particular that it would be a bad idea
- * to start a rekey because then we'd stop responding to anything
- * _other_ than transport-layer packets and deadlock the protocol.
- */
-bool ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp);
-
-BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx);
-
-/*
- * The initial code to handle the SSH version exchange is also
- * structured as an implementation of BinaryPacketProtocol, because
- * that makes it easy to switch from that to the next BPP once it
- * tells us which one we're using.
- */
-struct ssh_version_receiver {
-    void (*got_ssh_version)(struct ssh_version_receiver *rcv,
-                            int major_version);
-};
-BinaryPacketProtocol *ssh_verstring_new(
-    Conf *conf, LogContext *logctx, bool bare_connection_mode,
-    const char *protoversion, struct ssh_version_receiver *rcv,
-    bool server_mode, const char *impl_name);
-const char *ssh_verstring_get_remote(BinaryPacketProtocol *);
-const char *ssh_verstring_get_local(BinaryPacketProtocol *);
-int ssh_verstring_get_bugs(BinaryPacketProtocol *);
-
-#ifdef MPEXT
-const ssh_cipher * ssh2_bpp_get_cscipher(BinaryPacketProtocol *bpp);
-const ssh_cipher * ssh2_bpp_get_sccipher(BinaryPacketProtocol *bpp);
-const struct ssh_compressor * ssh2_bpp_get_cscomp(BinaryPacketProtocol *bpp);
-const struct ssh_decompressor * ssh2_bpp_get_sccomp(BinaryPacketProtocol *bpp);
-#endif
-
-#endif /* PUTTY_SSHBPP_H */

+ 0 - 1066
source/putty/sshccp.c

@@ -1,1066 +0,0 @@
-/*
- * ChaCha20-Poly1305 Implementation for SSH-2
- *
- * Protocol spec:
- *  http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.chacha20poly1305?rev=1.2&content-type=text/x-cvsweb-markup
- *
- * ChaCha20 spec:
- *  http://cr.yp.to/chacha/chacha-20080128.pdf
- *
- * Salsa20 spec:
- *  http://cr.yp.to/snuffle/spec.pdf
- *
- * Poly1305-AES spec:
- *  http://cr.yp.to/mac/poly1305-20050329.pdf
- *
- * The nonce for the Poly1305 is the second part of the key output
- * from the first round of ChaCha20. This removes the AES requirement.
- * This is undocumented!
- *
- * This has an intricate link between the cipher and the MAC. The
- * keying of both is done in by the cipher and setting of the IV is
- * done by the MAC. One cannot operate without the other. The
- * configuration of the ssh_cipheralg structure ensures that the MAC is
- * set (and others ignored) if this cipher is chosen.
- *
- * This cipher also encrypts the length using a different
- * instantiation of the cipher using a different key and IV made from
- * the sequence number which is passed in addition when calling
- * encrypt/decrypt on it.
- */
-
-#include "ssh.h"
-#include "mpint_i.h"
-
-#ifndef INLINE
-#define INLINE
-#endif
-
-/* ChaCha20 implementation, only supporting 256-bit keys */
-
-/* State for each ChaCha20 instance */
-struct chacha20 {
-    /* Current context, usually with the count incremented
-     * 0-3 are the static constant
-     * 4-11 are the key
-     * 12-13 are the counter
-     * 14-15 are the IV */
-    uint32_t state[16];
-    /* The output of the state above ready to xor */
-    unsigned char current[64];
-    /* The index of the above currently used to allow a true streaming cipher */
-    int currentIndex;
-};
-
-static INLINE void chacha20_round(struct chacha20 *ctx)
-{
-    int i;
-    uint32_t copy[16];
-
-    /* Take a copy */
-    memcpy(copy, ctx->state, sizeof(copy));
-
-    /* A circular rotation for a 32bit number */
-#define rotl(x, shift) x = ((x << shift) | (x >> (32 - shift)))
-
-    /* What to do for each quarter round operation */
-#define qrop(a, b, c, d)                        \
-    copy[a] += copy[b];                         \
-    copy[c] ^= copy[a];                         \
-    rotl(copy[c], d)
-
-    /* A quarter round */
-#define quarter(a, b, c, d)                     \
-    qrop(a, b, d, 16);                          \
-    qrop(c, d, b, 12);                          \
-    qrop(a, b, d, 8);                           \
-    qrop(c, d, b, 7)
-
-    /* Do 20 rounds, in pairs because every other is different */
-    for (i = 0; i < 20; i += 2) {
-        /* A round */
-        quarter(0, 4, 8, 12);
-        quarter(1, 5, 9, 13);
-        quarter(2, 6, 10, 14);
-        quarter(3, 7, 11, 15);
-        /* Another slightly different round */
-        quarter(0, 5, 10, 15);
-        quarter(1, 6, 11, 12);
-        quarter(2, 7, 8, 13);
-        quarter(3, 4, 9, 14);
-    }
-
-    /* Dump the macros, don't need them littering */
-#undef rotl
-#undef qrop
-#undef quarter
-
-    /* Add the initial state */
-    for (i = 0; i < 16; ++i) {
-        copy[i] += ctx->state[i];
-    }
-
-    /* Update the content of the xor buffer */
-    for (i = 0; i < 16; ++i) {
-        ctx->current[i * 4 + 0] = copy[i] >> 0;
-        ctx->current[i * 4 + 1] = copy[i] >> 8;
-        ctx->current[i * 4 + 2] = copy[i] >> 16;
-        ctx->current[i * 4 + 3] = copy[i] >> 24;
-    }
-    /* State full, reset pointer to beginning */
-    ctx->currentIndex = 0;
-    smemclr(copy, sizeof(copy));
-
-    /* Increment round counter */
-    ++ctx->state[12];
-    /* Check for overflow, not done in one line so the 32 bits are chopped by the type */
-    if (!(uint32_t)(ctx->state[12])) {
-        ++ctx->state[13];
-    }
-}
-
-/* Initialise context with 256bit key */
-static void chacha20_key(struct chacha20 *ctx, const unsigned char *key)
-{
-    static const char constant[16] = "expand 32-byte k";
-
-    /* Add the fixed string to the start of the state */
-    ctx->state[0] = GET_32BIT_LSB_FIRST(constant + 0);
-    ctx->state[1] = GET_32BIT_LSB_FIRST(constant + 4);
-    ctx->state[2] = GET_32BIT_LSB_FIRST(constant + 8);
-    ctx->state[3] = GET_32BIT_LSB_FIRST(constant + 12);
-
-    /* Add the key */
-    ctx->state[4]  = GET_32BIT_LSB_FIRST(key + 0);
-    ctx->state[5]  = GET_32BIT_LSB_FIRST(key + 4);
-    ctx->state[6]  = GET_32BIT_LSB_FIRST(key + 8);
-    ctx->state[7]  = GET_32BIT_LSB_FIRST(key + 12);
-    ctx->state[8]  = GET_32BIT_LSB_FIRST(key + 16);
-    ctx->state[9]  = GET_32BIT_LSB_FIRST(key + 20);
-    ctx->state[10] = GET_32BIT_LSB_FIRST(key + 24);
-    ctx->state[11] = GET_32BIT_LSB_FIRST(key + 28);
-
-    /* New key, dump context */
-    ctx->currentIndex = 64;
-}
-
-static void chacha20_iv(struct chacha20 *ctx, const unsigned char *iv)
-{
-    ctx->state[12] = 0;
-    ctx->state[13] = 0;
-    ctx->state[14] = GET_32BIT_MSB_FIRST(iv);
-    ctx->state[15] = GET_32BIT_MSB_FIRST(iv + 4);
-
-    /* New IV, dump context */
-    ctx->currentIndex = 64;
-}
-
-static void chacha20_encrypt(struct chacha20 *ctx, unsigned char *blk, int len)
-{
-    while (len) {
-        /* If we don't have any state left, then cycle to the next */
-        if (ctx->currentIndex >= 64) {
-            chacha20_round(ctx);
-        }
-
-        /* Do the xor while there's some state left and some plaintext left */
-        while (ctx->currentIndex < 64 && len) {
-            *blk++ ^= ctx->current[ctx->currentIndex++];
-            --len;
-        }
-    }
-}
-
-/* Decrypt is encrypt... It's xor against a PRNG... */
-static INLINE void chacha20_decrypt(struct chacha20 *ctx,
-                                    unsigned char *blk, int len)
-{
-    chacha20_encrypt(ctx, blk, len);
-}
-
-/* Poly1305 implementation (no AES, nonce is not encrypted) */
-
-#define NWORDS ((130 + BIGNUM_INT_BITS-1) / BIGNUM_INT_BITS)
-typedef struct bigval {
-    BignumInt w[NWORDS];
-} bigval;
-
-static void bigval_clear(bigval *r)
-{
-    int i;
-    for (i = 0; i < NWORDS; i++)
-        r->w[i] = 0;
-}
-
-static void bigval_import_le(bigval *r, const void *vdata, int len)
-{
-    const unsigned char *data = (const unsigned char *)vdata;
-    int i;
-    bigval_clear(r);
-    for (i = 0; i < len; i++)
-        r->w[i / BIGNUM_INT_BYTES] |=
-            (BignumInt)data[i] << (8 * (i % BIGNUM_INT_BYTES));
-}
-
-static void bigval_export_le(const bigval *r, void *vdata, int len)
-{
-    unsigned char *data = (unsigned char *)vdata;
-    int i;
-    for (i = 0; i < len; i++)
-        data[i] = r->w[i / BIGNUM_INT_BYTES] >> (8 * (i % BIGNUM_INT_BYTES));
-}
-
-/*
- * Core functions to do arithmetic mod p = 2^130-5. The whole
- * collection of these, up to and including the surrounding #if, are
- * generated automatically for various sizes of BignumInt by
- * contrib/make1305.py.
- */
-
-#if BIGNUM_INT_BITS == 16
-
-static void bigval_add(bigval *r, const bigval *a, const bigval *b)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14;
-    BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26;
-    BignumCarry carry;
-
-    v0 = a->w[0];
-    v1 = a->w[1];
-    v2 = a->w[2];
-    v3 = a->w[3];
-    v4 = a->w[4];
-    v5 = a->w[5];
-    v6 = a->w[6];
-    v7 = a->w[7];
-    v8 = a->w[8];
-    v9 = b->w[0];
-    v10 = b->w[1];
-    v11 = b->w[2];
-    v12 = b->w[3];
-    v13 = b->w[4];
-    v14 = b->w[5];
-    v15 = b->w[6];
-    v16 = b->w[7];
-    v17 = b->w[8];
-    BignumADC(v18, carry, v0, v9, 0);
-    BignumADC(v19, carry, v1, v10, carry);
-    BignumADC(v20, carry, v2, v11, carry);
-    BignumADC(v21, carry, v3, v12, carry);
-    BignumADC(v22, carry, v4, v13, carry);
-    BignumADC(v23, carry, v5, v14, carry);
-    BignumADC(v24, carry, v6, v15, carry);
-    BignumADC(v25, carry, v7, v16, carry);
-    v26 = v8 + v17 + carry;
-    r->w[0] = v18;
-    r->w[1] = v19;
-    r->w[2] = v20;
-    r->w[3] = v21;
-    r->w[4] = v22;
-    r->w[5] = v23;
-    r->w[6] = v24;
-    r->w[7] = v25;
-    r->w[8] = v26;
-}
-
-static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14;
-    BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27;
-    BignumInt v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40;
-    BignumInt v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53;
-    BignumInt v54, v55, v56, v57, v58, v59, v60, v61, v62, v63, v64, v65, v66;
-    BignumInt v67, v68, v69, v70, v71, v72, v73, v74, v75, v76, v77, v78, v79;
-    BignumInt v80, v81, v82, v83, v84, v85, v86, v87, v88, v89, v90, v91, v92;
-    BignumInt v93, v94, v95, v96, v97, v98, v99, v100, v101, v102, v103, v104;
-    BignumInt v105, v106, v107, v108, v109, v110, v111, v112, v113, v114;
-    BignumInt v115, v116, v117, v118, v119, v120, v121, v122, v123, v124;
-    BignumInt v125, v126, v127, v128, v129, v130, v131, v132, v133, v134;
-    BignumInt v135, v136, v137, v138, v139, v140, v141, v142, v143, v144;
-    BignumInt v145, v146, v147, v148, v149, v150, v151, v152, v153, v154;
-    BignumInt v155, v156, v157, v158, v159, v160, v161, v162, v163, v164;
-    BignumInt v165, v166, v167, v168, v169, v170, v171, v172, v173, v174;
-    BignumInt v175, v176, v177, v178, v180, v181, v182, v183, v184, v185;
-    BignumInt v186, v187, v188, v189, v190, v191, v192, v193, v194, v195;
-    BignumInt v196, v197, v198, v199, v200, v201, v202, v203, v204, v205;
-    BignumInt v206, v207, v208, v210, v212, v213, v214, v215, v216, v217;
-    BignumInt v218, v219, v220, v221, v222, v223, v224, v225, v226, v227;
-    BignumInt v228, v229;
-    BignumCarry carry;
-
-    v0 = a->w[0];
-    v1 = a->w[1];
-    v2 = a->w[2];
-    v3 = a->w[3];
-    v4 = a->w[4];
-    v5 = a->w[5];
-    v6 = a->w[6];
-    v7 = a->w[7];
-    v8 = a->w[8];
-    v9 = b->w[0];
-    v10 = b->w[1];
-    v11 = b->w[2];
-    v12 = b->w[3];
-    v13 = b->w[4];
-    v14 = b->w[5];
-    v15 = b->w[6];
-    v16 = b->w[7];
-    v17 = b->w[8];
-    BignumMUL(v19, v18, v0, v9);
-    BignumMULADD(v21, v20, v0, v10, v19);
-    BignumMULADD(v23, v22, v0, v11, v21);
-    BignumMULADD(v25, v24, v0, v12, v23);
-    BignumMULADD(v27, v26, v0, v13, v25);
-    BignumMULADD(v29, v28, v0, v14, v27);
-    BignumMULADD(v31, v30, v0, v15, v29);
-    BignumMULADD(v33, v32, v0, v16, v31);
-    BignumMULADD(v35, v34, v0, v17, v33);
-    BignumMULADD(v37, v36, v1, v9, v20);
-    BignumMULADD2(v39, v38, v1, v10, v22, v37);
-    BignumMULADD2(v41, v40, v1, v11, v24, v39);
-    BignumMULADD2(v43, v42, v1, v12, v26, v41);
-    BignumMULADD2(v45, v44, v1, v13, v28, v43);
-    BignumMULADD2(v47, v46, v1, v14, v30, v45);
-    BignumMULADD2(v49, v48, v1, v15, v32, v47);
-    BignumMULADD2(v51, v50, v1, v16, v34, v49);
-    BignumMULADD2(v53, v52, v1, v17, v35, v51);
-    BignumMULADD(v55, v54, v2, v9, v38);
-    BignumMULADD2(v57, v56, v2, v10, v40, v55);
-    BignumMULADD2(v59, v58, v2, v11, v42, v57);
-    BignumMULADD2(v61, v60, v2, v12, v44, v59);
-    BignumMULADD2(v63, v62, v2, v13, v46, v61);
-    BignumMULADD2(v65, v64, v2, v14, v48, v63);
-    BignumMULADD2(v67, v66, v2, v15, v50, v65);
-    BignumMULADD2(v69, v68, v2, v16, v52, v67);
-    BignumMULADD2(v71, v70, v2, v17, v53, v69);
-    BignumMULADD(v73, v72, v3, v9, v56);
-    BignumMULADD2(v75, v74, v3, v10, v58, v73);
-    BignumMULADD2(v77, v76, v3, v11, v60, v75);
-    BignumMULADD2(v79, v78, v3, v12, v62, v77);
-    BignumMULADD2(v81, v80, v3, v13, v64, v79);
-    BignumMULADD2(v83, v82, v3, v14, v66, v81);
-    BignumMULADD2(v85, v84, v3, v15, v68, v83);
-    BignumMULADD2(v87, v86, v3, v16, v70, v85);
-    BignumMULADD2(v89, v88, v3, v17, v71, v87);
-    BignumMULADD(v91, v90, v4, v9, v74);
-    BignumMULADD2(v93, v92, v4, v10, v76, v91);
-    BignumMULADD2(v95, v94, v4, v11, v78, v93);
-    BignumMULADD2(v97, v96, v4, v12, v80, v95);
-    BignumMULADD2(v99, v98, v4, v13, v82, v97);
-    BignumMULADD2(v101, v100, v4, v14, v84, v99);
-    BignumMULADD2(v103, v102, v4, v15, v86, v101);
-    BignumMULADD2(v105, v104, v4, v16, v88, v103);
-    BignumMULADD2(v107, v106, v4, v17, v89, v105);
-    BignumMULADD(v109, v108, v5, v9, v92);
-    BignumMULADD2(v111, v110, v5, v10, v94, v109);
-    BignumMULADD2(v113, v112, v5, v11, v96, v111);
-    BignumMULADD2(v115, v114, v5, v12, v98, v113);
-    BignumMULADD2(v117, v116, v5, v13, v100, v115);
-    BignumMULADD2(v119, v118, v5, v14, v102, v117);
-    BignumMULADD2(v121, v120, v5, v15, v104, v119);
-    BignumMULADD2(v123, v122, v5, v16, v106, v121);
-    BignumMULADD2(v125, v124, v5, v17, v107, v123);
-    BignumMULADD(v127, v126, v6, v9, v110);
-    BignumMULADD2(v129, v128, v6, v10, v112, v127);
-    BignumMULADD2(v131, v130, v6, v11, v114, v129);
-    BignumMULADD2(v133, v132, v6, v12, v116, v131);
-    BignumMULADD2(v135, v134, v6, v13, v118, v133);
-    BignumMULADD2(v137, v136, v6, v14, v120, v135);
-    BignumMULADD2(v139, v138, v6, v15, v122, v137);
-    BignumMULADD2(v141, v140, v6, v16, v124, v139);
-    BignumMULADD2(v143, v142, v6, v17, v125, v141);
-    BignumMULADD(v145, v144, v7, v9, v128);
-    BignumMULADD2(v147, v146, v7, v10, v130, v145);
-    BignumMULADD2(v149, v148, v7, v11, v132, v147);
-    BignumMULADD2(v151, v150, v7, v12, v134, v149);
-    BignumMULADD2(v153, v152, v7, v13, v136, v151);
-    BignumMULADD2(v155, v154, v7, v14, v138, v153);
-    BignumMULADD2(v157, v156, v7, v15, v140, v155);
-    BignumMULADD2(v159, v158, v7, v16, v142, v157);
-    BignumMULADD2(v161, v160, v7, v17, v143, v159);
-    BignumMULADD(v163, v162, v8, v9, v146);
-    BignumMULADD2(v165, v164, v8, v10, v148, v163);
-    BignumMULADD2(v167, v166, v8, v11, v150, v165);
-    BignumMULADD2(v169, v168, v8, v12, v152, v167);
-    BignumMULADD2(v171, v170, v8, v13, v154, v169);
-    BignumMULADD2(v173, v172, v8, v14, v156, v171);
-    BignumMULADD2(v175, v174, v8, v15, v158, v173);
-    BignumMULADD2(v177, v176, v8, v16, v160, v175);
-    v178 = v8 * v17 + v161 + v177;
-    v180 = (v162) & ((((BignumInt)1) << 2)-1);
-    v181 = ((v162) >> 2) | ((v164) << 14);
-    v182 = ((v164) >> 2) | ((v166) << 14);
-    v183 = ((v166) >> 2) | ((v168) << 14);
-    v184 = ((v168) >> 2) | ((v170) << 14);
-    v185 = ((v170) >> 2) | ((v172) << 14);
-    v186 = ((v172) >> 2) | ((v174) << 14);
-    v187 = ((v174) >> 2) | ((v176) << 14);
-    v188 = ((v176) >> 2) | ((v178) << 14);
-    v189 = (v178) >> 2;
-    v190 = (v189) & ((((BignumInt)1) << 2)-1);
-    v191 = (v178) >> 4;
-    BignumMUL(v193, v192, 5, v181);
-    BignumMULADD(v195, v194, 5, v182, v193);
-    BignumMULADD(v197, v196, 5, v183, v195);
-    BignumMULADD(v199, v198, 5, v184, v197);
-    BignumMULADD(v201, v200, 5, v185, v199);
-    BignumMULADD(v203, v202, 5, v186, v201);
-    BignumMULADD(v205, v204, 5, v187, v203);
-    BignumMULADD(v207, v206, 5, v188, v205);
-    v208 = 5 * v190 + v207;
-    v210 = 25 * v191;
-    BignumADC(v212, carry, v18, v192, 0);
-    BignumADC(v213, carry, v36, v194, carry);
-    BignumADC(v214, carry, v54, v196, carry);
-    BignumADC(v215, carry, v72, v198, carry);
-    BignumADC(v216, carry, v90, v200, carry);
-    BignumADC(v217, carry, v108, v202, carry);
-    BignumADC(v218, carry, v126, v204, carry);
-    BignumADC(v219, carry, v144, v206, carry);
-    v220 = v180 + v208 + carry;
-    BignumADC(v221, carry, v212, v210, 0);
-    BignumADC(v222, carry, v213, 0, carry);
-    BignumADC(v223, carry, v214, 0, carry);
-    BignumADC(v224, carry, v215, 0, carry);
-    BignumADC(v225, carry, v216, 0, carry);
-    BignumADC(v226, carry, v217, 0, carry);
-    BignumADC(v227, carry, v218, 0, carry);
-    BignumADC(v228, carry, v219, 0, carry);
-    v229 = v220 + 0 + carry;
-    r->w[0] = v221;
-    r->w[1] = v222;
-    r->w[2] = v223;
-    r->w[3] = v224;
-    r->w[4] = v225;
-    r->w[5] = v226;
-    r->w[6] = v227;
-    r->w[7] = v228;
-    r->w[8] = v229;
-}
-
-static void bigval_final_reduce(bigval *n)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v13, v14, v15;
-    BignumInt v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28;
-    BignumInt v29, v30, v31, v32, v34, v35, v36, v37, v38, v39, v40, v41, v42;
-    BignumInt v43;
-    BignumCarry carry;
-
-    v0 = n->w[0];
-    v1 = n->w[1];
-    v2 = n->w[2];
-    v3 = n->w[3];
-    v4 = n->w[4];
-    v5 = n->w[5];
-    v6 = n->w[6];
-    v7 = n->w[7];
-    v8 = n->w[8];
-    v9 = (v8) >> 2;
-    v10 = (v8) & ((((BignumInt)1) << 2)-1);
-    v11 = 5 * v9;
-    BignumADC(v13, carry, v0, v11, 0);
-    BignumADC(v14, carry, v1, 0, carry);
-    BignumADC(v15, carry, v2, 0, carry);
-    BignumADC(v16, carry, v3, 0, carry);
-    BignumADC(v17, carry, v4, 0, carry);
-    BignumADC(v18, carry, v5, 0, carry);
-    BignumADC(v19, carry, v6, 0, carry);
-    BignumADC(v20, carry, v7, 0, carry);
-    v21 = v10 + 0 + carry;
-    BignumADC(v22, carry, v13, 5, 0);
-    (void)v22;
-    BignumADC(v23, carry, v14, 0, carry);
-    (void)v23;
-    BignumADC(v24, carry, v15, 0, carry);
-    (void)v24;
-    BignumADC(v25, carry, v16, 0, carry);
-    (void)v25;
-    BignumADC(v26, carry, v17, 0, carry);
-    (void)v26;
-    BignumADC(v27, carry, v18, 0, carry);
-    (void)v27;
-    BignumADC(v28, carry, v19, 0, carry);
-    (void)v28;
-    BignumADC(v29, carry, v20, 0, carry);
-    (void)v29;
-    v30 = v21 + 0 + carry;
-    v31 = (v30) >> 2;
-    v32 = 5 * v31;
-    BignumADC(v34, carry, v13, v32, 0);
-    BignumADC(v35, carry, v14, 0, carry);
-    BignumADC(v36, carry, v15, 0, carry);
-    BignumADC(v37, carry, v16, 0, carry);
-    BignumADC(v38, carry, v17, 0, carry);
-    BignumADC(v39, carry, v18, 0, carry);
-    BignumADC(v40, carry, v19, 0, carry);
-    BignumADC(v41, carry, v20, 0, carry);
-    v42 = v21 + 0 + carry;
-    v43 = (v42) & ((((BignumInt)1) << 2)-1);
-    n->w[0] = v34;
-    n->w[1] = v35;
-    n->w[2] = v36;
-    n->w[3] = v37;
-    n->w[4] = v38;
-    n->w[5] = v39;
-    n->w[6] = v40;
-    n->w[7] = v41;
-    n->w[8] = v43;
-}
-
-#elif BIGNUM_INT_BITS == 32
-
-static void bigval_add(bigval *r, const bigval *a, const bigval *b)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14;
-    BignumCarry carry;
-
-    v0 = a->w[0];
-    v1 = a->w[1];
-    v2 = a->w[2];
-    v3 = a->w[3];
-    v4 = a->w[4];
-    v5 = b->w[0];
-    v6 = b->w[1];
-    v7 = b->w[2];
-    v8 = b->w[3];
-    v9 = b->w[4];
-    BignumADC(v10, carry, v0, v5, 0);
-    BignumADC(v11, carry, v1, v6, carry);
-    BignumADC(v12, carry, v2, v7, carry);
-    BignumADC(v13, carry, v3, v8, carry);
-    v14 = v4 + v9 + carry;
-    r->w[0] = v10;
-    r->w[1] = v11;
-    r->w[2] = v12;
-    r->w[3] = v13;
-    r->w[4] = v14;
-}
-
-static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14;
-    BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27;
-    BignumInt v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40;
-    BignumInt v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53;
-    BignumInt v54, v55, v56, v57, v58, v60, v61, v62, v63, v64, v65, v66, v67;
-    BignumInt v68, v69, v70, v71, v72, v73, v74, v75, v76, v78, v80, v81, v82;
-    BignumInt v83, v84, v85, v86, v87, v88, v89;
-    BignumCarry carry;
-
-    v0 = a->w[0];
-    v1 = a->w[1];
-    v2 = a->w[2];
-    v3 = a->w[3];
-    v4 = a->w[4];
-    v5 = b->w[0];
-    v6 = b->w[1];
-    v7 = b->w[2];
-    v8 = b->w[3];
-    v9 = b->w[4];
-    BignumMUL(v11, v10, v0, v5);
-    BignumMULADD(v13, v12, v0, v6, v11);
-    BignumMULADD(v15, v14, v0, v7, v13);
-    BignumMULADD(v17, v16, v0, v8, v15);
-    BignumMULADD(v19, v18, v0, v9, v17);
-    BignumMULADD(v21, v20, v1, v5, v12);
-    BignumMULADD2(v23, v22, v1, v6, v14, v21);
-    BignumMULADD2(v25, v24, v1, v7, v16, v23);
-    BignumMULADD2(v27, v26, v1, v8, v18, v25);
-    BignumMULADD2(v29, v28, v1, v9, v19, v27);
-    BignumMULADD(v31, v30, v2, v5, v22);
-    BignumMULADD2(v33, v32, v2, v6, v24, v31);
-    BignumMULADD2(v35, v34, v2, v7, v26, v33);
-    BignumMULADD2(v37, v36, v2, v8, v28, v35);
-    BignumMULADD2(v39, v38, v2, v9, v29, v37);
-    BignumMULADD(v41, v40, v3, v5, v32);
-    BignumMULADD2(v43, v42, v3, v6, v34, v41);
-    BignumMULADD2(v45, v44, v3, v7, v36, v43);
-    BignumMULADD2(v47, v46, v3, v8, v38, v45);
-    BignumMULADD2(v49, v48, v3, v9, v39, v47);
-    BignumMULADD(v51, v50, v4, v5, v42);
-    BignumMULADD2(v53, v52, v4, v6, v44, v51);
-    BignumMULADD2(v55, v54, v4, v7, v46, v53);
-    BignumMULADD2(v57, v56, v4, v8, v48, v55);
-    v58 = v4 * v9 + v49 + v57;
-    v60 = (v50) & ((((BignumInt)1) << 2)-1);
-    v61 = ((v50) >> 2) | ((v52) << 30);
-    v62 = ((v52) >> 2) | ((v54) << 30);
-    v63 = ((v54) >> 2) | ((v56) << 30);
-    v64 = ((v56) >> 2) | ((v58) << 30);
-    v65 = (v58) >> 2;
-    v66 = (v65) & ((((BignumInt)1) << 2)-1);
-    v67 = (v58) >> 4;
-    BignumMUL(v69, v68, 5, v61);
-    BignumMULADD(v71, v70, 5, v62, v69);
-    BignumMULADD(v73, v72, 5, v63, v71);
-    BignumMULADD(v75, v74, 5, v64, v73);
-    v76 = 5 * v66 + v75;
-    v78 = 25 * v67;
-    BignumADC(v80, carry, v10, v68, 0);
-    BignumADC(v81, carry, v20, v70, carry);
-    BignumADC(v82, carry, v30, v72, carry);
-    BignumADC(v83, carry, v40, v74, carry);
-    v84 = v60 + v76 + carry;
-    BignumADC(v85, carry, v80, v78, 0);
-    BignumADC(v86, carry, v81, 0, carry);
-    BignumADC(v87, carry, v82, 0, carry);
-    BignumADC(v88, carry, v83, 0, carry);
-    v89 = v84 + 0 + carry;
-    r->w[0] = v85;
-    r->w[1] = v86;
-    r->w[2] = v87;
-    r->w[3] = v88;
-    r->w[4] = v89;
-}
-
-static void bigval_final_reduce(bigval *n)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v9, v10, v11, v12, v13, v14;
-    BignumInt v15, v16, v17, v18, v19, v20, v22, v23, v24, v25, v26, v27;
-    BignumCarry carry;
-
-    v0 = n->w[0];
-    v1 = n->w[1];
-    v2 = n->w[2];
-    v3 = n->w[3];
-    v4 = n->w[4];
-    v5 = (v4) >> 2;
-    v6 = (v4) & ((((BignumInt)1) << 2)-1);
-    v7 = 5 * v5;
-    BignumADC(v9, carry, v0, v7, 0);
-    BignumADC(v10, carry, v1, 0, carry);
-    BignumADC(v11, carry, v2, 0, carry);
-    BignumADC(v12, carry, v3, 0, carry);
-    v13 = v6 + 0 + carry;
-    BignumADC(v14, carry, v9, 5, 0);
-    (void)v14;
-    BignumADC(v15, carry, v10, 0, carry);
-    (void)v15;
-    BignumADC(v16, carry, v11, 0, carry);
-    (void)v16;
-    BignumADC(v17, carry, v12, 0, carry);
-    (void)v17;
-    v18 = v13 + 0 + carry;
-    v19 = (v18) >> 2;
-    v20 = 5 * v19;
-    BignumADC(v22, carry, v9, v20, 0);
-    BignumADC(v23, carry, v10, 0, carry);
-    BignumADC(v24, carry, v11, 0, carry);
-    BignumADC(v25, carry, v12, 0, carry);
-    v26 = v13 + 0 + carry;
-    v27 = (v26) & ((((BignumInt)1) << 2)-1);
-    n->w[0] = v22;
-    n->w[1] = v23;
-    n->w[2] = v24;
-    n->w[3] = v25;
-    n->w[4] = v27;
-}
-
-#elif BIGNUM_INT_BITS == 64
-
-static void bigval_add(bigval *r, const bigval *a, const bigval *b)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8;
-    BignumCarry carry;
-
-    v0 = a->w[0];
-    v1 = a->w[1];
-    v2 = a->w[2];
-    v3 = b->w[0];
-    v4 = b->w[1];
-    v5 = b->w[2];
-    BignumADC(v6, carry, v0, v3, 0);
-    BignumADC(v7, carry, v1, v4, carry);
-    v8 = v2 + v5 + carry;
-    r->w[0] = v6;
-    r->w[1] = v7;
-    r->w[2] = v8;
-}
-
-static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14;
-    BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v24, v25, v26, v27, v28;
-    BignumInt v29, v30, v31, v32, v33, v34, v36, v38, v39, v40, v41, v42, v43;
-    BignumCarry carry;
-
-    v0 = a->w[0];
-    v1 = a->w[1];
-    v2 = a->w[2];
-    v3 = b->w[0];
-    v4 = b->w[1];
-    v5 = b->w[2];
-    BignumMUL(v7, v6, v0, v3);
-    BignumMULADD(v9, v8, v0, v4, v7);
-    BignumMULADD(v11, v10, v0, v5, v9);
-    BignumMULADD(v13, v12, v1, v3, v8);
-    BignumMULADD2(v15, v14, v1, v4, v10, v13);
-    BignumMULADD2(v17, v16, v1, v5, v11, v15);
-    BignumMULADD(v19, v18, v2, v3, v14);
-    BignumMULADD2(v21, v20, v2, v4, v16, v19);
-    v22 = v2 * v5 + v17 + v21;
-    v24 = (v18) & ((((BignumInt)1) << 2)-1);
-    v25 = ((v18) >> 2) | ((v20) << 62);
-    v26 = ((v20) >> 2) | ((v22) << 62);
-    v27 = (v22) >> 2;
-    v28 = (v27) & ((((BignumInt)1) << 2)-1);
-    v29 = (v22) >> 4;
-    BignumMUL(v31, v30, 5, v25);
-    BignumMULADD(v33, v32, 5, v26, v31);
-    v34 = 5 * v28 + v33;
-    v36 = 25 * v29;
-    BignumADC(v38, carry, v6, v30, 0);
-    BignumADC(v39, carry, v12, v32, carry);
-    v40 = v24 + v34 + carry;
-    BignumADC(v41, carry, v38, v36, 0);
-    BignumADC(v42, carry, v39, 0, carry);
-    v43 = v40 + 0 + carry;
-    r->w[0] = v41;
-    r->w[1] = v42;
-    r->w[2] = v43;
-}
-
-static void bigval_final_reduce(bigval *n)
-{
-    BignumInt v0, v1, v2, v3, v4, v5, v7, v8, v9, v10, v11, v12, v13, v14;
-    BignumInt v16, v17, v18, v19;
-    BignumCarry carry;
-
-    v0 = n->w[0];
-    v1 = n->w[1];
-    v2 = n->w[2];
-    v3 = (v2) >> 2;
-    v4 = (v2) & ((((BignumInt)1) << 2)-1);
-    v5 = 5 * v3;
-    BignumADC(v7, carry, v0, v5, 0);
-    BignumADC(v8, carry, v1, 0, carry);
-    v9 = v4 + 0 + carry;
-    BignumADC(v10, carry, v7, 5, 0);
-    (void)v10;
-    BignumADC(v11, carry, v8, 0, carry);
-    (void)v11;
-    v12 = v9 + 0 + carry;
-    v13 = (v12) >> 2;
-    v14 = 5 * v13;
-    BignumADC(v16, carry, v7, v14, 0);
-    BignumADC(v17, carry, v8, 0, carry);
-    v18 = v9 + 0 + carry;
-    v19 = (v18) & ((((BignumInt)1) << 2)-1);
-    n->w[0] = v16;
-    n->w[1] = v17;
-    n->w[2] = v19;
-}
-
-#else
-#error Add another bit count to contrib/make1305.py and rerun it
-#endif
-
-struct poly1305 {
-    unsigned char nonce[16];
-    bigval r;
-    bigval h;
-
-    /* Buffer in case we get less that a multiple of 16 bytes */
-    unsigned char buffer[16];
-    int bufferIndex;
-};
-
-static void poly1305_init(struct poly1305 *ctx)
-{
-    memset(ctx->nonce, 0, 16);
-    ctx->bufferIndex = 0;
-    bigval_clear(&ctx->h);
-}
-
-static void poly1305_key(struct poly1305 *ctx, ptrlen key)
-{
-    pinitassert(key.len == 32);             /* Takes a 256 bit key */
-
-    unsigned char key_copy[16];
-    memcpy(key_copy, key.ptr, 16);
-
-    /* Key the MAC itself
-     * bytes 4, 8, 12 and 16 are required to have their top four bits clear */
-    key_copy[3] &= 0x0f;
-    key_copy[7] &= 0x0f;
-    key_copy[11] &= 0x0f;
-    key_copy[15] &= 0x0f;
-    /* bytes 5, 9 and 13 are required to have their bottom two bits clear */
-    key_copy[4] &= 0xfc;
-    key_copy[8] &= 0xfc;
-    key_copy[12] &= 0xfc;
-    bigval_import_le(&ctx->r, key_copy, 16);
-    smemclr(key_copy, sizeof(key_copy));
-
-    /* Use second 128 bits as the nonce */
-    memcpy(ctx->nonce, (const char *)key.ptr + 16, 16);
-}
-
-/* Feed up to 16 bytes (should only be less for the last chunk) */
-static void poly1305_feed_chunk(struct poly1305 *ctx,
-                                const unsigned char *chunk, int len)
-{
-    bigval c;
-    bigval_import_le(&c, chunk, len);
-    c.w[len / BIGNUM_INT_BYTES] |=
-        (BignumInt)1 << (8 * (len % BIGNUM_INT_BYTES));
-    bigval_add(&c, &c, &ctx->h);
-    bigval_mul_mod_p(&ctx->h, &c, &ctx->r);
-}
-
-static void poly1305_feed(struct poly1305 *ctx,
-                          const unsigned char *buf, int len)
-{
-    /* Check for stuff left in the buffer from last time */
-    if (ctx->bufferIndex) {
-        /* Try to fill up to 16 */
-        while (ctx->bufferIndex < 16 && len) {
-            ctx->buffer[ctx->bufferIndex++] = *buf++;
-            --len;
-        }
-        if (ctx->bufferIndex == 16) {
-            poly1305_feed_chunk(ctx, ctx->buffer, 16);
-            ctx->bufferIndex = 0;
-        }
-    }
-
-    /* Process 16 byte whole chunks */
-    while (len >= 16) {
-        poly1305_feed_chunk(ctx, buf, 16);
-        len -= 16;
-        buf += 16;
-    }
-
-    /* Cache stuff that's left over */
-    if (len) {
-        memcpy(ctx->buffer, buf, len);
-        ctx->bufferIndex = len;
-    }
-}
-
-/* Finalise and populate buffer with 16 byte with MAC */
-static void poly1305_finalise(struct poly1305 *ctx, unsigned char *mac)
-{
-    bigval tmp;
-
-    if (ctx->bufferIndex) {
-        poly1305_feed_chunk(ctx, ctx->buffer, ctx->bufferIndex);
-    }
-
-    bigval_import_le(&tmp, ctx->nonce, 16);
-    bigval_final_reduce(&ctx->h);
-    bigval_add(&tmp, &tmp, &ctx->h);
-    bigval_export_le(&tmp, mac, 16);
-}
-
-/* SSH-2 wrapper */
-
-struct ccp_context {
-    struct chacha20 a_cipher; /* Used for length */
-    struct chacha20 b_cipher; /* Used for content */
-
-    /* Cache of the first 4 bytes because they are the sequence number */
-    /* Kept in 8 bytes with the top as zero to allow easy passing to setiv */
-    int mac_initialised; /* Where we have got to in filling mac_iv */
-    unsigned char mac_iv[8];
-
-    struct poly1305 mac;
-
-    BinarySink_IMPLEMENTATION;
-    ssh_cipher ciph;
-    ssh2_mac mac_if;
-};
-
-static ssh2_mac *poly_ssh2_new(
-    const ssh2_macalg *alg, ssh_cipher *cipher)
-{
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    ctx->mac_if.vt = alg;
-    BinarySink_DELEGATE_INIT(&ctx->mac_if, ctx);
-    return &ctx->mac_if;
-}
-
-static void poly_ssh2_free(ssh2_mac *mac)
-{
-    /* Not allocated, just forwarded, no need to free */
-}
-
-static void poly_setkey(ssh2_mac *mac, ptrlen key)
-{
-    /* Uses the same context as ChaCha20, so ignore */
-}
-
-static void poly_start(ssh2_mac *mac)
-{
-    struct ccp_context *ctx = container_of(mac, struct ccp_context, mac_if);
-
-    ctx->mac_initialised = 0;
-    memset(ctx->mac_iv, 0, 8);
-    poly1305_init(&ctx->mac);
-}
-
-static void poly_BinarySink_write(BinarySink *bs, const void *blkv, size_t len)
-{
-    struct ccp_context *ctx = BinarySink_DOWNCAST(bs, struct ccp_context);
-    const unsigned char *blk = (const unsigned char *)blkv;
-
-    /* First 4 bytes are the IV */
-    while (ctx->mac_initialised < 4 && len) {
-        ctx->mac_iv[7 - ctx->mac_initialised] = *blk++;
-        ++ctx->mac_initialised;
-        --len;
-    }
-
-    /* Initialise the IV if needed */
-    if (ctx->mac_initialised == 4) {
-        chacha20_iv(&ctx->b_cipher, ctx->mac_iv);
-        ++ctx->mac_initialised;  /* Don't do it again */
-
-        /* Do first rotation */
-        chacha20_round(&ctx->b_cipher);
-
-        /* Set the poly key */
-        poly1305_key(&ctx->mac, make_ptrlen(ctx->b_cipher.current, 32));
-
-        /* Set the first round as used */
-        ctx->b_cipher.currentIndex = 64;
-    }
-
-    /* Update the MAC with anything left */
-    if (len) {
-        poly1305_feed(&ctx->mac, blk, len);
-    }
-}
-
-static void poly_genresult(ssh2_mac *mac, unsigned char *blk)
-{
-    struct ccp_context *ctx = container_of(mac, struct ccp_context, mac_if);
-    poly1305_finalise(&ctx->mac, blk);
-}
-
-static const char *poly_text_name(ssh2_mac *mac)
-{
-    return "Poly1305";
-}
-
-const ssh2_macalg ssh2_poly1305 = {
-    // WINSCP
-    /*.new =*/ poly_ssh2_new,
-    /*.free =*/ poly_ssh2_free,
-    /*.setkey =*/ poly_setkey,
-    /*.start =*/ poly_start,
-    /*.genresult =*/ poly_genresult,
-    /*.text_name =*/ poly_text_name,
-    /*.name =*/ "",
-    /*.etm_name =*/ "", /* Not selectable individually, just part of
-                     * ChaCha20-Poly1305 */
-    /*.len =*/ 16,
-    /*.keylen =*/ 0,
-    NULL, // WINSCP
-};
-
-static ssh_cipher *ccp_new(const ssh_cipheralg *alg)
-{
-    struct ccp_context *ctx = snew(struct ccp_context);
-    BinarySink_INIT(ctx, poly_BinarySink_write);
-    poly1305_init(&ctx->mac);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void ccp_free(ssh_cipher *cipher)
-{
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher));
-    smemclr(&ctx->b_cipher, sizeof(ctx->b_cipher));
-    smemclr(&ctx->mac, sizeof(ctx->mac));
-    sfree(ctx);
-}
-
-static void ccp_iv(ssh_cipher *cipher, const void *iv)
-{
-    /* struct ccp_context *ctx =
-           container_of(cipher, struct ccp_context, ciph); */
-    /* IV is set based on the sequence number */
-}
-
-static void ccp_key(ssh_cipher *cipher, const void *vkey)
-{
-    const unsigned char *key = (const unsigned char *)vkey;
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    /* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */
-    chacha20_key(&ctx->a_cipher, key + 32);
-    /* Initialise the b_cipher (for content and MAC) with the second 256 bits */
-    chacha20_key(&ctx->b_cipher, key);
-}
-
-static void ccp_encrypt(ssh_cipher *cipher, void *blk, int len)
-{
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    chacha20_encrypt(&ctx->b_cipher, blk, len);
-}
-
-static void ccp_decrypt(ssh_cipher *cipher, void *blk, int len)
-{
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    chacha20_decrypt(&ctx->b_cipher, blk, len);
-}
-
-static void ccp_length_op(struct ccp_context *ctx, void *blk, int len,
-                          unsigned long seq)
-{
-    unsigned char iv[8];
-    /*
-     * According to RFC 4253 (section 6.4), the packet sequence number wraps
-     * at 2^32, so its 32 high-order bits will always be zero.
-     */
-    PUT_32BIT_LSB_FIRST(iv, 0);
-    PUT_32BIT_LSB_FIRST(iv + 4, seq);
-    chacha20_iv(&ctx->a_cipher, iv);
-    chacha20_iv(&ctx->b_cipher, iv);
-    /* Reset content block count to 1, as the first is the key for Poly1305 */
-    ++ctx->b_cipher.state[12];
-    smemclr(iv, sizeof(iv));
-}
-
-static void ccp_encrypt_length(ssh_cipher *cipher, void *blk, int len,
-                               unsigned long seq)
-{
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    ccp_length_op(ctx, blk, len, seq);
-    chacha20_encrypt(&ctx->a_cipher, blk, len);
-}
-
-static void ccp_decrypt_length(ssh_cipher *cipher, void *blk, int len,
-                               unsigned long seq)
-{
-    struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph);
-    ccp_length_op(ctx, blk, len, seq);
-    chacha20_decrypt(&ctx->a_cipher, blk, len);
-}
-
-const ssh_cipheralg ssh2_chacha20_poly1305 = {
-    // WINSCP
-    /*.new =*/ ccp_new,
-    /*.free =*/ ccp_free,
-    /*.setiv =*/ ccp_iv,
-    /*.setkey =*/ ccp_key,
-    /*.encrypt =*/ ccp_encrypt,
-    /*.decrypt =*/ ccp_decrypt,
-    /*.encrypt_length =*/ ccp_encrypt_length,
-    /*.decrypt_length =*/ ccp_decrypt_length,
-    /*.ssh2_id =*/ "[email protected]",
-    /*.blksize =*/ 1,
-    /*.real_keybits =*/ 512,
-    /*.padded_keybytes =*/ 64,
-    /*.flags =*/ SSH_CIPHER_SEPARATE_LENGTH,
-    /*.text_name =*/ "ChaCha20",
-    /*.required_mac =*/ &ssh2_poly1305,
-    NULL, // WINSCP
-};
-
-static const ssh_cipheralg *const ccp_list[] = {
-    &ssh2_chacha20_poly1305
-};
-
-const ssh2_ciphers ssh2_ccp = { lenof(ccp_list), ccp_list };

+ 0 - 316
source/putty/sshchan.h

@@ -1,316 +0,0 @@
-/*
- * Abstraction of the various ways to handle the local end of an SSH
- * connection-layer channel.
- */
-
-#ifndef PUTTY_SSHCHAN_H
-#define PUTTY_SSHCHAN_H
-
-typedef struct ChannelVtable ChannelVtable;
-
-struct ChannelVtable {
-    void (*free)(Channel *);
-
-    /* Called for channel types that were created at the same time as
-     * we sent an outgoing CHANNEL_OPEN, when the confirmation comes
-     * back from the server indicating that the channel has been
-     * opened, or the failure message indicating that it hasn't,
-     * respectively. In the latter case, this must _not_ free the
-     * Channel structure - the client will call the free method
-     * separately. But it might do logging or other local cleanup. */
-    void (*open_confirmation)(Channel *);
-    void (*open_failed)(Channel *, const char *error_text);
-
-    size_t (*send)(Channel *, bool is_stderr, const void *buf, size_t len);
-    void (*send_eof)(Channel *);
-    void (*set_input_wanted)(Channel *, bool wanted);
-
-    char *(*log_close_msg)(Channel *);
-
-    bool (*want_close)(Channel *, bool sent_local_eof, bool rcvd_remote_eof);
-
-    /* A method for every channel request we know of. All of these
-     * return true for success or false for failure. */
-    bool (*rcvd_exit_status)(Channel *, int status);
-    bool (*rcvd_exit_signal)(
-        Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg);
-    bool (*rcvd_exit_signal_numeric)(
-        Channel *chan, int signum, bool core_dumped, ptrlen msg);
-    bool (*run_shell)(Channel *chan);
-    bool (*run_command)(Channel *chan, ptrlen command);
-    bool (*run_subsystem)(Channel *chan, ptrlen subsys);
-    bool (*enable_x11_forwarding)(
-        Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
-        unsigned screen_number);
-    bool (*enable_agent_forwarding)(Channel *chan);
-    bool (*allocate_pty)(
-        Channel *chan, ptrlen termtype, unsigned width, unsigned height,
-        unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
-    bool (*set_env)(Channel *chan, ptrlen var, ptrlen value);
-    bool (*send_break)(Channel *chan, unsigned length);
-    bool (*send_signal)(Channel *chan, ptrlen signame);
-    bool (*change_window_size)(
-        Channel *chan, unsigned width, unsigned height,
-        unsigned pixwidth, unsigned pixheight);
-
-    /* A method for signalling success/failure responses to channel
-     * requests initiated from the SshChannel vtable with want_reply
-     * true. */
-    void (*request_response)(Channel *, bool success);
-};
-
-struct Channel {
-    const struct ChannelVtable *vt;
-    unsigned initial_fixed_window_size;
-};
-
-static inline void chan_free(Channel *ch)
-{ ch->vt->free(ch); }
-static inline void chan_open_confirmation(Channel *ch)
-{ ch->vt->open_confirmation(ch); }
-static inline void chan_open_failed(Channel *ch, const char *err)
-{ ch->vt->open_failed(ch, err); }
-static inline size_t chan_send(
-    Channel *ch, bool err, const void *buf, size_t len)
-{ return ch->vt->send(ch, err, buf, len); }
-static inline void chan_send_eof(Channel *ch)
-{ ch->vt->send_eof(ch); }
-static inline void chan_set_input_wanted(Channel *ch, bool wanted)
-{ ch->vt->set_input_wanted(ch, wanted); }
-static inline char *chan_log_close_msg(Channel *ch)
-{ return ch->vt->log_close_msg(ch); }
-static inline bool chan_want_close(Channel *ch, bool leof, bool reof)
-{ return ch->vt->want_close(ch, leof, reof); }
-static inline bool chan_rcvd_exit_status(Channel *ch, int status)
-{ return ch->vt->rcvd_exit_status(ch, status); }
-static inline bool chan_rcvd_exit_signal(
-        Channel *ch, ptrlen sig, bool core, ptrlen msg)
-{ return ch->vt->rcvd_exit_signal(ch, sig, core, msg); }
-static inline bool chan_rcvd_exit_signal_numeric(
-        Channel *ch, int sig, bool core, ptrlen msg)
-{ return ch->vt->rcvd_exit_signal_numeric(ch, sig, core, msg); }
-static inline bool chan_run_shell(Channel *ch)
-{ return ch->vt->run_shell(ch); }
-static inline bool chan_run_command(Channel *ch, ptrlen cmd)
-{ return ch->vt->run_command(ch, cmd); }
-static inline bool chan_run_subsystem(Channel *ch, ptrlen subsys)
-{ return ch->vt->run_subsystem(ch, subsys); }
-static inline bool chan_enable_x11_forwarding(
-    Channel *ch, bool oneshot, ptrlen ap, ptrlen ad, unsigned scr)
-{ return ch->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr); }
-static inline bool chan_enable_agent_forwarding(Channel *ch)
-{ return ch->vt->enable_agent_forwarding(ch); }
-static inline bool chan_allocate_pty(
-    Channel *ch, ptrlen termtype, unsigned w, unsigned h,
-    unsigned pw, unsigned ph, struct ssh_ttymodes modes)
-{ return ch->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes); }
-static inline bool chan_set_env(Channel *ch, ptrlen var, ptrlen value)
-{ return ch->vt->set_env(ch, var, value); }
-static inline bool chan_send_break(Channel *ch, unsigned length)
-{ return ch->vt->send_break(ch, length); }
-static inline bool chan_send_signal(Channel *ch, ptrlen signame)
-{ return ch->vt->send_signal(ch, signame); }
-static inline bool chan_change_window_size(
-    Channel *ch, unsigned w, unsigned h, unsigned pw, unsigned ph)
-{ return ch->vt->change_window_size(ch, w, h, pw, ph); }
-static inline void chan_request_response(Channel *ch, bool success)
-{ ch->vt->request_response(ch, success); }
-
-/*
- * Reusable methods you can put in vtables to give default handling of
- * some of those functions.
- */
-
-/* open_confirmation / open_failed for any channel it doesn't apply to */
-void chan_remotely_opened_confirmation(Channel *chan);
-void chan_remotely_opened_failure(Channel *chan, const char *errtext);
-
-/* want_close for any channel that wants the default behaviour of not
- * closing until both directions have had an EOF */
-bool chan_default_want_close(Channel *, bool, bool);
-
-/* default implementations that refuse all the channel requests */
-bool chan_no_exit_status(Channel *, int);
-bool chan_no_exit_signal(Channel *, ptrlen, bool, ptrlen);
-bool chan_no_exit_signal_numeric(Channel *, int, bool, ptrlen);
-bool chan_no_run_shell(Channel *chan);
-bool chan_no_run_command(Channel *chan, ptrlen command);
-bool chan_no_run_subsystem(Channel *chan, ptrlen subsys);
-bool chan_no_enable_x11_forwarding(
-    Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
-    unsigned screen_number);
-bool chan_no_enable_agent_forwarding(Channel *chan);
-bool chan_no_allocate_pty(
-    Channel *chan, ptrlen termtype, unsigned width, unsigned height,
-    unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
-bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value);
-bool chan_no_send_break(Channel *chan, unsigned length);
-bool chan_no_send_signal(Channel *chan, ptrlen signame);
-bool chan_no_change_window_size(
-    Channel *chan, unsigned width, unsigned height,
-    unsigned pixwidth, unsigned pixheight);
-
-/* default implementation that never expects to receive a response */
-void chan_no_request_response(Channel *, bool);
-
-/*
- * Constructor for a trivial do-nothing implementation of
- * ChannelVtable. Used for 'zombie' channels, i.e. channels whose
- * proper local source of data has been shut down or otherwise stopped
- * existing, but the SSH side is still there and needs some kind of a
- * Channel implementation to talk to. In particular, the want_close
- * method for this channel always returns 'yes, please close this
- * channel asap', regardless of whether local and/or remote EOF have
- * been sent - indeed, even if _neither_ has.
- */
-Channel *zombiechan_new(void);
-
-/* ----------------------------------------------------------------------
- * This structure is owned by an SSH connection layer, and identifies
- * the connection layer's end of the channel, for the Channel
- * implementation to talk back to.
- */
-
-typedef struct SshChannelVtable SshChannelVtable;
-
-struct SshChannelVtable {
-    size_t (*write)(SshChannel *c, bool is_stderr, const void *, size_t);
-    void (*write_eof)(SshChannel *c);
-    void (*initiate_close)(SshChannel *c, const char *err);
-    void (*unthrottle)(SshChannel *c, size_t bufsize);
-    Conf *(*get_conf)(SshChannel *c);
-    void (*window_override_removed)(SshChannel *c);
-    void (*x11_sharing_handover)(SshChannel *c,
-                                 ssh_sharing_connstate *share_cs,
-                                 share_channel *share_chan,
-                                 const char *peer_addr, int peer_port,
-                                 int endian, int protomajor, int protominor,
-                                 const void *initial_data, int initial_len);
-
-    /*
-     * All the outgoing channel requests we support. Each one has a
-     * want_reply flag, which will cause a callback to
-     * chan_request_response when the result is available.
-     *
-     * The ones that return 'bool' use it to indicate that the SSH
-     * protocol in use doesn't support this request at all.
-     *
-     * (It's also intentional that not all of them have a want_reply
-     * flag: the ones that don't are because SSH-1 has no method for
-     * signalling success or failure of that request, or because we
-     * wouldn't do anything usefully different with the reply in any
-     * case.)
-     */
-    void (*send_exit_status)(SshChannel *c, int status);
-    void (*send_exit_signal)(
-        SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg);
-    void (*send_exit_signal_numeric)(
-        SshChannel *c, int signum, bool core_dumped, ptrlen msg);
-    void (*request_x11_forwarding)(
-        SshChannel *c, bool want_reply, const char *authproto,
-        const char *authdata, int screen_number, bool oneshot);
-    void (*request_agent_forwarding)(
-        SshChannel *c, bool want_reply);
-    void (*request_pty)(
-        SshChannel *c, bool want_reply, Conf *conf, int w, int h);
-    bool (*send_env_var)(
-        SshChannel *c, bool want_reply, const char *var, const char *value);
-    void (*start_shell)(
-        SshChannel *c, bool want_reply);
-    void (*start_command)(
-        SshChannel *c, bool want_reply, const char *command);
-    bool (*start_subsystem)(
-        SshChannel *c, bool want_reply, const char *subsystem);
-    bool (*send_serial_break)(
-        SshChannel *c, bool want_reply, int length); /* length=0 for default */
-    bool (*send_signal)(
-        SshChannel *c, bool want_reply, const char *signame);
-    void (*send_terminal_size_change)(
-        SshChannel *c, int w, int h);
-    void (*hint_channel_is_simple)(SshChannel *c);
-};
-
-struct SshChannel {
-    const struct SshChannelVtable *vt;
-    ConnectionLayer *cl;
-};
-
-static inline size_t sshfwd_write_ext(
-    SshChannel *c, bool is_stderr, const void *data, size_t len)
-{ return c->vt->write(c, is_stderr, data, len); }
-static inline size_t sshfwd_write(SshChannel *c, const void *data, size_t len)
-{ return sshfwd_write_ext(c, false, data, len); }
-static inline void sshfwd_write_eof(SshChannel *c)
-{ c->vt->write_eof(c); }
-static inline void sshfwd_initiate_close(SshChannel *c, const char *err)
-{ c->vt->initiate_close(c, err); }
-static inline void sshfwd_unthrottle(SshChannel *c, size_t bufsize)
-{ c->vt->unthrottle(c, bufsize); }
-static inline Conf *sshfwd_get_conf(SshChannel *c)
-{ return c->vt->get_conf(c); }
-static inline void sshfwd_window_override_removed(SshChannel *c)
-{ c->vt->window_override_removed(c); }
-static inline void sshfwd_x11_sharing_handover(
-    SshChannel *c, ssh_sharing_connstate *cs, share_channel *sch,
-    const char *addr, int port, int endian, int maj, int min,
-    const void *idata, int ilen)
-{ c->vt->x11_sharing_handover(c, cs, sch, addr, port, endian,
-                              maj, min, idata, ilen); }
-static inline void sshfwd_send_exit_status(SshChannel *c, int status)
-{ c->vt->send_exit_status(c, status); }
-static inline void sshfwd_send_exit_signal(
-    SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg)
-{ c->vt->send_exit_signal(c, signame, core_dumped, msg); }
-static inline void sshfwd_send_exit_signal_numeric(
-    SshChannel *c, int signum, bool core_dumped, ptrlen msg)
-{ c->vt->send_exit_signal_numeric(c, signum, core_dumped, msg); }
-static inline void sshfwd_request_x11_forwarding(
-    SshChannel *c, bool want_reply, const char *proto,
-    const char *data, int scr, bool once)
-{ c->vt->request_x11_forwarding(c, want_reply, proto, data, scr, once); }
-static inline void sshfwd_request_agent_forwarding(
-    SshChannel *c, bool want_reply)
-{ c->vt->request_agent_forwarding(c, want_reply); }
-static inline void sshfwd_request_pty(
-    SshChannel *c, bool want_reply, Conf *conf, int w, int h)
-{ c->vt->request_pty(c, want_reply, conf, w, h); }
-static inline bool sshfwd_send_env_var(
-    SshChannel *c, bool want_reply, const char *var, const char *value)
-{ return c->vt->send_env_var(c, want_reply, var, value); }
-static inline void sshfwd_start_shell(
-    SshChannel *c, bool want_reply)
-{ c->vt->start_shell(c, want_reply); }
-static inline void sshfwd_start_command(
-    SshChannel *c, bool want_reply, const char *command)
-{ c->vt->start_command(c, want_reply, command); }
-static inline bool sshfwd_start_subsystem(
-    SshChannel *c, bool want_reply, const char *subsystem)
-{ return c->vt->start_subsystem(c, want_reply, subsystem); }
-static inline bool sshfwd_send_serial_break(
-    SshChannel *c, bool want_reply, int length)
-{ return c->vt->send_serial_break(c, want_reply, length); }
-static inline bool sshfwd_send_signal(
-    SshChannel *c, bool want_reply, const char *signame)
-{ return c->vt->send_signal(c, want_reply, signame); }
-static inline void sshfwd_send_terminal_size_change(
-    SshChannel *c, int w, int h)
-{ c->vt->send_terminal_size_change(c, w, h); }
-static inline void sshfwd_hint_channel_is_simple(SshChannel *c)
-{ c->vt->hint_channel_is_simple(c); }
-
-/* ----------------------------------------------------------------------
- * The 'main' or primary channel of the SSH connection is special,
- * because it's the one that's connected directly to parts of the
- * frontend such as the terminal and the specials menu. So it exposes
- * a richer API.
- */
-
-mainchan *mainchan_new(
-    PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
-    int term_width, int term_height, bool is_simple, SshChannel **sc_out);
-void mainchan_get_specials(
-    mainchan *mc, add_special_fn_t add_special, void *ctx);
-void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg);
-void mainchan_terminal_size(mainchan *mc, int width, int height);
-
-#endif /* PUTTY_SSHCHAN_H */

+ 0 - 982
source/putty/sshcommon.c

@@ -1,982 +0,0 @@
-/*
- * Supporting routines used in common by all the various components of
- * the SSH system.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "putty.h"
-#include "mpint.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshppl.h"
-#include "sshchan.h"
-
-/* ----------------------------------------------------------------------
- * Implementation of PacketQueue.
- */
-
-static void pq_ensure_unlinked(PacketQueueNode *node)
-{
-    if (node->on_free_queue) {
-        node->next->prev = node->prev;
-        node->prev->next = node->next;
-    } else {
-        assert(!node->next);
-        assert(!node->prev);
-    }
-}
-
-void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
-{
-    pq_ensure_unlinked(node);
-    node->next = &pqb->end;
-    node->prev = pqb->end.prev;
-    node->next->prev = node;
-    node->prev->next = node;
-    pqb->total_size += node->formal_size;
-
-    if (pqb->ic)
-        queue_idempotent_callback(pqb->ic);
-}
-
-void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
-{
-    pq_ensure_unlinked(node);
-    node->prev = &pqb->end;
-    node->next = pqb->end.next;
-    node->next->prev = node;
-    node->prev->next = node;
-    pqb->total_size += node->formal_size;
-
-    if (pqb->ic)
-        queue_idempotent_callback(pqb->ic);
-}
-
-#ifndef WINSCP
-static PacketQueueNode pktin_freeq_head = {
-    &pktin_freeq_head, &pktin_freeq_head, true
-};
-#endif
-
-/*WINSCP static*/ void pktin_free_queue_callback(void *vctx)
-{
-    struct callback_set * set = (struct callback_set *)vctx;
-    while (set->pktin_freeq_head->next != set->pktin_freeq_head) {
-        PacketQueueNode *node = set->pktin_freeq_head->next;
-        PktIn *pktin = container_of(node, PktIn, qnode);
-        set->pktin_freeq_head->next = node->next;
-        sfree(pktin);
-    }
-
-    set->pktin_freeq_head->prev = set->pktin_freeq_head;
-}
-
-#ifndef WINSCP
-static IdempotentCallback ic_pktin_free = {
-    pktin_free_queue_callback, NULL, false
-};
-#endif
-
-static inline void pq_unlink_common(PacketQueueBase *pqb,
-                                    PacketQueueNode *node)
-{
-    node->next->prev = node->prev;
-    node->prev->next = node->next;
-
-    /* Check total_size doesn't drift out of sync downwards, by
-     * ensuring it doesn't underflow when we do this subtraction */
-    assert(pqb->total_size >= node->formal_size);
-    pqb->total_size -= node->formal_size;
-
-    /* Check total_size doesn't drift out of sync upwards, by checking
-     * that it's returned to exactly zero whenever a queue is
-     * emptied */
-    assert(pqb->end.next != &pqb->end || pqb->total_size == 0);
-}
-
-static PktIn *pq_in_after(PacketQueueBase *pqb,
-                          PacketQueueNode *prev, bool pop)
-{
-    PacketQueueNode *node = prev->next;
-    if (node == &pqb->end)
-        return NULL;
-
-    if (pop) {
-        #ifdef WINSCP
-        struct callback_set * set = get_seat_callback_set(pqb->seat);
-        assert(set != NULL);
-        if (set->ic_pktin_free == NULL)
-        {
-            set->pktin_freeq_head = snew(PacketQueueNode);
-            set->pktin_freeq_head->next = set->pktin_freeq_head;
-            set->pktin_freeq_head->prev = set->pktin_freeq_head;
-            set->pktin_freeq_head->on_free_queue = TRUE;
-
-            set->ic_pktin_free = snew(IdempotentCallback);
-            set->ic_pktin_free->fn = pktin_free_queue_callback;
-            set->ic_pktin_free->ctx = set;
-            set->ic_pktin_free->queued = FALSE;
-            set->ic_pktin_free->set = set;
-        }
-        #endif
-
-        pq_unlink_common(pqb, node);
-
-        node->prev = set->pktin_freeq_head->prev; // WINSCP
-        node->next = set->pktin_freeq_head; // WINSCP
-        node->next->prev = node;
-        node->prev->next = node;
-        node->on_free_queue = true;
-
-        queue_idempotent_callback(set->ic_pktin_free); // WINSCP
-    }
-
-    return container_of(node, PktIn, qnode);
-}
-
-static PktOut *pq_out_after(PacketQueueBase *pqb,
-                            PacketQueueNode *prev, bool pop)
-{
-    PacketQueueNode *node = prev->next;
-    if (node == &pqb->end)
-        return NULL;
-
-    if (pop) {
-        pq_unlink_common(pqb, node);
-
-        node->prev = node->next = NULL;
-    }
-
-    return container_of(node, PktOut, qnode);
-}
-
-void pq_in_init(PktInQueue *pq, Seat * seat) // WINSCP
-{
-    pq->pqb.ic = NULL;
-    pq->pqb.seat = seat;
-    pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
-    pq->after = pq_in_after;
-    pq->pqb.total_size = 0;
-}
-
-void pq_out_init(PktOutQueue *pq, Seat * seat) // WINSCP
-{
-    pq->pqb.ic = NULL;
-    pq->pqb.seat = seat;
-    pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
-    pq->after = pq_out_after;
-    pq->pqb.total_size = 0;
-}
-
-void pq_in_clear(PktInQueue *pq)
-{
-    PktIn *pkt;
-    pq->pqb.ic = NULL;
-    while ((pkt = pq_pop(pq)) != NULL) {
-        /* No need to actually free these packets: pq_pop on a
-         * PktInQueue will automatically move them to the free
-         * queue. */
-    }
-}
-
-void pq_out_clear(PktOutQueue *pq)
-{
-    PktOut *pkt;
-    pq->pqb.ic = NULL;
-    while ((pkt = pq_pop(pq)) != NULL)
-        ssh_free_pktout(pkt);
-}
-
-/*
- * Concatenate the contents of the two queues q1 and q2, and leave the
- * result in qdest. qdest must be either empty, or one of the input
- * queues.
- */
-void pq_base_concatenate(PacketQueueBase *qdest,
-                         PacketQueueBase *q1, PacketQueueBase *q2)
-{
-    struct PacketQueueNode *head1, *tail1, *head2, *tail2;
-
-    size_t total_size = q1->total_size + q2->total_size;
-
-    /*
-     * Extract the contents from both input queues, and empty them.
-     */
-
-    head1 = (q1->end.next == &q1->end ? NULL : q1->end.next);
-    tail1 = (q1->end.prev == &q1->end ? NULL : q1->end.prev);
-    head2 = (q2->end.next == &q2->end ? NULL : q2->end.next);
-    tail2 = (q2->end.prev == &q2->end ? NULL : q2->end.prev);
-
-    q1->end.next = q1->end.prev = &q1->end;
-    q2->end.next = q2->end.prev = &q2->end;
-    q1->total_size = q2->total_size = 0;
-
-    /*
-     * Link the two lists together, handling the case where one or
-     * both is empty.
-     */
-
-    if (tail1)
-        tail1->next = head2;
-    else
-        head1 = head2;
-
-    if (head2)
-        head2->prev = tail1;
-    else
-        tail2 = tail1;
-
-    /*
-     * Check the destination queue is currently empty. (If it was one
-     * of the input queues, then it will be, because we emptied both
-     * of those just a moment ago.)
-     */
-
-    assert(qdest->end.next == &qdest->end);
-    assert(qdest->end.prev == &qdest->end);
-
-    /*
-     * If our concatenated list has anything in it, then put it in
-     * dest.
-     */
-
-    if (!head1) {
-        assert(!tail2);
-    } else {
-        assert(tail2);
-        qdest->end.next = head1;
-        qdest->end.prev = tail2;
-        head1->prev = &qdest->end;
-        tail2->next = &qdest->end;
-
-        if (qdest->ic)
-            queue_idempotent_callback(qdest->ic);
-    }
-
-    qdest->total_size = total_size;
-}
-
-/* ----------------------------------------------------------------------
- * Low-level functions for the packet structures themselves.
- */
-
-static void ssh_pkt_BinarySink_write(BinarySink *bs,
-                                     const void *data, size_t len);
-PktOut *ssh_new_packet(void)
-{
-    PktOut *pkt = snew(PktOut);
-
-    BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
-    pkt->data = NULL;
-    pkt->length = 0;
-    pkt->maxlen = 0;
-    pkt->downstream_id = 0;
-    pkt->additional_log_text = NULL;
-    pkt->qnode.next = pkt->qnode.prev = NULL;
-    pkt->qnode.on_free_queue = false;
-
-    return pkt;
-}
-
-static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
-{
-    sgrowarrayn_nm(pkt->data, pkt->maxlen, pkt->length, len);
-    memcpy(pkt->data + pkt->length, data, len);
-    pkt->length += len;
-    pkt->qnode.formal_size = pkt->length;
-}
-
-static void ssh_pkt_BinarySink_write(BinarySink *bs,
-                                     const void *data, size_t len)
-{
-    PktOut *pkt = BinarySink_DOWNCAST(bs, PktOut);
-    ssh_pkt_adddata(pkt, data, len);
-}
-
-void ssh_free_pktout(PktOut *pkt)
-{
-    sfree(pkt->data);
-    sfree(pkt);
-}
-
-/* ----------------------------------------------------------------------
- * Implement zombiechan_new() and its trivial vtable.
- */
-
-static void zombiechan_free(Channel *chan);
-static size_t zombiechan_send(
-    Channel *chan, bool is_stderr, const void *, size_t);
-static void zombiechan_set_input_wanted(Channel *chan, bool wanted);
-static void zombiechan_do_nothing(Channel *chan);
-static void zombiechan_open_failure(Channel *chan, const char *);
-static bool zombiechan_want_close(Channel *chan, bool sent_eof, bool rcvd_eof);
-static char *zombiechan_log_close_msg(Channel *chan) { return NULL; }
-
-static const ChannelVtable zombiechan_channelvt = {
-    /*.free =*/ zombiechan_free,
-    /*.open_confirmation =*/ zombiechan_do_nothing,
-    /*.open_failed =*/ zombiechan_open_failure,
-    /*.send =*/ zombiechan_send,
-    /*.send_eof =*/ zombiechan_do_nothing,
-    /*.set_input_wanted =*/ zombiechan_set_input_wanted,
-    /*.log_close_msg =*/ zombiechan_log_close_msg,
-    /*.want_close =*/ zombiechan_want_close,
-    /*.rcvd_exit_status =*/ chan_no_exit_status,
-    /*.rcvd_exit_signal =*/ chan_no_exit_signal,
-    /*.rcvd_exit_signal_numeric =*/ chan_no_exit_signal_numeric,
-    /*.run_shell =*/ chan_no_run_shell,
-    /*.run_command =*/ chan_no_run_command,
-    /*.run_subsystem =*/ chan_no_run_subsystem,
-    /*.enable_x11_forwarding =*/ chan_no_enable_x11_forwarding,
-    /*.enable_agent_forwarding =*/ chan_no_enable_agent_forwarding,
-    /*.allocate_pty =*/ chan_no_allocate_pty,
-    /*.set_env =*/ chan_no_set_env,
-    /*.send_break =*/ chan_no_send_break,
-    /*.send_signal =*/ chan_no_send_signal,
-    /*.change_window_size =*/ chan_no_change_window_size,
-    /*.request_response =*/ chan_no_request_response,
-};
-
-Channel *zombiechan_new(void)
-{
-    Channel *chan = snew(Channel);
-    chan->vt = &zombiechan_channelvt;
-    chan->initial_fixed_window_size = 0;
-    return chan;
-}
-
-static void zombiechan_free(Channel *chan)
-{
-    assert(chan->vt == &zombiechan_channelvt);
-    sfree(chan);
-}
-
-static void zombiechan_do_nothing(Channel *chan)
-{
-    assert(chan->vt == &zombiechan_channelvt);
-}
-
-static void zombiechan_open_failure(Channel *chan, const char *errtext)
-{
-    assert(chan->vt == &zombiechan_channelvt);
-}
-
-static size_t zombiechan_send(Channel *chan, bool is_stderr,
-                              const void *data, size_t length)
-{
-    assert(chan->vt == &zombiechan_channelvt);
-    return 0;
-}
-
-static void zombiechan_set_input_wanted(Channel *chan, bool enable)
-{
-    assert(chan->vt == &zombiechan_channelvt);
-}
-
-static bool zombiechan_want_close(Channel *chan, bool sent_eof, bool rcvd_eof)
-{
-    return true;
-}
-
-/* ----------------------------------------------------------------------
- * Common routines for handling SSH tty modes.
- */
-
-static unsigned real_ttymode_opcode(unsigned our_opcode, int ssh_version)
-{
-    switch (our_opcode) {
-      case TTYMODE_ISPEED:
-        return ssh_version == 1 ? TTYMODE_ISPEED_SSH1 : TTYMODE_ISPEED_SSH2;
-      case TTYMODE_OSPEED:
-        return ssh_version == 1 ? TTYMODE_OSPEED_SSH1 : TTYMODE_OSPEED_SSH2;
-      default:
-        return our_opcode;
-    }
-}
-
-static unsigned our_ttymode_opcode(unsigned real_opcode, int ssh_version)
-{
-    if (ssh_version == 1) {
-        switch (real_opcode) {
-          case TTYMODE_ISPEED_SSH1:
-            return TTYMODE_ISPEED;
-          case TTYMODE_OSPEED_SSH1:
-            return TTYMODE_OSPEED;
-          default:
-            return real_opcode;
-        }
-    } else {
-        switch (real_opcode) {
-          case TTYMODE_ISPEED_SSH2:
-            return TTYMODE_ISPEED;
-          case TTYMODE_OSPEED_SSH2:
-            return TTYMODE_OSPEED;
-          default:
-            return real_opcode;
-        }
-    }
-}
-
-struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf)
-{
-    struct ssh_ttymodes modes;
-    size_t i;
-
-    static const struct mode_name_type {
-        const char *mode;
-        int opcode;
-        enum { TYPE_CHAR, TYPE_BOOL } type;
-    } modes_names_types[] = {
-        #define TTYMODE_CHAR(name, val, index) { #name, val, TYPE_CHAR },
-        #define TTYMODE_FLAG(name, val, field, mask) { #name, val, TYPE_BOOL },
-        #include "sshttymodes.h"
-        #undef TTYMODE_CHAR
-        #undef TTYMODE_FLAG
-    };
-
-    memset(&modes, 0, sizeof(modes));
-
-    for (i = 0; i < lenof(modes_names_types); i++) {
-        const struct mode_name_type *mode = &modes_names_types[i];
-        const char *sval = conf_get_str_str(conf, CONF_ttymodes, mode->mode);
-        char *to_free = NULL;
-
-        if (!sval)
-            sval = "N";                /* just in case */
-
-        /*
-         * sval[0] can be
-         *  - 'V', indicating that an explicit value follows it;
-         *  - 'A', indicating that we should pass the value through from
-         *    the local environment via get_ttymode; or
-         *  - 'N', indicating that we should explicitly not send this
-         *    mode.
-         */
-        if (sval[0] == 'A') {
-            sval = to_free = seat_get_ttymode(seat, mode->mode);
-        } else if (sval[0] == 'V') {
-            sval++;                    /* skip the 'V' */
-        } else {
-            /* else 'N', or something from the future we don't understand */
-            continue;
-        }
-
-        if (sval) {
-            /*
-             * Parse the string representation of the tty mode
-             * into the integer value it will take on the wire.
-             */
-            unsigned ival = 0;
-
-            switch (mode->type) {
-              case TYPE_CHAR:
-                if (*sval) {
-                    char *next = NULL;
-                    /* We know ctrlparse won't write to the string, so
-                     * casting away const is ugly but allowable. */
-                    ival = ctrlparse((char *)sval, &next);
-                    if (!next)
-                        ival = sval[0];
-                } else {
-                    ival = 255; /* special value meaning "don't set" */
-                }
-                break;
-              case TYPE_BOOL:
-                if (stricmp(sval, "yes") == 0 ||
-                    stricmp(sval, "on") == 0 ||
-                    stricmp(sval, "true") == 0 ||
-                    stricmp(sval, "+") == 0)
-                    ival = 1;      /* true */
-                else if (stricmp(sval, "no") == 0 ||
-                         stricmp(sval, "off") == 0 ||
-                         stricmp(sval, "false") == 0 ||
-                         stricmp(sval, "-") == 0)
-                    ival = 0;      /* false */
-                else
-                    ival = (atoi(sval) != 0);
-                break;
-              default:
-                unreachable("Bad mode->type");
-            }
-
-            modes.have_mode[mode->opcode] = true;
-            modes.mode_val[mode->opcode] = ival;
-        }
-
-        sfree(to_free);
-    }
-
-    {
-        unsigned ospeed, ispeed;
-
-        /* Unpick the terminal-speed config string. */
-        ospeed = ispeed = 38400;           /* last-resort defaults */
-        sscanf(conf_get_str(conf, CONF_termspeed), "%u,%u", &ospeed, &ispeed);
-        /* Currently we unconditionally set these */
-        modes.have_mode[TTYMODE_ISPEED] = true;
-        modes.mode_val[TTYMODE_ISPEED] = ispeed;
-        modes.have_mode[TTYMODE_OSPEED] = true;
-        modes.mode_val[TTYMODE_OSPEED] = ospeed;
-    }
-
-    return modes;
-}
-
-struct ssh_ttymodes read_ttymodes_from_packet(
-    BinarySource *bs, int ssh_version)
-{
-    struct ssh_ttymodes modes;
-    memset(&modes, 0, sizeof(modes));
-
-    while (1) {
-        unsigned real_opcode, our_opcode;
-
-        real_opcode = get_byte(bs);
-        if (real_opcode == TTYMODE_END_OF_LIST)
-            break;
-        if (real_opcode >= 160) {
-            /*
-             * RFC 4254 (and the SSH 1.5 spec): "Opcodes 160 to 255
-             * are not yet defined, and cause parsing to stop (they
-             * should only be used after any other data)."
-             *
-             * My interpretation of this is that if one of these
-             * opcodes appears, it's not a parse _error_, but it is
-             * something that we don't know how to parse even well
-             * enough to step over it to find the next opcode, so we
-             * stop parsing now and assume that the rest of the string
-             * is composed entirely of things we don't understand and
-             * (as usual for unsupported terminal modes) silently
-             * ignore.
-             */
-            return modes;
-        }
-
-        our_opcode = our_ttymode_opcode(real_opcode, ssh_version);
-        assert(our_opcode < TTYMODE_LIMIT);
-        modes.have_mode[our_opcode] = true;
-
-        if (ssh_version == 1 && real_opcode >= 1 && real_opcode <= 127)
-            modes.mode_val[our_opcode] = get_byte(bs);
-        else
-            modes.mode_val[our_opcode] = get_uint32(bs);
-    }
-
-    return modes;
-}
-
-void write_ttymodes_to_packet(BinarySink *bs, int ssh_version,
-                              struct ssh_ttymodes modes)
-{
-    unsigned i;
-
-    for (i = 0; i < TTYMODE_LIMIT; i++) {
-        if (modes.have_mode[i]) {
-            unsigned val = modes.mode_val[i];
-            unsigned opcode = real_ttymode_opcode(i, ssh_version);
-
-            put_byte(bs, opcode);
-            if (ssh_version == 1 && opcode >= 1 && opcode <= 127)
-                put_byte(bs, val);
-            else
-                put_uint32(bs, val);
-        }
-    }
-
-    put_byte(bs, TTYMODE_END_OF_LIST);
-}
-
-/* ----------------------------------------------------------------------
- * Routine for allocating a new channel ID, given a means of finding
- * the index field in a given channel structure.
- */
-
-unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset)
-{
-    const unsigned CHANNEL_NUMBER_OFFSET = 256;
-    search234_state ss;
-
-    /*
-     * First-fit allocation of channel numbers: we always pick the
-     * lowest unused one.
-     *
-     * Every channel before that, and no channel after it, has an ID
-     * exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So
-     * we can use the search234 system to identify the length of that
-     * initial sequence, in a single log-time pass down the channels
-     * tree.
-     */
-    search234_start(&ss, channels);
-    while (ss.element) {
-        unsigned localid = *(unsigned *)((char *)ss.element + localid_offset);
-        if (localid == ss.index + CHANNEL_NUMBER_OFFSET)
-            search234_step(&ss, +1);
-        else
-            search234_step(&ss, -1);
-    }
-
-    /*
-     * Now ss.index gives exactly the number of channels in that
-     * initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must
-     * give precisely the lowest unused channel number.
-     */
-    return ss.index + CHANNEL_NUMBER_OFFSET;
-}
-
-/* ----------------------------------------------------------------------
- * Functions for handling the comma-separated strings used to store
- * lists of protocol identifiers in SSH-2.
- */
-
-void add_to_commasep(strbuf *buf, const char *data)
-{
-    if (buf->len > 0)
-        put_byte(buf, ',');
-    put_data(buf, data, strlen(data));
-}
-
-bool get_commasep_word(ptrlen *list, ptrlen *word)
-{
-    const char *comma;
-
-    /*
-     * Discard empty list elements, should there be any, because we
-     * never want to return one as if it was a real string. (This
-     * introduces a mild tolerance of badly formatted data in lists we
-     * receive, but I think that's acceptable.)
-     */
-    while (list->len > 0 && *(const char *)list->ptr == ',') {
-        list->ptr = (const char *)list->ptr + 1;
-        list->len--;
-    }
-
-    if (!list->len)
-        return false;
-
-    comma = memchr(list->ptr, ',', list->len);
-    if (!comma) {
-        *word = *list;
-        list->len = 0;
-    } else {
-        size_t wordlen = comma - (const char *)list->ptr;
-        word->ptr = list->ptr;
-        word->len = wordlen;
-        list->ptr = (const char *)list->ptr + wordlen + 1;
-        list->len -= wordlen + 1;
-    }
-    return true;
-}
-
-/* ----------------------------------------------------------------------
- * Functions for translating SSH packet type codes into their symbolic
- * string names.
- */
-
-#define TRANSLATE_UNIVERSAL(y, name, value)      \
-    if (type == value) return #name;
-#define TRANSLATE_KEX(y, name, value, ctx) \
-    if (type == value && pkt_kctx == ctx) return #name;
-#define TRANSLATE_AUTH(y, name, value, ctx) \
-    if (type == value && pkt_actx == ctx) return #name;
-
-const char *ssh1_pkt_type(int type)
-{
-    SSH1_MESSAGE_TYPES(TRANSLATE_UNIVERSAL, y);
-    return "unknown";
-}
-const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
-{
-    SSH2_MESSAGE_TYPES(TRANSLATE_UNIVERSAL, TRANSLATE_KEX, TRANSLATE_AUTH, y);
-    return "unknown";
-}
-
-#undef TRANSLATE_UNIVERSAL
-#undef TRANSLATE_KEX
-#undef TRANSLATE_AUTH
-
-/* ----------------------------------------------------------------------
- * Common helper function for clients and implementations of
- * PacketProtocolLayer.
- */
-
-void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new)
-{
-    new->bpp = old->bpp;
-    ssh_ppl_setup_queues(new, old->in_pq, old->out_pq);
-    new->selfptr = old->selfptr;
-    new->user_input = old->user_input;
-    new->seat = old->seat;
-    new->ssh = old->ssh;
-
-    *new->selfptr = new;
-    ssh_ppl_free(old);
-
-    /* The new layer might need to be the first one that sends a
-     * packet, so trigger a call to its main coroutine immediately. If
-     * it doesn't need to go first, the worst that will do is return
-     * straight away. */
-    queue_idempotent_callback(&new->ic_process_queue);
-}
-
-void ssh_ppl_free(PacketProtocolLayer *ppl)
-{
-    delete_callbacks_for_context(get_seat_callback_set(ppl->seat), ppl); // WINSCP
-    ppl->vt->free(ppl);
-}
-
-static void ssh_ppl_ic_process_queue_callback(void *context)
-{
-    PacketProtocolLayer *ppl = (PacketProtocolLayer *)context;
-    ssh_ppl_process_queue(ppl);
-}
-
-void ssh_ppl_setup_queues(PacketProtocolLayer *ppl,
-                          PktInQueue *inq, PktOutQueue *outq)
-{
-    ppl->in_pq = inq;
-    ppl->out_pq = outq;
-    ppl->in_pq->pqb.ic = &ppl->ic_process_queue;
-    ppl->ic_process_queue.fn = ssh_ppl_ic_process_queue_callback;
-    ppl->ic_process_queue.ctx = ppl;
-    ppl->ic_process_queue.set = get_seat_callback_set(ppl->seat);
-
-    /* If there's already something on the input queue, it will want
-     * handling immediately. */
-    if (pq_peek(ppl->in_pq))
-        queue_idempotent_callback(&ppl->ic_process_queue);
-}
-
-void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text)
-{
-    /* Messages sent via this function are from the SSH layer, not
-     * from the server-side process, so they always have the stderr
-     * flag set. */
-    int stderrflag = -1; // WINSCP
-    seat_output(ppl->seat, *((bool*)&stderrflag), text, strlen(text)); // WINSCP
-    sfree(text);
-}
-
-size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl)
-{
-    return ppl->out_pq->pqb.total_size;
-}
-
-/* ----------------------------------------------------------------------
- * Common helper functions for clients and implementations of
- * BinaryPacketProtocol.
- */
-
-static void ssh_bpp_input_raw_data_callback(void *context)
-{
-    BinaryPacketProtocol *bpp = (BinaryPacketProtocol *)context;
-    Ssh *ssh = bpp->ssh;               /* in case bpp is about to get freed */
-    ssh_bpp_handle_input(bpp);
-    /* If we've now cleared enough backlog on the input connection, we
-     * may need to unfreeze it. */
-    ssh_conn_processed_data(ssh);
-}
-
-static void ssh_bpp_output_packet_callback(void *context)
-{
-    BinaryPacketProtocol *bpp = (BinaryPacketProtocol *)context;
-    ssh_bpp_handle_output(bpp);
-}
-
-void ssh_bpp_common_setup(BinaryPacketProtocol *bpp)
-{
-    pq_in_init(&bpp->in_pq, get_log_seat(bpp->logctx)); // WINSCP
-    pq_out_init(&bpp->out_pq, get_log_seat(bpp->logctx)); // WINSCP
-    bpp->input_eof = false;
-    bpp->ic_in_raw.fn = ssh_bpp_input_raw_data_callback;
-    bpp->ic_in_raw.set = get_log_callback_set(bpp->logctx);
-    bpp->ic_in_raw.ctx = bpp;
-    bpp->ic_out_pq.fn = ssh_bpp_output_packet_callback;
-    bpp->ic_out_pq.set = get_log_callback_set(bpp->logctx);
-    bpp->ic_out_pq.ctx = bpp;
-    bpp->out_pq.pqb.ic = &bpp->ic_out_pq;
-}
-
-void ssh_bpp_free(BinaryPacketProtocol *bpp)
-{
-    // WINSCP
-    delete_callbacks_for_context(get_log_callback_set(bpp->logctx), bpp);
-    bpp->vt->free(bpp);
-}
-
-void ssh2_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
-                               const char *msg, int category)
-{
-    PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH2_MSG_DISCONNECT);
-    put_uint32(pkt, category);
-    put_stringz(pkt, msg);
-    put_stringz(pkt, "en");            /* language tag */
-    pq_push(&bpp->out_pq, pkt);
-}
-
-#define BITMAP_UNIVERSAL(y, name, value)         \
-    | (value >= y && value < y+32 ? 1UL << (value-y) : 0)
-#define BITMAP_CONDITIONAL(y, name, value, ctx) \
-    BITMAP_UNIVERSAL(y, name, value)
-#define SSH2_BITMAP_WORD(y) \
-    (0 SSH2_MESSAGE_TYPES(BITMAP_UNIVERSAL, BITMAP_CONDITIONAL, \
-                          BITMAP_CONDITIONAL, (32*y)))
-
-bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin)
-{
-    #pragma warn -osh
-    static const unsigned valid_bitmap[] = {
-        SSH2_BITMAP_WORD(0),
-        SSH2_BITMAP_WORD(1),
-        SSH2_BITMAP_WORD(2),
-        SSH2_BITMAP_WORD(3),
-        SSH2_BITMAP_WORD(4),
-        SSH2_BITMAP_WORD(5),
-        SSH2_BITMAP_WORD(6),
-        SSH2_BITMAP_WORD(7),
-    };
-    #pragma warn +osh
-
-    if (pktin->type < 0x100 &&
-        !((valid_bitmap[pktin->type >> 5] >> (pktin->type & 0x1F)) & 1)) {
-        PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH2_MSG_UNIMPLEMENTED);
-        put_uint32(pkt, pktin->sequence);
-        pq_push(&bpp->out_pq, pkt);
-        return true;
-    }
-
-    return false;
-}
-
-#undef BITMAP_UNIVERSAL
-#undef BITMAP_CONDITIONAL
-#undef SSH1_BITMAP_WORD
-
-/* ----------------------------------------------------------------------
- * Function to check a host key against any manually configured in Conf.
- */
-
-int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key)
-{
-    if (!conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, 0))
-        return -1;                     /* no manual keys configured */
-
-    if (fingerprints) {
-        size_t i; // WINSCP
-        for (i = 0; i < SSH_N_FPTYPES; i++) {
-            /*
-             * Each fingerprint string we've been given will have
-             * things like 'ssh-rsa 2048' at the front of it. Strip
-             * those off and narrow down to just the hash at the end
-             * of the string.
-             */
-            const char *fingerprint = fingerprints[i];
-            if (!fingerprint)
-                continue;
-            { // WINSCP
-            const char *p = strrchr(fingerprint, ' ');
-            fingerprint = p ? p+1 : fingerprint;
-            if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
-                                     fingerprint))
-                return 1;                  /* success */
-            } // WINSCP
-        }
-    }
-
-    if (key) {
-        /*
-         * Construct the base64-encoded public key blob and see if
-         * that's listed.
-         */
-        strbuf *binblob;
-        char *base64blob;
-        int atoms, i;
-        binblob = strbuf_new();
-        ssh_key_public_blob(key, BinarySink_UPCAST(binblob));
-        atoms = (binblob->len + 2) / 3;
-        base64blob = snewn(atoms * 4 + 1, char);
-        for (i = 0; i < atoms; i++)
-            base64_encode_atom(binblob->u + 3*i,
-                               binblob->len - 3*i, base64blob + 4*i);
-        base64blob[atoms * 4] = '\0';
-        strbuf_free(binblob);
-        if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, base64blob)) {
-            sfree(base64blob);
-            return 1;                  /* success */
-        }
-        sfree(base64blob);
-    }
-
-    return 0;
-}
-
-/* ----------------------------------------------------------------------
- * Common functions shared between SSH-1 layers.
- */
-
-bool ssh1_common_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
-{
-    /*
-     * Don't bother offering IGNORE if we've decided the remote
-     * won't cope with it, since we wouldn't bother sending it if
-     * asked anyway.
-     */
-    if (!(ppl->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
-        add_special(ctx, "IGNORE message", SS_NOP, 0);
-        return true;
-    }
-
-    return false;
-}
-
-bool ssh1_common_filter_queue(PacketProtocolLayer *ppl)
-{
-    PktIn *pktin;
-    ptrlen msg;
-
-    while ((pktin = pq_peek(ppl->in_pq)) != NULL) {
-        switch (pktin->type) {
-          case SSH1_MSG_DISCONNECT:
-            msg = get_string(pktin);
-            ssh_remote_error(ppl->ssh,
-                             "Remote side sent disconnect message:\n\"%.*s\"",
-                             PTRLEN_PRINTF(msg));
-            /* don't try to pop the queue, because we've been freed! */
-            return true;               /* indicate that we've been freed */
-
-          case SSH1_MSG_DEBUG:
-            msg = get_string(pktin);
-            ppl_logevent("Remote debug message: %.*s", PTRLEN_PRINTF(msg));
-            pq_pop(ppl->in_pq);
-            break;
-
-          case SSH1_MSG_IGNORE:
-            /* Do nothing, because we're ignoring it! Duhh. */
-            pq_pop(ppl->in_pq);
-            break;
-
-          default:
-            return false;
-        }
-    }
-
-    return false;
-}
-
-void ssh1_compute_session_id(
-    unsigned char *session_id, const unsigned char *cookie,
-    RSAKey *hostkey, RSAKey *servkey)
-{
-    ssh_hash *hash = ssh_hash_new(&ssh_md5);
-    size_t i; // WINSCP
-
-    for (i = (mp_get_nbits(hostkey->modulus) + 7) / 8; i-- ;)
-        put_byte(hash, mp_get_byte(hostkey->modulus, i));
-    for (i = (mp_get_nbits(servkey->modulus) + 7) / 8; i-- ;)
-        put_byte(hash, mp_get_byte(servkey->modulus, i));
-    put_data(hash, cookie, 8);
-    ssh_hash_final(hash, session_id);
-}

+ 0 - 1
source/putty/sshcr.h

@@ -18,7 +18,6 @@
  * Edit and Continue debugging feature causes their compiler to
  * violate ANSI C. To disable Edit and Continue debugging:
  *
- *  - right-click ssh.c in the FileView
  *  - click Settings
  *  - select the C/C++ tab and the General category
  *  - under `Debug info:', select anything _other_ than `Program

+ 0 - 1092
source/putty/sshdes.c

@@ -1,1092 +0,0 @@
-/*
- * sshdes.c: implementation of DES.
- */
-
-/*
- * Background
- * ----------
- *
- * The basic structure of DES is a Feistel network: the 64-bit cipher
- * block is divided into two 32-bit halves L and R, and in each round,
- * a mixing function is applied to one of them, the result is XORed
- * into the other, and then the halves are swapped so that the other
- * one will be the input to the mixing function next time. (This
- * structure guarantees reversibility no matter whether the mixing
- * function itself is bijective.)
- *
- * The mixing function for DES goes like this:
- *  + Extract eight contiguous 6-bit strings from the 32-bit word.
- *    They start at positions 4 bits apart, so each string overlaps
- *    the next one by one bit. At least one has to wrap cyclically
- *    round the end of the word.
- *  + XOR each of those strings with 6 bits of data from the key
- *    schedule (which consists of 8 x 6-bit strings per round).
- *  + Use the resulting 6-bit numbers as the indices into eight
- *    different lookup tables ('S-boxes'), each of which delivers a
- *    4-bit output.
- *  + Concatenate those eight 4-bit values into a 32-bit word.
- *  + Finally, apply a fixed permutation P to that word.
- *
- * DES adds one more wrinkle on top of this structure, which is to
- * conjugate it by a bitwise permutation of the cipher block. That is,
- * before starting the main cipher rounds, the input bits are permuted
- * according to a 64-bit permutation called IP, and after the rounds
- * are finished, the output bits are permuted back again by applying
- * the inverse of IP.
- *
- * This gives a lot of leeway to redefine the components of the cipher
- * without actually changing the input and output. You could permute
- * the bits in the output of any or all of the S-boxes, or reorder the
- * S-boxes among themselves, and adjust the following permutation P to
- * compensate. And you could adjust IP by post-composing a rotation of
- * each 32-bit half, and adjust the starting offsets of the 6-bit
- * S-box indices to compensate.
- *
- * test/desref.py demonstrates this by providing two equivalent forms
- * of the cipher, called DES and SGTDES, which give the same output.
- * DES is the form described in the original spec: if you make it
- * print diagnostic output during the cipher and check it against the
- * original, you should recognise the S-box outputs as matching the
- * ones you expect. But SGTDES, which I egotistically name after
- * myself, is much closer to the form implemented here: I've changed
- * the permutation P to suit my implementation strategy and
- * compensated by permuting the S-boxes, and also I've added a
- * rotation right by 1 bit to IP so that only one S-box index has to
- * wrap round the word and also so that the indices are nicely aligned
- * for the constant-time selection system I'm using.
- */
-
-#include <stdio.h>
-
-#include "ssh.h"
-#include "mpint_i.h"               /* we reuse the BignumInt system */
-
-/* If you compile with -DDES_DIAGNOSTICS, intermediate results will be
- * sent to debug() (so you also need to compile with -DDEBUG).
- * Otherwise this ifdef will condition away all the debug() calls. */
-#ifndef DES_DIAGNOSTICS
-#undef debug
-#define debug(...) ((void)0)
-#endif
-
-/*
- * General utility functions.
- */
-static inline uint32_t rol(uint32_t x, unsigned c)
-{
-    return (x << (31 & c)) | (x >> (31 & -(int/*WINSCP*/)c));
-}
-static inline uint32_t ror(uint32_t x, unsigned c)
-{
-    return rol(x, -(int/*WINSCP*/)c);
-}
-
-/*
- * The hard part of doing DES in constant time is the S-box lookup.
- *
- * My strategy is to iterate over the whole lookup table! That's slow,
- * but I don't see any way to avoid _something_ along those lines: in
- * every round, every entry in every S-box is potentially needed, and
- * if you can't change your memory access pattern based on the input
- * data, it follows that you have to read a quantity of information
- * equal to the size of all the S-boxes. (Unless they were to turn out
- * to be significantly compressible, but I for one couldn't show them
- * to be.)
- *
- * In more detail, I construct a sort of counter-based 'selection
- * gadget', which is 15 bits wide and starts off with the top bit
- * zero, the next eight bits all 1, and the bottom six set to the
- * input S-box index:
- *
- *     011111111xxxxxx
- *
- * Now if you add 1 in the lowest bit position, then either it carries
- * into the top section (resetting it to 100000000), or it doesn't do
- * that yet. If you do that 64 times, then it will _guarantee_ to have
- * ticked over into 100000000. In between those increments, the eight
- * bits that started off as 11111111 will have stayed that way for
- * some number of iterations and then become 00000000, and exactly how
- * many iterations depends on the input index.
- *
- * The purpose of the 0 bit at the top is to absorb the carry when the
- * switch happens, which means you can pack more than one gadget into
- * the same machine word and have them all work in parallel without
- * each one intefering with the next.
- *
- * The next step is to use each of those 8-bit segments as a bit mask:
- * each one is ANDed with a lookup table entry, and all the results
- * are XORed together. So you end up with the bitwise XOR of some
- * initial segment of the table entries. And the stored S-box tables
- * are transformed in such a way that the real S-box values are given
- * not by the individual entries, but by the cumulative XORs
- * constructed in this way.
- *
- * A refinement is that I increment each gadget by 2 rather than 1
- * each time, so I only iterate 32 times instead of 64. That's why
- * there are 8 selection bits instead of 4: each gadget selects enough
- * bits to reconstruct _two_ S-box entries, for a pair of indices
- * (2n,2n+1), and then finally I use the low bit of the index to do a
- * parallel selection between each of those pairs.
- *
- * The selection gadget is not quite 16 bits wide. So you can fit four
- * of them across a 64-bit word at 16-bit intervals, which is also
- * convenient because the place the S-box indices are coming from also
- * has pairs of them separated by 16-bit distances, so it's easy to
- * copy them into the gadgets in the first place.
- */
-
-/*
- * The S-box data. Each pair of nonzero columns here describes one of
- * the S-boxes, corresponding to the SGTDES tables in test/desref.py,
- * under the following transformation.
- *
- * Take S-box #3 as an example. Its values in successive rows of this
- * table are eb,e8,54,3d, ... So the cumulative XORs of initial
- * sequences of those values are eb,(eb^e8),(eb^e8^54), ... which
- * comes to eb,03,57,... Of _those_ values, the top nibble (e,0,5,...)
- * gives the even-numbered entries in the S-box, in _reverse_ order
- * (because a lower input index selects the XOR of a longer
- * subsequence). The odd-numbered entries are given by XORing the two
- * digits together: (e^b),(0^3),(5^7),... = 5,3,2,... And indeed, if
- * you check SGTDES.sboxes[3] you find it ends ... 52 03 e5.
- */
-#define SBOX_ITERATION(X)                       \
-    /*  66  22  44  00      77  33  55  11 */   \
-    X(0xf600970083008500, 0x0e00eb007b002e00)   \
-    X(0xda00e4009000e000, 0xad00e800a700b400)   \
-    X(0x1a009d003f003600, 0xf60054004300cd00)   \
-    X(0xaf00c500e900a900, 0x63003d00f2005900)   \
-    X(0xf300750079001400, 0x80005000a2008900)   \
-    X(0xa100d400d6007b00, 0xd3009000d300e100)   \
-    X(0x450087002600ac00, 0xae003c0031009c00)   \
-    X(0xd000b100b6003600, 0x3e006f0092005900)   \
-    X(0x4d008a0026001000, 0x89007a00b8004a00)   \
-    X(0xca00f5003f00ac00, 0x6f00f0003c009400)   \
-    X(0x92008d0090001000, 0x8c00c600ce004a00)   \
-    X(0xe2005900e9006d00, 0x790078007800fa00)   \
-    X(0x1300b10090008d00, 0xa300170027001800)   \
-    X(0xc70058005f006a00, 0x9c00c100e0006300)   \
-    X(0x9b002000f000f000, 0xf70057001600f900)   \
-    X(0xeb00b0009000af00, 0xa9006300b0005800)   \
-    X(0xa2001d00cf000000, 0x3800b00066000000)   \
-    X(0xf100da007900d000, 0xbc00790094007900)   \
-    X(0x570015001900ad00, 0x6f00ef005100cb00)   \
-    X(0xc3006100e9006d00, 0xc000b700f800f200)   \
-    X(0x1d005800b600d000, 0x67004d00cd002c00)   \
-    X(0xf400b800d600e000, 0x5e00a900b000e700)   \
-    X(0x5400d1003f009c00, 0xc90069002c005300)   \
-    X(0xe200e50060005900, 0x6a00b800c500f200)   \
-    X(0xdf0047007900d500, 0x7000ec004c00ea00)   \
-    X(0x7100d10060009c00, 0x3f00b10095005e00)   \
-    X(0x82008200f0002000, 0x87001d00cd008000)   \
-    X(0xd0007000af00c000, 0xe200be006100f200)   \
-    X(0x8000930060001000, 0x36006e0081001200)   \
-    X(0x6500a300d600ac00, 0xcf003d007d00c000)   \
-    X(0x9000700060009800, 0x62008100ad009200)   \
-    X(0xe000e4003f00f400, 0x5a00ed009000f200)   \
-    /* end of list */
-
-/*
- * The S-box mapping function. Expects two 32-bit input words: si6420
- * contains the table indices for S-boxes 0,2,4,6 with their low bits
- * starting at position 2 (for S-box 0) and going up in steps of 8.
- * si7531 has indices 1,3,5,7 in the same bit positions.
- */
-static inline uint32_t des_S(uint32_t si6420, uint32_t si7531)
-{
-    debug("sindices: %02x %02x %02x %02x %02x %02x %02x %02x\n",
-          0x3F & (si6420 >>  2), 0x3F & (si7531 >>  2),
-          0x3F & (si6420 >> 10), 0x3F & (si7531 >> 10),
-          0x3F & (si6420 >> 18), 0x3F & (si7531 >> 18),
-          0x3F & (si6420 >> 26), 0x3F & (si7531 >> 26));
-
-#ifdef SIXTY_FOUR_BIT
-    /*
-     * On 64-bit machines, we store the table in exactly the form
-     * shown above, and make two 64-bit words containing four
-     * selection gadgets each.
-     */
-
-    /* Set up the gadgets. The 'cNNNN' variables will be gradually
-     * incremented, and the bits in positions FF00FF00FF00FF00 will
-     * act as selectors for the words in the table.
-     *
-     * A side effect of moving the input indices further apart is that
-     * they change order, because it's easier to keep a pair that were
-     * originally 16 bits apart still 16 bits apart, which now makes
-     * them adjacent instead of separated by one. So the fact that
-     * si6420 turns into c6240 (with the 2,4 reversed) is not a typo!
-     * This will all be undone when we rebuild the output word later.
-     */
-    uint64_t c6240 = ((si6420 | ((uint64_t)si6420 << 24))
-                      & 0x00FC00FC00FC00FC) | 0xFF00FF00FF00FF00;
-    uint64_t c7351 = ((si7531 | ((uint64_t)si7531 << 24))
-                      & 0x00FC00FC00FC00FC) | 0xFF00FF00FF00FF00;
-    debug("S in:  c6240=%016"PRIx64" c7351=%016"PRIx64"\n", c6240, c7351);
-
-    /* Iterate over the table. The 'sNNNN' variables accumulate the
-     * XOR of all the table entries not masked out. */
-    static const struct tbl { uint64_t t6240, t7351; } tbl[32] = {
-#define TABLE64(a, b) { a, b },
-        SBOX_ITERATION(TABLE64)
-#undef TABLE64
-    };
-    uint64_t s6240 = 0, s7351 = 0;
-    for (const struct tbl *t = tbl, *limit = tbl + 32; t < limit; t++) {
-        s6240 ^= c6240 & t->t6240; c6240 += 0x0008000800080008;
-        s7351 ^= c7351 & t->t7351; c7351 += 0x0008000800080008;
-    }
-    debug("S out: s6240=%016"PRIx64" s7351=%016"PRIx64"\n", s6240, s7351);
-
-    /* Final selection between each even/odd pair: mask off the low
-     * bits of all the input indices (which haven't changed throughout
-     * the iteration), and multiply by a bit mask that will turn each
-     * set bit into a mask covering the upper nibble of the selected
-     * pair. Then use those masks to control which set of lower
-     * nibbles is XORed into the upper nibbles. */
-    s6240 ^= (s6240 << 4) & ((0xf000/0x004) * (c6240 & 0x0004000400040004));
-    s7351 ^= (s7351 << 4) & ((0xf000/0x004) * (c7351 & 0x0004000400040004));
-
-    /* Now the eight final S-box outputs are in the upper nibble of
-     * each selection position. Mask away the rest of the clutter. */
-    s6240 &= 0xf000f000f000f000;
-    s7351 &= 0xf000f000f000f000;
-    debug("s0=%x s1=%x s2=%x s3=%x s4=%x s5=%x s6=%x s7=%x\n",
-          (unsigned)(0xF & (s6240 >> 12)),
-          (unsigned)(0xF & (s7351 >> 12)),
-          (unsigned)(0xF & (s6240 >> 44)),
-          (unsigned)(0xF & (s7351 >> 44)),
-          (unsigned)(0xF & (s6240 >> 28)),
-          (unsigned)(0xF & (s7351 >> 28)),
-          (unsigned)(0xF & (s6240 >> 60)),
-          (unsigned)(0xF & (s7351 >> 60)));
-
-    /* Combine them all into a single 32-bit output word, which will
-     * come out in the order 76543210. */
-    uint64_t combined = (s6240 >> 12) | (s7351 >> 8);
-    return combined | (combined >> 24);
-
-#else /* SIXTY_FOUR_BIT */
-    /*
-     * For 32-bit platforms, we do the same thing but in four 32-bit
-     * words instead of two 64-bit ones, so the CPU doesn't have to
-     * waste time propagating carries or shifted bits between the two
-     * halves of a uint64 that weren't needed anyway.
-     */
-
-    /* Set up the gadgets */
-    { // WINSCP
-    uint32_t c40 = ((si6420     ) & 0x00FC00FC) | 0xFF00FF00;
-    uint32_t c62 = ((si6420 >> 8) & 0x00FC00FC) | 0xFF00FF00;
-    uint32_t c51 = ((si7531     ) & 0x00FC00FC) | 0xFF00FF00;
-    uint32_t c73 = ((si7531 >> 8) & 0x00FC00FC) | 0xFF00FF00;
-    debug("S in:  c40=%08"PRIx32" c62=%08"PRIx32
-          " c51=%08"PRIx32" c73=%08"PRIx32"\n", c40, c62, c51, c73);
-
-    /* Iterate over the table */
-    { // WINSCP
-    static const struct tbl { uint32_t t40, t62, t51, t73; } tbl[32] = {
-#define TABLE32(a, b) { ((uint32_t)a##LL), (a##LL>>32), ((uint32_t)b##LL), (b##LL>>32) }, // WINSCP
-        SBOX_ITERATION(TABLE32)
-#undef TABLE32
-    };
-    uint32_t s40 = 0, s62 = 0, s51 = 0, s73 = 0;
-    const struct tbl *t, *limit; // WINSCP
-    for (t = tbl, limit = tbl + 32; t < limit; t++) {
-        s40 ^= c40 & t->t40; c40 += 0x00080008;
-        s62 ^= c62 & t->t62; c62 += 0x00080008;
-        s51 ^= c51 & t->t51; c51 += 0x00080008;
-        s73 ^= c73 & t->t73; c73 += 0x00080008;
-    }
-    debug("S out: s40=%08"PRIx32" s62=%08"PRIx32
-           " s51=%08"PRIx32" s73=%08"PRIx32"\n", s40, s62, s51, s73);
-
-    /* Final selection within each pair */
-    s40 ^= (s40 << 4) & ((0xf000/0x004) * (c40 & 0x00040004));
-    s62 ^= (s62 << 4) & ((0xf000/0x004) * (c62 & 0x00040004));
-    s51 ^= (s51 << 4) & ((0xf000/0x004) * (c51 & 0x00040004));
-    s73 ^= (s73 << 4) & ((0xf000/0x004) * (c73 & 0x00040004));
-
-    /* Clean up the clutter */
-    s40 &= 0xf000f000;
-    s62 &= 0xf000f000;
-    s51 &= 0xf000f000;
-    s73 &= 0xf000f000;
-    debug("s0=%x s1=%x s2=%x s3=%x s4=%x s5=%x s6=%x s7=%x\n",
-          (unsigned)(0xF & (s40 >> 12)),
-          (unsigned)(0xF & (s51 >> 12)),
-          (unsigned)(0xF & (s62 >> 12)),
-          (unsigned)(0xF & (s73 >> 12)),
-          (unsigned)(0xF & (s40 >> 28)),
-          (unsigned)(0xF & (s51 >> 28)),
-          (unsigned)(0xF & (s62 >> 28)),
-          (unsigned)(0xF & (s73 >> 28)));
-
-    /* Recombine and return */
-    return (s40 >> 12) | (s62 >> 4) | (s51 >> 8) | (s73);
-    } // WINSCP
-    } // WINSCP
-
-#endif /* SIXTY_FOUR_BIT */
-
-}
-
-/*
- * Now for the permutation P. The basic strategy here is to use a
- * Benes network: in each stage, the bit at position i is allowed to
- * either stay where it is or swap with i ^ D, where D is a power of 2
- * that varies with each phase. (So when D=1, pairs of the form
- * {2n,2n+1} can swap; when D=2, the pairs are {4n+j,4n+j+2} for
- * j={0,1}, and so on.)
- *
- * You can recursively construct a Benes network for an arbitrary
- * permutation, in which the values of D iterate across all the powers
- * of 2 less than the permutation size and then go back again. For
- * example, the typical presentation for 32 bits would have D iterate
- * over 16,8,4,2,1,2,4,8,16, and there's an easy algorithm that can
- * express any permutation in that form by deciding which pairs of
- * bits to swap in the outer pair of stages and then recursing to do
- * all the stages in between.
- *
- * Actually implementing the swaps is easy when they're all between
- * bits at the same separation: make the value x ^ (x >> D), mask out
- * just the bits in the low position of a pair that needs to swap, and
- * then use the resulting value y to make x ^ y ^ (y << D) which is
- * the swapped version.
- *
- * In this particular case, I processed the bit indices in the other
- * order (going 1,2,4,8,16,8,4,2,1), which makes no significant
- * difference to the construction algorithm (it's just a relabelling),
- * but it now means that the first two steps only permute entries
- * within the output of each S-box - and therefore we can leave them
- * completely out, in favour of just defining the S-boxes so that
- * those permutation steps are already applied. Furthermore, by
- * exhaustive search over the rest of the possible bit-orders for each
- * S-box, I was able to find a version of P which could be represented
- * in such a way that two further phases had all their control bits
- * zero and could be skipped. So the number of swap stages is reduced
- * to 5 from the 9 that might have been needed.
- */
-
-static inline uint32_t des_benes_step(uint32_t v, unsigned D, uint32_t mask)
-{
-    uint32_t diff = (v ^ (v >> D)) & mask;
-    return v ^ diff ^ (diff << D);
-}
-
-static inline uint32_t des_P(uint32_t v_orig)
-{
-    uint32_t v = v_orig;
-
-    /* initial stages with distance 1,2 are part of the S-box data table */
-    v = des_benes_step(v,  4, 0x07030702);
-    v = des_benes_step(v,  8, 0x004E009E);
-    v = des_benes_step(v, 16, 0x0000D9D3);
-/*  v = des_benes_step(v,  8, 0x00000000);  no-op, so we can skip it */
-    v = des_benes_step(v,  4, 0x05040004);
-/*  v = des_benes_step(v,  2, 0x00000000);  no-op, so we can skip it */
-    v = des_benes_step(v,  1, 0x04045015);
-
-    debug("P(%08"PRIx32") = %08"PRIx32"\n", v_orig, v);
-
-    return v;
-}
-
-/*
- * Putting the S and P functions together, and adding in the round key
- * as well, gives us the full mixing function f.
- */
-
-static inline uint32_t des_f(uint32_t R, uint32_t K7531, uint32_t K6420)
-{
-    uint32_t s7531 = R ^ K7531, s6420 = rol(R, 4) ^ K6420;
-    return des_P(des_S(s6420, s7531));
-}
-
-/*
- * The key schedule, and the function to set it up.
- */
-
-typedef struct des_keysched des_keysched;
-struct des_keysched {
-    uint32_t k7531[16], k6420[16];
-};
-
-/*
- * Simplistic function to select an arbitrary sequence of bits from
- * one value and glue them together into another value. bitnums[]
- * gives the sequence of bit indices of the input, from the highest
- * output bit downwards. An index of -1 means that output bit is left
- * at zero.
- *
- * This function is only used during key setup, so it doesn't need to
- * be highly optimised.
- */
-static inline uint64_t bitsel(
-    uint64_t input, const int8_t *bitnums, size_t size)
-{
-    uint64_t ret = 0;
-    while (size-- > 0) {
-        int bitpos = *bitnums++;
-        ret <<= 1;
-        if (bitpos >= 0)
-            ret |= 1 & (input >> bitpos);
-    }
-    return ret;
-}
-
-static void des_key_setup(uint64_t key, des_keysched *sched)
-{
-    static const int8_t PC1[] = {
-         7, 15, 23, 31, 39, 47, 55, 63,  6, 14, 22, 30, 38, 46,
-        54, 62,  5, 13, 21, 29, 37, 45, 53, 61,  4, 12, 20, 28,
-        -1, -1, -1, -1,
-         1,  9, 17, 25, 33, 41, 49, 57,  2, 10, 18, 26, 34, 42,
-        50, 58,  3, 11, 19, 27, 35, 43, 51, 59, 36, 44, 52, 60,
-    };
-    static const int8_t PC2_7531[] = {
-        46, 43, 49, 36, 59, 55, -1, -1, /* index into S-box 7 */
-        37, 41, 48, 56, 34, 52, -1, -1, /* index into S-box 5 */
-        15,  4, 25, 19,  9,  1, -1, -1, /* index into S-box 3 */
-        12,  7, 17,  0, 22,  3, -1, -1, /* index into S-box 1 */
-    };
-    static const int8_t PC2_6420[] = {
-        57, 32, 45, 54, 39, 50, -1, -1, /* index into S-box 6 */
-        44, 53, 33, 40, 47, 58, -1, -1, /* index into S-box 4 */
-        26, 16,  5, 11, 23,  8, -1, -1, /* index into S-box 2 */
-        10, 14,  6, 20, 27, 24, -1, -1, /* index into S-box 0 */
-    };
-    static const int leftshifts[] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
-
-    /* Select 56 bits from the 64-bit input key integer (the low bit
-     * of each input byte is unused), into a word consisting of two
-     * 28-bit integers starting at bits 0 and 32. */
-    uint64_t CD = bitsel(key, PC1, lenof(PC1));
-
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < 16; i++) {
-        /* Rotate each 28-bit half of CD left by 1 or 2 bits (varying
-         * between rounds) */
-        CD <<= leftshifts[i];
-        CD = (CD & 0x0FFFFFFF0FFFFFFFLL) | ((CD & 0xF0000000F0000000LL) >> 28); // WINSCP
-
-        /* Select key bits from the rotated word to use during the
-         * actual cipher */
-        sched->k7531[i] = bitsel(CD, PC2_7531, lenof(PC2_7531));
-        sched->k6420[i] = bitsel(CD, PC2_6420, lenof(PC2_6420));
-    }
-    } // WINSCP
-}
-
-/*
- * Helper routines for dealing with 64-bit blocks in the form of an L
- * and R word.
- */
-
-typedef struct LR LR;
-struct LR { uint32_t L, R; };
-
-static inline LR des_load_lr(const void *vp)
-{
-    const uint8_t *p = (const uint8_t *)vp;
-    LR out;
-    out.L = GET_32BIT_MSB_FIRST(p);
-    out.R = GET_32BIT_MSB_FIRST(p+4);
-    return out;
-}
-
-static inline void des_store_lr(void *vp, LR lr)
-{
-    uint8_t *p = (uint8_t *)vp;
-    PUT_32BIT_MSB_FIRST(p, lr.L);
-    PUT_32BIT_MSB_FIRST(p+4, lr.R);
-}
-
-static inline LR des_xor_lr(LR a, LR b)
-{
-    a.L ^= b.L;
-    a.R ^= b.R;
-    return a;
-}
-
-static inline LR des_swap_lr(LR in)
-{
-    LR out;
-    out.L = in.R;
-    out.R = in.L;
-    return out;
-}
-
-/*
- * The initial and final permutations of official DES are in a
- * restricted form, in which the 'before' and 'after' positions of a
- * given data bit are derived from each other by permuting the bits of
- * the _index_ and flipping some of them. This allows the permutation
- * to be performed effectively by a method that looks rather like
- * _half_ of a general Benes network, because the restricted form
- * means only half of it is actually needed.
- *
- * _Our_ initial and final permutations include a rotation by 1 bit,
- * but it's still easier to just suffix that to the standard IP/FP
- * than to regenerate everything using a more general method.
- *
- * Because we're permuting 64 bits in this case, between two 32-bit
- * words, there's a separate helper function for this code that
- * doesn't look quite like des_benes_step() above.
- */
-
-static inline void des_bitswap_IP_FP(uint32_t *L, uint32_t *R,
-                                     unsigned D, uint32_t mask)
-{
-    uint32_t diff = mask & ((*R >> D) ^ *L);
-    *R ^= diff << D;
-    *L ^= diff;
-}
-
-static inline LR des_IP(LR lr)
-{
-    des_bitswap_IP_FP(&lr.R, &lr.L,  4, 0x0F0F0F0F);
-    des_bitswap_IP_FP(&lr.R, &lr.L, 16, 0x0000FFFF);
-    des_bitswap_IP_FP(&lr.L, &lr.R,  2, 0x33333333);
-    des_bitswap_IP_FP(&lr.L, &lr.R,  8, 0x00FF00FF);
-    des_bitswap_IP_FP(&lr.R, &lr.L,  1, 0x55555555);
-
-    lr.L = ror(lr.L, 1);
-    lr.R = ror(lr.R, 1);
-
-    return lr;
-}
-
-static inline LR des_FP(LR lr)
-{
-    lr.L = rol(lr.L, 1);
-    lr.R = rol(lr.R, 1);
-
-    des_bitswap_IP_FP(&lr.R, &lr.L,  1, 0x55555555);
-    des_bitswap_IP_FP(&lr.L, &lr.R,  8, 0x00FF00FF);
-    des_bitswap_IP_FP(&lr.L, &lr.R,  2, 0x33333333);
-    des_bitswap_IP_FP(&lr.R, &lr.L, 16, 0x0000FFFF);
-    des_bitswap_IP_FP(&lr.R, &lr.L,  4, 0x0F0F0F0F);
-
-    return lr;
-}
-
-/*
- * The main cipher functions, which are identical except that they use
- * the key schedule in opposite orders.
- *
- * We provide a version without the initial and final permutations,
- * for use in triple-DES mode (no sense undoing and redoing it in
- * between the phases).
- */
-
-static inline LR des_round(LR in, const des_keysched *sched, size_t round)
-{
-    LR out;
-    out.L = in.R;
-    out.R = in.L ^ des_f(in.R, sched->k7531[round], sched->k6420[round]);
-    return out;
-}
-
-static inline LR des_inner_cipher(LR lr, const des_keysched *sched,
-                                  size_t start, size_t step)
-{
-    lr = des_round(lr, sched, start+0x0*step);
-    lr = des_round(lr, sched, start+0x1*step);
-    lr = des_round(lr, sched, start+0x2*step);
-    lr = des_round(lr, sched, start+0x3*step);
-    lr = des_round(lr, sched, start+0x4*step);
-    lr = des_round(lr, sched, start+0x5*step);
-    lr = des_round(lr, sched, start+0x6*step);
-    lr = des_round(lr, sched, start+0x7*step);
-    lr = des_round(lr, sched, start+0x8*step);
-    lr = des_round(lr, sched, start+0x9*step);
-    lr = des_round(lr, sched, start+0xa*step);
-    lr = des_round(lr, sched, start+0xb*step);
-    lr = des_round(lr, sched, start+0xc*step);
-    lr = des_round(lr, sched, start+0xd*step);
-    lr = des_round(lr, sched, start+0xe*step);
-    lr = des_round(lr, sched, start+0xf*step);
-    return des_swap_lr(lr);
-}
-
-static inline LR des_full_cipher(LR lr, const des_keysched *sched,
-                                 size_t start, size_t step)
-{
-    lr = des_IP(lr);
-    lr = des_inner_cipher(lr, sched, start, step);
-    lr = des_FP(lr);
-    return lr;
-}
-
-/*
- * Parameter pairs for the start,step arguments to the cipher routines
- * above, causing them to use the same key schedule in opposite orders.
- */
-#define ENCIPHER 0, 1                  /* for encryption */
-#define DECIPHER 15, -1                /* for decryption */
-
-/* ----------------------------------------------------------------------
- * Single-DES
- */
-
-struct des_cbc_ctx {
-    des_keysched sched;
-    LR iv;
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *des_cbc_new(const ssh_cipheralg *alg)
-{
-    struct des_cbc_ctx *ctx = snew(struct des_cbc_ctx);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void des_cbc_free(ssh_cipher *ciph)
-{
-    struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void des_cbc_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph);
-    const uint8_t *key = (const uint8_t *)vkey;
-    des_key_setup(GET_64BIT_MSB_FIRST(key), &ctx->sched);
-}
-
-static void des_cbc_setiv(ssh_cipher *ciph, const void *iv)
-{
-    struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph);
-    ctx->iv = des_load_lr(iv);
-}
-
-static void des_cbc_encrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    for (; len > 0; len -= 8, data += 8) {
-        LR plaintext = des_load_lr(data);
-        LR cipher_in = des_xor_lr(plaintext, ctx->iv);
-        LR ciphertext = des_full_cipher(cipher_in, &ctx->sched, ENCIPHER);
-        des_store_lr(data, ciphertext);
-        ctx->iv = ciphertext;
-    }
-}
-
-static void des_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    for (; len > 0; len -= 8, data += 8) {
-        LR ciphertext = des_load_lr(data);
-        LR cipher_out = des_full_cipher(ciphertext, &ctx->sched, DECIPHER);
-        LR plaintext = des_xor_lr(cipher_out, ctx->iv);
-        des_store_lr(data, plaintext);
-        ctx->iv = ciphertext;
-    }
-}
-
-const ssh_cipheralg ssh_des = {
-    // WINSCP
-    /*.new =*/ des_cbc_new,
-    /*.free =*/ des_cbc_free,
-    /*.setiv =*/ des_cbc_setiv,
-    /*.setkey =*/ des_cbc_setkey,
-    /*.encrypt =*/ des_cbc_encrypt,
-    /*.decrypt =*/ des_cbc_decrypt,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "des-cbc",
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 56,
-    /*.padded_keybytes =*/ 8,
-    /*.flags =*/ SSH_CIPHER_IS_CBC,
-    /*.text_name =*/ "single-DES CBC",
-    NULL, NULL, // WINSCP
-};
-
-const ssh_cipheralg ssh_des_sshcom_ssh2 = {
-    /* Same as ssh_des_cbc, but with a different SSH-2 ID */
-    // WINSCP
-    /*.new =*/ des_cbc_new,
-    /*.free =*/ des_cbc_free,
-    /*.setiv =*/ des_cbc_setiv,
-    /*.setkey =*/ des_cbc_setkey,
-    /*.encrypt =*/ des_cbc_encrypt,
-    /*.decrypt =*/ des_cbc_decrypt,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "[email protected]",
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 56,
-    /*.padded_keybytes =*/ 8,
-    /*.flags =*/ SSH_CIPHER_IS_CBC,
-    /*.text_name =*/ "single-DES CBC",
-    NULL, NULL, // WINSCP
-};
-
-static const ssh_cipheralg *const des_list[] = {
-    &ssh_des,
-    &ssh_des_sshcom_ssh2
-};
-
-const ssh2_ciphers ssh2_des = { lenof(des_list), des_list };
-
-/* ----------------------------------------------------------------------
- * Triple-DES CBC, SSH-2 style. The CBC mode treats the three
- * invocations of DES as a single unified cipher, and surrounds it
- * with just one layer of CBC, so only one IV is needed.
- */
-
-struct des3_cbc1_ctx {
-    des_keysched sched[3];
-    LR iv;
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *des3_cbc1_new(const ssh_cipheralg *alg)
-{
-    struct des3_cbc1_ctx *ctx = snew(struct des3_cbc1_ctx);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void des3_cbc1_free(ssh_cipher *ciph)
-{
-    struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void des3_cbc1_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph);
-    const uint8_t *key = (const uint8_t *)vkey;
-    size_t i; // WINSCP
-    for (i = 0; i < 3; i++)
-        des_key_setup(GET_64BIT_MSB_FIRST(key + 8*i), &ctx->sched[i]);
-}
-
-static void des3_cbc1_setiv(ssh_cipher *ciph, const void *iv)
-{
-    struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph);
-    ctx->iv = des_load_lr(iv);
-}
-
-static void des3_cbc1_cbc_encrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    for (; len > 0; len -= 8, data += 8) {
-        LR plaintext = des_load_lr(data);
-        LR cipher_in = des_xor_lr(plaintext, ctx->iv);
-
-        /* Run three copies of the cipher, without undoing and redoing
-         * IP/FP in between. */
-        LR lr = des_IP(cipher_in);
-        lr = des_inner_cipher(lr, &ctx->sched[0], ENCIPHER);
-        lr = des_inner_cipher(lr, &ctx->sched[1], DECIPHER);
-        lr = des_inner_cipher(lr, &ctx->sched[2], ENCIPHER);
-        { // WINSCP
-        LR ciphertext = des_FP(lr);
-
-        des_store_lr(data, ciphertext);
-        ctx->iv = ciphertext;
-        } // WINSCP
-    }
-}
-
-static void des3_cbc1_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    for (; len > 0; len -= 8, data += 8) {
-        LR ciphertext = des_load_lr(data);
-
-        /* Similarly to encryption, but with the order reversed. */
-        LR lr = des_IP(ciphertext);
-        lr = des_inner_cipher(lr, &ctx->sched[2], DECIPHER);
-        lr = des_inner_cipher(lr, &ctx->sched[1], ENCIPHER);
-        lr = des_inner_cipher(lr, &ctx->sched[0], DECIPHER);
-        { // WINSCP
-        LR cipher_out = des_FP(lr);
-
-        LR plaintext = des_xor_lr(cipher_out, ctx->iv);
-        des_store_lr(data, plaintext);
-        ctx->iv = ciphertext;
-        } // WINSCP
-    }
-}
-
-const ssh_cipheralg ssh_3des_ssh2 = {
-    // WINSCP
-    /*.new =*/ des3_cbc1_new,
-    /*.free =*/ des3_cbc1_free,
-    /*.setiv =*/ des3_cbc1_setiv,
-    /*.setkey =*/ des3_cbc1_setkey,
-    /*.encrypt =*/ des3_cbc1_cbc_encrypt,
-    /*.decrypt =*/ des3_cbc1_cbc_decrypt,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "3des-cbc",
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 168,
-    /*.padded_keybytes =*/ 24,
-    /*.flags =*/ SSH_CIPHER_IS_CBC,
-    /*.text_name =*/ "triple-DES CBC",
-    NULL, NULL, // WINSCP
-};
-
-/* ----------------------------------------------------------------------
- * Triple-DES in SDCTR mode. Again, the three DES instances are
- * treated as one big cipher, with a single counter encrypted through
- * all three.
- */
-
-#define SDCTR_WORDS (8 / BIGNUM_INT_BYTES)
-
-struct des3_sdctr_ctx {
-    des_keysched sched[3];
-    BignumInt counter[SDCTR_WORDS];
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *des3_sdctr_new(const ssh_cipheralg *alg)
-{
-    struct des3_sdctr_ctx *ctx = snew(struct des3_sdctr_ctx);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void des3_sdctr_free(ssh_cipher *ciph)
-{
-    struct des3_sdctr_ctx *ctx = container_of(
-        ciph, struct des3_sdctr_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void des3_sdctr_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    struct des3_sdctr_ctx *ctx = container_of(
-        ciph, struct des3_sdctr_ctx, ciph);
-    const uint8_t *key = (const uint8_t *)vkey;
-    size_t i; // WINSCP
-    for (i = 0; i < 3; i++)
-        des_key_setup(GET_64BIT_MSB_FIRST(key + 8*i), &ctx->sched[i]);
-}
-
-static void des3_sdctr_setiv(ssh_cipher *ciph, const void *viv)
-{
-    struct des3_sdctr_ctx *ctx = container_of(
-        ciph, struct des3_sdctr_ctx, ciph);
-    const uint8_t *iv = (const uint8_t *)viv;
-
-    /* Import the initial counter value into the internal representation */
-    unsigned i = 0; // WINSCP
-    for (i = 0; i < SDCTR_WORDS; i++)
-        ctx->counter[i] = GET_BIGNUMINT_MSB_FIRST(
-            iv + 8 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES);
-}
-
-static void des3_sdctr_encrypt_decrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des3_sdctr_ctx *ctx = container_of(
-        ciph, struct des3_sdctr_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    uint8_t iv_buf[8];
-    for (; len > 0; len -= 8, data += 8) {
-        /* Format the counter value into the buffer. */
-        unsigned i; // WINSCP
-        for (i = 0; i < SDCTR_WORDS; i++)
-            PUT_BIGNUMINT_MSB_FIRST(
-                iv_buf + 8 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES,
-                ctx->counter[i]);
-
-        /* Increment the counter. */
-        { // WINSCP
-        BignumCarry carry = 1;
-        for (i = 0; i < SDCTR_WORDS; i++) // WINSCP
-            BignumADC(ctx->counter[i], carry, ctx->counter[i], 0, carry);
-
-        /* Triple-encrypt the counter value from the IV. */
-        { // WINSCP
-        LR lr = des_IP(des_load_lr(iv_buf));
-        lr = des_inner_cipher(lr, &ctx->sched[0], ENCIPHER);
-        lr = des_inner_cipher(lr, &ctx->sched[1], DECIPHER);
-        lr = des_inner_cipher(lr, &ctx->sched[2], ENCIPHER);
-        { // WINSCP
-        LR keystream = des_FP(lr);
-
-        LR input = des_load_lr(data);
-        LR output = des_xor_lr(input, keystream);
-        des_store_lr(data, output);
-        } // WINSCP
-        } // WINSCP
-        } // WINSCP
-    }
-    smemclr(iv_buf, sizeof(iv_buf));
-}
-
-const ssh_cipheralg ssh_3des_ssh2_ctr = {
-    // WINSCP
-    /*.new =*/ des3_sdctr_new,
-    /*.free =*/ des3_sdctr_free,
-    /*.setiv =*/ des3_sdctr_setiv,
-    /*.setkey =*/ des3_sdctr_setkey,
-    /*.encrypt =*/ des3_sdctr_encrypt_decrypt,
-    /*.decrypt =*/ des3_sdctr_encrypt_decrypt,
-    NULL, NULL, // WINSCP
-    /*.ssh2_id =*/ "3des-ctr",
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 168,
-    /*.padded_keybytes =*/ 24,
-    /*.flags =*/ 0,
-    /*.text_name =*/ "triple-DES SDCTR",
-    NULL, NULL, // WINSCP
-};
-
-static const ssh_cipheralg *const des3_list[] = {
-    &ssh_3des_ssh2_ctr,
-    &ssh_3des_ssh2
-};
-
-const ssh2_ciphers ssh2_3des = { lenof(des3_list), des3_list };
-
-/* ----------------------------------------------------------------------
- * Triple-DES, SSH-1 style. SSH-1 replicated the whole CBC structure
- * three times, so there have to be three separate IVs, one in each
- * layer.
- */
-
-struct des3_cbc3_ctx {
-    des_keysched sched[3];
-    LR iv[3];
-    ssh_cipher ciph;
-};
-
-static ssh_cipher *des3_cbc3_new(const ssh_cipheralg *alg)
-{
-    struct des3_cbc3_ctx *ctx = snew(struct des3_cbc3_ctx);
-    ctx->ciph.vt = alg;
-    return &ctx->ciph;
-}
-
-static void des3_cbc3_free(ssh_cipher *ciph)
-{
-    struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph);
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-static void des3_cbc3_setkey(ssh_cipher *ciph, const void *vkey)
-{
-    struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph);
-    const uint8_t *key = (const uint8_t *)vkey;
-    size_t i; // WINSCP
-    for (i = 0; i < 3; i++)
-        des_key_setup(GET_64BIT_MSB_FIRST(key + 8*i), &ctx->sched[i]);
-}
-
-static void des3_cbc3_setiv(ssh_cipher *ciph, const void *viv)
-{
-    struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph);
-
-    /*
-     * In principle, we ought to provide an interface for the user to
-     * input 24 instead of 8 bytes of IV. But that would make this an
-     * ugly exception to the otherwise universal rule that IV size =
-     * cipher block size, and there's really no need to violate that
-     * rule given that this is a historical one-off oddity and SSH-1
-     * always initialises all three IVs to zero anyway. So we fudge it
-     * by just setting all the IVs to the same value.
-     */
-
-    LR iv = des_load_lr(viv);
-
-    /* But we store the IVs in permuted form, so that we can handle
-     * all three CBC layers without having to do IP/FP in between. */
-    iv = des_IP(iv);
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < 3; i++)
-        ctx->iv[i] = iv;
-    } // WINSCP
-}
-
-static void des3_cbc3_cbc_encrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    for (; len > 0; len -= 8, data += 8) {
-        /* Load and IP the input. */
-        LR plaintext = des_IP(des_load_lr(data));
-        LR lr = plaintext;
-
-        /* Do three passes of CBC, with the middle one inverted. */
-
-        lr = des_xor_lr(lr, ctx->iv[0]);
-        lr = des_inner_cipher(lr, &ctx->sched[0], ENCIPHER);
-        ctx->iv[0] = lr;
-
-        { // WINSCP
-        LR ciphertext = lr;
-        lr = des_inner_cipher(ciphertext, &ctx->sched[1], DECIPHER);
-        lr = des_xor_lr(lr, ctx->iv[1]);
-        ctx->iv[1] = ciphertext;
-
-        lr = des_xor_lr(lr, ctx->iv[2]);
-        lr = des_inner_cipher(lr, &ctx->sched[2], ENCIPHER);
-        ctx->iv[2] = lr;
-
-        des_store_lr(data, des_FP(lr));
-        } // WINSCP
-    }
-}
-
-static void des3_cbc3_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len)
-{
-    struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph);
-    uint8_t *data = (uint8_t *)vdata;
-    for (; len > 0; len -= 8, data += 8) {
-        /* Load and IP the input */
-        LR lr = des_IP(des_load_lr(data));
-        LR ciphertext;
-
-        /* Do three passes of CBC, with the middle one inverted. */
-        ciphertext = lr;
-        lr = des_inner_cipher(ciphertext, &ctx->sched[2], DECIPHER);
-        lr = des_xor_lr(lr, ctx->iv[2]);
-        ctx->iv[2] = ciphertext;
-
-        lr = des_xor_lr(lr, ctx->iv[1]);
-        lr = des_inner_cipher(lr, &ctx->sched[1], ENCIPHER);
-        ctx->iv[1] = lr;
-
-        ciphertext = lr;
-        lr = des_inner_cipher(ciphertext, &ctx->sched[0], DECIPHER);
-        lr = des_xor_lr(lr, ctx->iv[0]);
-        ctx->iv[0] = ciphertext;
-
-        des_store_lr(data, des_FP(lr));
-    }
-}
-
-const ssh_cipheralg ssh_3des_ssh1 = {
-    // WINSCP
-    /*.new =*/ des3_cbc3_new,
-    /*.free =*/ des3_cbc3_free,
-    /*.setiv =*/ des3_cbc3_setiv,
-    /*.setkey =*/ des3_cbc3_setkey,
-    /*.encrypt =*/ des3_cbc3_cbc_encrypt,
-    /*.decrypt =*/ des3_cbc3_cbc_decrypt,
-    NULL, NULL, // WINSCP
-    NULL, // WINSCP
-    /*.blksize =*/ 8,
-    /*.real_keybits =*/ 168,
-    /*.padded_keybytes =*/ 24,
-    /*.flags =*/ SSH_CIPHER_IS_CBC,
-    /*.text_name =*/ "triple-DES inner-CBC",
-    NULL, NULL, // WINSCP
-};

+ 0 - 274
source/putty/sshdh.c

@@ -1,274 +0,0 @@
-/*
- * Diffie-Hellman implementation for PuTTY.
- */
-
-#include <assert.h>
-
-#include "ssh.h"
-#include "misc.h"
-#include "mpint.h"
-
-struct dh_ctx {
-    mp_int *x, *e, *p, *q, *g;
-};
-
-struct dh_extra {
-    bool gex;
-    void (*construct)(dh_ctx *ctx);
-};
-
-static void dh_group1_construct(dh_ctx *ctx)
-{
-    ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF");
-    ctx->g = mp_from_integer(2);
-}
-
-static void dh_group14_construct(dh_ctx *ctx)
-{
-    ctx->p = MP_LITERAL_WINSCP_STR("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF");
-    ctx->g = mp_from_integer(2);
-}
-
-static const struct dh_extra extra_group1 = {
-    false, dh_group1_construct,
-};
-
-static const ssh_kex ssh_diffiehellman_group1_sha1 = {
-    "diffie-hellman-group1-sha1", "group1",
-    KEXTYPE_DH, &ssh_sha1, &extra_group1,
-};
-
-static const ssh_kex *const group1_list[] = {
-    &ssh_diffiehellman_group1_sha1
-};
-
-const ssh_kexes ssh_diffiehellman_group1 = { lenof(group1_list), group1_list };
-
-static const struct dh_extra extra_group14 = {
-    false, dh_group14_construct,
-};
-
-static 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 = {
-    "diffie-hellman-group14-sha1", "group14",
-    KEXTYPE_DH, &ssh_sha1, &extra_group14,
-};
-
-static const ssh_kex *const group14_list[] = {
-    &ssh_diffiehellman_group14_sha256,
-    &ssh_diffiehellman_group14_sha1
-};
-
-const ssh_kexes ssh_diffiehellman_group14 = {
-    lenof(group14_list), group14_list
-};
-
-static const struct dh_extra extra_gex = { true };
-
-static const ssh_kex ssh_diffiehellman_gex_sha256 = {
-    "diffie-hellman-group-exchange-sha256", NULL,
-    KEXTYPE_DH, &ssh_sha256, &extra_gex,
-};
-
-static const ssh_kex ssh_diffiehellman_gex_sha1 = {
-    "diffie-hellman-group-exchange-sha1", NULL,
-    KEXTYPE_DH, &ssh_sha1, &extra_gex,
-};
-
-static const ssh_kex *const gex_list[] = {
-    &ssh_diffiehellman_gex_sha256,
-    &ssh_diffiehellman_gex_sha1
-};
-
-const ssh_kexes ssh_diffiehellman_gex = { lenof(gex_list), gex_list };
-
-/*
- * Suffix on GSSAPI SSH protocol identifiers that indicates Kerberos 5
- * as the mechanism.
- *
- * This suffix is the base64-encoded MD5 hash of the byte sequence
- * 06 09 2A 86 48 86 F7 12 01 02 02, which in turn is the ASN.1 DER
- * encoding of the object ID 1.2.840.113554.1.2.2 which designates
- * Kerberos v5.
- *
- * (The same encoded OID, minus the two-byte DER header, is defined in
- * pgssapi.c as GSS_MECH_KRB5.)
- */
-#define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g=="
-
-static const ssh_kex ssh_gssk5_diffiehellman_gex_sha1 = {
-    "gss-gex-sha1-" GSS_KRB5_OID_HASH, NULL,
-    KEXTYPE_GSS, &ssh_sha1, &extra_gex,
-};
-
-static const ssh_kex ssh_gssk5_diffiehellman_group14_sha1 = {
-    "gss-group14-sha1-" GSS_KRB5_OID_HASH, "group14",
-    KEXTYPE_GSS, &ssh_sha1, &extra_group14,
-};
-
-static const ssh_kex ssh_gssk5_diffiehellman_group1_sha1 = {
-    "gss-group1-sha1-" GSS_KRB5_OID_HASH, "group1",
-    KEXTYPE_GSS, &ssh_sha1, &extra_group1,
-};
-
-static const ssh_kex *const gssk5_sha1_kex_list[] = {
-    &ssh_gssk5_diffiehellman_gex_sha1,
-    &ssh_gssk5_diffiehellman_group14_sha1,
-    &ssh_gssk5_diffiehellman_group1_sha1
-};
-
-const ssh_kexes ssh_gssk5_sha1_kex = {
-    lenof(gssk5_sha1_kex_list), gssk5_sha1_kex_list
-};
-
-/*
- * Common DH initialisation.
- */
-static void dh_init(dh_ctx *ctx)
-{
-    ctx->q = mp_rshift_fixed(ctx->p, 1);
-    ctx->x = ctx->e = NULL;
-}
-
-bool dh_is_gex(const ssh_kex *kex)
-{
-    const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
-    return extra->gex;
-}
-
-/*
- * Initialise DH for a standard group.
- */
-dh_ctx *dh_setup_group(const ssh_kex *kex)
-{
-    const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
-    pinitassert(!extra->gex);
-    dh_ctx *ctx = snew(dh_ctx);
-    extra->construct(ctx);
-    dh_init(ctx);
-    return ctx;
-}
-
-/*
- * Initialise DH for a server-supplied group.
- */
-dh_ctx *dh_setup_gex(mp_int *pval, mp_int *gval)
-{
-    dh_ctx *ctx = snew(dh_ctx);
-    ctx->p = mp_copy(pval);
-    ctx->g = mp_copy(gval);
-    dh_init(ctx);
-    return ctx;
-}
-
-/*
- * Return size of DH modulus p.
- */
-int dh_modulus_bit_size(const dh_ctx *ctx)
-{
-    return mp_get_nbits(ctx->p);
-}
-
-/*
- * Clean up and free a context.
- */
-void dh_cleanup(dh_ctx *ctx)
-{
-    if (ctx->x)
-        mp_free(ctx->x);
-    if (ctx->e)
-        mp_free(ctx->e);
-    if (ctx->p)
-        mp_free(ctx->p);
-    if (ctx->g)
-        mp_free(ctx->g);
-    if (ctx->q)
-        mp_free(ctx->q);
-    sfree(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)
-{
-    /*
-     * Lower limit is just 2.
-     */
-    mp_int *lo = mp_from_integer(2);
-
-    /*
-     * Upper limit.
-     */
-    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.
-     */
-    ctx->x = mp_random_in_range(lo, hi);
-    mp_free(lo);
-    mp_free(hi);
-
-    /*
-     * Now compute e = g^x mod p.
-     */
-    ctx->e = mp_modpow(ctx->g, ctx->x, ctx->p);
-
-    return ctx->e;
-}
-
-/*
- * DH stage 2-epsilon: given a number f, validate it to ensure it's in
- * range. (RFC 4253 section 8: "Values of 'e' or 'f' that are not in
- * the range [1, p-1] MUST NOT be sent or accepted by either side."
- * Also, we rule out 1 and p-1 too, since that's easy to do and since
- * they lead to obviously weak keys that even a passive eavesdropper
- * can figure out.)
- */
-const char *dh_validate_f(dh_ctx *ctx, mp_int *f)
-{
-    if (!mp_hs_integer(f, 2)) {
-        return "f value received is too small";
-    } else {
-        mp_int *pm1 = mp_copy(ctx->p);
-        mp_sub_integer_into(pm1, pm1, 1);
-        { // WINSCP
-        unsigned cmp = mp_cmp_hs(f, pm1);
-        mp_free(pm1);
-        if (cmp)
-            return "f value received is too large";
-        } // WINSCP
-    }
-    return NULL;
-}
-
-/*
- * DH stage 2: given a number f, compute K = f^x mod p.
- */
-mp_int *dh_find_K(dh_ctx *ctx, mp_int *f)
-{
-    return mp_modpow(f, ctx->x, ctx->p);
-}

+ 0 - 525
source/putty/sshdss.c

@@ -1,525 +0,0 @@
-/*
- * Digital Signature Standard implementation for PuTTY.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "ssh.h"
-#include "mpint.h"
-#include "misc.h"
-
-static void dss_freekey(ssh_key *key);    /* forward reference */
-
-static ssh_key *dss_new_pub(const ssh_keyalg *self, ptrlen data)
-{
-    BinarySource src[1];
-    struct dss_key *dss;
-
-    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;
-
-    if (get_err(src) ||
-        mp_eq_integer(dss->p, 0) || mp_eq_integer(dss->q, 0)) {
-        /* Invalid key. */
-        dss_freekey(&dss->sshk);
-        return NULL;
-    }
-
-    return &dss->sshk;
-}
-
-static void dss_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);
-}
-
-static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
-{
-    if (sb->len > 0)
-        put_byte(sb, ',');
-    put_data(sb, "0x", 2);
-    { // WINSCP
-    char *hex = mp_get_hex(x);
-    size_t hexlen = strlen(hex);
-    put_data(sb, hex, hexlen);
-    smemclr(hex, hexlen);
-    sfree(hex);
-    } // WINSCP
-}
-
-static char *dss_cache_str(ssh_key *key)
-{
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
-    strbuf *sb = strbuf_new();
-
-    if (!dss->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);
-
-    return strbuf_to_str(sb);
-}
-
-static key_components *dss_components(ssh_key *key)
-{
-    struct dss_key *dss = container_of(key, struct dss_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);
-
-    return kc;
-}
-
-static char *dss_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)
-{
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
-    BinarySource src[1];
-    unsigned char hash[20];
-    bool toret;
-
-    if (!dss->p)
-        return false;
-
-    BinarySource_BARE_INIT_PL(src, sig);
-
-    /*
-     * Commercial SSH (2.0.13) and OpenSSH disagree over the format
-     * of a DSA signature. OpenSSH is in line with RFC 4253:
-     * it uses a string "ssh-dss", followed by a 40-byte string
-     * containing two 160-bit integers end-to-end. Commercial SSH
-     * can't be bothered with the header bit, and considers a DSA
-     * signature blob to be _just_ the 40-byte string containing
-     * the two 160-bit integers. We tell them apart by measuring
-     * the length: length 40 means the commercial-SSH bug, anything
-     * else is assumed to be RFC-compliant.
-     */
-    if (sig.len != 40) {      /* bug not present; read admin fields */
-        ptrlen type = get_string(src);
-        sig = get_string(src);
-
-        if (get_err(src) || !ptrlen_eq_string(type, "ssh-dss") ||
-            sig.len != 40)
-            return false;
-    }
-
-    /* Now we're sitting on a 40-byte string for sure. */
-    { // WINSCP
-    mp_int *r = mp_from_bytes_be(make_ptrlen(sig.ptr, 20));
-    mp_int *s = mp_from_bytes_be(make_ptrlen((const char *)sig.ptr + 20, 20));
-    if (!r || !s) {
-        if (r)
-            mp_free(r);
-        if (s)
-            mp_free(s);
-        return false;
-    }
-
-    /* Basic sanity checks: 0 < r,s < q */
-    { // WINSCP
-    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);
-    if (invalid) {
-        mp_free(r);
-        mp_free(s);
-        return false;
-    }
-
-    /*
-     * Step 1. w <- s^-1 mod q.
-     */
-    { // WINSCP
-    mp_int *w = mp_invert(s, dss->q);
-    if (!w) {
-        mp_free(r);
-        mp_free(s);
-        return false;
-    }
-
-    /*
-     * Step 2. u1 <- SHA(message) * w mod q.
-     */
-    hash_simple(&ssh_sha1, data, hash);
-    { // WINSCP
-    mp_int *sha = mp_from_bytes_be(make_ptrlen(hash, 20));
-    mp_int *u1 = mp_modmul(sha, w, dss->q);
-
-    /*
-     * Step 3. u2 <- r * w mod q.
-     */
-    mp_int *u2 = mp_modmul(r, w, dss->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);
-
-    /*
-     * Step 5. v should now be equal to r.
-     */
-
-    toret = mp_cmp_eq(v, r);
-
-    mp_free(w);
-    mp_free(sha);
-    mp_free(u1);
-    mp_free(u2);
-    mp_free(gu1p);
-    mp_free(yu2p);
-    mp_free(gu1yu2p);
-    mp_free(v);
-    mp_free(r);
-    mp_free(s);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-
-    return toret;
-}
-
-static void dss_public_blob(ssh_key *key, BinarySink *bs)
-{
-    struct dss_key *dss = container_of(key, struct dss_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);
-}
-
-static void dss_private_blob(ssh_key *key, BinarySink *bs)
-{
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
-
-    put_mp_ssh2(bs, dss->x);
-}
-
-static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv)
-{
-    BinarySource src[1];
-    ssh_key *sshk;
-    struct dss_key *dss;
-    ptrlen hash;
-    unsigned char digest[20];
-    mp_int *ytest;
-
-    sshk = dss_new_pub(self, pub);
-    if (!sshk)
-        return NULL;
-
-    dss = container_of(sshk, struct dss_key, sshk);
-    BinarySource_BARE_INIT_PL(src, priv);
-    dss->x = get_mp_ssh2(src);
-    if (get_err(src)) {
-        dss_freekey(&dss->sshk);
-        return NULL;
-    }
-
-    /*
-     * Check the obsolete hash in the old DSS 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);
-        ssh_hash_final(h, digest);
-        if (!smemeq(hash.ptr, digest, 20)) {
-            dss_freekey(&dss->sshk);
-            return NULL;
-        }
-    }
-
-    /*
-     * 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)) {
-        mp_free(ytest);
-        dss_freekey(&dss->sshk);
-        return NULL;
-    }
-    mp_free(ytest);
-
-    return &dss->sshk;
-}
-
-static ssh_key *dss_new_priv_openssh(const ssh_keyalg *self,
-                                     BinarySource *src)
-{
-    struct dss_key *dss;
-
-    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 = get_mp_ssh2(src);
-
-    if (get_err(src) ||
-        mp_eq_integer(dss->q, 0) || mp_eq_integer(dss->p, 0)) {
-        /* Invalid key. */
-        dss_freekey(&dss->sshk);
-        return NULL;
-    }
-
-    return &dss->sshk;
-}
-
-static void dss_openssh_blob(ssh_key *key, BinarySink *bs)
-{
-    struct dss_key *dss = container_of(key, struct dss_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);
-}
-
-static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
-{
-    ssh_key *sshk;
-    struct dss_key *dss;
-    int ret;
-
-    sshk = dss_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);
-
-    return ret;
-}
-
-mp_int *dss_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:
-     *
-     *  - invent a random k between 1 and q-1 (exclusive).
-     *  - Compute r = (g^k mod p) mod q.
-     *  - Compute s = k^-1 * (hash + x*r) mod q.
-     *
-     * This has the dangerous properties that:
-     *
-     *  - if an attacker in possession of the public key _and_ the
-     *    signature (for example, the host you just authenticated
-     *    to) can guess your k, he can reverse the computation of s
-     *    and work out x = r^-1 * (s*k - hash) mod q. That is, he
-     *    can deduce the private half of your key, and masquerade
-     *    as you for as long as the key is still valid.
-     *
-     *  - since r is a function purely of k and the public key, if
-     *    the attacker only has a _range of possibilities_ for k
-     *    it's easy for him to work through them all and check each
-     *    one against r; he'll never be unsure of whether he's got
-     *    the right one.
-     *
-     *  - if you ever sign two different hashes with the same k, it
-     *    will be immediately obvious because the two signatures
-     *    will have the same r, and moreover an attacker in
-     *    possession of both signatures (and the public key of
-     *    course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q,
-     *    and from there deduce x as before.
-     *
-     *  - the Bleichenbacher attack on DSA makes use of methods of
-     *    generating k which are significantly non-uniformly
-     *    distributed; in particular, generating a 160-bit random
-     *    number and reducing it mod q is right out.
-     *
-     * For this reason we must be pretty careful about how we
-     * generate our k. Since this code runs on Windows, with no
-     * particularly good system entropy sources, we can't trust our
-     * RNG itself to produce properly unpredictable data. Hence, we
-     * use a totally different scheme instead.
-     *
-     * What we do is to take a SHA-512 (_big_) hash of the private
-     * key x, and then feed this into another SHA-512 hash that
-     * also includes the message hash being signed. That is:
-     *
-     *   proto_k = SHA512 ( SHA512(x) || SHA160(message) )
-     *
-     * This number is 512 bits long, so reducing it mod q won't be
-     * noticeably non-uniform. So
-     *
-     *   k = proto_k mod q
-     *
-     * This has the interesting property that it's _deterministic_:
-     * signing the same hash twice with the same key yields the
-     * same signature.
-     *
-     * Despite this determinism, it's still not predictable to an
-     * attacker, because in order to repeat the SHA-512
-     * construction that created it, the attacker would have to
-     * know the private key value x - and by assumption he doesn't,
-     * because if he knew that he wouldn't be attacking k!
-     *
-     * (This trick doesn't, _per se_, protect against reuse of k.
-     * Reuse of k is left to chance; all it does is prevent
-     * _excessively high_ chances of reuse of k due to entropy
-     * problems.)
-     *
-     * Thanks to Colin Plumb for the general idea of using x to
-     * ensure k is hard to guess, and to the Cambridge University
-     * Computer Security Group for helping to argue out all the
-     * fine details.
-     */
-    ssh_hash *h;
-    unsigned char digest512[64];
-
-    /*
-     * Hash some identifying text plus x.
-     */
-    h = ssh_hash_new(&ssh_sha512);
-    put_asciz(h, id_string);
-    put_mp_ssh2(h, private_key);
-    ssh_hash_digest(h, digest512);
-
-    /*
-     * Now hash that digest plus the message hash.
-     */
-    ssh_hash_reset(h);
-    put_data(h, digest512, sizeof(digest512));
-    put_data(h, digest, digest_len);
-    ssh_hash_final(h, digest512);
-
-    /*
-     * Now convert the result into a bignum, and coerce it to the
-     * range [2,q), which we do by reducing it mod q-2 and adding 2.
-     */
-    { // WINSCP
-    mp_int *modminus2 = mp_copy(modulus);
-    mp_sub_integer_into(modminus2, modminus2, 2);
-    { // WINSCP
-    mp_int *proto_k = mp_from_bytes_be(make_ptrlen(digest512, 64));
-    mp_int *k = mp_mod(proto_k, modminus2);
-    mp_free(proto_k);
-    mp_free(modminus2);
-    mp_add_integer_into(k, k, 2);
-
-    smemclr(digest512, sizeof(digest512));
-
-    return k;
-    } // WINSCP
-    } // WINSCP
-}
-
-static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
-{
-    struct dss_key *dss = container_of(key, struct dss_key, sshk);
-    unsigned char digest[20];
-    int i;
-
-    hash_simple(&ssh_sha1, data, digest);
-
-    { // WINSCP
-    mp_int *k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x,
-                          digest, sizeof(digest));
-    mp_int *kinv = mp_invert(k, dss->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_free(gkp);
-
-    { // WINSCP
-    mp_int *hash = mp_from_bytes_be(make_ptrlen(digest, 20));
-    mp_int *xr = mp_mul(dss->x, r);
-    mp_int *hxr = mp_add(xr, hash);         /* hash + x*r */
-    { // WINSCP
-    mp_int *s = mp_modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash+x*r) mod q */
-    mp_free(hxr);
-    mp_free(xr);
-    mp_free(kinv);
-    mp_free(k);
-    mp_free(hash);
-
-    put_stringz(bs, "ssh-dss");
-    put_uint32(bs, 40);
-    for (i = 0; i < 20; i++)
-        put_byte(bs, mp_get_byte(r, 19 - i));
-    for (i = 0; i < 20; i++)
-        put_byte(bs, mp_get_byte(s, 19 - i));
-    mp_free(r);
-    mp_free(s);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-const ssh_keyalg ssh_dss = {
-    // WINSCP
-    /*.new_pub =*/ dss_new_pub,
-    /*.new_priv =*/ dss_new_priv,
-    /*.new_priv_openssh =*/ dss_new_priv_openssh,
-    /*.freekey =*/ dss_freekey,
-    /*.invalid =*/ dss_invalid,
-    /*.sign =*/ dss_sign,
-    /*.verify =*/ dss_verify,
-    /*.public_blob =*/ dss_public_blob,
-    /*.private_blob =*/ dss_private_blob,
-    /*.openssh_blob =*/ dss_openssh_blob,
-    /*.cache_str =*/ dss_cache_str,
-    /*.components =*/ dss_components,
-    /*.pubkey_bits =*/ dss_pubkey_bits,
-    /*.ssh_id =*/ "ssh-dss",
-    /*.cache_id =*/ "dss",
-    NULL, NULL,
-};

+ 0 - 1949
source/putty/sshecc.c

@@ -1,1949 +0,0 @@
-/*
- * 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)
- */
-
-/*
- * References:
- *
- * Elliptic curves in SSH are specified in RFC 5656:
- *   http://tools.ietf.org/html/rfc5656
- *
- * That specification delegates details of public key formatting and a
- * lot of underlying mechanism to SEC 1:
- *   http://www.secg.org/sec1-v2.pdf
- *
- * Montgomery maths from:
- * Handbook of elliptic and hyperelliptic curve cryptography, Chapter 13
- *   http://cs.ucsb.edu/~koc/ccs130h/2013/EllipticHyperelliptic-CohenFrey.pdf
- *
- * Curve25519 spec from libssh (with reference to other things in the
- * libssh code):
- *   https://git.libssh.org/users/aris/libssh.git/tree/doc/[email protected]
- *
- * Edwards DSA:
- *   http://ed25519.cr.yp.to/ed25519-20110926.pdf
- */
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include "ssh.h"
-#include "mpint.h"
-#include "ecc.h"
-
-#ifdef MPEXT
-int ec_curve_cleanup = 0;
-
-static void finalize_common(struct ec_curve * curve)
-{
-    mp_free(curve->p);
-}
-
-static void finalize_wcurve(struct ec_curve *curve)
-{
-    ecc_weierstrass_curve_free(curve->w.wc);
-    ecc_weierstrass_point_free(curve->w.G);
-    mp_free(curve->w.G_order);
-    finalize_common(curve);
-}
-
-static void finalize_mcurve(struct ec_curve *curve)
-{
-    ecc_montgomery_curve_free(curve->m.mc);
-    ecc_montgomery_point_free(curve->m.G);
-    finalize_common(curve);
-}
-
-static void finalize_ecurve(struct ec_curve *curve)
-{
-    ecc_edwards_curve_free(curve->e.ec);
-    ecc_edwards_point_free(curve->e.G);
-    mp_free(curve->e.G_order);
-    finalize_common(curve);
-}
-#endif
-
-/* ----------------------------------------------------------------------
- * Elliptic curve definitions
- */
-
-static void initialise_common(
-    struct ec_curve *curve, EllipticCurveType type, mp_int *p,
-    unsigned extrabits)
-{
-    curve->type = type;
-    curve->p = mp_copy(p);
-    curve->fieldBits = mp_get_nbits(p);
-    curve->fieldBytes = (curve->fieldBits + extrabits + 7) / 8;
-}
-
-static void initialise_wcurve(
-    struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b,
-    mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order)
-{
-    initialise_common(curve, EC_WEIERSTRASS, p, 0);
-
-    curve->w.wc = ecc_weierstrass_curve(p, a, b, nonsquare);
-
-    curve->w.G = ecc_weierstrass_point_new(curve->w.wc, G_x, G_y);
-    curve->w.G_order = mp_copy(G_order);
-}
-
-static void initialise_mcurve(
-    struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b,
-    mp_int *G_x, unsigned log2_cofactor)
-{
-    initialise_common(curve, EC_MONTGOMERY, p, 0);
-
-    curve->m.mc = ecc_montgomery_curve(p, a, b);
-    curve->m.log2_cofactor = log2_cofactor;
-
-    curve->m.G = ecc_montgomery_point_new(curve->m.mc, G_x);
-}
-
-static void initialise_ecurve(
-    struct ec_curve *curve, mp_int *p, mp_int *d, mp_int *a,
-    mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order,
-    unsigned log2_cofactor)
-{
-    /* Ensure curve->fieldBytes is long enough to store an extra bit
-     * for a compressed point */
-    initialise_common(curve, EC_EDWARDS, p, 1);
-
-    curve->e.ec = ecc_edwards_curve(p, d, a, nonsquare);
-    curve->e.log2_cofactor = log2_cofactor;
-
-    curve->e.G = ecc_edwards_point_new(curve->e.ec, G_x, G_y);
-    curve->e.G_order = mp_copy(G_order);
-}
-
-static struct ec_curve *ec_p256(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    #ifdef MPEXT
-    if (ec_curve_cleanup)
-    {
-        if (initialised) finalize_wcurve(&curve);
-        initialised = 0;
-        return NULL;
-    }
-    #endif
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff);
-        mp_int *a = MP_LITERAL(0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc);
-        mp_int *b = MP_LITERAL(0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b);
-        mp_int *G_x = MP_LITERAL(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296);
-        mp_int *G_y = MP_LITERAL(0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5);
-        mp_int *G_order = MP_LITERAL(0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551);
-        mp_int *nonsquare_mod_p = mp_from_integer(3);
-        initialise_wcurve(&curve, p, a, b, nonsquare_mod_p, G_x, G_y, G_order);
-        mp_free(p);
-        mp_free(a);
-        mp_free(b);
-        mp_free(G_x);
-        mp_free(G_y);
-        mp_free(G_order);
-        mp_free(nonsquare_mod_p);
-
-        curve.textname = curve.name = "nistp256";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-static struct ec_curve *ec_p384(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    #ifdef MPEXT
-    if (ec_curve_cleanup)
-    {
-        if (initialised) finalize_wcurve(&curve);
-        initialised = 0;
-        return NULL;
-    }
-    #endif
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff);
-        mp_int *a = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc);
-        mp_int *b = MP_LITERAL(0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef);
-        mp_int *G_x = MP_LITERAL(0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7);
-        mp_int *G_y = MP_LITERAL(0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f);
-        mp_int *G_order = MP_LITERAL(0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973);
-        mp_int *nonsquare_mod_p = mp_from_integer(19);
-        initialise_wcurve(&curve, p, a, b, nonsquare_mod_p, G_x, G_y, G_order);
-        mp_free(p);
-        mp_free(a);
-        mp_free(b);
-        mp_free(G_x);
-        mp_free(G_y);
-        mp_free(G_order);
-        mp_free(nonsquare_mod_p);
-
-        curve.textname = curve.name = "nistp384";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-static struct ec_curve *ec_p521(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    #ifdef MPEXT
-    if (ec_curve_cleanup)
-    {
-        if (initialised) finalize_wcurve(&curve);
-        initialised = 0;
-        return NULL;
-    }
-    #endif
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
-        mp_int *a = MP_LITERAL(0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc);
-        mp_int *b = MP_LITERAL(0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00);
-        mp_int *G_x = MP_LITERAL(0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66);
-        mp_int *G_y = MP_LITERAL(0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650);
-        mp_int *G_order = MP_LITERAL(0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409);
-        mp_int *nonsquare_mod_p = mp_from_integer(3);
-        initialise_wcurve(&curve, p, a, b, nonsquare_mod_p, G_x, G_y, G_order);
-        mp_free(p);
-        mp_free(a);
-        mp_free(b);
-        mp_free(G_x);
-        mp_free(G_y);
-        mp_free(G_order);
-        mp_free(nonsquare_mod_p);
-
-        curve.textname = curve.name = "nistp521";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-static struct ec_curve *ec_curve25519(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    #ifdef MPEXT
-    if (ec_curve_cleanup)
-    {
-        if (initialised) finalize_mcurve(&curve);
-        initialised = 0;
-        return NULL;
-    }
-    #endif
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed);
-        mp_int *a = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000076d06);
-        mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000001);
-        mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000009);
-        initialise_mcurve(&curve, p, a, b, G_x, 3);
-        mp_free(p);
-        mp_free(a);
-        mp_free(b);
-        mp_free(G_x);
-
-        /* This curve doesn't need a name, because it's never used in
-         * any format that embeds the curve name */
-        curve.name = NULL;
-        curve.textname = "Curve25519";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-static struct ec_curve *ec_curve448(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
-        mp_int *a = MP_LITERAL(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000262a6);
-        mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001);
-        mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005);
-        initialise_mcurve(&curve, p, a, b, G_x, 2);
-        mp_free(p);
-        mp_free(a);
-        mp_free(b);
-        mp_free(G_x);
-
-        /* This curve doesn't need a name, because it's never used in
-         * any format that embeds the curve name */
-        curve.name = NULL;
-        curve.textname = "Curve448";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-static struct ec_curve *ec_ed25519(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    #ifdef MPEXT
-    if (ec_curve_cleanup)
-    {
-        if (initialised) finalize_ecurve(&curve);
-        initialised = 0;
-        return NULL;
-    }
-    #endif
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed);
-        mp_int *d = MP_LITERAL(0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3);
-        mp_int *a = MP_LITERAL(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec); /* == p-1 */
-        mp_int *G_x = MP_LITERAL(0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a);
-        mp_int *G_y = MP_LITERAL(0x6666666666666666666666666666666666666666666666666666666666666658);
-        mp_int *G_order = MP_LITERAL(0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed);
-        mp_int *nonsquare_mod_p = mp_from_integer(2);
-        initialise_ecurve(&curve, p, d, a, nonsquare_mod_p,
-                          G_x, G_y, G_order, 3);
-        mp_free(p);
-        mp_free(d);
-        mp_free(a);
-        mp_free(G_x);
-        mp_free(G_y);
-        mp_free(G_order);
-        mp_free(nonsquare_mod_p);
-
-        /* This curve doesn't need a name, because it's never used in
-         * any format that embeds the curve name */
-        curve.name = NULL;
-
-        curve.textname = "Ed25519";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-static struct ec_curve *ec_ed448(void)
-{
-    static struct ec_curve curve = { 0 };
-    static bool initialised = false;
-
-    if (!initialised)
-    {
-        mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
-        mp_int *d = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756); /* = p - 39081 */
-        mp_int *a = MP_LITERAL(0x1);
-        mp_int *G_x = MP_LITERAL(0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e);
-        mp_int *G_y = MP_LITERAL(0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14);
-        mp_int *G_order = MP_LITERAL(0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3);
-        mp_int *nonsquare_mod_p = mp_from_integer(7);
-        initialise_ecurve(&curve, p, d, a, nonsquare_mod_p,
-                          G_x, G_y, G_order, 2);
-        mp_free(p);
-        mp_free(d);
-        mp_free(a);
-        mp_free(G_x);
-        mp_free(G_y);
-        mp_free(G_order);
-        mp_free(nonsquare_mod_p);
-
-        /* This curve doesn't need a name, because it's never used in
-         * any format that embeds the curve name */
-        curve.name = NULL;
-
-        curve.textname = "Ed448";
-
-        /* Now initialised, no need to do it again */
-        initialised = true;
-    }
-
-    return &curve;
-}
-
-/* ----------------------------------------------------------------------
- * Public point from private
- */
-
-struct ecsign_extra {
-    struct ec_curve *(*curve)(void);
-    const ssh_hashalg *hash;
-
-    /* These fields are used by the OpenSSH PEM format importer/exporter */
-    const unsigned char *oid;
-    int oidlen;
-
-    /* Some EdDSA instances prefix a string to all hash preimages, to
-     * disambiguate which signature variant they're being used with */
-    ptrlen hash_prefix;
-};
-
-WeierstrassPoint *ecdsa_public(mp_int *private_key, const ssh_keyalg *alg)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    pinitassert(curve->type == EC_WEIERSTRASS);
-
-    mp_int *priv_reduced = mp_mod(private_key, curve->p);
-    WeierstrassPoint *toret = ecc_weierstrass_multiply(
-        curve->w.G, priv_reduced);
-    mp_free(priv_reduced);
-    return toret;
-}
-
-static mp_int *eddsa_exponent_from_hash(
-    ptrlen hash, const struct ec_curve *curve)
-{
-    /*
-     * Make an integer out of the hash data, little-endian.
-     */
-    pinitassert(hash.len >= curve->fieldBytes);
-    mp_int *e = mp_from_bytes_le(make_ptrlen(hash.ptr, curve->fieldBytes));
-
-    /*
-     * Set the highest bit that fits in the modulus, and clear any
-     * above that.
-     */
-    mp_set_bit(e, curve->fieldBits - 1, 1);
-    mp_reduce_mod_2to(e, curve->fieldBits);
-
-    /*
-     * Clear a curve-specific number of low bits.
-     */
-    { // WINSCP
-    unsigned bit; // WINSCP 
-    for (bit = 0; bit < curve->e.log2_cofactor; bit++)
-        mp_set_bit(e, bit, 0);
-    } // WINSCP
-
-    return e;
-}
-
-EdwardsPoint *eddsa_public(mp_int *private_key, const ssh_keyalg *alg)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    pinitassert(curve->type == EC_EDWARDS);
-
-    ssh_hash *h = ssh_hash_new(extra->hash);
-    size_t i; // WINSCP
-    for (i = 0; i < curve->fieldBytes; ++i)
-        put_byte(h, mp_get_byte(private_key, i));
-
-    { // WINSCP
-    unsigned char hash[MAX_HASH_LEN];
-    ssh_hash_final(h, hash);
-
-    { // WINSCP
-    mp_int *exponent = eddsa_exponent_from_hash(
-        make_ptrlen(hash, extra->hash->hlen), curve);
-
-    EdwardsPoint *toret = ecc_edwards_multiply(curve->e.G, exponent);
-    mp_free(exponent);
-
-    return toret;
-    } // WINSCP
-    } // WINSCP
-}
-
-/* ----------------------------------------------------------------------
- * Marshalling and unmarshalling functions
- */
-
-static mp_int *BinarySource_get_mp_le(BinarySource *src)
-{
-    return mp_from_bytes_le(get_string(src));
-}
-#define get_mp_le(src) BinarySource_get_mp_le(BinarySource_UPCAST(src))
-
-static void BinarySink_put_mp_le_fixedlen(BinarySink *bs, mp_int *x,
-                                          size_t bytes)
-{
-    put_uint32(bs, bytes);
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < bytes; ++i)
-        put_byte(bs, mp_get_byte(x, i));
-    } // WINSCP
-}
-#define put_mp_le_fixedlen(bs, x, bytes)                        \
-    BinarySink_put_mp_le_fixedlen(BinarySink_UPCAST(bs), x, bytes)
-
-static WeierstrassPoint *ecdsa_decode(
-    ptrlen encoded, const struct ec_curve *curve)
-{
-    pinitassert(curve->type == EC_WEIERSTRASS);
-    BinarySource src[1];
-
-    BinarySource_BARE_INIT_PL(src, encoded);
-    { // WINSCP
-    unsigned char format_type = get_byte(src);
-
-    WeierstrassPoint *P;
-
-    size_t len = get_avail(src);
-    mp_int *x;
-    mp_int *y;
-
-    switch (format_type) {
-      case 0:
-        /* The identity. */
-        P = ecc_weierstrass_point_new_identity(curve->w.wc);
-        break;
-      case 2:
-      case 3:
-        /* A compressed point, in which the x-coordinate is stored in
-         * full, and y is deduced from that and a single bit
-         * indicating its parity (stored in the format type byte). */
-        x = mp_from_bytes_be(get_data(src, len));
-        P = ecc_weierstrass_point_new_from_x(curve->w.wc, x, format_type & 1);
-        mp_free(x);
-        if (!P)            /* this can fail if the input is invalid */
-            return NULL;
-        break;
-      case 4:
-        /* An uncompressed point: the x,y coordinates are stored in
-         * full. We expect the rest of the string to have even length,
-         * and be divided half and half between the two values. */
-        if (len % 2 != 0)
-            return NULL;
-        len /= 2;
-        x = mp_from_bytes_be(get_data(src, len));
-        y = mp_from_bytes_be(get_data(src, len));
-        P = ecc_weierstrass_point_new(curve->w.wc, x, y);
-        mp_free(x);
-        mp_free(y);
-        break;
-      default:
-        /* An unrecognised type byte. */
-        return NULL;
-    }
-
-    /* Verify the point is on the curve */
-    if (!ecc_weierstrass_point_valid(P)) {
-        ecc_weierstrass_point_free(P);
-        return NULL;
-    }
-
-    return P;
-    } // WINSCP
-}
-
-static WeierstrassPoint *BinarySource_get_wpoint(
-    BinarySource *src, const struct ec_curve *curve)
-{
-    ptrlen str = get_string(src);
-    if (get_err(src))
-        return NULL;
-    return ecdsa_decode(str, curve);
-}
-#define get_wpoint(src, curve) \
-    BinarySource_get_wpoint(BinarySource_UPCAST(src), curve)
-
-static void BinarySink_put_wpoint(
-    BinarySink *bs, WeierstrassPoint *point, const struct ec_curve *curve,
-    bool bare)
-{
-    strbuf *sb;
-    BinarySink *bs_inner;
-
-    if (!bare) {
-        /*
-         * Encapsulate the raw data inside an outermost string layer.
-         */
-        sb = strbuf_new();
-        bs_inner = BinarySink_UPCAST(sb);
-    } else {
-        /*
-         * Just write the data directly to the output.
-         */
-        bs_inner = bs;
-    }
-
-    if (ecc_weierstrass_is_identity(point)) {
-        put_byte(bs_inner, 0);
-    } else {
-        mp_int *x, *y;
-        ecc_weierstrass_get_affine(point, &x, &y);
-
-        /*
-         * For ECDSA, we only ever output uncompressed points.
-         */
-        put_byte(bs_inner, 0x04);
-        { // WINSCP
-        size_t i; // WINSCP
-        for (i = curve->fieldBytes; i--;)
-            put_byte(bs_inner, mp_get_byte(x, i));
-        for (i = curve->fieldBytes; i--;)
-            put_byte(bs_inner, mp_get_byte(y, i));
-        } // WINSCP
-
-        mp_free(x);
-        mp_free(y);
-    }
-
-    if (!bare)
-        put_stringsb(bs, sb);
-}
-#define put_wpoint(bs, point, curve, bare)                              \
-    BinarySink_put_wpoint(BinarySink_UPCAST(bs), point, curve, bare)
-
-static EdwardsPoint *eddsa_decode(ptrlen encoded, const struct ec_curve *curve)
-{
-    assert(curve->type == EC_EDWARDS);
-
-    { // WINSCP
-    mp_int *y = mp_from_bytes_le(encoded);
-
-    /* The topmost bit of the encoding isn't part of y, so it stores
-     * the bottom bit of x. Extract it, and zero that bit in y. */
-    unsigned desired_x_parity = mp_get_bit(y, curve->fieldBytes * 8 - 1);
-    mp_set_bit(y, curve->fieldBytes * 8 - 1, 0);
-
-    /* What's left should now be within the range of the curve's modulus */
-    if (mp_cmp_hs(y, curve->p)) {
-        mp_free(y);
-        return NULL;
-    }
-
-    { // WINSCP
-    { // WINSCP
-    EdwardsPoint *P = ecc_edwards_point_new_from_y(
-        curve->e.ec, y, desired_x_parity);
-    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. */
-    return P;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static EdwardsPoint *BinarySource_get_epoint(
-    BinarySource *src, const struct ec_curve *curve)
-{
-    ptrlen str = get_string(src);
-    if (get_err(src))
-        return NULL;
-    return eddsa_decode(str, curve);
-}
-#define get_epoint(src, curve) \
-    BinarySource_get_epoint(BinarySource_UPCAST(src), curve)
-
-static void BinarySink_put_epoint(
-    BinarySink *bs, EdwardsPoint *point, const struct ec_curve *curve,
-    bool bare)
-{
-    mp_int *x, *y;
-    ecc_edwards_get_affine(point, &x, &y);
-
-    assert(curve->fieldBytes >= 2);
-
-    /*
-     * EdDSA requires point compression. We store a single integer,
-     * with bytes in little-endian order, which mostly contains y but
-     * in which the topmost bit is the low bit of x.
-     */
-    if (!bare)
-        put_uint32(bs, curve->fieldBytes);   /* string length field */
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < curve->fieldBytes - 1; i++)
-        put_byte(bs, mp_get_byte(y, i));
-    } // WINSCP
-    put_byte(bs, (mp_get_byte(y, curve->fieldBytes - 1) & 0x7F) |
-             (mp_get_bit(x, 0) << 7));
-
-    mp_free(x);
-    mp_free(y);
-}
-#define put_epoint(bs, point, curve, bare)                      \
-    BinarySink_put_epoint(BinarySink_UPCAST(bs), point, curve, bare)
-
-/* ----------------------------------------------------------------------
- * Exposed ECDSA interface
- */
-
-static void ecdsa_freekey(ssh_key *key)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-
-    if (ek->publicKey)
-        ecc_weierstrass_point_free(ek->publicKey);
-    if (ek->privateKey)
-        mp_free(ek->privateKey);
-    sfree(ek);
-}
-
-static void eddsa_freekey(ssh_key *key)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-
-    if (ek->publicKey)
-        ecc_edwards_point_free(ek->publicKey);
-    if (ek->privateKey)
-        mp_free(ek->privateKey);
-    sfree(ek);
-}
-
-static char *ec_signkey_invalid(ssh_key *key, unsigned flags)
-{
-    /* All validity criteria for both ECDSA and EdDSA were checked
-     * when we loaded the key in the first place */
-    return NULL;
-}
-
-static ssh_key *ecdsa_new_pub(const ssh_keyalg *alg, ptrlen data)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    pinitassert(curve->type == EC_WEIERSTRASS);
-
-    BinarySource src[1];
-    BinarySource_BARE_INIT_PL(src, data);
-    get_string(src);
-
-    /* Curve name is duplicated for Weierstrass form */
-    if (!ptrlen_eq_string(get_string(src), curve->name))
-        return NULL;
-
-    { // WINSCP
-    struct ecdsa_key *ek = snew(struct ecdsa_key);
-    ek->sshk.vt = alg;
-    ek->curve = curve;
-    ek->privateKey = NULL;
-
-    ek->publicKey = get_wpoint(src, curve);
-    if (!ek->publicKey) {
-        ecdsa_freekey(&ek->sshk);
-        return NULL;
-    }
-
-    return &ek->sshk;
-    } // WINSCP
-}
-
-static ssh_key *eddsa_new_pub(const ssh_keyalg *alg, ptrlen data)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    pinitassert(curve->type == EC_EDWARDS);
-
-    BinarySource src[1];
-    BinarySource_BARE_INIT_PL(src, data);
-    get_string(src);
-
-    { // WINSCP
-    struct eddsa_key *ek = snew(struct eddsa_key);
-    ek->sshk.vt = alg;
-    ek->curve = curve;
-    ek->privateKey = NULL;
-
-    ek->publicKey = get_epoint(src, curve);
-    if (!ek->publicKey) {
-        eddsa_freekey(&ek->sshk);
-        return NULL;
-    }
-
-    return &ek->sshk;
-    } // WINSCP
-}
-
-static char *ecc_cache_str_shared(
-    const char *curve_name, mp_int *x, mp_int *y)
-{
-    strbuf *sb = strbuf_new();
-
-    if (curve_name)
-        strbuf_catf(sb, "%s,", curve_name);
-
-    { // WINSCP
-    char *hx = mp_get_hex(x);
-    char *hy = mp_get_hex(y);
-    strbuf_catf(sb, "0x%s,0x%s", hx, hy);
-    sfree(hx);
-    sfree(hy);
-    } // WINSCP
-
-    return strbuf_to_str(sb);
-}
-
-static char *ecdsa_cache_str(ssh_key *key)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-    mp_int *x, *y;
-
-    ecc_weierstrass_get_affine(ek->publicKey, &x, &y);
-    { // WINSCP
-    char *toret = ecc_cache_str_shared(ek->curve->name, x, y);
-    mp_free(x);
-    mp_free(y);
-    return toret;
-    } // WINSCP
-}
-
-static key_components *ecdsa_components(ssh_key *key)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-    key_components *kc = key_components_new();
-
-    key_components_add_text(kc, "key_type", "ECDSA");
-    key_components_add_text(kc, "curve_name", ek->curve->textname);
-
-    { // WINSCP
-    mp_int *x, *y;
-    ecc_weierstrass_get_affine(ek->publicKey, &x, &y);
-    key_components_add_mp(kc, "public_affine_x", x);
-    key_components_add_mp(kc, "public_affine_y", y);
-    mp_free(x);
-    mp_free(y);
-
-    if (ek->privateKey)
-        key_components_add_mp(kc, "private_exponent", ek->privateKey);
-
-    return kc;
-    } // WINSCP
-}
-
-static char *eddsa_cache_str(ssh_key *key)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-    mp_int *x, *y;
-
-    ecc_edwards_get_affine(ek->publicKey, &x, &y);
-    { // WINSCP
-    char *toret = ecc_cache_str_shared(ek->curve->name, x, y);
-    mp_free(x);
-    mp_free(y);
-    return toret;
-    } // WINSCP
-}
-
-static key_components *eddsa_components(ssh_key *key)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-    key_components *kc = key_components_new();
-
-    key_components_add_text(kc, "key_type", "EdDSA");
-    key_components_add_text(kc, "curve_name", ek->curve->textname);
-
-    { // WINSCP
-    mp_int *x, *y;
-    ecc_edwards_get_affine(ek->publicKey, &x, &y);
-    key_components_add_mp(kc, "public_affine_x", x);
-    key_components_add_mp(kc, "public_affine_y", y);
-    mp_free(x);
-    mp_free(y);
-
-    if (ek->privateKey)
-        key_components_add_mp(kc, "private_exponent", ek->privateKey);
-
-    return kc;
-    } // WINSCP
-}
-
-static void ecdsa_public_blob(ssh_key *key, BinarySink *bs)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-
-    put_stringz(bs, ek->sshk.vt->ssh_id);
-    put_stringz(bs, ek->curve->name);
-    put_wpoint(bs, ek->publicKey, ek->curve, false);
-}
-
-static void eddsa_public_blob(ssh_key *key, BinarySink *bs)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-
-    put_stringz(bs, ek->sshk.vt->ssh_id);
-    put_epoint(bs, ek->publicKey, ek->curve, false);
-}
-
-static void ecdsa_private_blob(ssh_key *key, BinarySink *bs)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-
-    /* ECDSA uses ordinary SSH-2 mpint format to store the private key */
-    assert(ek->privateKey);
-    put_mp_ssh2(bs, ek->privateKey);
-}
-
-static void eddsa_private_blob(ssh_key *key, BinarySink *bs)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-
-    /* EdDSA stores the private key integer little-endian and unsigned */
-    assert(ek->privateKey);
-    put_mp_le_fixedlen(bs, ek->privateKey, ek->curve->fieldBytes);
-}
-
-static ssh_key *ecdsa_new_priv(const ssh_keyalg *alg, ptrlen pub, ptrlen priv)
-{
-    ssh_key *sshk = ecdsa_new_pub(alg, pub);
-    if (!sshk)
-        return NULL;
-    { // WINSCP
-    struct ecdsa_key *ek = container_of(sshk, struct ecdsa_key, sshk);
-
-    BinarySource src[1];
-    BinarySource_BARE_INIT_PL(src, priv);
-    ek->privateKey = get_mp_ssh2(src);
-
-    return &ek->sshk;
-    } // WINSCP
-}
-
-static ssh_key *eddsa_new_priv(const ssh_keyalg *alg, ptrlen pub, ptrlen priv)
-{
-    ssh_key *sshk = eddsa_new_pub(alg, pub);
-    if (!sshk)
-        return NULL;
-    { // WINSCP
-    struct eddsa_key *ek = container_of(sshk, struct eddsa_key, sshk);
-
-    BinarySource src[1];
-    BinarySource_BARE_INIT_PL(src, priv);
-    ek->privateKey = get_mp_le(src);
-
-    return &ek->sshk;
-    } // WINSCP
-}
-
-static ssh_key *eddsa_new_priv_openssh(
-    const ssh_keyalg *alg, BinarySource *src)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    assert(curve->type == EC_EDWARDS);
-
-    { // WINSCP
-    ptrlen pubkey_pl = get_string(src);
-    ptrlen privkey_extended_pl = get_string(src);
-    if (get_err(src) || pubkey_pl.len != curve->fieldBytes)
-        return NULL;
-
-    /*
-     * The OpenSSH format for ed25519 private keys also for some
-     * reason encodes an extra copy of the public key in the second
-     * half of the secret-key string. Check that that's present and
-     * correct as well, otherwise the key we think we've imported
-     * won't behave identically to the way OpenSSH would have treated
-     * it.
-     *
-     * We assume that Ed448 will work the same way, as and when
-     * OpenSSH implements it, which at the time of writing this they
-     * had not.
-     */
-    { // WINSCP
-    BinarySource subsrc[1];
-    BinarySource_BARE_INIT_PL(subsrc, privkey_extended_pl);
-    { // WINSCP
-    ptrlen privkey_pl = get_data(subsrc, curve->fieldBytes);
-    ptrlen pubkey_copy_pl = get_data(subsrc, curve->fieldBytes);
-    if (get_err(subsrc) || get_avail(subsrc))
-        return NULL;
-    if (!ptrlen_eq_ptrlen(pubkey_pl, pubkey_copy_pl))
-        return NULL;
-
-    { // WINSCP
-    struct eddsa_key *ek = snew(struct eddsa_key);
-    ek->sshk.vt = alg;
-    ek->curve = curve;
-    ek->privateKey = NULL;
-
-    ek->publicKey = eddsa_decode(pubkey_pl, curve);
-    if (!ek->publicKey) {
-        eddsa_freekey(&ek->sshk);
-        return NULL;
-    }
-
-    ek->privateKey = mp_from_bytes_le(privkey_pl);
-
-    return &ek->sshk;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static void eddsa_openssh_blob(ssh_key *key, BinarySink *bs)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-    assert(ek->curve->type == EC_EDWARDS);
-
-    /* Encode the public and private points as strings */
-    { // WINSCP
-    strbuf *pub_sb = strbuf_new();
-    put_epoint(pub_sb, ek->publicKey, ek->curve, false);
-    { // WINSCP
-    ptrlen pub = make_ptrlen(pub_sb->s + 4, pub_sb->len - 4);
-
-    strbuf *priv_sb = strbuf_new_nm();
-    put_mp_le_fixedlen(priv_sb, ek->privateKey, ek->curve->fieldBytes);
-    { // WINSCP
-    ptrlen priv = make_ptrlen(priv_sb->s + 4, priv_sb->len - 4);
-
-    put_stringpl(bs, pub);
-
-    /* Encode the private key as the concatenation of the
-     * little-endian key integer and the public key again */
-    put_uint32(bs, priv.len + pub.len);
-    put_datapl(bs, priv);
-    put_datapl(bs, pub);
-
-    strbuf_free(pub_sb);
-    strbuf_free(priv_sb);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static ssh_key *ecdsa_new_priv_openssh(
-    const ssh_keyalg *alg, BinarySource *src)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    assert(curve->type == EC_WEIERSTRASS);
-
-    get_string(src);
-
-    { // WINSCP
-    struct ecdsa_key *ek = snew(struct ecdsa_key);
-    ek->sshk.vt = alg;
-    ek->curve = curve;
-    ek->privateKey = NULL;
-
-    ek->publicKey = get_wpoint(src, curve);
-    if (!ek->publicKey) {
-        ecdsa_freekey(&ek->sshk);
-        return NULL;
-    }
-
-    ek->privateKey = get_mp_ssh2(src);
-
-    return &ek->sshk;
-    } // WINSCP
-}
-
-static void ecdsa_openssh_blob(ssh_key *key, BinarySink *bs)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-    put_stringz(bs, ek->curve->name);
-    put_wpoint(bs, ek->publicKey, ek->curve, false);
-    put_mp_ssh2(bs, ek->privateKey);
-}
-
-static int ec_shared_pubkey_bits(const ssh_keyalg *alg, ptrlen blob)
-{
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)alg->extra;
-    struct ec_curve *curve = extra->curve();
-    return curve->fieldBits;
-}
-
-static mp_int *ecdsa_signing_exponent_from_data(
-    const struct ec_curve *curve, const struct ecsign_extra *extra,
-    ptrlen data)
-{
-    /* Hash the data being signed. */
-    unsigned char hash[MAX_HASH_LEN];
-    ssh_hash *h = ssh_hash_new(extra->hash);
-    put_datapl(h, data);
-    ssh_hash_final(h, hash);
-
-    /*
-     * Take the leftmost b bits of the hash of the signed data (where
-     * b is the number of bits in order(G)), interpreted big-endian.
-     */
-    { // WINSCP
-    mp_int *z = mp_from_bytes_be(make_ptrlen(hash, extra->hash->hlen));
-    size_t zbits = mp_get_nbits(z);
-    size_t nbits = mp_get_nbits(curve->w.G_order);
-    size_t shift = zbits - nbits;
-    /* Bound the shift count below at 0, using bit twiddling to avoid
-     * a conditional branch */
-    shift &= ~-(int)(shift >> (CHAR_BIT * sizeof(size_t) - 1)); // WINSCP
-    { // WINSCP
-    mp_int *toret = mp_rshift_safe(z, shift);
-    mp_free(z);
-
-    return toret;
-    } // WINSCP
-    } // WINSCP
-}
-
-static bool ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)ek->sshk.vt->extra;
-
-    BinarySource src[1];
-    BinarySource_BARE_INIT_PL(src, sig);
-
-    /* Check the signature starts with the algorithm name */
-    if (!ptrlen_eq_string(get_string(src), ek->sshk.vt->ssh_id))
-        return false;
-
-    /* Everything else is nested inside a sub-string. Descend into that. */
-    { // WINSCP
-    ptrlen sigstr = get_string(src);
-    if (get_err(src))
-        return false;
-    BinarySource_BARE_INIT_PL(src, sigstr);
-
-    /* Extract the signature integers r,s */
-    { // WINSCP
-    mp_int *r = get_mp_ssh2(src);
-    mp_int *s = get_mp_ssh2(src);
-    if (get_err(src)) {
-        mp_free(r);
-        mp_free(s);
-        return false;
-    }
-
-    /* Basic sanity checks: 0 < r,s < order(G) */
-    { // WINSCP
-    unsigned invalid = 0;
-    invalid |= mp_eq_integer(r, 0);
-    invalid |= mp_eq_integer(s, 0);
-    invalid |= mp_cmp_hs(r, ek->curve->w.G_order);
-    invalid |= mp_cmp_hs(s, ek->curve->w.G_order);
-
-    /* Get the hash of the signed data, converted to an integer */
-    { // WINSCP
-    mp_int *z = ecdsa_signing_exponent_from_data(ek->curve, extra, data);
-
-    /* Verify the signature integers against the hash */
-    mp_int *w = mp_invert(s, ek->curve->w.G_order);
-    mp_int *u1 = mp_modmul(z, w, ek->curve->w.G_order);
-    mp_free(z);
-    { // WINSCP
-    mp_int *u2 = mp_modmul(r, w, ek->curve->w.G_order);
-    mp_free(w);
-    { // WINSCP
-    WeierstrassPoint *u1G = ecc_weierstrass_multiply(ek->curve->w.G, u1);
-    mp_free(u1);
-    { // WINSCP
-    WeierstrassPoint *u2P = ecc_weierstrass_multiply(ek->publicKey, u2);
-    mp_free(u2);
-    { // WINSCP
-    WeierstrassPoint *sum = ecc_weierstrass_add_general(u1G, u2P);
-    ecc_weierstrass_point_free(u1G);
-    ecc_weierstrass_point_free(u2P);
-
-    { // WINSCP
-    mp_int *x;
-    ecc_weierstrass_get_affine(sum, &x, NULL);
-    ecc_weierstrass_point_free(sum);
-
-    mp_divmod_into(x, ek->curve->w.G_order, NULL, x);
-    invalid |= (1 ^ mp_cmp_eq(r, x));
-    mp_free(x);
-
-    mp_free(r);
-    mp_free(s);
-
-    return !invalid;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static mp_int *eddsa_signing_exponent_from_data(
-    struct eddsa_key *ek, const struct ecsign_extra *extra,
-    ptrlen r_encoded, ptrlen data)
-{
-    /* Hash (r || public key || message) */
-    unsigned char hash[MAX_HASH_LEN];
-    ssh_hash *h = ssh_hash_new(extra->hash);
-    put_datapl(h, extra->hash_prefix);
-    put_datapl(h, r_encoded);
-    put_epoint(h, ek->publicKey, ek->curve, true); /* omit string header */
-    put_datapl(h, data);
-    ssh_hash_final(h, hash);
-
-    /* Convert to an integer */
-    { // WINSCP
-    mp_int *toret = mp_from_bytes_le(make_ptrlen(hash, extra->hash->hlen));
-
-    smemclr(hash, extra->hash->hlen);
-    return toret;
-    } // WINSCP
-}
-
-static bool eddsa_verify(ssh_key *key, ptrlen sig, ptrlen data)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)ek->sshk.vt->extra;
-
-    BinarySource src[1];
-    BinarySource_BARE_INIT_PL(src, sig);
-
-    /* Check the signature starts with the algorithm name */
-    if (!ptrlen_eq_string(get_string(src), ek->sshk.vt->ssh_id))
-        return false;
-
-    /* Now expect a single string which is the concatenation of an
-     * encoded curve point r and an integer s. */
-    { // WINSCP
-    ptrlen sigstr = get_string(src);
-    if (get_err(src))
-        return false;
-    BinarySource_BARE_INIT_PL(src, sigstr);
-    { // WINSCP
-    ptrlen rstr = get_data(src, ek->curve->fieldBytes);
-    ptrlen sstr = get_data(src, ek->curve->fieldBytes);
-    if (get_err(src) || get_avail(src))
-        return false;
-
-    { // WINSCP
-    EdwardsPoint *r = eddsa_decode(rstr, ek->curve);
-    if (!r)
-        return false;
-    { // WINSCP
-    mp_int *s = mp_from_bytes_le(sstr);
-
-    mp_int *H = eddsa_signing_exponent_from_data(ek, extra, rstr, data);
-
-    /* Verify that s*G == r + H*publicKey */
-    EdwardsPoint *lhs = ecc_edwards_multiply(ek->curve->e.G, s);
-    mp_free(s);
-    { // WINSCP
-    EdwardsPoint *hpk = ecc_edwards_multiply(ek->publicKey, H);
-    mp_free(H);
-    { // WINSCP
-    EdwardsPoint *rhs = ecc_edwards_add(r, hpk);
-    ecc_edwards_point_free(hpk);
-    { // WINSCP
-    unsigned valid = ecc_edwards_eq(lhs, rhs);
-    ecc_edwards_point_free(lhs);
-    ecc_edwards_point_free(rhs);
-    ecc_edwards_point_free(r);
-
-    return valid;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static void ecdsa_sign(ssh_key *key, ptrlen data,
-                       unsigned flags, BinarySink *bs)
-{
-    struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk);
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)ek->sshk.vt->extra;
-    assert(ek->privateKey);
-
-    { // WINSCP
-    mp_int *z = ecdsa_signing_exponent_from_data(ek->curve, extra, data);
-
-    /* Generate k between 1 and curve->n, using the same deterministic
-     * k generation system we use for conventional DSA. */
-    mp_int *k;
-    {
-        unsigned char digest[20];
-        hash_simple(&ssh_sha1, data, digest);
-        k = dss_gen_k(
-            "ECDSA deterministic k generator", ek->curve->w.G_order,
-            ek->privateKey, digest, sizeof(digest));
-    }
-
-    { // WINSCP
-    WeierstrassPoint *kG = ecc_weierstrass_multiply(ek->curve->w.G, k);
-    mp_int *x;
-    ecc_weierstrass_get_affine(kG, &x, NULL);
-    ecc_weierstrass_point_free(kG);
-
-    /* r = kG.x mod order(G) */
-    { // WINSCP
-    mp_int *r = mp_mod(x, ek->curve->w.G_order);
-    mp_free(x);
-
-    /* s = (z + r * priv)/k mod n */
-    { // WINSCP
-    mp_int *rPriv = mp_modmul(r, ek->privateKey, ek->curve->w.G_order);
-    mp_int *numerator = mp_modadd(z, rPriv, ek->curve->w.G_order);
-    mp_free(z);
-    mp_free(rPriv);
-    { // WINSCP
-    mp_int *kInv = mp_invert(k, ek->curve->w.G_order);
-    mp_free(k);
-    { // WINSCP
-    mp_int *s = mp_modmul(numerator, kInv, ek->curve->w.G_order);
-    mp_free(numerator);
-    mp_free(kInv);
-
-    /* Format the output */
-    put_stringz(bs, ek->sshk.vt->ssh_id);
-
-    { // WINSCP
-    strbuf *substr = strbuf_new();
-    put_mp_ssh2(substr, r);
-    put_mp_ssh2(substr, s);
-    put_stringsb(bs, substr);
-    } // WINSCP
-
-    mp_free(r);
-    mp_free(s);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static void eddsa_sign(ssh_key *key, ptrlen data,
-                       unsigned flags, BinarySink *bs)
-{
-    struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk);
-    const struct ecsign_extra *extra =
-        (const struct ecsign_extra *)ek->sshk.vt->extra;
-    assert(ek->privateKey);
-
-    /*
-     * EdDSA prescribes a specific method of generating the random
-     * nonce integer for the signature. (A verifier can't tell
-     * whether you followed that method, but it's important to
-     * follow it anyway, because test vectors will want a specific
-     * signature for a given message, and because this preserves
-     * determinism of signatures even if the same signature were
-     * made twice by different software.)
-     */
-
-    /*
-     * First, we hash the private key integer (bare, little-endian)
-     * into a hash generating 2*fieldBytes of output.
-     */
-    { // WINSCP
-    unsigned char hash[MAX_HASH_LEN];
-    ssh_hash *h = ssh_hash_new(extra->hash);
-    size_t i; // WINSCP
-    for (i = 0; i < ek->curve->fieldBytes; ++i)
-        put_byte(h, mp_get_byte(ek->privateKey, i));
-    ssh_hash_final(h, hash);
-
-    /*
-     * The first half of the output hash is converted into an
-     * integer a, by the standard EdDSA transformation.
-     */
-    { // WINSCP
-    mp_int *a = eddsa_exponent_from_hash(
-        make_ptrlen(hash, ek->curve->fieldBytes), ek->curve);
-
-    /*
-     * The second half of the hash of the private key is hashed again
-     * with the message to be signed, and used as an exponent to
-     * generate the signature point r.
-     */
-    h = ssh_hash_new(extra->hash);
-    put_datapl(h, extra->hash_prefix);
-    put_data(h, hash + ek->curve->fieldBytes,
-             extra->hash->hlen - ek->curve->fieldBytes);
-    put_datapl(h, data);
-    ssh_hash_final(h, hash);
-    { // WINSCP
-    mp_int *log_r_unreduced = mp_from_bytes_le(
-        make_ptrlen(hash, extra->hash->hlen));
-    mp_int *log_r = mp_mod(log_r_unreduced, ek->curve->e.G_order);
-    mp_free(log_r_unreduced);
-    { // WINSCP
-    EdwardsPoint *r = ecc_edwards_multiply(ek->curve->e.G, log_r);
-
-    /*
-     * Encode r now, because we'll need its encoding for the next
-     * hashing step as well as to write into the actual signature.
-     */
-    strbuf *r_enc = strbuf_new();
-    put_epoint(r_enc, r, ek->curve, true); /* omit string header */
-    ecc_edwards_point_free(r);
-
-    /*
-     * Compute the hash of (r || public key || message) just as
-     * eddsa_verify does.
-     */
-    { // WINSCP
-    mp_int *H = eddsa_signing_exponent_from_data(
-        ek, extra, ptrlen_from_strbuf(r_enc), data);
-
-    /* And then s = (log(r) + H*a) mod order(G). */
-    mp_int *Ha = mp_modmul(H, a, ek->curve->e.G_order);
-    mp_int *s = mp_modadd(log_r, Ha, ek->curve->e.G_order);
-    mp_free(H);
-    mp_free(a);
-    mp_free(Ha);
-    mp_free(log_r);
-
-    /* Format the output */
-    put_stringz(bs, ek->sshk.vt->ssh_id);
-    put_uint32(bs, r_enc->len + ek->curve->fieldBytes);
-    put_data(bs, r_enc->u, r_enc->len);
-    strbuf_free(r_enc);
-    { // WINSCP
-    size_t i;
-    for (i = 0; i < ek->curve->fieldBytes; ++i)
-        put_byte(bs, mp_get_byte(s, i));
-    mp_free(s);
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-static const struct ecsign_extra sign_extra_ed25519 = {
-    ec_ed25519, &ssh_sha512,
-    NULL, 0, PTRLEN_DECL_LITERAL(""),
-};
-const ssh_keyalg ssh_ecdsa_ed25519 = {
-    // WINSCP
-    /*.new_pub =*/ eddsa_new_pub,
-    /*.new_priv =*/ eddsa_new_priv,
-    /*.new_priv_openssh =*/ eddsa_new_priv_openssh,
-    /*.freekey =*/ eddsa_freekey,
-    /*.invalid =*/ ec_signkey_invalid,
-    /*.sign =*/ eddsa_sign,
-    /*.verify =*/ eddsa_verify,
-    /*.public_blob =*/ eddsa_public_blob,
-    /*.private_blob =*/ eddsa_private_blob,
-    /*.openssh_blob =*/ eddsa_openssh_blob,
-    /*.cache_str =*/ eddsa_cache_str,
-    /*.components =*/ eddsa_components,
-    /*.pubkey_bits =*/ ec_shared_pubkey_bits,
-    /*.ssh_id =*/ "ssh-ed25519",
-    /*.cache_id =*/ "ssh-ed25519",
-    /*.extra =*/ &sign_extra_ed25519,
-    0, // WINSCP
-};
-
-static const struct ecsign_extra sign_extra_ed448 = {
-    ec_ed448, &ssh_shake256_114bytes,
-    NULL, 0, PTRLEN_DECL_LITERAL("SigEd448\0\0"),
-};
-const ssh_keyalg ssh_ecdsa_ed448 = {
-    // WINSCP
-    /*.new_pub =*/ eddsa_new_pub,
-    /*.new_priv =*/ eddsa_new_priv,
-    /*.new_priv_openssh =*/ eddsa_new_priv_openssh,
-    /*.freekey =*/ eddsa_freekey,
-    /*.invalid =*/ ec_signkey_invalid,
-    /*.sign =*/ eddsa_sign,
-    /*.verify =*/ eddsa_verify,
-    /*.public_blob =*/ eddsa_public_blob,
-    /*.private_blob =*/ eddsa_private_blob,
-    /*.openssh_blob =*/ eddsa_openssh_blob,
-    /*.cache_str =*/ eddsa_cache_str,
-    /*.components =*/ eddsa_components,
-    /*.pubkey_bits =*/ ec_shared_pubkey_bits,
-    /*.ssh_id =*/ "ssh-ed448",
-    /*.cache_id =*/ "ssh-ed448",
-    /*.extra =*/ &sign_extra_ed448,
-    0, // WINSCP
-};
-
-/* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */
-static const unsigned char nistp256_oid[] = {
-    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
-};
-static const struct ecsign_extra sign_extra_nistp256 = {
-    ec_p256, &ssh_sha256,
-    nistp256_oid, lenof(nistp256_oid),
-};
-const ssh_keyalg ssh_ecdsa_nistp256 = {
-    // WINSCP
-    /*.new_pub =*/ ecdsa_new_pub,
-    /*.new_priv =*/ ecdsa_new_priv,
-    /*.new_priv_openssh =*/ ecdsa_new_priv_openssh,
-    /*.freekey =*/ ecdsa_freekey,
-    /*.invalid =*/ ec_signkey_invalid,
-    /*.sign =*/ ecdsa_sign,
-    /*.verify =*/ ecdsa_verify,
-    /*.public_blob =*/ ecdsa_public_blob,
-    /*.private_blob =*/ ecdsa_private_blob,
-    /*.openssh_blob =*/ ecdsa_openssh_blob,
-    /*.cache_str =*/ ecdsa_cache_str,
-    /*.components =*/ ecdsa_components,
-    /*.pubkey_bits =*/ ec_shared_pubkey_bits,
-    /*.ssh_id =*/ "ecdsa-sha2-nistp256",
-    /*.cache_id =*/ "ecdsa-sha2-nistp256",
-    /*.extra =*/ &sign_extra_nistp256,
-    0, // WINSCP
-};
-
-/* OID: 1.3.132.0.34 (secp384r1) */
-static const unsigned char nistp384_oid[] = {
-    0x2b, 0x81, 0x04, 0x00, 0x22
-};
-static const struct ecsign_extra sign_extra_nistp384 = {
-    ec_p384, &ssh_sha384,
-    nistp384_oid, lenof(nistp384_oid),
-};
-const ssh_keyalg ssh_ecdsa_nistp384 = {
-    // WINSCP
-    /*.new_pub =*/ ecdsa_new_pub,
-    /*.new_priv =*/ ecdsa_new_priv,
-    /*.new_priv_openssh =*/ ecdsa_new_priv_openssh,
-    /*.freekey =*/ ecdsa_freekey,
-    /*.invalid =*/ ec_signkey_invalid,
-    /*.sign =*/ ecdsa_sign,
-    /*.verify =*/ ecdsa_verify,
-    /*.public_blob =*/ ecdsa_public_blob,
-    /*.private_blob =*/ ecdsa_private_blob,
-    /*.openssh_blob =*/ ecdsa_openssh_blob,
-    /*.cache_str =*/ ecdsa_cache_str,
-    /*.components =*/ ecdsa_components,
-    /*.pubkey_bits =*/ ec_shared_pubkey_bits,
-    /*.ssh_id =*/ "ecdsa-sha2-nistp384",
-    /*.cache_id =*/ "ecdsa-sha2-nistp384",
-    /*.extra =*/ &sign_extra_nistp384,
-    0, // WINSCP
-};
-
-/* OID: 1.3.132.0.35 (secp521r1) */
-static const unsigned char nistp521_oid[] = {
-    0x2b, 0x81, 0x04, 0x00, 0x23
-};
-static const struct ecsign_extra sign_extra_nistp521 = {
-    ec_p521, &ssh_sha512,
-    nistp521_oid, lenof(nistp521_oid),
-};
-const ssh_keyalg ssh_ecdsa_nistp521 = {
-    // WINSCP
-    /*.new_pub =*/ ecdsa_new_pub,
-    /*.new_priv =*/ ecdsa_new_priv,
-    /*.new_priv_openssh =*/ ecdsa_new_priv_openssh,
-    /*.freekey =*/ ecdsa_freekey,
-    /*.invalid =*/ ec_signkey_invalid,
-    /*.sign =*/ ecdsa_sign,
-    /*.verify =*/ ecdsa_verify,
-    /*.public_blob =*/ ecdsa_public_blob,
-    /*.private_blob =*/ ecdsa_private_blob,
-    /*.openssh_blob =*/ ecdsa_openssh_blob,
-    /*.cache_str =*/ ecdsa_cache_str,
-    /*.components =*/ ecdsa_components,
-    /*.pubkey_bits =*/ ec_shared_pubkey_bits,
-    /*.ssh_id =*/ "ecdsa-sha2-nistp521",
-    /*.cache_id =*/ "ecdsa-sha2-nistp521",
-    /*.extra =*/ &sign_extra_nistp521,
-    0, // WINSCP
-};
-
-/* ----------------------------------------------------------------------
- * Exposed ECDH interface
- */
-
-struct eckex_extra {
-    struct ec_curve *(*curve)(void);
-    void (*setup)(ecdh_key *dh);
-    void (*cleanup)(ecdh_key *dh);
-    void (*getpublic)(ecdh_key *dh, BinarySink *bs);
-    mp_int *(*getkey)(ecdh_key *dh, ptrlen remoteKey);
-};
-
-struct ecdh_key {
-    const struct eckex_extra *extra;
-    const struct ec_curve *curve;
-    mp_int *private;
-    union {
-        WeierstrassPoint *w_public;
-        MontgomeryPoint *m_public;
-    };
-};
-
-const char *ssh_ecdhkex_curve_textname(const ssh_kex *kex)
-{
-    const struct eckex_extra *extra = (const struct eckex_extra *)kex->extra;
-    struct ec_curve *curve = extra->curve();
-    return curve->textname;
-}
-
-static void ssh_ecdhkex_w_setup(ecdh_key *dh)
-{
-    mp_int *one = mp_from_integer(1);
-    dh->private = mp_random_in_range(one, dh->curve->w.G_order);
-    mp_free(one);
-
-    dh->w_public = ecc_weierstrass_multiply(dh->curve->w.G, dh->private);
-}
-
-static void ssh_ecdhkex_m_setup(ecdh_key *dh)
-{
-    strbuf *bytes = strbuf_new_nm();
-    random_read(strbuf_append(bytes, dh->curve->fieldBytes),
-                dh->curve->fieldBytes);
-
-    dh->private = mp_from_bytes_le(ptrlen_from_strbuf(bytes));
-
-    /* Ensure the private key has the highest valid bit set, and no
-     * bits _above_ the highest valid one */
-    mp_reduce_mod_2to(dh->private, dh->curve->fieldBits);
-    mp_set_bit(dh->private, dh->curve->fieldBits - 1, 1);
-
-    /* Clear a curve-specific number of low bits */
-    { // WINSCP
-    unsigned bit;
-    for (bit = 0; bit < dh->curve->m.log2_cofactor; bit++)
-        mp_set_bit(dh->private, bit, 0);
-    } // WINSCP
-
-    strbuf_free(bytes);
-
-    dh->m_public = ecc_montgomery_multiply(dh->curve->m.G, dh->private);
-}
-
-ecdh_key *ssh_ecdhkex_newkey(const ssh_kex *kex)
-{
-    const struct eckex_extra *extra = (const struct eckex_extra *)kex->extra;
-    const struct ec_curve *curve = extra->curve();
-
-    ecdh_key *dh = snew(ecdh_key);
-    dh->extra = extra;
-    dh->curve = curve;
-    dh->extra->setup(dh);
-    return dh;
-}
-
-static void ssh_ecdhkex_w_getpublic(ecdh_key *dh, BinarySink *bs)
-{
-    put_wpoint(bs, dh->w_public, dh->curve, true);
-}
-
-static void ssh_ecdhkex_m_getpublic(ecdh_key *dh, BinarySink *bs)
-{
-    mp_int *x;
-    size_t i; // WINSCP
-    ecc_montgomery_get_affine(dh->m_public, &x);
-    for (i = 0; i < dh->curve->fieldBytes; ++i)
-        put_byte(bs, mp_get_byte(x, i));
-    mp_free(x);
-}
-
-void ssh_ecdhkex_getpublic(ecdh_key *dh, BinarySink *bs)
-{
-    dh->extra->getpublic(dh, bs);
-}
-
-static mp_int *ssh_ecdhkex_w_getkey(ecdh_key *dh, ptrlen remoteKey)
-{
-    WeierstrassPoint *remote_p = ecdsa_decode(remoteKey, dh->curve);
-    if (!remote_p)
-        return NULL;
-
-    if (ecc_weierstrass_is_identity(remote_p)) {
-        /* Not a sensible Diffie-Hellman input value */
-        ecc_weierstrass_point_free(remote_p);
-        return NULL;
-    }
-
-    { // WINSCP
-    WeierstrassPoint *p = ecc_weierstrass_multiply(remote_p, dh->private);
-
-    mp_int *x;
-    ecc_weierstrass_get_affine(p, &x, NULL);
-
-    ecc_weierstrass_point_free(remote_p);
-    ecc_weierstrass_point_free(p);
-
-    return x;
-    } // WINSCP
-}
-
-static mp_int *ssh_ecdhkex_m_getkey(ecdh_key *dh, ptrlen remoteKey)
-{
-    mp_int *remote_x = mp_from_bytes_le(remoteKey);
-
-    /* Per RFC 7748 section 5, discard any set bits of the other
-     * side's public value beyond the minimum number of bits required
-     * to represent all valid values. However, an overlarge value that
-     * still fits into the remaining number of bits is accepted, and
-     * will be reduced mod p. */
-    mp_reduce_mod_2to(remote_x, dh->curve->fieldBits);
-
-    { // WINSCP
-    MontgomeryPoint *remote_p = ecc_montgomery_point_new(
-        dh->curve->m.mc, remote_x);
-    mp_free(remote_x);
-
-    { // WINSCP
-    MontgomeryPoint *p = ecc_montgomery_multiply(remote_p, dh->private);
-
-    if (ecc_montgomery_is_identity(p)) {
-        ecc_montgomery_point_free(remote_p);
-        ecc_montgomery_point_free(p);
-        return NULL;
-    }
-
-    { // WINSCP
-    mp_int *x;
-    ecc_montgomery_get_affine(p, &x);
-
-    ecc_montgomery_point_free(remote_p);
-    ecc_montgomery_point_free(p);
-
-    /*
-     * Endianness-swap. The Curve25519 algorithm definition assumes
-     * you were doing your computation in arrays of 32 little-endian
-     * bytes, and now specifies that you take your final one of those
-     * and convert it into a bignum in _network_ byte order, i.e.
-     * big-endian.
-     *
-     * In particular, the spec says, you convert the _whole_ 32 bytes
-     * into a bignum. That is, on the rare occasions that x has come
-     * out with the most significant 8 bits zero, we have to imagine
-     * that being represented by a 32-byte string with the last byte
-     * being zero, so that has to be converted into an SSH-2 bignum
-     * with the _low_ byte zero, i.e. a multiple of 256.
-     */
-    { // WINSCP
-    strbuf *sb = strbuf_new();
-    size_t i;
-    for (i = 0; i < dh->curve->fieldBytes; ++i)
-        put_byte(sb, mp_get_byte(x, i));
-    mp_free(x);
-    x = mp_from_bytes_be(ptrlen_from_strbuf(sb));
-    strbuf_free(sb);
-
-    return x;
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-    } // WINSCP
-}
-
-mp_int *ssh_ecdhkex_getkey(ecdh_key *dh, ptrlen remoteKey)
-{
-    return dh->extra->getkey(dh, remoteKey);
-}
-
-static void ssh_ecdhkex_w_cleanup(ecdh_key *dh)
-{
-    ecc_weierstrass_point_free(dh->w_public);
-}
-
-static void ssh_ecdhkex_m_cleanup(ecdh_key *dh)
-{
-    ecc_montgomery_point_free(dh->m_public);
-}
-
-void ssh_ecdhkex_freekey(ecdh_key *dh)
-{
-    mp_free(dh->private);
-    dh->extra->cleanup(dh);
-    sfree(dh);
-}
-
-static const struct eckex_extra kex_extra_curve25519 = {
-    ec_curve25519,
-    ssh_ecdhkex_m_setup,
-    ssh_ecdhkex_m_cleanup,
-    ssh_ecdhkex_m_getpublic,
-    ssh_ecdhkex_m_getkey,
-};
-const ssh_kex ssh_ec_kex_curve25519 = {
-    "curve25519-sha256", NULL, KEXTYPE_ECDH,
-    &ssh_sha256, &kex_extra_curve25519,
-};
-/* Pre-RFC alias */
-const ssh_kex ssh_ec_kex_curve25519_libssh = {
-    "[email protected]", NULL, KEXTYPE_ECDH,
-    &ssh_sha256, &kex_extra_curve25519,
-};
-
-static const struct eckex_extra kex_extra_curve448 = {
-    ec_curve448,
-    ssh_ecdhkex_m_setup,
-    ssh_ecdhkex_m_cleanup,
-    ssh_ecdhkex_m_getpublic,
-    ssh_ecdhkex_m_getkey,
-};
-const ssh_kex ssh_ec_kex_curve448 = {
-    "curve448-sha512", NULL, KEXTYPE_ECDH,
-    &ssh_sha512, &kex_extra_curve448,
-};
-
-static const struct eckex_extra kex_extra_nistp256 = {
-    ec_p256,
-    ssh_ecdhkex_w_setup,
-    ssh_ecdhkex_w_cleanup,
-    ssh_ecdhkex_w_getpublic,
-    ssh_ecdhkex_w_getkey,
-};
-const ssh_kex ssh_ec_kex_nistp256 = {
-    "ecdh-sha2-nistp256", NULL, KEXTYPE_ECDH,
-    &ssh_sha256, &kex_extra_nistp256,
-};
-
-static const struct eckex_extra kex_extra_nistp384 = {
-    ec_p384,
-    ssh_ecdhkex_w_setup,
-    ssh_ecdhkex_w_cleanup,
-    ssh_ecdhkex_w_getpublic,
-    ssh_ecdhkex_w_getkey,
-};
-const ssh_kex ssh_ec_kex_nistp384 = {
-    "ecdh-sha2-nistp384", NULL, KEXTYPE_ECDH,
-    &ssh_sha384, &kex_extra_nistp384,
-};
-
-static const struct eckex_extra kex_extra_nistp521 = {
-    ec_p521,
-    ssh_ecdhkex_w_setup,
-    ssh_ecdhkex_w_cleanup,
-    ssh_ecdhkex_w_getpublic,
-    ssh_ecdhkex_w_getkey,
-};
-const ssh_kex ssh_ec_kex_nistp521 = {
-    "ecdh-sha2-nistp521", NULL, KEXTYPE_ECDH,
-    &ssh_sha512, &kex_extra_nistp521,
-};
-
-static const ssh_kex *const ec_kex_list[] = {
-    &ssh_ec_kex_curve448,
-    &ssh_ec_kex_curve25519,
-    &ssh_ec_kex_curve25519_libssh,
-    &ssh_ec_kex_nistp256,
-    &ssh_ec_kex_nistp384,
-    &ssh_ec_kex_nistp521,
-};
-
-const ssh_kexes ssh_ecdh_kex = { lenof(ec_kex_list), ec_kex_list };
-
-/* ----------------------------------------------------------------------
- * Helper functions for finding key algorithms and returning auxiliary
- * data.
- */
-
-const ssh_keyalg *ec_alg_by_oid(int len, const void *oid,
-                                        const struct ec_curve **curve)
-{
-    static const ssh_keyalg *algs_with_oid[] = {
-        &ssh_ecdsa_nistp256,
-        &ssh_ecdsa_nistp384,
-        &ssh_ecdsa_nistp521,
-    };
-    int i;
-
-    for (i = 0; i < lenof(algs_with_oid); i++) {
-        const ssh_keyalg *alg = algs_with_oid[i];
-        const struct ecsign_extra *extra =
-            (const struct ecsign_extra *)alg->extra;
-        if (len == extra->oidlen && !memcmp(oid, extra->oid, len)) {
-            *curve = extra->curve();
-            return alg;
-        }
-    }
-    return NULL;
-}
-
-const unsigned char *ec_alg_oid(const ssh_keyalg *alg,
-                                int *oidlen)
-{
-    const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra;
-    *oidlen = extra->oidlen;
-    return extra->oid;
-}
-
-const int ec_nist_curve_lengths[] = { 256, 384, 521 };
-const int n_ec_nist_curve_lengths = lenof(ec_nist_curve_lengths);
-
-const int ec_ed_curve_lengths[] = { 255, 448 };
-const int n_ec_ed_curve_lengths = lenof(ec_ed_curve_lengths);
-
-bool ec_nist_alg_and_curve_by_bits(
-    int bits, const struct ec_curve **curve, const ssh_keyalg **alg)
-{
-    switch (bits) {
-      case 256: *alg = &ssh_ecdsa_nistp256; break;
-      case 384: *alg = &ssh_ecdsa_nistp384; break;
-      case 521: *alg = &ssh_ecdsa_nistp521; break;
-      default: return false;
-    }
-    *curve = ((struct ecsign_extra *)(*alg)->extra)->curve();
-    return true;
-}
-
-bool ec_ed_alg_and_curve_by_bits(
-    int bits, const struct ec_curve **curve, const ssh_keyalg **alg)
-{
-    switch (bits) {
-      case 255: case 256: *alg = &ssh_ecdsa_ed25519; break;
-      case 448: *alg = &ssh_ecdsa_ed448; break;
-      default: return false;
-    }
-    *curve = ((struct ecsign_extra *)(*alg)->extra)->curve();
-    return true;
-}
-
-#ifdef MPEXT
-
-void ec_cleanup(void)
-{
-  ec_curve_cleanup = 1;
-  ec_p256();
-  ec_p384();
-  ec_p521();
-  ec_curve25519();
-  ec_ed25519();
-  // in case we want to restart (unlikely)
-  ec_curve_cleanup = 0;
-}
-
-#endif

+ 0 - 217
source/putty/sshgss.h

@@ -1,217 +0,0 @@
-#ifndef PUTTY_SSHGSS_H
-#define PUTTY_SSHGSS_H
-#include "putty.h"
-#include "pgssapi.h"
-
-#ifndef NO_GSSAPI
-
-#define SSH2_GSS_OIDTYPE 0x06
-typedef void *Ssh_gss_ctx;
-
-typedef enum Ssh_gss_stat {
-    SSH_GSS_OK = 0,
-    SSH_GSS_S_CONTINUE_NEEDED,
-    SSH_GSS_NO_MEM,
-    SSH_GSS_BAD_HOST_NAME,
-    SSH_GSS_BAD_MIC,
-    SSH_GSS_NO_CREDS,
-    SSH_GSS_FAILURE
-} Ssh_gss_stat;
-
-#define SSH_GSS_S_COMPLETE SSH_GSS_OK
-
-#define SSH_GSS_CLEAR_BUF(buf) do {             \
-    (*buf).length = 0;                          \
-    (*buf).value = NULL;                                \
-} while (0)
-
-typedef gss_buffer_desc Ssh_gss_buf;
-typedef gss_name_t Ssh_gss_name;
-
-#define GSS_NO_EXPIRATION ((time_t)-1)
-
-#define GSS_DEF_REKEY_MINS 2    /* Default minutes between GSS cache checks */
-
-/* Functions, provided by either wingss.c or sshgssc.c */
-
-struct ssh_gss_library;
-
-/*
- * Prepare a collection of GSSAPI libraries for use in a single SSH
- * connection. Returns a structure containing a list of libraries,
- * with their ids (see struct ssh_gss_library below) filled in so
- * that the client can go through them in the SSH user's preferred
- * order.
- *
- * Must always return non-NULL. (Even if no libraries are available,
- * it must return an empty structure.)
- *
- * The free function cleans up the structure, and its associated
- * libraries (if any).
- */
-struct ssh_gss_liblist {
-    struct ssh_gss_library *libraries;
-    int nlibraries;
-};
-struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, LogContext *logctx);
-void ssh_gss_cleanup(struct ssh_gss_liblist *list);
-
-/*
- * Fills in buf with a string describing the GSSAPI mechanism in
- * use. buf->data is not dynamically allocated.
- */
-typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib,
-                                                Ssh_gss_buf *buf);
-
-/*
- * Converts a name such as a hostname into a GSSAPI internal form,
- * which is placed in "out". The result should be freed by
- * ssh_gss_release_name().
- */
-typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib,
-                                              char *in, Ssh_gss_name *out);
-
-/*
- * Frees the contents of an Ssh_gss_name structure filled in by
- * ssh_gss_import_name().
- */
-typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,
-                                               Ssh_gss_name *name);
-
-/*
- * The main GSSAPI security context setup function. The "out"
- * parameter will need to be freed by ssh_gss_free_tok.
- */
-typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)
-    (struct ssh_gss_library *lib,
-     Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
-     Ssh_gss_buf *in, Ssh_gss_buf *out, time_t *expiry,
-     unsigned long *lifetime);
-
-/*
- * Frees the contents of an Ssh_gss_buf filled in by
- * ssh_gss_init_sec_context(). Do not accidentally call this on
- * something filled in by ssh_gss_get_mic() (which requires a
- * different free function) or something filled in by any other
- * way.
- */
-typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,
-                                           Ssh_gss_buf *);
-
-/*
- * Acquires the credentials to perform authentication in the first
- * place. Needs to be freed by ssh_gss_release_cred().
- */
-typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,
-                                               Ssh_gss_ctx *,
-                                               time_t *expiry);
-
-/*
- * Frees the contents of an Ssh_gss_ctx filled in by
- * ssh_gss_acquire_cred().
- */
-typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,
-                                               Ssh_gss_ctx *);
-
-/*
- * Gets a MIC for some input data. "out" needs to be freed by
- * ssh_gss_free_mic().
- */
-typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,
-                                          Ssh_gss_ctx ctx, Ssh_gss_buf *in,
-                                          Ssh_gss_buf *out);
-
-/*
- * Validates an input MIC for some input data.
- */
-typedef Ssh_gss_stat (*t_ssh_gss_verify_mic)(struct ssh_gss_library *lib,
-                                             Ssh_gss_ctx ctx,
-                                             Ssh_gss_buf *in_data,
-                                             Ssh_gss_buf *in_mic);
-
-/*
- * Frees the contents of an Ssh_gss_buf filled in by
- * ssh_gss_get_mic(). Do not accidentally call this on something
- * filled in by ssh_gss_init_sec_context() (which requires a
- * different free function) or something filled in by any other
- * way.
- */
-typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib,
-                                           Ssh_gss_buf *);
-
-/*
- * Return an error message after authentication failed. The
- * message string is returned in "buf", with buf->len giving the
- * number of characters of printable message text and buf->data
- * containing one more character which is a trailing NUL.
- * buf->data should be manually freed by the caller.
- */
-typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib,
-                                                 Ssh_gss_ctx, Ssh_gss_buf *buf);
-
-struct ssh_gss_library {
-    /*
-     * Identifying number in the enumeration used by the
-     * configuration code to specify a preference order.
-     */
-    int id;
-
-    /*
-     * Filled in at initialisation time, if there's anything
-     * interesting to say about how GSSAPI was initialised (e.g.
-     * which of a number of alternative libraries was used).
-     */
-    const char *gsslogmsg;
-
-    /*
-     * Function pointers implementing the SSH wrapper layer on top
-     * of GSSAPI. (Defined in sshgssc, typically, though Windows
-     * provides an alternative layer to sit on top of the annoyingly
-     * different SSPI.)
-     */
-    t_ssh_gss_indicate_mech indicate_mech;
-    t_ssh_gss_import_name import_name;
-    t_ssh_gss_release_name release_name;
-    t_ssh_gss_init_sec_context init_sec_context;
-    t_ssh_gss_free_tok free_tok;
-    t_ssh_gss_acquire_cred acquire_cred;
-    t_ssh_gss_release_cred release_cred;
-    t_ssh_gss_get_mic get_mic;
-    t_ssh_gss_verify_mic verify_mic;
-    t_ssh_gss_free_mic free_mic;
-    t_ssh_gss_display_status display_status;
-
-    /*
-     * Additional data for the wrapper layers.
-     */
-    union {
-        struct gssapi_functions gssapi;
-        /*
-         * The SSPI wrappers don't need to store their Windows API
-         * function pointers in this structure, because there can't
-         * be more than one set of them available.
-         */
-    } u;
-
-    /*
-     * Wrapper layers will often also need to store a library handle
-     * of some sort for cleanup time.
-     */
-    void *handle;
-};
-
-/*
- * State that has to be shared between all GSSAPI-using parts of the
- * same SSH connection, in particular between GSS key exchange and the
- * subsequent trivial userauth method that reuses its output.
- */
-struct ssh_connection_shared_gss_state {
-    struct ssh_gss_liblist *libs;
-    struct ssh_gss_library *lib;
-    Ssh_gss_name srv_name;
-    Ssh_gss_ctx ctx;
-};
-
-#endif /* NO_GSSAPI */
-
-#endif /*PUTTY_SSHGSS_H*/

+ 0 - 297
source/putty/sshgssc.c

@@ -1,297 +0,0 @@
-#include "putty.h"
-
-#include <string.h>
-#include <limits.h>
-#include "sshgssc.h"
-#include "misc.h"
-
-#ifndef NO_GSSAPI
-
-static Ssh_gss_stat ssh_gssapi_indicate_mech(struct ssh_gss_library *lib,
-                                             Ssh_gss_buf *mech)
-{
-    /* Copy constant into mech */
-    mech->length  = GSS_MECH_KRB5->length;
-    mech->value = GSS_MECH_KRB5->elements;
-    return SSH_GSS_OK;
-}
-
-static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,
-                                           char *host,
-                                           Ssh_gss_name *srv_name)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    OM_uint32 min_stat,maj_stat;
-    gss_buffer_desc host_buf;
-    char *pStr;
-
-    pStr = dupcat("host@", host);
-
-    host_buf.value = pStr;
-    host_buf.length = strlen(pStr);
-
-    maj_stat = gss->import_name(&min_stat, &host_buf,
-                                GSS_C_NT_HOSTBASED_SERVICE, srv_name);
-    /* Release buffer */
-    sfree(pStr);
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
-
-static Ssh_gss_stat ssh_gssapi_acquire_cred(struct ssh_gss_library *lib,
-                                            Ssh_gss_ctx *ctx,
-                                            time_t *expiry)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    #ifdef MPEXT
-    gss_OID_set_desc k5only;
-    #else
-    gss_OID_set_desc k5only = { 1, GSS_MECH_KRB5 };
-    #endif
-    gss_cred_id_t cred;
-    OM_uint32 dummy;
-    OM_uint32 time_rec;
-    gssapi_ssh_gss_ctx *gssctx = snew(gssapi_ssh_gss_ctx);
-
-    #ifdef MPEXT
-    k5only.count = 1;
-    k5only.elements = GSS_MECH_KRB5;
-    #endif
-
-    gssctx->ctx = GSS_C_NO_CONTEXT;
-    gssctx->expiry = 0;
-
-    gssctx->maj_stat =
-        gss->acquire_cred(&gssctx->min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
-                          &k5only, GSS_C_INITIATE, &cred,
-                          (gss_OID_set *)0, &time_rec);
-
-    if (gssctx->maj_stat != GSS_S_COMPLETE) {
-        sfree(gssctx);
-        return SSH_GSS_FAILURE;
-    }
-
-    /*
-     * When the credential lifetime is not yet available due to deferred
-     * processing, gss_acquire_cred should return a 0 lifetime which is
-     * distinct from GSS_C_INDEFINITE which signals a crential that never
-     * expires.  However, not all implementations get this right, and with
-     * Kerberos, initiator credentials always expire at some point.  So when
-     * lifetime is 0 or GSS_C_INDEFINITE we call gss_inquire_cred_by_mech() to
-     * complete deferred processing.
-     */
-    if (time_rec == GSS_C_INDEFINITE || time_rec == 0) {
-        gssctx->maj_stat =
-            gss->inquire_cred_by_mech(&gssctx->min_stat, cred,
-                                      (gss_OID) GSS_MECH_KRB5,
-                                      GSS_C_NO_NAME,
-                                      &time_rec,
-                                      NULL,
-                                      NULL);
-    }
-    (void) gss->release_cred(&dummy, &cred);
-
-    if (gssctx->maj_stat != GSS_S_COMPLETE) {
-        sfree(gssctx);
-        return SSH_GSS_FAILURE;
-    }
-
-    if (time_rec != GSS_C_INDEFINITE)
-        gssctx->expiry = time(NULL) + time_rec;
-    else
-        gssctx->expiry = GSS_NO_EXPIRATION;
-
-    if (expiry) {
-        *expiry = gssctx->expiry;
-    }
-
-    *ctx = (Ssh_gss_ctx) gssctx;
-    return SSH_GSS_OK;
-}
-
-static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib,
-                                                Ssh_gss_ctx *ctx,
-                                                Ssh_gss_name srv_name,
-                                                int to_deleg,
-                                                Ssh_gss_buf *recv_tok,
-                                                Ssh_gss_buf *send_tok,
-                                                time_t *expiry,
-                                                unsigned long *lifetime)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx*) *ctx;
-    OM_uint32 ret_flags;
-    OM_uint32 lifetime_rec;
-
-    if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;
-    gssctx->maj_stat = gss->init_sec_context(&gssctx->min_stat,
-                                             GSS_C_NO_CREDENTIAL,
-                                             &gssctx->ctx,
-                                             srv_name,
-                                             (gss_OID) GSS_MECH_KRB5,
-                                             GSS_C_MUTUAL_FLAG |
-                                             GSS_C_INTEG_FLAG | to_deleg,
-                                             0,
-                                             GSS_C_NO_CHANNEL_BINDINGS,
-                                             recv_tok,
-                                             NULL,   /* ignore mech type */
-                                             send_tok,
-                                             &ret_flags,
-                                             &lifetime_rec);
-
-    if (lifetime) {
-        if (lifetime_rec == GSS_C_INDEFINITE)
-            *lifetime = ULONG_MAX;
-        else
-            *lifetime = lifetime_rec;
-    }
-    if (expiry) {
-        if (lifetime_rec == GSS_C_INDEFINITE)
-            *expiry = GSS_NO_EXPIRATION;
-        else
-            *expiry = time(NULL) + lifetime_rec;
-    }
-
-    if (gssctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;
-    if (gssctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
-    return SSH_GSS_FAILURE;
-}
-
-static Ssh_gss_stat ssh_gssapi_display_status(struct ssh_gss_library *lib,
-                                              Ssh_gss_ctx ctx,
-                                              Ssh_gss_buf *buf)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
-    OM_uint32 lmin,lmax;
-    OM_uint32 ccc;
-    gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER;
-    gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER;
-
-    /* Return empty buffer in case of failure */
-    SSH_GSS_CLEAR_BUF(buf);
-
-    /* get first mesg from GSS */
-    ccc=0;
-    lmax=gss->display_status(&lmin,gssctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_maj);
-
-    if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;
-
-    /* get first mesg from Kerberos */
-    ccc=0;
-    lmax=gss->display_status(&lmin,gssctx->min_stat,GSS_C_MECH_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_min);
-
-    if (lmax != GSS_S_COMPLETE) {
-        gss->release_buffer(&lmin, &msg_maj);
-        return SSH_GSS_FAILURE;
-    }
-
-    /* copy data into buffer */
-    buf->length = msg_maj.length + msg_min.length + 1;
-    buf->value = snewn(buf->length + 1, char);
-
-    /* copy mem */
-    memcpy((char *)buf->value, msg_maj.value, msg_maj.length);
-    ((char *)buf->value)[msg_maj.length] = ' ';
-    memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length);
-    ((char *)buf->value)[buf->length] = 0;
-    /* free mem & exit */
-    gss->release_buffer(&lmin, &msg_maj);
-    gss->release_buffer(&lmin, &msg_min);
-    return SSH_GSS_OK;
-}
-
-static Ssh_gss_stat ssh_gssapi_free_tok(struct ssh_gss_library *lib,
-                                        Ssh_gss_buf *send_tok)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    OM_uint32 min_stat,maj_stat;
-    maj_stat = gss->release_buffer(&min_stat, send_tok);
-
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
-
-static Ssh_gss_stat ssh_gssapi_release_cred(struct ssh_gss_library *lib,
-                                            Ssh_gss_ctx *ctx)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) *ctx;
-    OM_uint32 min_stat;
-    OM_uint32 maj_stat=GSS_S_COMPLETE;
-
-    if (gssctx == NULL) return SSH_GSS_FAILURE;
-    if (gssctx->ctx != GSS_C_NO_CONTEXT)
-        maj_stat = gss->delete_sec_context(&min_stat,&gssctx->ctx,GSS_C_NO_BUFFER);
-    sfree(gssctx);
-    *ctx = NULL;
-
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
-
-
-static Ssh_gss_stat ssh_gssapi_release_name(struct ssh_gss_library *lib,
-                                            Ssh_gss_name *srv_name)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    OM_uint32 min_stat,maj_stat;
-    maj_stat = gss->release_name(&min_stat, srv_name);
-
-    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
-    return SSH_GSS_FAILURE;
-}
-
-static Ssh_gss_stat ssh_gssapi_get_mic(struct ssh_gss_library *lib,
-                                       Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
-                                       Ssh_gss_buf *hash)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
-    if (gssctx == NULL) return SSH_GSS_FAILURE;
-    return gss->get_mic(&(gssctx->min_stat), gssctx->ctx, 0, buf, hash);
-}
-
-static Ssh_gss_stat ssh_gssapi_verify_mic(struct ssh_gss_library *lib,
-                                          Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
-                                          Ssh_gss_buf *hash)
-{
-    struct gssapi_functions *gss = &lib->u.gssapi;
-    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
-    if (gssctx == NULL) return SSH_GSS_FAILURE;
-    return gss->verify_mic(&(gssctx->min_stat), gssctx->ctx, buf, hash, NULL);
-}
-
-static Ssh_gss_stat ssh_gssapi_free_mic(struct ssh_gss_library *lib,
-                                        Ssh_gss_buf *hash)
-{
-    /* On Unix this is the same freeing process as ssh_gssapi_free_tok. */
-    return ssh_gssapi_free_tok(lib, hash);
-}
-
-void ssh_gssapi_bind_fns(struct ssh_gss_library *lib)
-{
-    lib->indicate_mech = ssh_gssapi_indicate_mech;
-    lib->import_name = ssh_gssapi_import_name;
-    lib->release_name = ssh_gssapi_release_name;
-    lib->init_sec_context = ssh_gssapi_init_sec_context;
-    lib->free_tok = ssh_gssapi_free_tok;
-    lib->acquire_cred = ssh_gssapi_acquire_cred;
-    lib->release_cred = ssh_gssapi_release_cred;
-    lib->get_mic = ssh_gssapi_get_mic;
-    lib->verify_mic = ssh_gssapi_verify_mic;
-    lib->free_mic = ssh_gssapi_free_mic;
-    lib->display_status = ssh_gssapi_display_status;
-}
-
-#else
-
-/* Dummy function so this source file defines something if NO_GSSAPI
-   is defined. */
-
-int ssh_gssapi_init(void)
-{
-    return 0;
-}
-
-#endif

+ 0 - 24
source/putty/sshgssc.h

@@ -1,24 +0,0 @@
-#ifndef PUTTY_SSHGSSC_H
-#define PUTTY_SSHGSSC_H
-#include "putty.h"
-#ifndef NO_GSSAPI
-
-#include "pgssapi.h"
-#include "sshgss.h"
-
-typedef struct gssapi_ssh_gss_ctx {
-    OM_uint32 maj_stat;
-    OM_uint32 min_stat;
-    gss_ctx_id_t ctx;
-    time_t expiry;
-} gssapi_ssh_gss_ctx;
-
-void ssh_gssapi_bind_fns(struct ssh_gss_library *lib);
-
-#else
-
-int ssh_gssapi_init(void);
-
-#endif /*NO_GSSAPI*/
-
-#endif /*PUTTY_SSHGSSC_H*/

+ 0 - 272
source/putty/sshhmac.c

@@ -1,272 +0,0 @@
-/*
- * Implementation of HMAC (RFC 2104) for PuTTY, in a general form that
- * can wrap any underlying hash function.
- */
-
-#include "ssh.h"
-
-struct hmac {
-    const ssh_hashalg *hashalg;
-    ssh_hash *h_outer, *h_inner, *h_live;
-    uint8_t *digest;
-    strbuf *text_name;
-    ssh2_mac mac;
-};
-
-struct hmac_extra {
-    const ssh_hashalg *hashalg_base;
-    const char *suffix, *annotation;
-};
-
-static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
-{
-    struct hmac *ctx = snew(struct hmac);
-    const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra;
-
-    ctx->h_outer = ssh_hash_new(extra->hashalg_base);
-    /* In case that hashalg was a selector vtable, we'll now switch to
-     * using whatever real one it selected, for all future purposes. */
-    ctx->hashalg = ssh_hash_alg(ctx->h_outer);
-    ctx->h_inner = ssh_hash_new(ctx->hashalg);
-    ctx->h_live = ssh_hash_new(ctx->hashalg);
-
-    /*
-     * HMAC is not well defined as a wrapper on an absolutely general
-     * hash function; it expects that the function it's wrapping will
-     * consume data in fixed-size blocks, and it's partially defined
-     * in terms of that block size. So we insist that the hash we're
-     * given must have defined a meaningful block size.
-     */
-    assert(ctx->hashalg->blocklen);
-
-    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);
-    if (extra->annotation || ctx->hashalg->annotation) {
-        strbuf_catf(ctx->text_name, " (");
-        { // WINSCP
-        const char *sep = "";
-        if (extra->annotation) {
-            strbuf_catf(ctx->text_name, "%s%s", sep, extra->annotation);
-            sep = ", ";
-        }
-        if (ctx->hashalg->annotation) {
-            strbuf_catf(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation);
-            sep = ", ";
-        }
-        strbuf_catf(ctx->text_name, ")");
-        } // WINSCP
-    }
-
-    ctx->mac.vt = alg;
-    BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live);
-
-    return &ctx->mac;
-}
-
-static void hmac_free(ssh2_mac *mac)
-{
-    struct hmac *ctx = container_of(mac, struct hmac, mac);
-
-    ssh_hash_free(ctx->h_outer);
-    ssh_hash_free(ctx->h_inner);
-    ssh_hash_free(ctx->h_live);
-    smemclr(ctx->digest, ctx->hashalg->hlen);
-    sfree(ctx->digest);
-    strbuf_free(ctx->text_name);
-
-    smemclr(ctx, sizeof(*ctx));
-    sfree(ctx);
-}
-
-#define PAD_OUTER 0x5C
-#define PAD_INNER 0x36
-
-static void hmac_key(ssh2_mac *mac, ptrlen key)
-{
-    struct hmac *ctx = container_of(mac, struct hmac, mac);
-
-    const uint8_t *kp;
-    size_t klen;
-    strbuf *sb = NULL;
-
-    if (key.len > ctx->hashalg->blocklen) {
-        /*
-         * RFC 2104 section 2: if the key exceeds the block length of
-         * the underlying hash, then we start by hashing the key, and
-         * use that hash as the 'true' key for the HMAC construction.
-         */
-        sb = strbuf_new_nm();
-        strbuf_append(sb, ctx->hashalg->hlen);
-        hash_simple(ctx->hashalg, key, sb->u);
-        { // WINSCP
-        kp = sb->u;
-        klen = sb->len;
-        } // WINSCP
-    } else {
-        /*
-         * A short enough key is used as is.
-         */
-        kp = (const uint8_t *)key.ptr;
-        klen = key.len;
-    }
-
-    ssh_hash_reset(ctx->h_outer);
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < klen; i++)
-        put_byte(ctx->h_outer, PAD_OUTER ^ kp[i]);
-    for (i = klen; i < ctx->hashalg->blocklen; i++)
-        put_byte(ctx->h_outer, PAD_OUTER);
-
-    ssh_hash_reset(ctx->h_inner);
-    for (i = 0; i < klen; i++)
-        put_byte(ctx->h_inner, PAD_INNER ^ kp[i]);
-    for (i = klen; i < ctx->hashalg->blocklen; i++)
-        put_byte(ctx->h_inner, PAD_INNER);
-
-    if (sb)
-        strbuf_free(sb);
-    } // WINSCP
-}
-
-static void hmac_start(ssh2_mac *mac)
-{
-    struct hmac *ctx = container_of(mac, struct hmac, mac);
-    ssh_hash_copyfrom(ctx->h_live, ctx->h_inner);
-}
-
-static void hmac_genresult(ssh2_mac *mac, unsigned char *output)
-{
-    struct hmac *ctx = container_of(mac, struct hmac, mac);
-    ssh_hash *htmp;
-
-    /* Leave h_live and h_outer in place, so that the SSH-2 BPP can
-     * continue regenerating test results from different-length
-     * prefixes of the packet */
-    ssh_hash_digest_nondestructive(ctx->h_live, ctx->digest);
-
-    htmp = ssh_hash_copy(ctx->h_outer);
-    put_data(htmp, ctx->digest, ctx->hashalg->hlen);
-    ssh_hash_final(htmp, ctx->digest);
-
-    /*
-     * Some instances of HMAC truncate the output hash, so instead of
-     * writing it directly to 'output' we wrote it to our own
-     * full-length buffer, and now we copy the required amount.
-     */
-    memcpy(output, ctx->digest, mac->vt->len);
-    smemclr(ctx->digest, ctx->hashalg->hlen);
-}
-
-static const char *hmac_text_name(ssh2_mac *mac)
-{
-    struct hmac *ctx = container_of(mac, struct hmac, mac);
-    return ctx->text_name->s;
-}
-
-static const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" };
-const ssh2_macalg ssh_hmac_sha256 = {
-    // WINSCP
-    /*.new =*/ hmac_new,
-    /*.free =*/ hmac_free,
-    /*.setkey =*/ hmac_key,
-    /*.start =*/ hmac_start,
-    /*.genresult =*/ hmac_genresult,
-    /*.text_name =*/ hmac_text_name,
-    /*.name =*/ "hmac-sha2-256",
-    /*.etm_name =*/ "[email protected]",
-    /*.len =*/ 32,
-    /*.keylen =*/ 32,
-    /*.extra =*/ &ssh_hmac_sha256_extra,
-};
-
-static const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" };
-const ssh2_macalg ssh_hmac_md5 = {
-    // WINSCP
-    /*.new =*/ hmac_new,
-    /*.free =*/ hmac_free,
-    /*.setkey =*/ hmac_key,
-    /*.start =*/ hmac_start,
-    /*.genresult =*/ hmac_genresult,
-    /*.text_name =*/ hmac_text_name,
-    /*.name =*/ "hmac-md5",
-    /*.etm_name =*/ "[email protected]",
-    /*.len =*/ 16,
-    /*.keylen =*/ 16,
-    /*.extra =*/ &ssh_hmac_md5_extra,
-};
-
-static const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" };
-
-const ssh2_macalg ssh_hmac_sha1 = {
-    // WINSCP
-    /*.new =*/ hmac_new,
-    /*.free =*/ hmac_free,
-    /*.setkey =*/ hmac_key,
-    /*.start =*/ hmac_start,
-    /*.genresult =*/ hmac_genresult,
-    /*.text_name =*/ hmac_text_name,
-    /*.name =*/ "hmac-sha1",
-    /*.etm_name =*/ "[email protected]",
-    /*.len =*/ 20,
-    /*.keylen =*/ 20,
-    /*.extra =*/ &ssh_hmac_sha1_extra,
-};
-
-static const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" };
-
-const ssh2_macalg ssh_hmac_sha1_96 = {
-    // WINSCP
-    /*.new =*/ hmac_new,
-    /*.free =*/ hmac_free,
-    /*.setkey =*/ hmac_key,
-    /*.start =*/ hmac_start,
-    /*.genresult =*/ hmac_genresult,
-    /*.text_name =*/ hmac_text_name,
-    /*.name =*/ "hmac-sha1-96",
-    /*.etm_name =*/ "[email protected]",
-    /*.len =*/ 12,
-    /*.keylen =*/ 20,
-    /*.extra =*/ &ssh_hmac_sha1_96_extra,
-};
-
-static const struct hmac_extra ssh_hmac_sha1_buggy_extra = {
-    &ssh_sha1, "", "bug-compatible"
-};
-
-const ssh2_macalg ssh_hmac_sha1_buggy = {
-    // WINSCP
-    /*.new =*/ hmac_new,
-    /*.free =*/ hmac_free,
-    /*.setkey =*/ hmac_key,
-    /*.start =*/ hmac_start,
-    /*.genresult =*/ hmac_genresult,
-    /*.text_name =*/ hmac_text_name,
-    /*.name =*/ "hmac-sha1",
-    NULL, // WINSCP
-    /*.len =*/ 20,
-    /*.keylen =*/ 16,
-    /*.extra =*/ &ssh_hmac_sha1_buggy_extra,
-};
-
-static const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {
-    &ssh_sha1, "-96", "bug-compatible"
-};
-
-const ssh2_macalg ssh_hmac_sha1_96_buggy = {
-    // WINSCP
-    /*.new =*/ hmac_new,
-    /*.free =*/ hmac_free,
-    /*.setkey =*/ hmac_key,
-    /*.start =*/ hmac_start,
-    /*.genresult =*/ hmac_genresult,
-    /*.text_name =*/ hmac_text_name,
-    /*.name =*/ "hmac-sha1-96",
-    NULL, // WINSCP
-    /*.len =*/ 12,
-    /*.keylen =*/ 16,
-    /*.extra =*/ &ssh_hmac_sha1_96_buggy_extra,
-};

+ 0 - 43
source/putty/sshmac.c

@@ -1,43 +0,0 @@
-/*
- * Centralised parts of the SSH-2 MAC API, which don't need to vary
- * with the MAC implementation.
- */
-
-#include <assert.h>
-
-#include "ssh.h"
-
-bool ssh2_mac_verresult(ssh2_mac *mac, const void *candidate)
-{
-    unsigned char correct[64]; /* at least as big as all known MACs */
-    bool toret;
-
-    assert(mac->vt->len <= sizeof(correct));
-    ssh2_mac_genresult(mac, correct);
-    toret = smemeq(correct, candidate, mac->vt->len);
-
-    smemclr(correct, sizeof(correct));
-
-    return toret;
-}
-
-static void ssh2_mac_prepare(ssh2_mac *mac, const void *blk, int len,
-                             unsigned long seq)
-{
-    ssh2_mac_start(mac);
-    put_uint32(mac, seq);
-    put_data(mac, blk, len);
-}
-
-void ssh2_mac_generate(ssh2_mac *mac, void *blk, int len, unsigned long seq)
-{
-    ssh2_mac_prepare(mac, blk, len, seq);
-    ssh2_mac_genresult(mac, (unsigned char *)blk + len);
-}
-
-bool ssh2_mac_verify(
-    ssh2_mac *mac, const void *blk, int len, unsigned long seq)
-{
-    ssh2_mac_prepare(mac, blk, len, seq);
-    return ssh2_mac_verresult(mac, (const unsigned char *)blk + len);
-}

+ 0 - 256
source/putty/sshmd5.c

@@ -1,256 +0,0 @@
-/*
- * MD5 implementation for PuTTY. Written directly from the spec by
- * Simon Tatham.
- */
-
-#include <assert.h>
-#include "ssh.h"
-
-static const uint32_t md5_initial_state[] = {
-    0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476,
-};
-
-static const struct md5_round_constant {
-    uint32_t addition, rotation, msg_index;
-} md5_round_constants[] = {
-    { 0xd76aa478,  7,  0 }, { 0xe8c7b756, 12,  1 },
-    { 0x242070db, 17,  2 }, { 0xc1bdceee, 22,  3 },
-    { 0xf57c0faf,  7,  4 }, { 0x4787c62a, 12,  5 },
-    { 0xa8304613, 17,  6 }, { 0xfd469501, 22,  7 },
-    { 0x698098d8,  7,  8 }, { 0x8b44f7af, 12,  9 },
-    { 0xffff5bb1, 17, 10 }, { 0x895cd7be, 22, 11 },
-    { 0x6b901122,  7, 12 }, { 0xfd987193, 12, 13 },
-    { 0xa679438e, 17, 14 }, { 0x49b40821, 22, 15 },
-    { 0xf61e2562,  5,  1 }, { 0xc040b340,  9,  6 },
-    { 0x265e5a51, 14, 11 }, { 0xe9b6c7aa, 20,  0 },
-    { 0xd62f105d,  5,  5 }, { 0x02441453,  9, 10 },
-    { 0xd8a1e681, 14, 15 }, { 0xe7d3fbc8, 20,  4 },
-    { 0x21e1cde6,  5,  9 }, { 0xc33707d6,  9, 14 },
-    { 0xf4d50d87, 14,  3 }, { 0x455a14ed, 20,  8 },
-    { 0xa9e3e905,  5, 13 }, { 0xfcefa3f8,  9,  2 },
-    { 0x676f02d9, 14,  7 }, { 0x8d2a4c8a, 20, 12 },
-    { 0xfffa3942,  4,  5 }, { 0x8771f681, 11,  8 },
-    { 0x6d9d6122, 16, 11 }, { 0xfde5380c, 23, 14 },
-    { 0xa4beea44,  4,  1 }, { 0x4bdecfa9, 11,  4 },
-    { 0xf6bb4b60, 16,  7 }, { 0xbebfbc70, 23, 10 },
-    { 0x289b7ec6,  4, 13 }, { 0xeaa127fa, 11,  0 },
-    { 0xd4ef3085, 16,  3 }, { 0x04881d05, 23,  6 },
-    { 0xd9d4d039,  4,  9 }, { 0xe6db99e5, 11, 12 },
-    { 0x1fa27cf8, 16, 15 }, { 0xc4ac5665, 23,  2 },
-    { 0xf4292244,  6,  0 }, { 0x432aff97, 10,  7 },
-    { 0xab9423a7, 15, 14 }, { 0xfc93a039, 21,  5 },
-    { 0x655b59c3,  6, 12 }, { 0x8f0ccc92, 10,  3 },
-    { 0xffeff47d, 15, 10 }, { 0x85845dd1, 21,  1 },
-    { 0x6fa87e4f,  6,  8 }, { 0xfe2ce6e0, 10, 15 },
-    { 0xa3014314, 15,  6 }, { 0x4e0811a1, 21, 13 },
-    { 0xf7537e82,  6,  4 }, { 0xbd3af235, 10, 11 },
-    { 0x2ad7d2bb, 15,  2 }, { 0xeb86d391, 21,  9 },
-};
-
-typedef struct md5_block md5_block;
-struct md5_block {
-    uint8_t block[64];
-    size_t used;
-    uint64_t len;
-};
-
-static inline void md5_block_setup(md5_block *blk)
-{
-    blk->used = 0;
-    blk->len = 0;
-}
-
-static inline bool md5_block_write(
-    md5_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 md5_block_pad(md5_block *blk, BinarySink *bs)
-{
-    uint64_t final_len = blk->len << 3;
-    size_t pad = 63 & (55 - blk->used);
-
-    put_byte(bs, 0x80);
-    put_padding(bs, pad, 0);
-
-    { // WINSCP
-    unsigned char buf[8];
-    PUT_64BIT_LSB_FIRST(buf, final_len);
-    put_data(bs, buf, 8);
-    smemclr(buf, 8);
-    } // WINSCP
-
-    assert(blk->used == 0 && "Should have exactly hit a block boundary");
-}
-
-static inline uint32_t rol(uint32_t x, unsigned y)
-{
-#pragma option push -w-ngu // WINSCP
-    return (x << (31 & y)) | (x >> (31 & -y));
-#pragma option pop // WINSCP
-}
-
-static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0)
-{
-    return if0 ^ (ctrl & (if1 ^ if0));
-}
-
-/* Parameter functions for the four MD5 round types */
-static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z)
-{ return Ch(x, y, z); }
-static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z)
-{ return Ch(z, x, y); }
-static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z)
-{ return x ^ y ^ z; }
-static inline uint32_t I(uint32_t x, uint32_t y, uint32_t z)
-{ return y ^ (x | ~z); }
-
-static inline void md5_round(
-    unsigned round_index, const uint32_t *message,
-    uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d,
-    uint32_t (*f)(uint32_t, uint32_t, uint32_t))
-{
-    struct md5_round_constant rc = md5_round_constants[round_index];
-
-    *a = *b + rol(*a + f(*b, *c, *d) + message[rc.msg_index] + rc.addition,
-                  rc.rotation);
-}
-
-static void md5_do_block(uint32_t *core, const uint8_t *block)
-{
-    uint32_t message_words[16];
-    size_t i; // WINSCP
-    for (i = 0; i < 16; i++)
-        message_words[i] = GET_32BIT_LSB_FIRST(block + 4*i);
-
-    { // WINSCP
-    uint32_t a = core[0], b = core[1], c = core[2], d = core[3];
-
-    size_t t = 0;
-    size_t u; // WINSCP
-    for (u = 0; u < 4; u++) {
-        md5_round(t++, message_words, &a, &b, &c, &d, F);
-        md5_round(t++, message_words, &d, &a, &b, &c, F);
-        md5_round(t++, message_words, &c, &d, &a, &b, F);
-        md5_round(t++, message_words, &b, &c, &d, &a, F);
-    }
-    for (u = 0; u < 4; u++) {
-        md5_round(t++, message_words, &a, &b, &c, &d, G);
-        md5_round(t++, message_words, &d, &a, &b, &c, G);
-        md5_round(t++, message_words, &c, &d, &a, &b, G);
-        md5_round(t++, message_words, &b, &c, &d, &a, G);
-    }
-    for (u = 0; u < 4; u++) {
-        md5_round(t++, message_words, &a, &b, &c, &d, H);
-        md5_round(t++, message_words, &d, &a, &b, &c, H);
-        md5_round(t++, message_words, &c, &d, &a, &b, H);
-        md5_round(t++, message_words, &b, &c, &d, &a, H);
-    }
-    for (u = 0; u < 4; u++) {
-        md5_round(t++, message_words, &a, &b, &c, &d, I);
-        md5_round(t++, message_words, &d, &a, &b, &c, I);
-        md5_round(t++, message_words, &c, &d, &a, &b, I);
-        md5_round(t++, message_words, &b, &c, &d, &a, I);
-    }
-
-    core[0] += a;
-    core[1] += b;
-    core[2] += c;
-    core[3] += d;
-    } // WINSCP
-
-    smemclr(message_words, sizeof(message_words));
-}
-
-typedef struct md5 {
-    uint32_t core[4];
-    md5_block blk;
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} md5;
-
-static void md5_write(BinarySink *bs, const void *vp, size_t len);
-
-static ssh_hash *md5_new(const ssh_hashalg *alg)
-{
-    md5 *s = snew(md5);
-
-    s->hash.vt = alg;
-    BinarySink_INIT(s, md5_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-    return &s->hash;
-}
-
-static void md5_reset(ssh_hash *hash)
-{
-    md5 *s = container_of(hash, md5, hash);
-
-    memcpy(s->core, md5_initial_state, sizeof(s->core));
-    md5_block_setup(&s->blk);
-}
-
-static void md5_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    md5 *copy = container_of(hcopy, md5, hash);
-    md5 *orig = container_of(horig, md5, hash);
-
-    memcpy(copy, orig, sizeof(*copy));
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-static void md5_free(ssh_hash *hash)
-{
-    md5 *s = container_of(hash, md5, hash);
-
-    smemclr(s, sizeof(*s));
-    sfree(s);
-}
-
-static void md5_write(BinarySink *bs, const void *vp, size_t len)
-{
-    md5 *s = BinarySink_DOWNCAST(bs, md5);
-
-    while (len > 0)
-        if (md5_block_write(&s->blk, &vp, &len))
-            md5_do_block(s->core, s->blk.block);
-}
-
-static void md5_digest(ssh_hash *hash, uint8_t *digest)
-{
-    md5 *s = container_of(hash, md5, hash);
-
-    size_t i; // WINSCP
-    md5_block_pad(&s->blk, BinarySink_UPCAST(s));
-    for (i = 0; i < 4; i++)
-        PUT_32BIT_LSB_FIRST(digest + 4*i, s->core[i]);
-}
-
-const ssh_hashalg ssh_md5 = {
-    // WINSCP
-    /*.new =*/ md5_new,
-    /*.reset =*/ md5_reset,
-    /*.copyfrom =*/ md5_copyfrom,
-    /*.digest =*/ md5_digest,
-    /*.free =*/ md5_free,
-    /*.hlen =*/ 16,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_BARE("MD5"),
-    NULL, // WINSCP
-};

+ 0 - 179
source/putty/sshppl.h

@@ -1,179 +0,0 @@
-/*
- * Abstraction of the various layers of SSH packet-level protocol,
- * general enough to take in all three of the main SSH-2 layers and
- * both of the SSH-1 phases.
- */
-
-#ifndef PUTTY_SSHPPL_H
-#define PUTTY_SSHPPL_H
-
-typedef void (*packet_handler_fn_t)(PacketProtocolLayer *ppl, PktIn *pktin);
-typedef struct PacketProtocolLayerVtable PacketProtocolLayerVtable;
-
-struct PacketProtocolLayerVtable {
-    void (*free)(PacketProtocolLayer *);
-    void (*process_queue)(PacketProtocolLayer *ppl);
-    bool (*get_specials)(
-        PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
-    void (*special_cmd)(
-        PacketProtocolLayer *ppl, SessionSpecialCode code, int arg);
-    bool (*want_user_input)(PacketProtocolLayer *ppl);
-    void (*got_user_input)(PacketProtocolLayer *ppl);
-    void (*reconfigure)(PacketProtocolLayer *ppl, Conf *conf);
-    size_t (*queued_data_size)(PacketProtocolLayer *ppl);
-
-    /* Protocol-level name of this layer. */
-    const char *name;
-
-    unsigned int (*winscp_query)(PacketProtocolLayer *ppl, int query);
-};
-
-struct PacketProtocolLayer {
-    const struct PacketProtocolLayerVtable *vt;
-
-    /* Link to the underlying SSH BPP. */
-    BinaryPacketProtocol *bpp;
-
-    /* Queue from which the layer receives its input packets, and one
-     * to put its output packets on. */
-    PktInQueue *in_pq;
-    PktOutQueue *out_pq;
-
-    /* Idempotent callback that in_pq will be linked to, causing a
-     * call to the process_queue method. in_pq points to this, so it
-     * will be automatically triggered by pushing things on the
-     * layer's input queue, but it can also be triggered on purpose. */
-    IdempotentCallback ic_process_queue;
-
-    /* Owner's pointer to this layer. Permits a layer to unilaterally
-     * abdicate in favour of a replacement, by overwriting this
-     * pointer and then freeing itself. */
-    PacketProtocolLayer **selfptr;
-
-    /* Bufchain of keyboard input from the user, for login prompts and
-     * similar. */
-    bufchain *user_input;
-
-    /* Logging and error-reporting facilities. */
-    LogContext *logctx;
-    Seat *seat;             /* for dialog boxes, session output etc */
-    Ssh *ssh;   /* for session termination + assorted connection-layer ops */
-
-    /* Known bugs in the remote implementation. */
-    unsigned remote_bugs;
-};
-
-static inline void ssh_ppl_process_queue(PacketProtocolLayer *ppl)
-{ ppl->vt->process_queue(ppl); }
-static inline bool ssh_ppl_get_specials(
-    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
-{ return ppl->vt->get_specials(ppl, add_special, ctx); }
-static inline void ssh_ppl_special_cmd(
-    PacketProtocolLayer *ppl, SessionSpecialCode code, int arg)
-{ ppl->vt->special_cmd(ppl, code, arg); }
-static inline bool ssh_ppl_want_user_input(PacketProtocolLayer *ppl)
-{ return ppl->vt->want_user_input(ppl); }
-static inline void ssh_ppl_got_user_input(PacketProtocolLayer *ppl)
-{ ppl->vt->got_user_input(ppl); }
-static inline void ssh_ppl_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
-{ ppl->vt->reconfigure(ppl, conf); }
-static inline size_t ssh_ppl_queued_data_size(PacketProtocolLayer *ppl)
-{ return ppl->vt->queued_data_size(ppl); }
-static inline unsigned int ssh_ppl_winscp_query(PacketProtocolLayer *ppl, int query)
-{ return ppl->vt->winscp_query(ppl, query); }
-
-/* ssh_ppl_free is more than just a macro wrapper on the vtable; it
- * does centralised parts of the freeing too. */
-void ssh_ppl_free(PacketProtocolLayer *ppl);
-
-/* Helper routine to point a PPL at its input and output queues. Also
- * sets up the IdempotentCallback on the input queue to trigger a call
- * to process_queue whenever packets are added to it. */
-void ssh_ppl_setup_queues(PacketProtocolLayer *ppl,
-                          PktInQueue *inq, PktOutQueue *outq);
-
-/* Routine a PPL can call to abdicate in favour of a replacement, by
- * overwriting ppl->selfptr. Has the side effect of freeing 'old', so
- * if 'old' actually called this (which is likely) then it should
- * avoid dereferencing itself on return from this function! */
-void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new);
-
-/* Default implementation of queued_data_size, which just adds up the
- * sizes of all the packets in pq_out. A layer can override this if it
- * has other things to take into account as well. */
-size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl);
-
-PacketProtocolLayer *ssh1_login_new(
-    Conf *conf, const char *host, int port,
-    PacketProtocolLayer *successor_layer);
-PacketProtocolLayer *ssh1_connection_new(
-    Ssh *ssh, Conf *conf, ConnectionLayer **cl_out);
-
-struct DataTransferStats;
-struct ssh_connection_shared_gss_state;
-PacketProtocolLayer *ssh2_transport_new(
-    Conf *conf, const char *host, int port, const char *fullhostname,
-    const char *client_greeting, const char *server_greeting,
-    struct ssh_connection_shared_gss_state *shgss,
-    struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
-    const SshServerConfig *ssc);
-PacketProtocolLayer *ssh2_userauth_new(
-    PacketProtocolLayer *successor_layer,
-    const char *hostname, const char *fullhostname,
-    Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth,
-    const char *default_username, bool change_username,
-    bool try_ki_auth,
-    bool try_gssapi_auth, bool try_gssapi_kex_auth,
-    bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss,
-    const char * loghost, bool change_password, Seat *seat); // WINSCP
-PacketProtocolLayer *ssh2_connection_new(
-    Ssh *ssh, ssh_sharing_state *connshare, bool is_simple,
-    Conf *conf, const char *peer_verstring, ConnectionLayer **cl_out);
-
-/* Can't put this in the userauth constructor without having a
- * dependency loop at setup time (transport and userauth can't _both_
- * be constructed second and given a pointer to the other). */
-void ssh2_userauth_set_transport_layer(PacketProtocolLayer *userauth,
-                                       PacketProtocolLayer *transport);
-
-/* Convenience macro for protocol layers to send formatted strings to
- * the Event Log. Assumes a function parameter called 'ppl' is in
- * scope. */
-#define ppl_logevent(...) ( \
-        logevent_and_free((ppl)->logctx, dupprintf(__VA_ARGS__)))
-
-/* Convenience macro for protocol layers to send formatted strings to
- * the terminal. Also expects 'ppl' to be in scope. */
-#define ppl_printf(...) \
-    ssh_ppl_user_output_string_and_free(ppl, dupprintf(__VA_ARGS__))
-void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text);
-
-/* Methods for userauth to communicate back to the transport layer */
-ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ssh2_transport_ptr);
-void ssh2_transport_notify_auth_done(PacketProtocolLayer *ssh2_transport_ptr);
-
-/* Shared method between ssh2 layers (defined in ssh2transport.c) to
- * handle the common packets between login and connection: DISCONNECT,
- * DEBUG and IGNORE. Those messages are handled by the ssh2transport
- * layer if we have one, but in bare ssh2-connection mode they have to
- * be handled by ssh2connection. */
-bool ssh2_common_filter_queue(PacketProtocolLayer *ppl);
-
-/* Methods for ssh1login to pass protocol flags to ssh1connection */
-void ssh1_connection_set_protoflags(
-    PacketProtocolLayer *ppl, int local, int remote);
-
-/* Shared get_specials method between the two ssh1 layers */
-bool ssh1_common_get_specials(PacketProtocolLayer *, add_special_fn_t, void *);
-
-/* Other shared functions between ssh1 layers  */
-bool ssh1_common_filter_queue(PacketProtocolLayer *ppl);
-void ssh1_compute_session_id(
-    unsigned char *session_id, const unsigned char *cookie,
-    RSAKey *hostkey, RSAKey *servkey);
-
-/* Method used by the SSH server */
-void ssh2_transport_provide_hostkeys(PacketProtocolLayer *ssh2_transport_ptr,
-                                     ssh_key *const *hostkeys, int nhostkeys);
-
-#endif /* PUTTY_SSHPPL_H */

+ 0 - 307
source/putty/sshprng.c

@@ -1,307 +0,0 @@
-/*
- * sshprng.c: 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.
- */
-
-#include "putty.h"
-#include "ssh.h"
-#include "mpint_i.h"
-
-#ifdef PRNG_DIAGNOSTICS
-#define prngdebug debug
-#else
-#define prngdebug(...) ((void)0)
-#endif
-
-/*
- * This random number generator is based on the 'Fortuna' design by
- * Niels Ferguson and Bruce Schneier. The biggest difference is that I
- * use SHA-256 in place of a block cipher: the generator side of the
- * system works by computing HASH(key || counter) instead of
- * ENCRYPT(counter, key).
- *
- * Rationale: the Fortuna description itself suggests that using
- * SHA-256 would be nice but people wouldn't accept it because it's
- * too slow - but PuTTY isn't a heavy enough user of random numbers to
- * make that a serious worry. In fact even with SHA-256 this generator
- * is faster than the one we previously used. Also the Fortuna
- * description worries about periodic rekeying to avoid the barely
- * detectable pattern of never repeating a cipher block - but with
- * SHA-256, even that shouldn't be a worry, because the output
- * 'blocks' are twice the size, and also SHA-256 has no guarantee of
- * bijectivity, so it surely _could_ be possible to generate the same
- * block from two counter values. Thirdly, Fortuna has to have a hash
- * function anyway, for reseeding and entropy collection, so reusing
- * the same one means it only depends on one underlying primitive and
- * can be easily reinstantiated with a larger hash function if you
- * decide you'd like to do that on a particular occasion.
- */
-
-#define NCOLLECTORS 32
-#define RESEED_DATA_SIZE 64
-
-typedef struct prng_impl prng_impl;
-struct prng_impl {
-    prng Prng;
-
-    const ssh_hashalg *hashalg;
-
-    /*
-     * Generation side:
-     *
-     * 'generator' is a hash object with the current key preloaded
-     * into it. The counter-mode generation is achieved by copying
-     * that hash object, appending the counter value to the copy, and
-     * calling ssh_hash_final.
-     */
-    ssh_hash *generator;
-    BignumInt counter[128 / BIGNUM_INT_BITS];
-
-    /*
-     * When re-seeding the generator, you call prng_seed_begin(),
-     * which sets up a hash object in 'keymaker'. You write your new
-     * seed data into it (which you can do by calling put_data on the
-     * PRNG object itself) and then call prng_seed_finish(), which
-     * finalises this hash and uses the output to set up the new
-     * generator.
-     *
-     * The keymaker hash preimage includes the previous key, so if you
-     * just want to change keys for the sake of not keeping the same
-     * one for too long, you don't have to put any extra seed data in
-     * at all.
-     */
-    ssh_hash *keymaker;
-
-    /*
-     * Collection side:
-     *
-     * There are NCOLLECTORS hash objects collecting entropy. Each
-     * separately numbered entropy source puts its output into those
-     * hash objects in the order 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,...,
-     * that is to say, each entropy source has a separate counter
-     * which is incremented every time that source generates an event,
-     * and the event data is added to the collector corresponding to
-     * the index of the lowest set bit in the current counter value.
-     *
-     * Whenever collector #0 has at least RESEED_DATA_SIZE bytes (and
-     * it's not at least 100ms since the last reseed), the PRNG is
-     * reseeded, with seed data on reseed #n taken from the first j
-     * collectors, where j is one more than the number of factors of 2
-     * in n. That is, collector #0 is used in every reseed; #1 in
-     * every other one, #2 in every fourth, etc.
-     *
-     * 'until_reseed' counts the amount of data that still needs to be
-     * added to collector #0 before a reseed will be triggered.
-     */
-    uint32_t source_counters[NOISE_MAX_SOURCES];
-    ssh_hash *collectors[NCOLLECTORS];
-    size_t until_reseed;
-    uint32_t reseeds;
-    uint64_t last_reseed_time;
-};
-
-static void prng_seed_BinarySink_write(
-    BinarySink *bs, const void *data, size_t len);
-
-prng *prng_new(const ssh_hashalg *hashalg)
-{
-    prng_impl *pi = snew(prng_impl);
-
-    memset(pi, 0, sizeof(prng_impl));
-    pi->hashalg = hashalg;
-    pi->keymaker = NULL;
-    pi->generator = NULL;
-    memset(pi->counter, 0, sizeof(pi->counter));
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < NCOLLECTORS; i++)
-        pi->collectors[i] = ssh_hash_new(pi->hashalg);
-    pi->until_reseed = 0;
-    BinarySink_INIT(&pi->Prng, prng_seed_BinarySink_write);
-
-    pi->Prng.savesize = pi->hashalg->hlen * 4;
-
-    return &pi->Prng;
-    } // WINSCP
-}
-
-void prng_free(prng *pr)
-{
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-
-    smemclr(pi->counter, sizeof(pi->counter));
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < NCOLLECTORS; i++)
-        ssh_hash_free(pi->collectors[i]);
-    if (pi->generator)
-        ssh_hash_free(pi->generator);
-    if (pi->keymaker)
-        ssh_hash_free(pi->keymaker);
-    smemclr(pi, sizeof(*pi));
-    sfree(pi);
-    } // WINSCP
-}
-
-void prng_seed_begin(prng *pr)
-{
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-
-    assert(!pi->keymaker);
-
-    prngdebug("prng: reseed begin\n");
-
-    /*
-     * Make a hash instance that will generate the key for the new one.
-     */
-    if (pi->generator) {
-        pi->keymaker = pi->generator;
-        pi->generator = NULL;
-    } else {
-        pi->keymaker = ssh_hash_new(pi->hashalg);
-    }
-
-    put_byte(pi->keymaker, 'R');
-}
-
-static void prng_seed_BinarySink_write(
-    BinarySink *bs, const void *data, size_t len)
-{
-    prng *pr = BinarySink_DOWNCAST(bs, prng);
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-    assert(pi->keymaker);
-    prngdebug("prng: got %"SIZEu" bytes of seed\n", len);
-    put_data(pi->keymaker, data, len);
-}
-
-void prng_seed_finish(prng *pr)
-{
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-    unsigned char buf[MAX_HASH_LEN];
-
-    assert(pi->keymaker);
-
-    prngdebug("prng: reseed finish\n");
-
-    /*
-     * Actually generate the key.
-     */
-    ssh_hash_final(pi->keymaker, buf);
-    pi->keymaker = NULL;
-
-    /*
-     * Load that key into a fresh hash instance, which will become the
-     * new generator.
-     */
-    assert(!pi->generator);
-    pi->generator = ssh_hash_new(pi->hashalg);
-    put_data(pi->generator, buf, pi->hashalg->hlen);
-
-    pi->until_reseed = RESEED_DATA_SIZE;
-    pi->last_reseed_time = prng_reseed_time_ms();
-
-    smemclr(buf, sizeof(buf));
-}
-
-static inline void prng_generate(prng_impl *pi, void *outbuf)
-{
-    ssh_hash *h = ssh_hash_copy(pi->generator);
-
-    prngdebug("prng_generate\n");
-    put_byte(h, 'G');
-    { // WINSCP
-    unsigned i; // WINSCP
-    for (i = 0; i < 128; i += 8)
-        put_byte(h, pi->counter[i/BIGNUM_INT_BITS] >> (i%BIGNUM_INT_BITS));
-    { // WINSCP
-    BignumCarry c = 1;
-    for (i = 0; i < lenof(pi->counter); i++)
-        BignumADC(pi->counter[i], c, pi->counter[i], 0, c);
-    ssh_hash_final(h, outbuf);
-    } // WINSCP
-    } // WINSCP
-}
-
-void prng_read(prng *pr, void *vout, size_t size)
-{
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-    unsigned char buf[MAX_HASH_LEN];
-
-    assert(!pi->keymaker);
-
-    prngdebug("prng_read %"SIZEu"\n", size);
-
-    { // WINSCP
-    uint8_t *out = (uint8_t *)vout;
-    while (size > 0) {
-        prng_generate(pi, buf);
-        { // WINSCP
-        size_t to_use = size > pi->hashalg->hlen ? pi->hashalg->hlen : size;
-        memcpy(out, buf, to_use);
-        out += to_use;
-        size -= to_use;
-        } // WINSCP
-    }
-
-    smemclr(buf, sizeof(buf));
-
-    prng_seed_begin(&pi->Prng);
-    prng_seed_finish(&pi->Prng);
-    } // WINSCP
-}
-
-void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data)
-{
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-
-    pinitassert(source_id < NOISE_MAX_SOURCES);
-    uint32_t counter = ++pi->source_counters[source_id];
-
-    size_t index = 0;
-    while (index+1 < NCOLLECTORS && !(counter & 1)) {
-        counter >>= 1;
-        index++;
-    }
-
-    prngdebug("prng_add_entropy source=%u size=%"SIZEu" -> collector %zi\n",
-              source_id, data.len, index);
-
-    put_datapl(pi->collectors[index], data);
-
-    if (index == 0)
-        pi->until_reseed = (pi->until_reseed < data.len ? 0 :
-                            pi->until_reseed - data.len);
-
-    if (pi->until_reseed == 0 &&
-        prng_reseed_time_ms() - pi->last_reseed_time >= 100) {
-        prng_seed_begin(&pi->Prng);
-
-        { // WINSCP
-        unsigned char buf[MAX_HASH_LEN];
-        uint32_t reseed_index = ++pi->reseeds;
-        prngdebug("prng entropy reseed #%"PRIu32"\n", reseed_index);
-        { // WINSCP
-        size_t i; // WINSCP
-        for (i = 0; i < NCOLLECTORS; i++) {
-            prngdebug("emptying collector %"SIZEu"\n", i);
-            ssh_hash_digest(pi->collectors[i], buf);
-            put_data(&pi->Prng, buf, pi->hashalg->hlen);
-            ssh_hash_reset(pi->collectors[i]);
-            if (reseed_index & 1)
-                break;
-            reseed_index >>= 1;
-        }
-        smemclr(buf, sizeof(buf));
-        prng_seed_finish(&pi->Prng);
-        } // WINSCP
-        } // WINSCP
-    }
-}
-
-size_t prng_seed_bits(prng *pr)
-{
-    prng_impl *pi = container_of(pr, prng_impl, Prng);
-    return pi->hashalg->hlen * 8;
-}

+ 23 - 23
source/putty/sshpubk.c

@@ -609,7 +609,7 @@ const ssh_keyalg *const all_keyalgs[] = {
     &ssh_rsa,
     &ssh_rsa_sha256,
     &ssh_rsa_sha512,
-    &ssh_dss,
+    &ssh_dsa,
     &ssh_ecdsa_nistp256,
     &ssh_ecdsa_nistp384,
     &ssh_ecdsa_nistp521,
@@ -1620,36 +1620,36 @@ strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase,
 
     { // WINSCP
     strbuf *out = strbuf_new_nm();
-    strbuf_catf(out, "PuTTY-User-Key-File-%u: %s\n",
-                params.fmt_version, ssh_key_ssh_id(key->key));
-    strbuf_catf(out, "Encryption: %s\n", cipherstr);
-    strbuf_catf(out, "Comment: %s\n", key->comment);
-    strbuf_catf(out, "Public-Lines: %d\n", base64_lines(pub_blob->len));
+    put_fmt(out, "PuTTY-User-Key-File-%u: %s\n",
+            params.fmt_version, ssh_key_ssh_id(key->key));
+    put_fmt(out, "Encryption: %s\n", cipherstr);
+    put_fmt(out, "Comment: %s\n", key->comment);
+    put_fmt(out, "Public-Lines: %d\n", base64_lines(pub_blob->len));
     base64_encode_s(BinarySink_UPCAST(out), pub_blob->u, pub_blob->len, 64);
     if (params.fmt_version == 3 && ciphertype->keylen != 0) {
-        strbuf_catf(out, "Key-Derivation: %s\n",
-                    params.argon2_flavour == Argon2d ? "Argon2d" :
-                    params.argon2_flavour == Argon2i ? "Argon2i" : "Argon2id");
-        strbuf_catf(out, "Argon2-Memory: %"PRIu32"\n", params.argon2_mem);
+        put_fmt(out, "Key-Derivation: %s\n",
+                params.argon2_flavour == Argon2d ? "Argon2d" :
+                params.argon2_flavour == Argon2i ? "Argon2i" : "Argon2id");
+        put_fmt(out, "Argon2-Memory: %"PRIu32"\n", params.argon2_mem);
         assert(!params.argon2_passes_auto);
-        strbuf_catf(out, "Argon2-Passes: %"PRIu32"\n", params.argon2_passes);
-        strbuf_catf(out, "Argon2-Parallelism: %"PRIu32"\n",
-                    params.argon2_parallelism);
-        strbuf_catf(out, "Argon2-Salt: ");
+        put_fmt(out, "Argon2-Passes: %"PRIu32"\n", params.argon2_passes);
+        put_fmt(out, "Argon2-Parallelism: %"PRIu32"\n",
+                params.argon2_parallelism);
+        put_fmt(out, "Argon2-Salt: ");
         { // WINSCP
         size_t i;
         for (i = 0; i < passphrase_salt->len; i++)
-            strbuf_catf(out, "%02x", passphrase_salt->u[i]);
+            put_fmt(out, "%02x", passphrase_salt->u[i]);
+        put_fmt(out, "\n");
         } // WINSCP
-        strbuf_catf(out, "\n");
     }
-    strbuf_catf(out, "Private-Lines: %d\n", base64_lines(priv_encrypted_len));
+    put_fmt(out, "Private-Lines: %d\n", base64_lines(priv_encrypted_len));
     base64_encode_s(BinarySink_UPCAST(out),
                     priv_blob_encrypted, priv_encrypted_len, 64);
-    strbuf_catf(out, "Private-MAC: ");
+    put_fmt(out, "Private-MAC: ");
     for (i = 0; i < macalg->len; i++)
-        strbuf_catf(out, "%02x", priv_mac[i]);
-    strbuf_catf(out, "\n");
+        put_fmt(out, "%02x", priv_mac[i]);
+    put_fmt(out, "\n");
 
     strbuf_free(cipher_mac_keys_blob);
     strbuf_free(passphrase_salt);
@@ -1819,7 +1819,7 @@ static void ssh2_fingerprint_blob_md5(ptrlen blob, strbuf *sb)
     unsigned i; // WINSCP
     hash_simple(&ssh_md5, blob, digest);
     for (i = 0; i < 16; i++)
-        strbuf_catf(sb, "%02x%s", digest[i], i==15 ? "" : ":");
+        put_fmt(sb, "%02x%s", digest[i], i==15 ? "" : ":");
 }
 
 static void ssh2_fingerprint_blob_sha256(ptrlen blob, strbuf *sb)
@@ -1861,9 +1861,9 @@ char *ssh2_fingerprint_blob(ptrlen blob, FingerprintType fptype)
         const ssh_keyalg *alg = find_pubkey_alg_len(algname);
         if (alg) {
             int bits = ssh_key_public_bits(alg, blob);
-            strbuf_catf(sb, "%.*s %d ", PTRLEN_PRINTF(algname), bits);
+            put_fmt(sb, "%.*s %d ", PTRLEN_PRINTF(algname), bits);
         } else {
-            strbuf_catf(sb, "%.*s ", PTRLEN_PRINTF(algname));
+            put_fmt(sb, "%.*s ", PTRLEN_PRINTF(algname));
         }
     }
     } // WINSCP

+ 0 - 1147
source/putty/sshrsa.c

@@ -1,1147 +0,0 @@
-/*
- * RSA implementation for PuTTY.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "ssh.h"
-#include "mpint.h"
-#include "misc.h"
-
-void BinarySource_get_rsa_ssh1_pub(
-    BinarySource *src, RSAKey *rsa, RsaSsh1Order order)
-{
-    unsigned bits;
-    mp_int *e, *m;
-
-    bits = get_uint32(src);
-    if (order == RSA_SSH1_EXPONENT_FIRST) {
-        e = get_mp_ssh1(src);
-        m = get_mp_ssh1(src);
-    } else {
-        m = get_mp_ssh1(src);
-        e = get_mp_ssh1(src);
-    }
-
-    if (rsa) {
-        rsa->bits = bits;
-        rsa->exponent = e;
-        rsa->modulus = m;
-        rsa->bytes = (mp_get_nbits(m) + 7) / 8;
-    } else {
-        mp_free(e);
-        mp_free(m);
-    }
-}
-
-void BinarySource_get_rsa_ssh1_priv(
-    BinarySource *src, RSAKey *rsa)
-{
-    rsa->private_exponent = get_mp_ssh1(src);
-}
-
-key_components *rsa_components(RSAKey *rsa)
-{
-    key_components *kc = key_components_new();
-    key_components_add_text(kc, "key_type", "RSA");
-    key_components_add_mp(kc, "public_modulus", rsa->modulus);
-    key_components_add_mp(kc, "public_exponent", rsa->exponent);
-    if (rsa->private_exponent) {
-        key_components_add_mp(kc, "private_exponent", rsa->private_exponent);
-        key_components_add_mp(kc, "private_p", rsa->p);
-        key_components_add_mp(kc, "private_q", rsa->q);
-        key_components_add_mp(kc, "private_inverse_q_mod_p", rsa->iqmp);
-    }
-    return kc;
-}
-
-RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src)
-{
-    RSAKey *rsa = snew(RSAKey);
-    memset(rsa, 0, sizeof(RSAKey));
-
-    get_rsa_ssh1_pub(src, rsa, RSA_SSH1_MODULUS_FIRST);
-    get_rsa_ssh1_priv(src, rsa);
-
-    /* SSH-1 names p and q the other way round, i.e. we have the
-     * inverse of p mod q and not of q mod p. We swap the names,
-     * because our internal RSA wants iqmp. */
-    rsa->iqmp = get_mp_ssh1(src);
-    rsa->q = get_mp_ssh1(src);
-    rsa->p = get_mp_ssh1(src);
-
-    return rsa;
-}
-
-bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key)
-{
-    mp_int *b1, *b2;
-    int i;
-    unsigned char *p;
-
-    if (key->bytes < length + 4)
-        return false;                  /* RSA key too short! */
-
-    memmove(data + key->bytes - length, data, length);
-    data[0] = 0;
-    data[1] = 2;
-
-    { // WINSCP
-    size_t npad = key->bytes - length - 3;
-    /*
-     * Generate a sequence of nonzero padding bytes. We do this in a
-     * reasonably uniform way and without having to loop round
-     * retrying the random number generation, by first generating an
-     * integer in [0,2^n) for an appropriately large n; then we
-     * repeatedly multiply by 255 to give an integer in [0,255*2^n),
-     * extract the top 8 bits to give an integer in [0,255), and mask
-     * those bits off before multiplying up again for the next digit.
-     * This gives us a sequence of numbers in [0,255), and of course
-     * adding 1 to each of them gives numbers in [1,256) as we wanted.
-     *
-     * (You could imagine this being a sort of fixed-point operation:
-     * given a uniformly random binary _fraction_, multiplying it by k
-     * and subtracting off the integer part will yield you a sequence
-     * of integers each in [0,k). I'm just doing that scaled up by a
-     * power of 2 to avoid the fractions.)
-     */
-    size_t random_bits = (npad + 16) * 8;
-    mp_int *randval = mp_new(random_bits + 8);
-    mp_int *tmp = mp_random_bits(random_bits);
-    mp_copy_into(randval, tmp);
-    mp_free(tmp);
-    for (i = 2; i < key->bytes - length - 1; i++) {
-        mp_mul_integer_into(randval, randval, 255);
-        { // WINSCP
-        uint8_t byte = mp_get_byte(randval, random_bits / 8);
-        assert(byte != 255);
-        data[i] = byte + 1;
-        mp_reduce_mod_2to(randval, random_bits);
-        } // WINSCP
-    }
-    mp_free(randval);
-    data[key->bytes - length - 1] = 0;
-
-    b1 = mp_from_bytes_be(make_ptrlen(data, key->bytes));
-
-    b2 = mp_modpow(b1, key->exponent, key->modulus);
-
-    p = data;
-    for (i = key->bytes; i--;) {
-        *p++ = mp_get_byte(b2, i);
-    }
-
-    mp_free(b1);
-    mp_free(b2);
-
-    return true;
-    } // WINSCP
-}
-
-/*
- * Compute (base ^ exp) % mod, provided mod == p * q, with p,q
- * distinct primes, and iqmp is the multiplicative inverse of q mod p.
- * Uses Chinese Remainder Theorem to speed computation up over the
- * obvious implementation of a single big modpow.
- */
-static mp_int *crt_modpow(mp_int *base, mp_int *exp, mp_int *mod,
-                          mp_int *p, mp_int *q, mp_int *iqmp)
-{
-    mp_int *pm1, *qm1, *pexp, *qexp, *presult, *qresult;
-    mp_int *diff, *multiplier, *ret0, *ret;
-
-    /*
-     * Reduce the exponent mod phi(p) and phi(q), to save time when
-     * exponentiating mod p and mod q respectively. Of course, since p
-     * and q are prime, phi(p) == p-1 and similarly for q.
-     */
-    pm1 = mp_copy(p);
-    mp_sub_integer_into(pm1, pm1, 1);
-    qm1 = mp_copy(q);
-    mp_sub_integer_into(qm1, qm1, 1);
-    pexp = mp_mod(exp, pm1);
-    qexp = mp_mod(exp, qm1);
-
-    /*
-     * Do the two modpows.
-     */
-    { // WINSCP
-    mp_int *base_mod_p = mp_mod(base, p);
-    presult = mp_modpow(base_mod_p, pexp, p);
-    mp_free(base_mod_p);
-    } // WINSCP
-    { // WINSCP
-    mp_int *base_mod_q = mp_mod(base, q);
-    qresult = mp_modpow(base_mod_q, qexp, q);
-    mp_free(base_mod_q);
-    } // WINSCP
-
-    /*
-     * Recombine the results. We want a value which is congruent to
-     * qresult mod q, and to presult mod p.
-     *
-     * We know that iqmp * q is congruent to 1 * mod p (by definition
-     * of iqmp) and to 0 mod q (obviously). So we start with qresult
-     * (which is congruent to qresult mod both primes), and add on
-     * (presult-qresult) * (iqmp * q) which adjusts it to be congruent
-     * to presult mod p without affecting its value mod q.
-     *
-     * (If presult-qresult < 0, we add p to it to keep it positive.)
-     */
-    { // WINSCP
-    unsigned presult_too_small = mp_cmp_hs(qresult, presult);
-    mp_cond_add_into(presult, presult, p, presult_too_small);
-    } // WINSCP
-
-    diff = mp_sub(presult, qresult);
-    multiplier = mp_mul(iqmp, q);
-    ret0 = mp_mul(multiplier, diff);
-    mp_add_into(ret0, ret0, qresult);
-
-    /*
-     * Finally, reduce the result mod n.
-     */
-    ret = mp_mod(ret0, mod);
-
-    /*
-     * Free all the intermediate results before returning.
-     */
-    mp_free(pm1);
-    mp_free(qm1);
-    mp_free(pexp);
-    mp_free(qexp);
-    mp_free(presult);
-    mp_free(qresult);
-    mp_free(diff);
-    mp_free(multiplier);
-    mp_free(ret0);
-
-    return ret;
-}
-
-/*
- * Wrapper on crt_modpow that looks up all the right values from an
- * RSAKey.
- */
-static mp_int *rsa_privkey_op(mp_int *input, RSAKey *key)
-{
-    return crt_modpow(input, key->private_exponent,
-                      key->modulus, key->p, key->q, key->iqmp);
-}
-
-mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key)
-{
-    return rsa_privkey_op(input, key);
-}
-
-bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key,
-                            strbuf *outbuf)
-{
-    strbuf *data = strbuf_new_nm();
-    bool success = false;
-    BinarySource src[1];
-
-    {
-        mp_int *b = rsa_ssh1_decrypt(input, key);
-        size_t i; // WINSCP
-        for (i = (mp_get_nbits(key->modulus) + 7) / 8; i-- > 0 ;) {
-            put_byte(data, mp_get_byte(b, i));
-        }
-        mp_free(b);
-    }
-
-    BinarySource_BARE_INIT(src, data->u, data->len);
-
-    /* Check PKCS#1 formatting prefix */
-    if (get_byte(src) != 0) goto out;
-    if (get_byte(src) != 2) goto out;
-    while (1) {
-        unsigned char byte = get_byte(src);
-        if (get_err(src)) goto out;
-        if (byte == 0)
-            break;
-    }
-
-    /* Everything else is the payload */
-    success = true;
-    put_data(outbuf, get_ptr(src), get_avail(src));
-
-  out:
-    strbuf_free(data);
-    return success;
-}
-
-static void append_hex_to_strbuf(strbuf *sb, mp_int *x)
-{
-    if (sb->len > 0)
-        put_byte(sb, ',');
-    put_data(sb, "0x", 2);
-    { // WINSCP
-    char *hex = mp_get_hex(x);
-    size_t hexlen = strlen(hex);
-    put_data(sb, hex, hexlen);
-    smemclr(hex, hexlen);
-    sfree(hex);
-    } // WINSCP
-}
-
-char *rsastr_fmt(RSAKey *key)
-{
-    strbuf *sb = strbuf_new();
-
-    append_hex_to_strbuf(sb, key->exponent);
-    append_hex_to_strbuf(sb, key->modulus);
-
-    return strbuf_to_str(sb);
-}
-
-/*
- * Generate a fingerprint string for the key. Compatible with the
- * OpenSSH fingerprint code.
- */
-char *rsa_ssh1_fingerprint(RSAKey *key)
-{
-    unsigned char digest[16];
-    strbuf *out;
-    int i;
-
-    /*
-     * The hash preimage for SSH-1 key fingerprinting consists of the
-     * modulus and exponent _without_ any preceding length field -
-     * just the minimum number of bytes to represent each integer,
-     * stored big-endian, concatenated with no marker at the division
-     * between them.
-     */
-
-    ssh_hash *hash = ssh_hash_new(&ssh_md5);
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = (mp_get_nbits(key->modulus) + 7) / 8; i-- > 0 ;)
-        put_byte(hash, mp_get_byte(key->modulus, i));
-    for (i = (mp_get_nbits(key->exponent) + 7) / 8; i-- > 0 ;)
-        put_byte(hash, mp_get_byte(key->exponent, i));
-    } // WINSCP
-    ssh_hash_final(hash, digest);
-
-    out = strbuf_new();
-    strbuf_catf(out, "%"SIZEu" ", mp_get_nbits(key->modulus));
-    for (i = 0; i < 16; i++)
-        strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]);
-    if (key->comment)
-        strbuf_catf(out, " %s", key->comment);
-    return strbuf_to_str(out);
-}
-
-/*
- * Wrap the output of rsa_ssh1_fingerprint up into the same kind of
- * structure that comes from ssh2_all_fingerprints.
- */
-char **rsa_ssh1_fake_all_fingerprints(RSAKey *key)
-{
-    char **ret = snewn(SSH_N_FPTYPES, char *);
-    unsigned i; // WINSCP
-    for (i = 0; i < SSH_N_FPTYPES; i++)
-        ret[i] = NULL;
-    ret[SSH_FPTYPE_MD5] = rsa_ssh1_fingerprint(key);
-    return ret;
-}
-
-/*
- * Verify that the public data in an RSA key matches the private
- * data. We also check the private data itself: we ensure that p >
- * q and that iqmp really is the inverse of q mod p.
- */
-bool rsa_verify(RSAKey *key)
-{
-    mp_int *n, *ed, *pm1, *qm1;
-    unsigned ok = 1;
-
-    /* Preliminary checks: p,q can't be 0 or 1. (Of course no other
-     * very small value is any good either, but these are the values
-     * we _must_ check for to avoid assertion failures further down
-     * this function.) */
-    if (!(mp_hs_integer(key->p, 2) & mp_hs_integer(key->q, 2)))
-        return false;
-
-    /* n must equal pq. */
-    n = mp_mul(key->p, key->q);
-    ok &= mp_cmp_eq(n, key->modulus);
-    mp_free(n);
-
-    /* e * d must be congruent to 1, modulo (p-1) and modulo (q-1). */
-    pm1 = mp_copy(key->p);
-    mp_sub_integer_into(pm1, pm1, 1);
-    ed = mp_modmul(key->exponent, key->private_exponent, pm1);
-    mp_free(pm1);
-    ok &= mp_eq_integer(ed, 1);
-    mp_free(ed);
-
-    qm1 = mp_copy(key->q);
-    mp_sub_integer_into(qm1, qm1, 1);
-    ed = mp_modmul(key->exponent, key->private_exponent, qm1);
-    mp_free(qm1);
-    ok &= mp_eq_integer(ed, 1);
-    mp_free(ed);
-
-    /*
-     * Ensure p > q.
-     *
-     * I have seen key blobs in the wild which were generated with
-     * p < q, so instead of rejecting the key in this case we
-     * should instead flip them round into the canonical order of
-     * p > q. This also involves regenerating iqmp.
-     */
-    { // WINSCP
-    mp_int *p_new = mp_max(key->p, key->q);
-    mp_int *q_new = mp_min(key->p, key->q);
-    mp_free(key->p);
-    mp_free(key->q);
-    mp_free(key->iqmp);
-    key->p = p_new;
-    key->q = q_new;
-    key->iqmp = mp_invert(key->q, key->p);
-
-    return ok;
-    } // WINSCP
-}
-
-void rsa_ssh1_public_blob(BinarySink *bs, RSAKey *key,
-                          RsaSsh1Order order)
-{
-    put_uint32(bs, mp_get_nbits(key->modulus));
-    if (order == RSA_SSH1_EXPONENT_FIRST) {
-        put_mp_ssh1(bs, key->exponent);
-        put_mp_ssh1(bs, key->modulus);
-    } else {
-        put_mp_ssh1(bs, key->modulus);
-        put_mp_ssh1(bs, key->exponent);
-    }
-}
-
-void rsa_ssh1_private_blob_agent(BinarySink *bs, RSAKey *key)
-{
-    rsa_ssh1_public_blob(bs, key, RSA_SSH1_MODULUS_FIRST);
-    put_mp_ssh1(bs, key->private_exponent);
-    put_mp_ssh1(bs, key->iqmp);
-    put_mp_ssh1(bs, key->q);
-    put_mp_ssh1(bs, key->p);
-}
-
-/* Given an SSH-1 public key blob, determine its length. */
-int rsa_ssh1_public_blob_len(ptrlen data)
-{
-    BinarySource src[1];
-
-    BinarySource_BARE_INIT_PL(src, data);
-
-    /* Expect a length word, then exponent and modulus. (It doesn't
-     * even matter which order.) */
-    get_uint32(src);
-    mp_free(get_mp_ssh1(src));
-    mp_free(get_mp_ssh1(src));
-
-    if (get_err(src))
-        return -1;
-
-    /* Return the number of bytes consumed. */
-    return src->pos;
-}
-
-void freersapriv(RSAKey *key)
-{
-    if (key->private_exponent) {
-        mp_free(key->private_exponent);
-        key->private_exponent = NULL;
-    }
-    if (key->p) {
-        mp_free(key->p);
-        key->p = NULL;
-    }
-    if (key->q) {
-        mp_free(key->q);
-        key->q = NULL;
-    }
-    if (key->iqmp) {
-        mp_free(key->iqmp);
-        key->iqmp = NULL;
-    }
-}
-
-void freersakey(RSAKey *key)
-{
-    freersapriv(key);
-    if (key->modulus) {
-        mp_free(key->modulus);
-        key->modulus = NULL;
-    }
-    if (key->exponent) {
-        mp_free(key->exponent);
-        key->exponent = NULL;
-    }
-    if (key->comment) {
-        sfree(key->comment);
-        key->comment = NULL;
-    }
-}
-
-/* ----------------------------------------------------------------------
- * Implementation of the ssh-rsa signing key type family.
- */
-
-struct ssh2_rsa_extra {
-    unsigned signflags;
-};
-
-static void rsa2_freekey(ssh_key *key);   /* forward reference */
-
-static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data)
-{
-    BinarySource src[1];
-    RSAKey *rsa;
-
-    BinarySource_BARE_INIT_PL(src, data);
-    if (!ptrlen_eq_string(get_string(src), "ssh-rsa"))
-        return NULL;
-
-    rsa = snew(RSAKey);
-    rsa->sshk.vt = self;
-    rsa->exponent = get_mp_ssh2(src);
-    rsa->modulus = get_mp_ssh2(src);
-    rsa->private_exponent = NULL;
-    rsa->p = rsa->q = rsa->iqmp = NULL;
-    rsa->comment = NULL;
-
-    if (get_err(src)) {
-        rsa2_freekey(&rsa->sshk);
-        return NULL;
-    }
-
-    return &rsa->sshk;
-}
-
-static void rsa2_freekey(ssh_key *key)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-    freersakey(rsa);
-    sfree(rsa);
-}
-
-static char *rsa2_cache_str(ssh_key *key)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-    return rsastr_fmt(rsa);
-}
-
-static key_components *rsa2_components(ssh_key *key)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-    return rsa_components(rsa);
-}
-
-static void rsa2_public_blob(ssh_key *key, BinarySink *bs)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-
-    put_stringz(bs, "ssh-rsa");
-    put_mp_ssh2(bs, rsa->exponent);
-    put_mp_ssh2(bs, rsa->modulus);
-}
-
-static void rsa2_private_blob(ssh_key *key, BinarySink *bs)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-
-    put_mp_ssh2(bs, rsa->private_exponent);
-    put_mp_ssh2(bs, rsa->p);
-    put_mp_ssh2(bs, rsa->q);
-    put_mp_ssh2(bs, rsa->iqmp);
-}
-
-static ssh_key *rsa2_new_priv(const ssh_keyalg *self,
-                               ptrlen pub, ptrlen priv)
-{
-    BinarySource src[1];
-    ssh_key *sshk;
-    RSAKey *rsa;
-
-    sshk = rsa2_new_pub(self, pub);
-    if (!sshk)
-        return NULL;
-
-    rsa = container_of(sshk, RSAKey, sshk);
-    BinarySource_BARE_INIT_PL(src, priv);
-    rsa->private_exponent = get_mp_ssh2(src);
-    rsa->p = get_mp_ssh2(src);
-    rsa->q = get_mp_ssh2(src);
-    rsa->iqmp = get_mp_ssh2(src);
-
-    if (get_err(src) || !rsa_verify(rsa)) {
-        rsa2_freekey(&rsa->sshk);
-        return NULL;
-    }
-
-    return &rsa->sshk;
-}
-
-static ssh_key *rsa2_new_priv_openssh(const ssh_keyalg *self,
-                                      BinarySource *src)
-{
-    RSAKey *rsa;
-
-    rsa = snew(RSAKey);
-    rsa->sshk.vt = &ssh_rsa;
-    rsa->comment = NULL;
-
-    rsa->modulus = get_mp_ssh2(src);
-    rsa->exponent = get_mp_ssh2(src);
-    rsa->private_exponent = get_mp_ssh2(src);
-    rsa->iqmp = get_mp_ssh2(src);
-    rsa->p = get_mp_ssh2(src);
-    rsa->q = get_mp_ssh2(src);
-
-    if (get_err(src) || !rsa_verify(rsa)) {
-        rsa2_freekey(&rsa->sshk);
-        return NULL;
-    }
-
-    return &rsa->sshk;
-}
-
-static void rsa2_openssh_blob(ssh_key *key, BinarySink *bs)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-
-    put_mp_ssh2(bs, rsa->modulus);
-    put_mp_ssh2(bs, rsa->exponent);
-    put_mp_ssh2(bs, rsa->private_exponent);
-    put_mp_ssh2(bs, rsa->iqmp);
-    put_mp_ssh2(bs, rsa->p);
-    put_mp_ssh2(bs, rsa->q);
-}
-
-static int rsa2_pubkey_bits(const ssh_keyalg *self, ptrlen pub)
-{
-    ssh_key *sshk;
-    RSAKey *rsa;
-    int ret;
-
-    sshk = rsa2_new_pub(self, pub);
-    if (!sshk)
-        return -1;
-
-    rsa = container_of(sshk, RSAKey, sshk);
-    ret = mp_get_nbits(rsa->modulus);
-    rsa2_freekey(&rsa->sshk);
-
-    return ret;
-}
-
-static inline const ssh_hashalg *rsa2_hash_alg_for_flags(
-    unsigned flags, const char **protocol_id_out)
-{
-    const ssh_hashalg *halg;
-    const char *protocol_id;
-
-    if (flags & SSH_AGENT_RSA_SHA2_256) {
-        halg = &ssh_sha256;
-        protocol_id = "rsa-sha2-256";
-    } else if (flags & SSH_AGENT_RSA_SHA2_512) {
-        halg = &ssh_sha512;
-        protocol_id = "rsa-sha2-512";
-    } else {
-        halg = &ssh_sha1;
-        protocol_id = "ssh-rsa";
-    }
-
-    if (protocol_id_out)
-        *protocol_id_out = protocol_id;
-
-    return halg;
-}
-
-static inline ptrlen rsa_pkcs1_prefix_for_hash(const ssh_hashalg *halg)
-{
-    if (halg == &ssh_sha1) {
-        /*
-         * This is the magic ASN.1/DER prefix that goes in the decoded
-         * signature, between the string of FFs and the actual SHA-1
-         * hash value. The meaning of it is:
-         *
-         * 00 -- this marks the end of the FFs; not part of the ASN.1
-         * bit itself
-         *
-         * 30 21 -- a constructed SEQUENCE of length 0x21
-         *    30 09 -- a constructed sub-SEQUENCE of length 9
-         *       06 05 -- an object identifier, length 5
-         *          2B 0E 03 02 1A -- object id { 1 3 14 3 2 26 }
-         *                            (the 1,3 comes from 0x2B = 43 = 40*1+3)
-         *       05 00 -- NULL
-         *    04 14 -- a primitive OCTET STRING of length 0x14
-         *       [0x14 bytes of hash data follows]
-         *
-         * The object id in the middle there is listed as `id-sha1' in
-         * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1d2.asn
-         * (the ASN module for PKCS #1) and its expanded form is as
-         * follows:
-         *
-         * id-sha1                OBJECT IDENTIFIER ::= {
-         *    iso(1) identified-organization(3) oiw(14) secsig(3)
-         *    algorithms(2) 26 }
-         */
-        static const unsigned char sha1_asn1_prefix[] = {
-            0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B,
-            0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14,
-        };
-        return PTRLEN_FROM_CONST_BYTES(sha1_asn1_prefix);
-    }
-
-    if (halg == &ssh_sha256) {
-        /*
-         * A similar piece of ASN.1 used for signatures using SHA-256,
-         * in the same format but differing only in various length
-         * fields and OID.
-         */
-        static const unsigned char sha256_asn1_prefix[] = {
-            0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
-            0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
-            0x05, 0x00, 0x04, 0x20,
-        };
-        return PTRLEN_FROM_CONST_BYTES(sha256_asn1_prefix);
-    }
-
-    if (halg == &ssh_sha512) {
-        /*
-         * And one more for SHA-512.
-         */
-        static const unsigned char sha512_asn1_prefix[] = {
-            0x00, 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
-            0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
-            0x05, 0x00, 0x04, 0x40,
-        };
-        return PTRLEN_FROM_CONST_BYTES(sha512_asn1_prefix);
-    }
-
-    unreachable("bad hash algorithm for RSA PKCS#1");
-}
-
-static inline size_t rsa_pkcs1_length_of_fixed_parts(const ssh_hashalg *halg)
-{
-    ptrlen asn1_prefix = rsa_pkcs1_prefix_for_hash(halg);
-    return halg->hlen + asn1_prefix.len + 2;
-}
-
-static unsigned char *rsa_pkcs1_signature_string(
-    size_t nbytes, const ssh_hashalg *halg, ptrlen data)
-{
-    size_t fixed_parts = rsa_pkcs1_length_of_fixed_parts(halg);
-    pinitassert(nbytes >= fixed_parts);
-    size_t padding = nbytes - fixed_parts;
-
-    ptrlen asn1_prefix = rsa_pkcs1_prefix_for_hash(halg);
-
-    unsigned char *bytes = snewn(nbytes, unsigned char);
-
-    bytes[0] = 0;
-    bytes[1] = 1;
-
-    memset(bytes + 2, 0xFF, padding);
-
-    memcpy(bytes + 2 + padding, asn1_prefix.ptr, asn1_prefix.len);
-
-    { // WINSCP
-    ssh_hash *h = ssh_hash_new(halg);
-    put_datapl(h, data);
-    ssh_hash_final(h, bytes + 2 + padding + asn1_prefix.len);
-    } // WINSCP
-
-    return bytes;
-}
-
-static bool rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-    BinarySource src[1];
-    ptrlen type, in_pl;
-    mp_int *in, *out;
-
-    const struct ssh2_rsa_extra *extra =
-        (const struct ssh2_rsa_extra *)key->vt->extra;
-
-    const ssh_hashalg *halg = rsa2_hash_alg_for_flags(extra->signflags, NULL);
-
-    /* Start by making sure the key is even long enough to encode a
-     * signature. If not, everything fails to verify. */
-    size_t nbytes = (mp_get_nbits(rsa->modulus) + 7) / 8;
-    if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg))
-        return false;
-
-    BinarySource_BARE_INIT_PL(src, sig);
-    type = get_string(src);
-    /*
-     * RFC 4253 section 6.6: the signature integer in an ssh-rsa
-     * signature is 'without lengths or padding'. That is, we _don't_
-     * expect the usual leading zero byte if the topmost bit of the
-     * first byte is set. (However, because of the possibility of
-     * BUG_SSH2_RSA_PADDING at the other end, we tolerate it if it's
-     * there.) So we can't use get_mp_ssh2, which enforces that
-     * leading-byte scheme; instead we use get_string and
-     * mp_from_bytes_be, which will tolerate anything.
-     */
-    in_pl = get_string(src);
-    if (get_err(src) || !ptrlen_eq_string(type, key->vt->ssh_id))
-        return false;
-
-    in = mp_from_bytes_be(in_pl);
-    out = mp_modpow(in, rsa->exponent, rsa->modulus);
-    mp_free(in);
-
-
-    { // WINSCP
-    unsigned diff = 0;
-
-    unsigned char *bytes = rsa_pkcs1_signature_string(nbytes, halg, data);
-    size_t i; // WINSCP
-    for (i = 0; i < nbytes; i++)
-        diff |= bytes[nbytes-1 - i] ^ mp_get_byte(out, i);
-    smemclr(bytes, nbytes);
-    sfree(bytes);
-    mp_free(out);
-
-    return diff == 0;
-    } // WINSCP
-}
-
-static void rsa2_sign(ssh_key *key, ptrlen data,
-                      unsigned flags, BinarySink *bs)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-    unsigned char *bytes;
-    size_t nbytes;
-    mp_int *in, *out;
-    const ssh_hashalg *halg;
-    const char *sign_alg_name;
-
-    const struct ssh2_rsa_extra *extra =
-        (const struct ssh2_rsa_extra *)key->vt->extra;
-    flags |= extra->signflags;
-
-    halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name);
-
-    nbytes = (mp_get_nbits(rsa->modulus) + 7) / 8;
-
-    bytes = rsa_pkcs1_signature_string(nbytes, halg, data);
-    in = mp_from_bytes_be(make_ptrlen(bytes, nbytes));
-    smemclr(bytes, nbytes);
-    sfree(bytes);
-
-    out = rsa_privkey_op(in, rsa);
-    mp_free(in);
-
-    put_stringz(bs, sign_alg_name);
-    nbytes = (mp_get_nbits(out) + 7) / 8;
-    put_uint32(bs, nbytes);
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < nbytes; i++)
-        put_byte(bs, mp_get_byte(out, nbytes - 1 - i));
-    } // WINSCP
-
-    mp_free(out);
-}
-
-static char *rsa2_invalid(ssh_key *key, unsigned flags)
-{
-    RSAKey *rsa = container_of(key, RSAKey, sshk);
-    size_t bits = mp_get_nbits(rsa->modulus), nbytes = (bits + 7) / 8;
-    const char *sign_alg_name;
-    const ssh_hashalg *halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name);
-    if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) {
-        return dupprintf(
-            "%"SIZEu"-bit RSA key is too short to generate %s signatures",
-            bits, sign_alg_name);
-    }
-
-    return NULL;
-}
-
-static const struct ssh2_rsa_extra
-    rsa_extra = { 0 },
-    rsa_sha256_extra = { SSH_AGENT_RSA_SHA2_256 },
-    rsa_sha512_extra = { SSH_AGENT_RSA_SHA2_512 };
-
-// WINSCP
-#define COMMON_KEYALG_FIELDS                    \
-    /*.new_pub =*/ rsa2_new_pub,                    \
-    /*.new_priv =*/ rsa2_new_priv,                  \
-    /*.new_priv_openssh =*/ rsa2_new_priv_openssh,  \
-    /*.freekey =*/ rsa2_freekey,                    \
-    /*.invalid =*/ rsa2_invalid,                    \
-    /*.sign =*/ rsa2_sign,                          \
-    /*.verify =*/ rsa2_verify,                      \
-    /*.public_blob =*/ rsa2_public_blob,            \
-    /*.private_blob =*/ rsa2_private_blob,          \
-    /*.openssh_blob =*/ rsa2_openssh_blob,          \
-    /*.cache_str =*/ rsa2_cache_str,                \
-    /*.components =*/ rsa2_components,              \
-    /*.pubkey_bits =*/ rsa2_pubkey_bits
-#define COMMON_KEYALG_FIELDS2 \
-    /*.cache_id =*/ "rsa2"
-
-const ssh_keyalg ssh_rsa = {
-    // WINSCP
-    COMMON_KEYALG_FIELDS,
-    /*.ssh_id =*/ "ssh-rsa",
-    COMMON_KEYALG_FIELDS2,
-    /*.extra =*/ &rsa_extra,
-    /*.supported_flags =*/ SSH_AGENT_RSA_SHA2_256 | SSH_AGENT_RSA_SHA2_512,
-};
-
-const ssh_keyalg ssh_rsa_sha256 = {
-    // WINSCP
-    COMMON_KEYALG_FIELDS,
-    /*.ssh_id =*/ "rsa-sha2-256",
-    COMMON_KEYALG_FIELDS2,
-    /*.extra =*/ &rsa_sha256_extra,
-    /*.supported_flags =*/ 0,
-};
-
-const ssh_keyalg ssh_rsa_sha512 = {
-    // WINSCP
-    COMMON_KEYALG_FIELDS,
-    /*.ssh_id =*/ "rsa-sha2-512",
-    COMMON_KEYALG_FIELDS2,
-    /*.extra =*/ &rsa_sha512_extra,
-    /*.supported_flags =*/ 0,
-};
-
-RSAKey *ssh_rsakex_newkey(ptrlen data)
-{
-    ssh_key *sshk = rsa2_new_pub(&ssh_rsa, data);
-    if (!sshk)
-        return NULL;
-    return container_of(sshk, RSAKey, sshk);
-}
-
-void ssh_rsakex_freekey(RSAKey *key)
-{
-    rsa2_freekey(&key->sshk);
-}
-
-int ssh_rsakex_klen(RSAKey *rsa)
-{
-    return mp_get_nbits(rsa->modulus);
-}
-
-static void oaep_mask(const ssh_hashalg *h, void *seed, int seedlen,
-                      void *vdata, int datalen)
-{
-    unsigned char *data = (unsigned char *)vdata;
-    unsigned count = 0;
-
-    ssh_hash *s = ssh_hash_new(h);
-
-    while (datalen > 0) {
-        int i, max = (datalen > h->hlen ? h->hlen : datalen);
-        unsigned char hash[MAX_HASH_LEN];
-
-        ssh_hash_reset(s);
-        assert(h->hlen <= MAX_HASH_LEN);
-        put_data(s, seed, seedlen);
-        put_uint32(s, count);
-        ssh_hash_digest(s, hash);
-        count++;
-
-        for (i = 0; i < max; i++)
-            data[i] ^= hash[i];
-
-        data += max;
-        datalen -= max;
-    }
-
-    ssh_hash_free(s);
-}
-
-strbuf *ssh_rsakex_encrypt(RSAKey *rsa, const ssh_hashalg *h, ptrlen in)
-{
-    mp_int *b1, *b2;
-    int k, i;
-    char *p;
-    const int HLEN = h->hlen;
-
-    /*
-     * Here we encrypt using RSAES-OAEP. Essentially this means:
-     *
-     *  - we have a SHA-based `mask generation function' which
-     *    creates a pseudo-random stream of mask data
-     *    deterministically from an input chunk of data.
-     *
-     *  - we have a random chunk of data called a seed.
-     *
-     *  - we use the seed to generate a mask which we XOR with our
-     *    plaintext.
-     *
-     *  - then we use _the masked plaintext_ to generate a mask
-     *    which we XOR with the seed.
-     *
-     *  - then we concatenate the masked seed and the masked
-     *    plaintext, and RSA-encrypt that lot.
-     *
-     * The result is that the data input to the encryption function
-     * is random-looking and (hopefully) contains no exploitable
-     * structure such as PKCS1-v1_5 does.
-     *
-     * For a precise specification, see RFC 3447, section 7.1.1.
-     * Some of the variable names below are derived from that, so
-     * it'd probably help to read it anyway.
-     */
-
-    /* k denotes the length in octets of the RSA modulus. */
-    k = (7 + mp_get_nbits(rsa->modulus)) / 8;
-
-    /* The length of the input data must be at most k - 2hLen - 2. */
-    assert(in.len > 0 && in.len <= k - 2*HLEN - 2);
-
-    /* The length of the output data wants to be precisely k. */
-    { // WINSCP
-    strbuf *toret = strbuf_new_nm();
-    int outlen = k;
-    unsigned char *out = strbuf_append(toret, outlen);
-
-    /*
-     * Now perform EME-OAEP encoding. First set up all the unmasked
-     * output data.
-     */
-    /* Leading byte zero. */
-    out[0] = 0;
-    /* At position 1, the seed: HLEN bytes of random data. */
-    random_read(out + 1, HLEN);
-    /* At position 1+HLEN, the data block DB, consisting of: */
-    /* The hash of the label (we only support an empty label here) */
-    hash_simple(h, PTRLEN_LITERAL(""), out + HLEN + 1);
-    /* A bunch of zero octets */
-    memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1));
-    /* A single 1 octet, followed by the input message data. */
-    out[outlen - in.len - 1] = 1;
-    memcpy(out + outlen - in.len, in.ptr, in.len);
-
-    /*
-     * Now use the seed data to mask the block DB.
-     */
-    oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
-
-    /*
-     * And now use the masked DB to mask the seed itself.
-     */
-    oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
-
-    /*
-     * Now `out' contains precisely the data we want to
-     * RSA-encrypt.
-     */
-    b1 = mp_from_bytes_be(make_ptrlen(out, outlen));
-    b2 = mp_modpow(b1, rsa->exponent, rsa->modulus);
-    p = (char *)out;
-    for (i = outlen; i--;) {
-        *p++ = mp_get_byte(b2, i);
-    }
-    mp_free(b1);
-    mp_free(b2);
-
-    /*
-     * And we're done.
-     */
-    return toret;
-    } // WINSCP
-}
-
-mp_int *ssh_rsakex_decrypt(
-    RSAKey *rsa, const ssh_hashalg *h, ptrlen ciphertext)
-{
-    mp_int *b1, *b2;
-    int outlen, i;
-    unsigned char *out;
-    unsigned char labelhash[64];
-    BinarySource src[1];
-    const int HLEN = h->hlen;
-
-    /*
-     * Decryption side of the RSA key exchange operation.
-     */
-
-    /* The length of the encrypted data should be exactly the length
-     * in octets of the RSA modulus.. */
-    outlen = (7 + mp_get_nbits(rsa->modulus)) / 8;
-    if (ciphertext.len != outlen)
-        return NULL;
-
-    /* Do the RSA decryption, and extract the result into a byte array. */
-    b1 = mp_from_bytes_be(ciphertext);
-    b2 = rsa_privkey_op(b1, rsa);
-    out = snewn(outlen, unsigned char);
-    for (i = 0; i < outlen; i++)
-        out[i] = mp_get_byte(b2, outlen-1-i);
-    mp_free(b1);
-    mp_free(b2);
-
-    /* Do the OAEP masking operations, in the reverse order from encryption */
-    oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN);
-    oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1);
-
-    /* Check the leading byte is zero. */
-    if (out[0] != 0) {
-        sfree(out);
-        return NULL;
-    }
-    /* Check the label hash at position 1+HLEN */
-    assert(HLEN <= lenof(labelhash));
-    hash_simple(h, PTRLEN_LITERAL(""), labelhash);
-    if (memcmp(out + HLEN + 1, labelhash, HLEN)) {
-        sfree(out);
-        return NULL;
-    }
-    /* Expect zero bytes followed by a 1 byte */
-    for (i = 1 + 2 * HLEN; i < outlen; i++) {
-        if (out[i] == 1) {
-            i++;  /* skip over the 1 byte */
-            break;
-        } else if (out[i] != 0) {
-            sfree(out);
-            return NULL;
-        }
-    }
-    /* And what's left is the input message data, which should be
-     * encoded as an ordinary SSH-2 mpint. */
-    BinarySource_BARE_INIT(src, out + i, outlen - i);
-    b1 = get_mp_ssh2(src);
-    sfree(out);
-    if (get_err(src) || get_avail(src) != 0) {
-        mp_free(b1);
-        return NULL;
-    }
-
-    /* Success! */
-    return b1;
-}
-
-static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha1 = { 1024 };
-static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha256 = { 2048 };
-
-static const ssh_kex ssh_rsa_kex_sha1 = {
-    "rsa1024-sha1", NULL, KEXTYPE_RSA,
-    &ssh_sha1, &ssh_rsa_kex_extra_sha1,
-};
-
-static const ssh_kex ssh_rsa_kex_sha256 = {
-    "rsa2048-sha256", NULL, KEXTYPE_RSA,
-    &ssh_sha256, &ssh_rsa_kex_extra_sha256,
-};
-
-static const ssh_kex *const rsa_kex_list[] = {
-    &ssh_rsa_kex_sha256,
-    &ssh_rsa_kex_sha1
-};
-
-const ssh_kexes ssh_rsa_kex = { lenof(rsa_kex_list), rsa_kex_list };

+ 0 - 995
source/putty/sshsh256.c

@@ -1,995 +0,0 @@
-/*
- * SHA-256 algorithm as described at
- *
- *   http://csrc.nist.gov/cryptval/shs.html
- */
-
-#include "ssh.h"
-#include <assert.h>
-
-/*
- * Start by deciding whether we can support hardware SHA at all.
- */
-#define HW_SHA256_NONE 0
-#define HW_SHA256_NI 1
-#define HW_SHA256_NEON 2
-
-#ifdef _FORCE_SHA_NI
-#   define HW_SHA256 HW_SHA256_NI
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<wmmintrin.h>) &&       \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_SHA256 HW_SHA256_NI
-#   endif
-#elif defined(__GNUC__)
-#    if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
-        (defined(__x86_64__) || defined(__i386))
-#       define HW_SHA256 HW_SHA256_NI
-#    endif
-#elif defined (_MSC_VER)
-#   if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729
-#      define HW_SHA256 HW_SHA256_NI
-#   endif
-#endif
-
-// Should be (almost) working (when set to HW_SHA256_NI), but we do not have a HW to test this on.
-// Need to replace _mm_setr_epi8 and there's also objconv warning about alignment.
-// Restore "unaccelerated" annotation.
-#undef HW_SHA256
-
-#ifdef _FORCE_SHA_NEON
-#   define HW_SHA256 HW_SHA256_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 SHA without having to enable anything by hand */
-#   define HW_SHA256 HW_SHA256_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_SHA256 HW_SHA256_NEON
-#       define USE_CLANG_ATTR_TARGET_AARCH64
-#   endif
-#elif defined _MSC_VER
-    /* Visual Studio supports the crypto extension when targeting
-     * AArch64, but as of VS2017, the AArch32 header doesn't quite
-     * manage it (declaring the shae/shad intrinsics without a round
-     * key operand). */
-#   if defined _M_ARM64
-#       define HW_SHA256 HW_SHA256_NEON
-#       if defined _M_ARM64
-#           define USE_ARM64_NEON_H /* unusual header name in this case */
-#       endif
-#   endif
-#endif
-
-#if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA256
-#   undef HW_SHA256
-#   define HW_SHA256 HW_SHA256_NONE
-#endif
-
-#ifndef WINSCP_VS
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-bool sha256_hw_available(void);
-
-/*
- * The top-level selection function, caching the results of
- * sha256_hw_available() so it only has to run once.
- */
-/*WINSCP static*/ bool sha256_hw_available_cached(void)
-{
-    static bool initialised = false;
-    static bool hw_available;
-    if (!initialised) {
-        hw_available = sha256_hw_available();
-        initialised = true;
-    }
-    return hw_available;
-}
-
-static ssh_hash *sha256_select(const ssh_hashalg *alg)
-{
-    const ssh_hashalg *real_alg =
-        sha256_hw_available_cached() ? &ssh_sha256_hw : &ssh_sha256_sw;
-
-    return ssh_hash_new(real_alg);
-}
-
-const ssh_hashalg ssh_sha256 = {
-    // WINSCP
-    /*.new =*/ sha256_select,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    /*.hlen =*/ 32,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_ANNOTATED("SHA-256", "dummy selector vtable"),
-    NULL,
-};
-
-#else
-
-bool sha256_hw_available_cached(void);
-
-#endif
-
-/* ----------------------------------------------------------------------
- * Definitions likely to be helpful to multiple implementations.
- */
-
-static const uint32_t sha256_initial_state[] = {
-    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
-    0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
-};
-
-static const uint32_t sha256_round_constants[] = {
-    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,
-};
-
-#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;
-}
-
-#ifdef WINSCP_VS
-
-/*WINSCP 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;
-}
-
-/*WINSCP 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");
-}
-
-/* ----------------------------------------------------------------------
- * Software implementation of SHA-256.
- */
-
-static inline uint32_t ror(uint32_t x, unsigned y)
-{
-    return (x << (31 & /*WINSCP*/(uint32_t)(-(int32_t)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;
-}
-
-/*WINSCP 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));
-}
-
-#endif // WINSCP_VS
-
-#ifndef WINSCP_VS
-
-bool sha256_block_write(
-    sha256_block *blk, const void **vdata, size_t *len);
-void sha256_sw_block(uint32_t *core, const uint8_t *block);
-void sha256_block_pad(sha256_block *blk, BinarySink *bs);
-
-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));
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < 8; i++)
-        PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]);
-    } // WINSCP
-}
-
-const ssh_hashalg ssh_sha256_sw = {
-    // WINSCP
-    /*.new =*/ sha256_sw_new,
-    /*.reset =*/ sha256_sw_reset,
-    /*.copyfrom =*/ sha256_sw_copyfrom,
-    /*.digest =*/ sha256_sw_digest,
-    /*.free =*/ sha256_sw_free,
-    /*.hlen =*/ 32,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_BARE("SHA-256"), // WINSCP (removed "unaccelerated" annotation)
-    NULL,
-};
-#endif // !WINSCP_VS
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of SHA-256 using x86 SHA-NI.
- */
-
-#if HW_SHA256 == HW_SHA256_NI
-
-#ifdef WINSCP_VS
-
-/*
- * Set target architecture for Clang and GCC
- */
-#if defined(__clang__) || defined(__GNUC__)
-#    define FUNC_ISA __attribute__ ((target("sse4.1,sha")))
-#if !defined(__clang__)
-#    pragma GCC target("sha")
-#    pragma GCC target("sse4.1")
-#endif
-#else
-#    define FUNC_ISA
-#endif
-
-#include <wmmintrin.h>
-#include <smmintrin.h>
-#include <immintrin.h>
-#if defined(__clang__) || defined(__GNUC__)
-#include <shaintrin.h>
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#include <cpuid.h>
-#define GET_CPU_ID_0(out)                               \
-    __cpuid(0, (out)[0], (out)[1], (out)[2], (out)[3])
-#define GET_CPU_ID_7(out)                                       \
-    __cpuid_count(7, 0, (out)[0], (out)[1], (out)[2], (out)[3])
-#else
-#define GET_CPU_ID_0(out) __cpuid(out, 0)
-#define GET_CPU_ID_7(out) __cpuidex(out, 7, 0)
-#endif
-
-/*WINSCP static*/ bool sha256_hw_available(void)
-{
-    unsigned int CPUInfo[4];
-    GET_CPU_ID_0(CPUInfo);
-    if (CPUInfo[0] < 7)
-        return false;
-
-    GET_CPU_ID_7(CPUInfo);
-    return CPUInfo[1] & (1 << 29); /* Check SHA */
-}
-
-/* SHA256 implementation using new instructions
-   The code is based on Jeffrey Walton's SHA256 implementation:
-   https://github.com/noloader/SHA-Intrinsics
-*/
-FUNC_ISA
-static inline void sha256_ni_block(__m128i *core, const uint8_t *p)
-{
-    __m128i STATE0, STATE1;
-    __m128i MSG, TMP;
-    __m128i MSG0, MSG1, MSG2, MSG3;
-    const __m128i *block = (const __m128i *)p;
-    const __m128i MASK = _mm_set_epi64x(
-        0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL);
-
-    /* Load initial values */
-    STATE0 = core[0];
-    STATE1 = core[1];
-
-    /* Rounds 0-3 */
-    MSG = _mm_loadu_si128(block);
-    MSG0 = _mm_shuffle_epi8(MSG, MASK);
-    MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(
-                            0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-
-    /* Rounds 4-7 */
-    MSG1 = _mm_loadu_si128(block + 1);
-    MSG1 = _mm_shuffle_epi8(MSG1, MASK);
-    MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(
-                            0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1);
-
-    /* Rounds 8-11 */
-    MSG2 = _mm_loadu_si128(block + 2);
-    MSG2 = _mm_shuffle_epi8(MSG2, MASK);
-    MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(
-                            0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2);
-
-    /* Rounds 12-15 */
-    MSG3 = _mm_loadu_si128(block + 3);
-    MSG3 = _mm_shuffle_epi8(MSG3, MASK);
-    MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(
-                            0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG3, MSG2, 4);
-    MSG0 = _mm_add_epi32(MSG0, TMP);
-    MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3);
-
-    /* Rounds 16-19 */
-    MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(
-                            0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG0, MSG3, 4);
-    MSG1 = _mm_add_epi32(MSG1, TMP);
-    MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0);
-
-    /* Rounds 20-23 */
-    MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(
-                            0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG1, MSG0, 4);
-    MSG2 = _mm_add_epi32(MSG2, TMP);
-    MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1);
-
-    /* Rounds 24-27 */
-    MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(
-                            0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG2, MSG1, 4);
-    MSG3 = _mm_add_epi32(MSG3, TMP);
-    MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2);
-
-    /* Rounds 28-31 */
-    MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(
-                            0x1429296706CA6351ULL,  0xD5A79147C6E00BF3ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG3, MSG2, 4);
-    MSG0 = _mm_add_epi32(MSG0, TMP);
-    MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3);
-
-    /* Rounds 32-35 */
-    MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(
-                            0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG0, MSG3, 4);
-    MSG1 = _mm_add_epi32(MSG1, TMP);
-    MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0);
-
-    /* Rounds 36-39 */
-    MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(
-                            0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG1, MSG0, 4);
-    MSG2 = _mm_add_epi32(MSG2, TMP);
-    MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1);
-
-    /* Rounds 40-43 */
-    MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(
-                            0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG2, MSG1, 4);
-    MSG3 = _mm_add_epi32(MSG3, TMP);
-    MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2);
-
-    /* Rounds 44-47 */
-    MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(
-                            0x106AA070F40E3585ULL, 0xD6990624D192E819ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG3, MSG2, 4);
-    MSG0 = _mm_add_epi32(MSG0, TMP);
-    MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3);
-
-    /* Rounds 48-51 */
-    MSG = _mm_add_epi32(MSG0, _mm_set_epi64x(
-                            0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG0, MSG3, 4);
-    MSG1 = _mm_add_epi32(MSG1, TMP);
-    MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-    MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0);
-
-    /* Rounds 52-55 */
-    MSG = _mm_add_epi32(MSG1, _mm_set_epi64x(
-                            0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG1, MSG0, 4);
-    MSG2 = _mm_add_epi32(MSG2, TMP);
-    MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-
-    /* Rounds 56-59 */
-    MSG = _mm_add_epi32(MSG2, _mm_set_epi64x(
-                            0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    TMP = _mm_alignr_epi8(MSG2, MSG1, 4);
-    MSG3 = _mm_add_epi32(MSG3, TMP);
-    MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-
-    /* Rounds 60-63 */
-    MSG = _mm_add_epi32(MSG3, _mm_set_epi64x(
-                            0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL));
-    STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG);
-    MSG = _mm_shuffle_epi32(MSG, 0x0E);
-    STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG);
-
-    /* Combine state */
-    core[0] = _mm_add_epi32(STATE0, core[0]);
-    core[1] = _mm_add_epi32(STATE1, core[1]);
-}
-
-typedef struct sha256_ni {
-    /*
-     * These two vectors store the 8 words of the SHA-256 state, but
-     * not in the same order they appear in the spec: the first word
-     * holds A,B,E,F and the second word C,D,G,H.
-     */
-    __m128i core[2];
-    sha256_block blk;
-    void *pointer_to_free;
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} sha256_ni;
-
-static void sha256_ni_write(BinarySink *bs, const void *vp, size_t len);
-
-static sha256_ni *sha256_ni_alloc(void)
-{
-    /*
-     * 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(sha256_ni) + 15);
-    uintptr_t alloc_address = (uintptr_t)allocation;
-    uintptr_t aligned_address = (alloc_address + 15) & ~15;
-    sha256_ni *s = (sha256_ni *)aligned_address;
-    s->pointer_to_free = allocation;
-    return s;
-}
-
-/*WINSCP static*/ ssh_hash *sha256_ni_new(const ssh_hashalg *alg)
-{
-    if (!sha256_hw_available_cached())
-        return NULL;
-
-    sha256_ni *s = sha256_ni_alloc();
-
-    s->hash.vt = alg;
-    BinarySink_INIT(s, sha256_ni_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-
-    return &s->hash;
-}
-
-FUNC_ISA static void sha256_ni_reset(ssh_hash *hash)
-{
-    sha256_ni *s = container_of(hash, sha256_ni, hash);
-
-    /* Initialise the core vectors in their storage order */
-    s->core[0] = _mm_set_epi64x(
-        0x6a09e667bb67ae85ULL, 0x510e527f9b05688cULL);
-    s->core[1] = _mm_set_epi64x(
-        0x3c6ef372a54ff53aULL, 0x1f83d9ab5be0cd19ULL);
-
-    sha256_block_setup(&s->blk);
-}
-
-/*WINSCP static*/ void sha256_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    sha256_ni *copy = container_of(hcopy, sha256_ni, hash);
-    sha256_ni *orig = container_of(horig, sha256_ni, hash);
-
-    void *ptf_save = copy->pointer_to_free;
-    *copy = *orig; /* structure copy */
-    copy->pointer_to_free = ptf_save;
-
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-/*WINSCP static*/ void sha256_ni_free(ssh_hash *hash)
-{
-    sha256_ni *s = container_of(hash, sha256_ni, hash);
-
-    void *ptf = s->pointer_to_free;
-    smemclr(s, sizeof(*s));
-    sfree(ptf);
-}
-
-static void sha256_ni_write(BinarySink *bs, const void *vp, size_t len)
-{
-    sha256_ni *s = BinarySink_DOWNCAST(bs, sha256_ni);
-
-    while (len > 0)
-        if (sha256_block_write(&s->blk, &vp, &len))
-            sha256_ni_block(s->core, s->blk.block);
-}
-
-FUNC_ISA /*WINSCP static*/ void sha256_ni_digest(ssh_hash *hash, uint8_t *digest)
-{
-    sha256_ni *s = container_of(hash, sha256_ni, hash);
-
-    sha256_block_pad(&s->blk, BinarySink_UPCAST(s));
-
-    /* Rearrange the words into the output order */
-    __m128i feba = _mm_shuffle_epi32(s->core[0], 0x1B);
-    __m128i dchg = _mm_shuffle_epi32(s->core[1], 0xB1);
-    __m128i dcba = _mm_blend_epi16(feba, dchg, 0xF0);
-    __m128i hgfe = _mm_alignr_epi8(dchg, feba, 8);
-
-    /* Byte-swap them into the output endianness */
-    const __m128i mask = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12);
-    dcba = _mm_shuffle_epi8(dcba, mask);
-    hgfe = _mm_shuffle_epi8(hgfe, mask);
-
-    /* And store them */
-    __m128i *output = (__m128i *)digest;
-    _mm_storeu_si128(output, dcba);
-    _mm_storeu_si128(output+1, hgfe);
-}
-
-#endif // WINSCP_VS
-
-#ifndef WINSCP_VS
-
-ssh_hash *sha256_ni_new(const ssh_hashalg *alg);
-ssh_hash *sha256_ni_copy(ssh_hash *hash);
-void sha256_ni_final(ssh_hash *hash, uint8_t *digest);
-void sha256_ni_free(ssh_hash *hash);
-
-const ssh_hashalg ssh_sha256_hw = {
-    .new = sha256_ni_new,
-    .reset = sha256_ni_reset,
-    .copyfrom = sha256_ni_copyfrom,
-    .digest = sha256_ni_digest,
-    .free = sha256_ni_free,
-    .hlen = 32,
-    .blocklen = 64,
-    HASHALG_NAMES_ANNOTATED("SHA-256", "SHA-NI accelerated"),
-};
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of SHA-256 using Arm NEON.
- */
-
-#elif HW_SHA256 == HW_SHA256_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 SHA 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_SHA2 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 sha256_hw_available(void)
-{
-    /*
-     * For Arm, we delegate to a per-platform detection function (see
-     * explanation in sshaes.c).
-     */
-    return platform_sha256_hw_available();
-}
-
-typedef struct sha256_neon_core sha256_neon_core;
-struct sha256_neon_core {
-    uint32x4_t abcd, efgh;
-};
-
-FUNC_ISA
-static inline uint32x4_t sha256_neon_load_input(const uint8_t *p)
-{
-    return vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(p)));
-}
-
-FUNC_ISA
-static inline uint32x4_t sha256_neon_schedule_update(
-    uint32x4_t m4, uint32x4_t m3, uint32x4_t m2, uint32x4_t m1)
-{
-    return vsha256su1q_u32(vsha256su0q_u32(m4, m3), m2, m1);
-}
-
-FUNC_ISA
-static inline sha256_neon_core sha256_neon_round4(
-    sha256_neon_core old, uint32x4_t sched, unsigned round)
-{
-    sha256_neon_core new;
-
-    uint32x4_t round_input = vaddq_u32(
-        sched, vld1q_u32(sha256_round_constants + round));
-    new.abcd = vsha256hq_u32 (old.abcd, old.efgh, round_input);
-    new.efgh = vsha256h2q_u32(old.efgh, old.abcd, round_input);
-    return new;
-}
-
-FUNC_ISA
-static inline void sha256_neon_block(sha256_neon_core *core, const uint8_t *p)
-{
-    uint32x4_t s0, s1, s2, s3;
-    sha256_neon_core cr = *core;
-
-    s0 = sha256_neon_load_input(p);
-    cr = sha256_neon_round4(cr, s0, 0);
-    s1 = sha256_neon_load_input(p+16);
-    cr = sha256_neon_round4(cr, s1, 4);
-    s2 = sha256_neon_load_input(p+32);
-    cr = sha256_neon_round4(cr, s2, 8);
-    s3 = sha256_neon_load_input(p+48);
-    cr = sha256_neon_round4(cr, s3, 12);
-    s0 = sha256_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha256_neon_round4(cr, s0, 16);
-    s1 = sha256_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha256_neon_round4(cr, s1, 20);
-    s2 = sha256_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha256_neon_round4(cr, s2, 24);
-    s3 = sha256_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha256_neon_round4(cr, s3, 28);
-    s0 = sha256_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha256_neon_round4(cr, s0, 32);
-    s1 = sha256_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha256_neon_round4(cr, s1, 36);
-    s2 = sha256_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha256_neon_round4(cr, s2, 40);
-    s3 = sha256_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha256_neon_round4(cr, s3, 44);
-    s0 = sha256_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha256_neon_round4(cr, s0, 48);
-    s1 = sha256_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha256_neon_round4(cr, s1, 52);
-    s2 = sha256_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha256_neon_round4(cr, s2, 56);
-    s3 = sha256_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha256_neon_round4(cr, s3, 60);
-
-    core->abcd = vaddq_u32(core->abcd, cr.abcd);
-    core->efgh = vaddq_u32(core->efgh, cr.efgh);
-}
-
-typedef struct sha256_neon {
-    sha256_neon_core core;
-    sha256_block blk;
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} sha256_neon;
-
-static void sha256_neon_write(BinarySink *bs, const void *vp, size_t len);
-
-static ssh_hash *sha256_neon_new(const ssh_hashalg *alg)
-{
-    if (!sha256_hw_available_cached())
-        return NULL;
-
-    sha256_neon *s = snew(sha256_neon);
-
-    s->hash.vt = alg;
-    BinarySink_INIT(s, sha256_neon_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-    return &s->hash;
-}
-
-static void sha256_neon_reset(ssh_hash *hash)
-{
-    sha256_neon *s = container_of(hash, sha256_neon, hash);
-
-    s->core.abcd = vld1q_u32(sha256_initial_state);
-    s->core.efgh = vld1q_u32(sha256_initial_state + 4);
-
-    sha256_block_setup(&s->blk);
-}
-
-static void sha256_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    sha256_neon *copy = container_of(hcopy, sha256_neon, hash);
-    sha256_neon *orig = container_of(horig, sha256_neon, hash);
-
-    *copy = *orig; /* structure copy */
-
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-static void sha256_neon_free(ssh_hash *hash)
-{
-    sha256_neon *s = container_of(hash, sha256_neon, hash);
-    smemclr(s, sizeof(*s));
-    sfree(s);
-}
-
-static void sha256_neon_write(BinarySink *bs, const void *vp, size_t len)
-{
-    sha256_neon *s = BinarySink_DOWNCAST(bs, sha256_neon);
-
-    while (len > 0)
-        if (sha256_block_write(&s->blk, &vp, &len))
-            sha256_neon_block(&s->core, s->blk.block);
-}
-
-static void sha256_neon_digest(ssh_hash *hash, uint8_t *digest)
-{
-    sha256_neon *s = container_of(hash, sha256_neon, hash);
-
-    sha256_block_pad(&s->blk, BinarySink_UPCAST(s));
-    vst1q_u8(digest,      vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd)));
-    vst1q_u8(digest + 16, vrev32q_u8(vreinterpretq_u8_u32(s->core.efgh)));
-}
-
-const ssh_hashalg ssh_sha256_hw = {
-    .new = sha256_neon_new,
-    .reset = sha256_neon_reset,
-    .copyfrom = sha256_neon_copyfrom,
-    .digest = sha256_neon_digest,
-    .free = sha256_neon_free,
-    .hlen = 32,
-    .blocklen = 64,
-    HASHALG_NAMES_ANNOTATED("SHA-256", "NEON accelerated"),
-};
-
-#endif
-
-/* ----------------------------------------------------------------------
- * Stub functions if we have no hardware-accelerated SHA-256. In this
- * case, sha256_hw_new returns NULL (though it should also never be
- * selected by sha256_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_SHA256 == HW_SHA256_NONE
-
-#ifndef WINSCP_VS
-
-static bool sha256_hw_available(void)
-{
-    return false;
-}
-
-static ssh_hash *sha256_stub_new(const ssh_hashalg *alg)
-{
-    return NULL;
-}
-
-#define STUB_BODY { unreachable("Should never be called"); }
-
-static void sha256_stub_reset(ssh_hash *hash) STUB_BODY
-static void sha256_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY
-static void sha256_stub_free(ssh_hash *hash) STUB_BODY
-static void sha256_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY
-
-const ssh_hashalg ssh_sha256_hw = {
-    // WINSCP
-    /*.new =*/ sha256_stub_new,
-    /*.reset =*/ sha256_stub_reset,
-    /*.copyfrom =*/ sha256_stub_copyfrom,
-    /*.digest =*/ sha256_stub_digest,
-    /*.free =*/ sha256_stub_free,
-    /*.hlen =*/ 32,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_ANNOTATED("SHA-256", "!NONEXISTENT ACCELERATED VERSION!"),
-    NULL,
-};
-
-#endif // !WINSCP_VS
-
-#endif /* HW_SHA256 */

+ 0 - 853
source/putty/sshsh512.c

@@ -1,853 +0,0 @@
-/*
- * SHA-512 algorithm as described at
- *
- *   http://csrc.nist.gov/cryptval/shs.html
- *
- * Modifications made for SHA-384 also
- */
-
-#include <assert.h>
-#include "ssh.h"
-
-/*
- * Start by deciding whether we can support hardware SHA at all.
- */
-#define HW_SHA512_NONE 0
-#define HW_SHA512_NEON 1
-
-#ifdef _FORCE_SHA512_NEON
-#   define HW_SHA512 HW_SHA512_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_SHA512
-    /* If the Arm SHA-512 extension is available already, we can
-     * support NEON SHA without having to enable anything by hand */
-#   define HW_SHA512 HW_SHA512_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_SHA512 HW_SHA512_NEON
-#       define USE_CLANG_ATTR_TARGET_AARCH64
-#   endif
-#endif
-
-#if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA512
-#   undef HW_SHA512
-#   define HW_SHA512 HW_SHA512_NONE
-#endif
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-static bool sha512_hw_available(void);
-
-/*
- * The top-level selection function, caching the results of
- * sha512_hw_available() so it only has to run once.
- */
-static bool sha512_hw_available_cached(void)
-{
-    static bool initialised = false;
-    static bool hw_available;
-    if (!initialised) {
-        hw_available = sha512_hw_available();
-        initialised = true;
-    }
-    return hw_available;
-}
-
-struct sha512_select_options {
-    const ssh_hashalg *hw, *sw;
-};
-
-static ssh_hash *sha512_select(const ssh_hashalg *alg)
-{
-    const struct sha512_select_options *options =
-        (const struct sha512_select_options *)alg->extra;
-
-    const ssh_hashalg *real_alg =
-        sha512_hw_available_cached() ? options->hw : options->sw;
-
-    return ssh_hash_new(real_alg);
-}
-
-const struct sha512_select_options ssh_sha512_select_options = {
-    &ssh_sha512_hw, &ssh_sha512_sw,
-};
-const struct sha512_select_options ssh_sha384_select_options = {
-    &ssh_sha384_hw, &ssh_sha384_sw,
-};
-
-const ssh_hashalg ssh_sha512 = {
-    // WINSCP
-    /*.new =*/ sha512_select,
-    NULL, NULL, NULL, NULL, // WINSCP
-    /*.hlen =*/ 64,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_ANNOTATED("SHA-512", "dummy selector vtable"),
-    /*.extra =*/ &ssh_sha512_select_options,
-};
-
-const ssh_hashalg ssh_sha384 = {
-    // WINSCP
-    /*.new =*/ sha512_select,
-    NULL, NULL, NULL, NULL, // WINSCP
-    /*.hlen =*/ 48,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_ANNOTATED("SHA-384", "dummy selector vtable"),
-    /*.extra =*/ &ssh_sha384_select_options,
-};
-
-/* ----------------------------------------------------------------------
- * Definitions likely to be helpful to multiple implementations.
- */
-
-static const uint64_t sha512_initial_state[] = {
-    0x6a09e667f3bcc908ULL,
-    0xbb67ae8584caa73bULL,
-    0x3c6ef372fe94f82bULL,
-    0xa54ff53a5f1d36f1ULL,
-    0x510e527fade682d1ULL,
-    0x9b05688c2b3e6c1fULL,
-    0x1f83d9abfb41bd6bULL,
-    0x5be0cd19137e2179ULL,
-};
-
-static const uint64_t sha384_initial_state[] = {
-    0xcbbb9d5dc1059ed8ULL,
-    0x629a292a367cd507ULL,
-    0x9159015a3070dd17ULL,
-    0x152fecd8f70e5939ULL,
-    0x67332667ffc00b31ULL,
-    0x8eb44a8768581511ULL,
-    0xdb0c2e0d64f98fa7ULL,
-    0x47b5481dbefa4fa4ULL,
-};
-
-static const uint64_t sha512_round_constants[] = {
-    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,
-};
-
-#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;
-
-    { // WINSCP
-    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;
-    } // WINSCP
-}
-
-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");
-}
-
-/* ----------------------------------------------------------------------
- * Software implementation of SHA-512.
- */
-
-static inline uint64_t ror(uint64_t x, unsigned y)
-{
-#pragma option push -w-ngu // WINSCP
-    return (x << (63 & -y)) | (x >> (63 & y));
-#pragma option pop // WINSCP
-}
-
-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);
-
-    /* The 'extra' field in the ssh_hashalg indicates which
-     * initialisation vector we're using */
-    memcpy(s->core, hash->vt->extra, 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));
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < hash->vt->hlen / 8; i++)
-        PUT_64BIT_MSB_FIRST(digest + 8*i, s->core[i]);
-    }  // WINSCP
-}
-
-const ssh_hashalg ssh_sha512_sw = {
-    // WINSCP
-    /*.new =*/ sha512_sw_new,
-    /*.reset =*/ sha512_sw_reset,
-    /*.copyfrom =*/ sha512_sw_copyfrom,
-    /*.digest =*/ sha512_sw_digest,
-    /*.free =*/ sha512_sw_free,
-    /*.hlen =*/ 64,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_ANNOTATED("SHA-512", "unaccelerated"),
-    /*.extra =*/ sha512_initial_state,
-};
-
-const ssh_hashalg ssh_sha384_sw = {
-    // WINSCP
-    /*.new =*/ sha512_sw_new,
-    /*.reset =*/ sha512_sw_reset,
-    /*.copyfrom =*/ sha512_sw_copyfrom,
-    /*.digest =*/ sha512_sw_digest,
-    /*.free =*/ sha512_sw_free,
-    /*.hlen =*/ 48,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_ANNOTATED("SHA-384", "unaccelerated"),
-    /*.extra =*/ sha384_initial_state,
-};
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of SHA-512 using Arm NEON.
- */
-
-#if HW_SHA512 == HW_SHA512_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 SHA 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 FUNC_ISA __attribute__ ((target("neon,sha3")))
-#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 sha512_hw_available(void)
-{
-    /*
-     * For Arm, we delegate to a per-platform detection function (see
-     * explanation in sshaes.c).
-     */
-    return platform_sha512_hw_available();
-}
-
-#if defined __clang__
-/*
- * As of 2020-12-24, I've found that clang doesn't provide the SHA-512
- * NEON intrinsics. So I define my own set using inline assembler, and
- * use #define to effectively rename them over the top of the standard
- * names.
- *
- * The aim of that #define technique is that it should avoid a build
- * failure if these intrinsics _are_ defined in <arm_neon.h>.
- * Obviously it would be better in that situation to switch back to
- * using the real intrinsics, but until I see a version of clang that
- * supports them, I won't know what version number to test in the
- * ifdef.
- */
-static inline FUNC_ISA
-uint64x2_t vsha512su0q_u64_asm(uint64x2_t x, uint64x2_t y) {
-    __asm__("sha512su0 %0.2D,%1.2D" : "+w" (x) : "w" (y));
-    return x;
-}
-static inline FUNC_ISA
-uint64x2_t vsha512su1q_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) {
-    __asm__("sha512su1 %0.2D,%1.2D,%2.2D" : "+w" (x) : "w" (y), "w" (z));
-    return x;
-}
-static inline FUNC_ISA
-uint64x2_t vsha512hq_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) {
-    __asm__("sha512h %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z));
-    return x;
-}
-static inline FUNC_ISA
-uint64x2_t vsha512h2q_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) {
-    __asm__("sha512h2 %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z));
-    return x;
-}
-#undef vsha512su0q_u64
-#define vsha512su0q_u64 vsha512su0q_u64_asm
-#undef vsha512su1q_u64
-#define vsha512su1q_u64 vsha512su1q_u64_asm
-#undef vsha512hq_u64
-#define vsha512hq_u64 vsha512hq_u64_asm
-#undef vsha512h2q_u64
-#define vsha512h2q_u64 vsha512h2q_u64_asm
-#endif /* defined __clang__ */
-
-typedef struct sha512_neon_core sha512_neon_core;
-struct sha512_neon_core {
-    uint64x2_t ab, cd, ef, gh;
-};
-
-FUNC_ISA
-static inline uint64x2_t sha512_neon_load_input(const uint8_t *p)
-{
-    return vreinterpretq_u64_u8(vrev64q_u8(vld1q_u8(p)));
-}
-
-FUNC_ISA
-static inline uint64x2_t sha512_neon_schedule_update(
-    uint64x2_t m8, uint64x2_t m7, uint64x2_t m4, uint64x2_t m3, uint64x2_t m1)
-{
-    /*
-     * vsha512su0q_u64() takes words from a long way back in the
-     * schedule and performs the sigma_0 half of the computation of
-     * the next two 64-bit message-schedule words.
-     *
-     * vsha512su1q_u64() combines the result of that with the sigma_1
-     * steps, to output the finished version of those two words. The
-     * total amount of input data it requires fits nicely into three
-     * 128-bit vector registers, but one of those registers is
-     * misaligned compared to the 128-bit chunks that the message
-     * schedule is stored in. So we use vextq_u64 to make one of its
-     * input words out of the second half of m4 and the first half of
-     * m3.
-     */
-    return vsha512su1q_u64(vsha512su0q_u64(m8, m7), m1, vextq_u64(m4, m3, 1));
-}
-
-FUNC_ISA
-static inline void sha512_neon_round2(
-    unsigned round_index, uint64x2_t schedule_words,
-    uint64x2_t *ab, uint64x2_t *cd, uint64x2_t *ef, uint64x2_t *gh)
-{
-    /*
-     * vsha512hq_u64 performs the Sigma_1 and Ch half of the
-     * computation of two rounds of SHA-512 (including feeding back
-     * one of the outputs from the first of those half-rounds into the
-     * second one).
-     *
-     * vsha512h2q_u64 combines the result of that with the Sigma_0 and
-     * Maj steps, and outputs one 128-bit vector that replaces the gh
-     * piece of the input hash state, and a second that updates cd by
-     * addition.
-     *
-     * Similarly to vsha512su1q_u64 above, some of the input registers
-     * expected by these instructions are misaligned by 64 bits
-     * relative to the chunks we've divided the hash state into, so we
-     * have to start by making 'de' and 'fg' words out of our input
-     * cd,ef,gh, using vextq_u64.
-     *
-     * Also, one of the inputs to vsha512hq_u64 is expected to contain
-     * the results of summing gh + two round constants + two words of
-     * message schedule, but the two words of the message schedule
-     * have to be the opposite way round in the vector register from
-     * the way that vsha512su1q_u64 output them. Hence, there's
-     * another vextq_u64 in here that swaps the two halves of the
-     * initial_sum vector register.
-     *
-     * (This also means that I don't have to prepare a specially
-     * reordered version of the sha512_round_constants[] array: as
-     * long as I'm unavoidably doing a swap at run time _anyway_, I
-     * can load from the normally ordered version of that array, and
-     * just take care to fold in that data _before_ the swap rather
-     * than after.)
-     */
-
-    /* Load two round constants, with the first one in the low half */
-    uint64x2_t round_constants = vld1q_u64(
-        sha512_round_constants + round_index);
-
-    /* Add schedule words to round constants */
-    uint64x2_t initial_sum = vaddq_u64(schedule_words, round_constants);
-
-    /* Swap that sum around so the word used in the first of the two
-     * rounds is in the _high_ half of the vector, matching where h
-     * lives in the gh vector */
-    uint64x2_t swapped_initial_sum = vextq_u64(initial_sum, initial_sum, 1);
-
-    /* Add gh to that, now that they're matching ways round */
-    uint64x2_t sum = vaddq_u64(swapped_initial_sum, *gh);
-
-    /* Make the misaligned de and fg words */
-    uint64x2_t de = vextq_u64(*cd, *ef, 1);
-    uint64x2_t fg = vextq_u64(*ef, *gh, 1);
-
-    /* Now we're ready to put all the pieces together. The output from
-     * vsha512h2q_u64 can be used directly as the new gh, and the
-     * output from vsha512hq_u64 is simultaneously the intermediate
-     * value passed to h2 and the thing you have to add on to cd. */
-    uint64x2_t intermed = vsha512hq_u64(sum, fg, de);
-    *gh = vsha512h2q_u64(intermed, *cd, *ab);
-    *cd = vaddq_u64(*cd, intermed);
-}
-
-FUNC_ISA
-static inline void sha512_neon_block(sha512_neon_core *core, const uint8_t *p)
-{
-    uint64x2_t s0, s1, s2, s3, s4, s5, s6, s7;
-
-    uint64x2_t ab = core->ab, cd = core->cd, ef = core->ef, gh = core->gh;
-
-    s0 = sha512_neon_load_input(p + 16*0);
-    sha512_neon_round2(0, s0, &ab, &cd, &ef, &gh);
-    s1 = sha512_neon_load_input(p + 16*1);
-    sha512_neon_round2(2, s1, &gh, &ab, &cd, &ef);
-    s2 = sha512_neon_load_input(p + 16*2);
-    sha512_neon_round2(4, s2, &ef, &gh, &ab, &cd);
-    s3 = sha512_neon_load_input(p + 16*3);
-    sha512_neon_round2(6, s3, &cd, &ef, &gh, &ab);
-    s4 = sha512_neon_load_input(p + 16*4);
-    sha512_neon_round2(8, s4, &ab, &cd, &ef, &gh);
-    s5 = sha512_neon_load_input(p + 16*5);
-    sha512_neon_round2(10, s5, &gh, &ab, &cd, &ef);
-    s6 = sha512_neon_load_input(p + 16*6);
-    sha512_neon_round2(12, s6, &ef, &gh, &ab, &cd);
-    s7 = sha512_neon_load_input(p + 16*7);
-    sha512_neon_round2(14, s7, &cd, &ef, &gh, &ab);
-    s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7);
-    sha512_neon_round2(16, s0, &ab, &cd, &ef, &gh);
-    s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0);
-    sha512_neon_round2(18, s1, &gh, &ab, &cd, &ef);
-    s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1);
-    sha512_neon_round2(20, s2, &ef, &gh, &ab, &cd);
-    s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2);
-    sha512_neon_round2(22, s3, &cd, &ef, &gh, &ab);
-    s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3);
-    sha512_neon_round2(24, s4, &ab, &cd, &ef, &gh);
-    s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4);
-    sha512_neon_round2(26, s5, &gh, &ab, &cd, &ef);
-    s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5);
-    sha512_neon_round2(28, s6, &ef, &gh, &ab, &cd);
-    s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6);
-    sha512_neon_round2(30, s7, &cd, &ef, &gh, &ab);
-    s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7);
-    sha512_neon_round2(32, s0, &ab, &cd, &ef, &gh);
-    s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0);
-    sha512_neon_round2(34, s1, &gh, &ab, &cd, &ef);
-    s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1);
-    sha512_neon_round2(36, s2, &ef, &gh, &ab, &cd);
-    s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2);
-    sha512_neon_round2(38, s3, &cd, &ef, &gh, &ab);
-    s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3);
-    sha512_neon_round2(40, s4, &ab, &cd, &ef, &gh);
-    s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4);
-    sha512_neon_round2(42, s5, &gh, &ab, &cd, &ef);
-    s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5);
-    sha512_neon_round2(44, s6, &ef, &gh, &ab, &cd);
-    s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6);
-    sha512_neon_round2(46, s7, &cd, &ef, &gh, &ab);
-    s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7);
-    sha512_neon_round2(48, s0, &ab, &cd, &ef, &gh);
-    s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0);
-    sha512_neon_round2(50, s1, &gh, &ab, &cd, &ef);
-    s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1);
-    sha512_neon_round2(52, s2, &ef, &gh, &ab, &cd);
-    s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2);
-    sha512_neon_round2(54, s3, &cd, &ef, &gh, &ab);
-    s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3);
-    sha512_neon_round2(56, s4, &ab, &cd, &ef, &gh);
-    s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4);
-    sha512_neon_round2(58, s5, &gh, &ab, &cd, &ef);
-    s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5);
-    sha512_neon_round2(60, s6, &ef, &gh, &ab, &cd);
-    s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6);
-    sha512_neon_round2(62, s7, &cd, &ef, &gh, &ab);
-    s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7);
-    sha512_neon_round2(64, s0, &ab, &cd, &ef, &gh);
-    s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0);
-    sha512_neon_round2(66, s1, &gh, &ab, &cd, &ef);
-    s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1);
-    sha512_neon_round2(68, s2, &ef, &gh, &ab, &cd);
-    s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2);
-    sha512_neon_round2(70, s3, &cd, &ef, &gh, &ab);
-    s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3);
-    sha512_neon_round2(72, s4, &ab, &cd, &ef, &gh);
-    s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4);
-    sha512_neon_round2(74, s5, &gh, &ab, &cd, &ef);
-    s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5);
-    sha512_neon_round2(76, s6, &ef, &gh, &ab, &cd);
-    s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6);
-    sha512_neon_round2(78, s7, &cd, &ef, &gh, &ab);
-
-    core->ab = vaddq_u64(core->ab, ab);
-    core->cd = vaddq_u64(core->cd, cd);
-    core->ef = vaddq_u64(core->ef, ef);
-    core->gh = vaddq_u64(core->gh, gh);
-}
-
-typedef struct sha512_neon {
-    sha512_neon_core core;
-    sha512_block blk;
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} sha512_neon;
-
-static void sha512_neon_write(BinarySink *bs, const void *vp, size_t len);
-
-static ssh_hash *sha512_neon_new(const ssh_hashalg *alg)
-{
-    if (!sha512_hw_available_cached())
-        return NULL;
-
-    sha512_neon *s = snew(sha512_neon);
-
-    s->hash.vt = alg;
-    BinarySink_INIT(s, sha512_neon_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-    return &s->hash;
-}
-
-static void sha512_neon_reset(ssh_hash *hash)
-{
-    sha512_neon *s = container_of(hash, sha512_neon, hash);
-    const uint64_t *iv = (const uint64_t *)hash->vt->extra;
-
-    s->core.ab = vld1q_u64(iv);
-    s->core.cd = vld1q_u64(iv+2);
-    s->core.ef = vld1q_u64(iv+4);
-    s->core.gh = vld1q_u64(iv+6);
-
-    sha512_block_setup(&s->blk);
-}
-
-static void sha512_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    sha512_neon *copy = container_of(hcopy, sha512_neon, hash);
-    sha512_neon *orig = container_of(horig, sha512_neon, hash);
-
-    *copy = *orig; /* structure copy */
-
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-static void sha512_neon_free(ssh_hash *hash)
-{
-    sha512_neon *s = container_of(hash, sha512_neon, hash);
-    smemclr(s, sizeof(*s));
-    sfree(s);
-}
-
-static void sha512_neon_write(BinarySink *bs, const void *vp, size_t len)
-{
-    sha512_neon *s = BinarySink_DOWNCAST(bs, sha512_neon);
-
-    while (len > 0)
-        if (sha512_block_write(&s->blk, &vp, &len))
-            sha512_neon_block(&s->core, s->blk.block);
-}
-
-static void sha512_neon_digest(ssh_hash *hash, uint8_t *digest)
-{
-    sha512_neon *s = container_of(hash, sha512_neon, hash);
-
-    sha512_block_pad(&s->blk, BinarySink_UPCAST(s));
-
-    vst1q_u8(digest,    vrev64q_u8(vreinterpretq_u8_u64(s->core.ab)));
-    vst1q_u8(digest+16, vrev64q_u8(vreinterpretq_u8_u64(s->core.cd)));
-    vst1q_u8(digest+32, vrev64q_u8(vreinterpretq_u8_u64(s->core.ef)));
-    vst1q_u8(digest+48, vrev64q_u8(vreinterpretq_u8_u64(s->core.gh)));
-}
-
-static void sha384_neon_digest(ssh_hash *hash, uint8_t *digest)
-{
-    sha512_neon *s = container_of(hash, sha512_neon, hash);
-
-    sha512_block_pad(&s->blk, BinarySink_UPCAST(s));
-
-    vst1q_u8(digest,    vrev64q_u8(vreinterpretq_u8_u64(s->core.ab)));
-    vst1q_u8(digest+16, vrev64q_u8(vreinterpretq_u8_u64(s->core.cd)));
-    vst1q_u8(digest+32, vrev64q_u8(vreinterpretq_u8_u64(s->core.ef)));
-}
-
-const ssh_hashalg ssh_sha512_hw = {
-    .new = sha512_neon_new,
-    .reset = sha512_neon_reset,
-    .copyfrom = sha512_neon_copyfrom,
-    .digest = sha512_neon_digest,
-    .free = sha512_neon_free,
-    .hlen = 64,
-    .blocklen = 128,
-    HASHALG_NAMES_ANNOTATED("SHA-512", "NEON accelerated"),
-    .extra = sha512_initial_state,
-};
-
-const ssh_hashalg ssh_sha384_hw = {
-    .new = sha512_neon_new,
-    .reset = sha512_neon_reset,
-    .copyfrom = sha512_neon_copyfrom,
-    .digest = sha384_neon_digest,
-    .free = sha512_neon_free,
-    .hlen = 48,
-    .blocklen = 128,
-    HASHALG_NAMES_ANNOTATED("SHA-384", "NEON accelerated"),
-    .extra = sha384_initial_state,
-};
-
-/* ----------------------------------------------------------------------
- * Stub functions if we have no hardware-accelerated SHA-512. In this
- * case, sha512_hw_new returns NULL (though it should also never be
- * selected by sha512_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_SHA512 == HW_SHA512_NONE
-
-static bool sha512_hw_available(void)
-{
-    return false;
-}
-
-static ssh_hash *sha512_stub_new(const ssh_hashalg *alg)
-{
-    return NULL;
-}
-
-#define STUB_BODY { unreachable("Should never be called"); }
-
-static void sha512_stub_reset(ssh_hash *hash) STUB_BODY
-static void sha512_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY
-static void sha512_stub_free(ssh_hash *hash) STUB_BODY
-static void sha512_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY
-
-const ssh_hashalg ssh_sha512_hw = {
-    // WINSCP
-    /*.new =*/ sha512_stub_new,
-    /*.reset =*/ sha512_stub_reset,
-    /*.copyfrom =*/ sha512_stub_copyfrom,
-    /*.digest =*/ sha512_stub_digest,
-    /*.free =*/ sha512_stub_free,
-    /*.hlen =*/ 64,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_ANNOTATED("SHA-512", "!NONEXISTENT ACCELERATED VERSION!"),
-    NULL, // WINSCP
-};
-
-const ssh_hashalg ssh_sha384_hw = {
-    // WINSCP
-    /*.new =*/ sha512_stub_new,
-    /*.reset =*/ sha512_stub_reset,
-    /*.copyfrom =*/ sha512_stub_copyfrom,
-    /*.digest =*/ sha512_stub_digest,
-    /*.free =*/ sha512_stub_free,
-    /*.hlen =*/ 48,
-    /*.blocklen =*/ 128,
-    HASHALG_NAMES_ANNOTATED("SHA-384", "!NONEXISTENT ACCELERATED VERSION!"),
-    NULL, // WINSCP
-};
-
-#endif /* HW_SHA512 */

+ 0 - 954
source/putty/sshsha.c

@@ -1,954 +0,0 @@
-/*
- * SHA-1 algorithm as described at
- *
- *   http://csrc.nist.gov/cryptval/shs.html
- */
-
-#include "ssh.h"
-#include <assert.h>
-
-/*
- * Start by deciding whether we can support hardware SHA at all.
- */
-#define HW_SHA1_NONE 0
-#define HW_SHA1_NI 1
-#define HW_SHA1_NEON 2
-
-#ifdef _FORCE_SHA_NI
-#   define HW_SHA1 HW_SHA1_NI
-#elif defined(__clang__)
-#   if __has_attribute(target) && __has_include(<wmmintrin.h>) &&       \
-    (defined(__x86_64__) || defined(__i386))
-#       define HW_SHA1 HW_SHA1_NI
-#   endif
-#elif defined(__GNUC__)
-#    if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \
-        (defined(__x86_64__) || defined(__i386))
-#       define HW_SHA1 HW_SHA1_NI
-#    endif
-#elif defined (_MSC_VER)
-#   if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729
-#      define HW_SHA1 HW_SHA1_NI
-#   endif
-#endif
-
-#ifdef _FORCE_SHA_NEON
-#   define HW_SHA1 HW_SHA1_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 SHA without having to enable anything by hand */
-#   define HW_SHA1 HW_SHA1_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_SHA1 HW_SHA1_NEON
-#       define USE_CLANG_ATTR_TARGET_AARCH64
-#   endif
-#elif defined _MSC_VER
-    /* Visual Studio supports the crypto extension when targeting
-     * AArch64, but as of VS2017, the AArch32 header doesn't quite
-     * manage it (declaring the shae/shad intrinsics without a round
-     * key operand). */
-#   if defined _M_ARM64
-#       define HW_SHA1 HW_SHA1_NEON
-#       if defined _M_ARM64
-#           define USE_ARM64_NEON_H /* unusual header name in this case */
-#       endif
-#   endif
-#endif
-
-#if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA1
-#   undef HW_SHA1
-#   define HW_SHA1 HW_SHA1_NONE
-#endif
-
-/*
- * The actual query function that asks if hardware acceleration is
- * available.
- */
-static bool sha1_hw_available(void);
-
-/*
- * The top-level selection function, caching the results of
- * sha1_hw_available() so it only has to run once.
- */
-static bool sha1_hw_available_cached(void)
-{
-    static bool initialised = false;
-    static bool hw_available;
-    if (!initialised) {
-        hw_available = sha1_hw_available();
-        initialised = true;
-    }
-    return hw_available;
-}
-
-static ssh_hash *sha1_select(const ssh_hashalg *alg)
-{
-    const ssh_hashalg *real_alg =
-        sha1_hw_available_cached() ? &ssh_sha1_hw : &ssh_sha1_sw;
-
-    return ssh_hash_new(real_alg);
-}
-
-const ssh_hashalg ssh_sha1 = {
-    // WINSCP
-    /*.new =*/ sha1_select,
-    NULL,
-    NULL,
-    NULL,
-    NULL,
-    /*.hlen =*/ 20,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_ANNOTATED("SHA-1", "dummy selector vtable"),
-    NULL,
-};
-
-/* ----------------------------------------------------------------------
- * Definitions likely to be helpful to multiple implementations.
- */
-
-static const uint32_t sha1_initial_state[] = {
-    0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0,
-};
-
-#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);
-    { // WINSCP
-    size_t i;
-    for (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");
-    } // WINSCP
-}
-
-/* ----------------------------------------------------------------------
- * Software implementation of SHA-1.
- */
-
-static inline uint32_t rol(uint32_t x, unsigned y)
-{
-    return (x << (31 & y)) | (x >> (31 & (uint32_t)(-(int32_t)y))); // WINSCP
-}
-
-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;
-
-    size_t t; // WINSCP
-    for (t = 0; t < 16; t++)
-        w[t] = GET_32BIT_MSB_FIRST(block + 4*t);
-
-    for (t = 16; t < SHA1_ROUNDS; t++) // WINSCP
-        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];
-
-    t = 0;
-    { // WINSCP
-    size_t u; // WINSCP
-    for (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 (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 (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 (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));
-    } // WINSCP
-}
-
-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));
-    { // WINSCP
-    size_t i; // WINSCP
-    for (i = 0; i < 5; i++)
-        PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]);
-    } // WINSCP
-}
-
-const ssh_hashalg ssh_sha1_sw = {
-    // WINSCP
-    /*.new =*/ sha1_sw_new,
-    /*.reset =*/ sha1_sw_reset,
-    /*.copyfrom =*/ sha1_sw_copyfrom,
-    /*.digest =*/ sha1_sw_digest,
-    /*.free =*/ sha1_sw_free,
-    /*.hlen =*/ 20,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_BARE("SHA-1"), // WINSCP (removed "unaccelerated" annotation)
-    NULL,
-};
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of SHA-1 using x86 SHA-NI.
- */
-
-#if HW_SHA1 == HW_SHA1_NI
-
-/*
- * Set target architecture for Clang and GCC
- */
-
-#if defined(__clang__) || defined(__GNUC__)
-#    define FUNC_ISA __attribute__ ((target("sse4.1,sha")))
-#if !defined(__clang__)
-#    pragma GCC target("sha")
-#    pragma GCC target("sse4.1")
-#endif
-#else
-#    define FUNC_ISA
-#endif
-
-#include <wmmintrin.h>
-#include <smmintrin.h>
-#include <immintrin.h>
-#if defined(__clang__) || defined(__GNUC__)
-#include <shaintrin.h>
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#include <cpuid.h>
-#define GET_CPU_ID_0(out)                               \
-    __cpuid(0, (out)[0], (out)[1], (out)[2], (out)[3])
-#define GET_CPU_ID_7(out)                                       \
-    __cpuid_count(7, 0, (out)[0], (out)[1], (out)[2], (out)[3])
-#else
-#define GET_CPU_ID_0(out) __cpuid(out, 0)
-#define GET_CPU_ID_7(out) __cpuidex(out, 7, 0)
-#endif
-
-static bool sha1_hw_available(void)
-{
-    unsigned int CPUInfo[4];
-    GET_CPU_ID_0(CPUInfo);
-    if (CPUInfo[0] < 7)
-        return false;
-
-    GET_CPU_ID_7(CPUInfo);
-    return CPUInfo[1] & (1 << 29); /* Check SHA */
-}
-
-/* SHA1 implementation using new instructions
-   The code is based on Jeffrey Walton's SHA1 implementation:
-   https://github.com/noloader/SHA-Intrinsics
-*/
-FUNC_ISA
-static inline void sha1_ni_block(__m128i *core, const uint8_t *p)
-{
-    __m128i ABCD, E0, E1, MSG0, MSG1, MSG2, MSG3;
-    const __m128i MASK = _mm_set_epi64x(
-        0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL);
-
-    const __m128i *block = (const __m128i *)p;
-
-    /* Load initial values */
-    ABCD = core[0];
-    E0 = core[1];
-
-    /* Rounds 0-3 */
-    MSG0 = _mm_loadu_si128(block);
-    MSG0 = _mm_shuffle_epi8(MSG0, MASK);
-    E0 = _mm_add_epi32(E0, MSG0);
-    E1 = ABCD;
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
-
-    /* Rounds 4-7 */
-    MSG1 = _mm_loadu_si128(block + 1);
-    MSG1 = _mm_shuffle_epi8(MSG1, MASK);
-    E1 = _mm_sha1nexte_epu32(E1, MSG1);
-    E0 = ABCD;
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
-    MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
-
-    /* Rounds 8-11 */
-    MSG2 = _mm_loadu_si128(block + 2);
-    MSG2 = _mm_shuffle_epi8(MSG2, MASK);
-    E0 = _mm_sha1nexte_epu32(E0, MSG2);
-    E1 = ABCD;
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
-    MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
-    MSG0 = _mm_xor_si128(MSG0, MSG2);
-
-    /* Rounds 12-15 */
-    MSG3 = _mm_loadu_si128(block + 3);
-    MSG3 = _mm_shuffle_epi8(MSG3, MASK);
-    E1 = _mm_sha1nexte_epu32(E1, MSG3);
-    E0 = ABCD;
-    MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0);
-    MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
-    MSG1 = _mm_xor_si128(MSG1, MSG3);
-
-    /* Rounds 16-19 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG0);
-    E1 = ABCD;
-    MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0);
-    MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
-    MSG2 = _mm_xor_si128(MSG2, MSG0);
-
-    /* Rounds 20-23 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG1);
-    E0 = ABCD;
-    MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
-    MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
-    MSG3 = _mm_xor_si128(MSG3, MSG1);
-
-    /* Rounds 24-27 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG2);
-    E1 = ABCD;
-    MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
-    MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
-    MSG0 = _mm_xor_si128(MSG0, MSG2);
-
-    /* Rounds 28-31 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG3);
-    E0 = ABCD;
-    MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
-    MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
-    MSG1 = _mm_xor_si128(MSG1, MSG3);
-
-    /* Rounds 32-35 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG0);
-    E1 = ABCD;
-    MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1);
-    MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
-    MSG2 = _mm_xor_si128(MSG2, MSG0);
-
-    /* Rounds 36-39 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG1);
-    E0 = ABCD;
-    MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1);
-    MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
-    MSG3 = _mm_xor_si128(MSG3, MSG1);
-
-    /* Rounds 40-43 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG2);
-    E1 = ABCD;
-    MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
-    MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
-    MSG0 = _mm_xor_si128(MSG0, MSG2);
-
-    /* Rounds 44-47 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG3);
-    E0 = ABCD;
-    MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
-    MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
-    MSG1 = _mm_xor_si128(MSG1, MSG3);
-
-    /* Rounds 48-51 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG0);
-    E1 = ABCD;
-    MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
-    MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
-    MSG2 = _mm_xor_si128(MSG2, MSG0);
-
-    /* Rounds 52-55 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG1);
-    E0 = ABCD;
-    MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2);
-    MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1);
-    MSG3 = _mm_xor_si128(MSG3, MSG1);
-
-    /* Rounds 56-59 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG2);
-    E1 = ABCD;
-    MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2);
-    MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2);
-    MSG0 = _mm_xor_si128(MSG0, MSG2);
-
-    /* Rounds 60-63 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG3);
-    E0 = ABCD;
-    MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
-    MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3);
-    MSG1 = _mm_xor_si128(MSG1, MSG3);
-
-    /* Rounds 64-67 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG0);
-    E1 = ABCD;
-    MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
-    MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0);
-    MSG2 = _mm_xor_si128(MSG2, MSG0);
-
-    /* Rounds 68-71 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG1);
-    E0 = ABCD;
-    MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
-    MSG3 = _mm_xor_si128(MSG3, MSG1);
-
-    /* Rounds 72-75 */
-    E0 = _mm_sha1nexte_epu32(E0, MSG2);
-    E1 = ABCD;
-    MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2);
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3);
-
-    /* Rounds 76-79 */
-    E1 = _mm_sha1nexte_epu32(E1, MSG3);
-    E0 = ABCD;
-    ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3);
-
-    /* Combine state */
-    core[0] = _mm_add_epi32(ABCD, core[0]);
-    core[1] = _mm_sha1nexte_epu32(E0, core[1]);
-}
-
-typedef struct sha1_ni {
-    /*
-     * core[0] stores the first four words of the SHA-1 state. core[1]
-     * stores just the fifth word, in the vector lane at the highest
-     * address.
-     */
-    __m128i core[2];
-    sha1_block blk;
-    void *pointer_to_free;
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} sha1_ni;
-
-static void sha1_ni_write(BinarySink *bs, const void *vp, size_t len);
-
-static sha1_ni *sha1_ni_alloc(void)
-{
-    /*
-     * 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(sha1_ni) + 15);
-    uintptr_t alloc_address = (uintptr_t)allocation;
-    uintptr_t aligned_address = (alloc_address + 15) & ~15;
-    sha1_ni *s = (sha1_ni *)aligned_address;
-    s->pointer_to_free = allocation;
-    return s;
-}
-
-static ssh_hash *sha1_ni_new(const ssh_hashalg *alg)
-{
-    if (!sha1_hw_available_cached())
-        return NULL;
-
-    sha1_ni *s = sha1_ni_alloc();
-
-    s->hash.vt = alg;
-    BinarySink_INIT(s, sha1_ni_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-    return &s->hash;
-}
-
-FUNC_ISA static void sha1_ni_reset(ssh_hash *hash)
-{
-    sha1_ni *s = container_of(hash, sha1_ni, hash);
-
-    /* Initialise the core vectors in their storage order */
-    s->core[0] = _mm_set_epi64x(
-        0x67452301efcdab89ULL, 0x98badcfe10325476ULL);
-    s->core[1] = _mm_set_epi32(0xc3d2e1f0, 0, 0, 0);
-
-    sha1_block_setup(&s->blk);
-}
-
-static void sha1_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    sha1_ni *copy = container_of(hcopy, sha1_ni, hash);
-    sha1_ni *orig = container_of(horig, sha1_ni, hash);
-
-    void *ptf_save = copy->pointer_to_free;
-    *copy = *orig; /* structure copy */
-    copy->pointer_to_free = ptf_save;
-
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-static void sha1_ni_free(ssh_hash *hash)
-{
-    sha1_ni *s = container_of(hash, sha1_ni, hash);
-
-    void *ptf = s->pointer_to_free;
-    smemclr(s, sizeof(*s));
-    sfree(ptf);
-}
-
-static void sha1_ni_write(BinarySink *bs, const void *vp, size_t len)
-{
-    sha1_ni *s = BinarySink_DOWNCAST(bs, sha1_ni);
-
-    while (len > 0)
-        if (sha1_block_write(&s->blk, &vp, &len))
-            sha1_ni_block(s->core, s->blk.block);
-}
-
-FUNC_ISA static void sha1_ni_digest(ssh_hash *hash, uint8_t *digest)
-{
-    sha1_ni *s = container_of(hash, sha1_ni, hash);
-
-    sha1_block_pad(&s->blk, BinarySink_UPCAST(s));
-
-    /* Rearrange the first vector into its output order */
-    __m128i abcd = _mm_shuffle_epi32(s->core[0], 0x1B);
-
-    /* Byte-swap it into the output endianness */
-    const __m128i mask = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12);
-    abcd = _mm_shuffle_epi8(abcd, mask);
-
-    /* And store it */
-    _mm_storeu_si128((__m128i *)digest, abcd);
-
-    /* Finally, store the leftover word */
-    uint32_t e = _mm_extract_epi32(s->core[1], 3);
-    PUT_32BIT_MSB_FIRST(digest + 16, e);
-}
-
-const ssh_hashalg ssh_sha1_hw = {
-    .new = sha1_ni_new,
-    .reset = sha1_ni_reset,
-    .copyfrom = sha1_ni_copyfrom,
-    .digest = sha1_ni_digest,
-    .free = sha1_ni_free,
-    .hlen = 20,
-    .blocklen = 64,
-    HASHALG_NAMES_ANNOTATED("SHA-1", "SHA-NI accelerated"),
-};
-
-/* ----------------------------------------------------------------------
- * Hardware-accelerated implementation of SHA-1 using Arm NEON.
- */
-
-#elif HW_SHA1 == HW_SHA1_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 SHA 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_SHA2 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 sha1_hw_available(void)
-{
-    /*
-     * For Arm, we delegate to a per-platform detection function (see
-     * explanation in sshaes.c).
-     */
-    return platform_sha1_hw_available();
-}
-
-typedef struct sha1_neon_core sha1_neon_core;
-struct sha1_neon_core {
-    uint32x4_t abcd;
-    uint32_t e;
-};
-
-FUNC_ISA
-static inline uint32x4_t sha1_neon_load_input(const uint8_t *p)
-{
-    return vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(p)));
-}
-
-FUNC_ISA
-static inline uint32x4_t sha1_neon_schedule_update(
-    uint32x4_t m4, uint32x4_t m3, uint32x4_t m2, uint32x4_t m1)
-{
-    return vsha1su1q_u32(vsha1su0q_u32(m4, m3, m2), m1);
-}
-
-/*
- * SHA-1 has three different kinds of round, differing in whether they
- * use the Ch, Maj or Par functions defined above. Each one uses a
- * separate NEON instruction, so we define three inline functions for
- * the different round types using this macro.
- *
- * The two batches of Par-type rounds also use a different constant,
- * but that's passed in as an operand, so we don't need a fourth
- * inline function just for that.
- */
-#define SHA1_NEON_ROUND_FN(type)                                        \
-    FUNC_ISA static inline sha1_neon_core sha1_neon_round4_##type(      \
-        sha1_neon_core old, uint32x4_t sched, uint32x4_t constant)      \
-    {                                                                   \
-        sha1_neon_core new;                                             \
-        uint32x4_t round_input = vaddq_u32(sched, constant);            \
-        new.abcd = vsha1##type##q_u32(old.abcd, old.e, round_input);    \
-        new.e = vsha1h_u32(vget_lane_u32(vget_low_u32(old.abcd), 0));   \
-        return new;                                                     \
-    }
-SHA1_NEON_ROUND_FN(c)
-SHA1_NEON_ROUND_FN(p)
-SHA1_NEON_ROUND_FN(m)
-
-FUNC_ISA
-static inline void sha1_neon_block(sha1_neon_core *core, const uint8_t *p)
-{
-    uint32x4_t constant, s0, s1, s2, s3;
-    sha1_neon_core cr = *core;
-
-    constant = vdupq_n_u32(SHA1_STAGE0_CONSTANT);
-    s0 = sha1_neon_load_input(p);
-    cr = sha1_neon_round4_c(cr, s0, constant);
-    s1 = sha1_neon_load_input(p + 16);
-    cr = sha1_neon_round4_c(cr, s1, constant);
-    s2 = sha1_neon_load_input(p + 32);
-    cr = sha1_neon_round4_c(cr, s2, constant);
-    s3 = sha1_neon_load_input(p + 48);
-    cr = sha1_neon_round4_c(cr, s3, constant);
-    s0 = sha1_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha1_neon_round4_c(cr, s0, constant);
-
-    constant = vdupq_n_u32(SHA1_STAGE1_CONSTANT);
-    s1 = sha1_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha1_neon_round4_p(cr, s1, constant);
-    s2 = sha1_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha1_neon_round4_p(cr, s2, constant);
-    s3 = sha1_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha1_neon_round4_p(cr, s3, constant);
-    s0 = sha1_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha1_neon_round4_p(cr, s0, constant);
-    s1 = sha1_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha1_neon_round4_p(cr, s1, constant);
-
-    constant = vdupq_n_u32(SHA1_STAGE2_CONSTANT);
-    s2 = sha1_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha1_neon_round4_m(cr, s2, constant);
-    s3 = sha1_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha1_neon_round4_m(cr, s3, constant);
-    s0 = sha1_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha1_neon_round4_m(cr, s0, constant);
-    s1 = sha1_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha1_neon_round4_m(cr, s1, constant);
-    s2 = sha1_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha1_neon_round4_m(cr, s2, constant);
-
-    constant = vdupq_n_u32(SHA1_STAGE3_CONSTANT);
-    s3 = sha1_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha1_neon_round4_p(cr, s3, constant);
-    s0 = sha1_neon_schedule_update(s0, s1, s2, s3);
-    cr = sha1_neon_round4_p(cr, s0, constant);
-    s1 = sha1_neon_schedule_update(s1, s2, s3, s0);
-    cr = sha1_neon_round4_p(cr, s1, constant);
-    s2 = sha1_neon_schedule_update(s2, s3, s0, s1);
-    cr = sha1_neon_round4_p(cr, s2, constant);
-    s3 = sha1_neon_schedule_update(s3, s0, s1, s2);
-    cr = sha1_neon_round4_p(cr, s3, constant);
-
-    core->abcd = vaddq_u32(core->abcd, cr.abcd);
-    core->e += cr.e;
-}
-
-typedef struct sha1_neon {
-    sha1_neon_core core;
-    sha1_block blk;
-    BinarySink_IMPLEMENTATION;
-    ssh_hash hash;
-} sha1_neon;
-
-static void sha1_neon_write(BinarySink *bs, const void *vp, size_t len);
-
-static ssh_hash *sha1_neon_new(const ssh_hashalg *alg)
-{
-    if (!sha1_hw_available_cached())
-        return NULL;
-
-    sha1_neon *s = snew(sha1_neon);
-
-    s->hash.vt = alg;
-    BinarySink_INIT(s, sha1_neon_write);
-    BinarySink_DELEGATE_INIT(&s->hash, s);
-    return &s->hash;
-}
-
-static void sha1_neon_reset(ssh_hash *hash)
-{
-    sha1_neon *s = container_of(hash, sha1_neon, hash);
-
-    s->core.abcd = vld1q_u32(sha1_initial_state);
-    s->core.e = sha1_initial_state[4];
-
-    sha1_block_setup(&s->blk);
-}
-
-static void sha1_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig)
-{
-    sha1_neon *copy = container_of(hcopy, sha1_neon, hash);
-    sha1_neon *orig = container_of(horig, sha1_neon, hash);
-
-    *copy = *orig; /* structure copy */
-
-    BinarySink_COPIED(copy);
-    BinarySink_DELEGATE_INIT(&copy->hash, copy);
-}
-
-static void sha1_neon_free(ssh_hash *hash)
-{
-    sha1_neon *s = container_of(hash, sha1_neon, hash);
-    smemclr(s, sizeof(*s));
-    sfree(s);
-}
-
-static void sha1_neon_write(BinarySink *bs, const void *vp, size_t len)
-{
-    sha1_neon *s = BinarySink_DOWNCAST(bs, sha1_neon);
-
-    while (len > 0)
-        if (sha1_block_write(&s->blk, &vp, &len))
-            sha1_neon_block(&s->core, s->blk.block);
-}
-
-static void sha1_neon_digest(ssh_hash *hash, uint8_t *digest)
-{
-    sha1_neon *s = container_of(hash, sha1_neon, hash);
-
-    sha1_block_pad(&s->blk, BinarySink_UPCAST(s));
-    vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd)));
-    PUT_32BIT_MSB_FIRST(digest + 16, s->core.e);
-}
-
-const ssh_hashalg ssh_sha1_hw = {
-    .new = sha1_neon_new,
-    .reset = sha1_neon_reset,
-    .copyfrom = sha1_neon_copyfrom,
-    .digest = sha1_neon_digest,
-    .free = sha1_neon_free,
-    .hlen = 20,
-    .blocklen = 64,
-    HASHALG_NAMES_ANNOTATED("SHA-1", "NEON accelerated"),
-};
-
-/* ----------------------------------------------------------------------
- * Stub functions if we have no hardware-accelerated SHA-1. In this
- * case, sha1_hw_new returns NULL (though it should also never be
- * selected by sha1_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_SHA1 == HW_SHA1_NONE
-
-static bool sha1_hw_available(void)
-{
-    return false;
-}
-
-static ssh_hash *sha1_stub_new(const ssh_hashalg *alg)
-{
-    return NULL;
-}
-
-#define STUB_BODY { unreachable("Should never be called"); }
-
-static void sha1_stub_reset(ssh_hash *hash) STUB_BODY
-static void sha1_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY
-static void sha1_stub_free(ssh_hash *hash) STUB_BODY
-static void sha1_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY
-
-const ssh_hashalg ssh_sha1_hw = {
-    // WINSCP
-    /*.new =*/ sha1_stub_new,
-    /*.reset =*/ sha1_stub_reset,
-    /*.copyfrom =*/ sha1_stub_copyfrom,
-    /*.digest =*/ sha1_stub_digest,
-    /*.free =*/ sha1_stub_free,
-    /*.hlen =*/ 20,
-    /*.blocklen =*/ 64,
-    HASHALG_NAMES_ANNOTATED("SHA-1", "!NONEXISTENT ACCELERATED VERSION!"),
-    NULL,
-};
-
-#endif /* HW_SHA1 */

+ 0 - 357
source/putty/sshsha3.c

@@ -1,357 +0,0 @@
-/*
- * SHA-3, as defined in FIPS PUB 202.
- */
-
-#include <assert.h>
-#include <string.h>
-#include "ssh.h"
-
-static inline uint64_t rol(uint64_t x, unsigned shift)
-{
-    unsigned L = (+shift) & 63;
-#pragma option push -w-ngu // WINSCP
-    unsigned R = (-shift) & 63;
-#pragma option pop // WINSCP
-    return (x << L) | (x >> R);
-}
-
-/*
- * General Keccak is defined such that its state is a 5x5 array of
- * words which can be any power-of-2 size from 1 up to 64. SHA-3 fixes
- * on 64, and so do we.
- *
- * The number of rounds is defined as 12 + 2k if the word size is 2^k.
- * Here we have 64-bit words only, so k=6, so 24 rounds always.
- */
-typedef uint64_t keccak_core_state[5][5];
-#define NROUNDS 24               /* would differ for other word sizes */
-static const uint64_t round_constants[NROUNDS];
-static const unsigned rotation_counts[5][5];
-
-/*
- * Core Keccak transform: just squodge the state around internally,
- * without adding or extracting any data from it.
- */
-static void keccak_transform(keccak_core_state A)
-{
-    union {
-        uint64_t C[5];
-        uint64_t B[5][5];
-    } u;
-
-    unsigned round; // WINSCP
-    for (round = 0; round < NROUNDS; round++) {
-        /* theta step */
-        unsigned x; // WINSCP
-        for (x = 0; x < 5; x++)
-            u.C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4];
-        for (x = 0; x < 5; x++) { // WINSCP
-            uint64_t D = rol(u.C[(x+1) % 5], 1) ^ u.C[(x+4) % 5];
-            { // WINSCP
-            unsigned y; // WINSCP
-            for (y = 0; y < 5; y++)
-                A[x][y] ^= D;
-            } // WINSCP
-        }
-
-        /* rho and pi steps */
-        for (x = 0; x < 5; x++) // WINSCP
-        { // WINSCP
-            unsigned y; // WINSCP
-            for (y = 0; y < 5; y++)
-                u.B[y][(2*x+3*y) % 5] = rol(A[x][y], rotation_counts[x][y]);
-        } // WINSCP
-
-        /* chi step */
-        for (x = 0; x < 5; x++) // WINSCP
-        { // WINSCP
-            unsigned y; // WINSCP
-            for (y = 0; y < 5; y++)
-                A[x][y] = u.B[x][y] ^ (u.B[(x+2)%5][y] & ~u.B[(x+1)%5][y]);
-        } // WINSCP
-
-        /* iota step */
-        A[0][0] ^= round_constants[round];
-    }
-
-    smemclr(&u, sizeof(u));
-}
-
-typedef struct {
-    keccak_core_state A;
-    unsigned char bytes[25*8];
-    unsigned char first_pad_byte;
-    size_t bytes_got, bytes_wanted, hash_bytes;
-} keccak_state;
-
-/*
- * Keccak accumulation function: given a piece of message, add it to
- * the hash.
- */
-static void keccak_accumulate(keccak_state *s, const void *vdata, size_t len)
-{
-    const unsigned char *data = (const unsigned char *)vdata;
-
-    while (len >= s->bytes_wanted - s->bytes_got) {
-        size_t b = s->bytes_wanted - s->bytes_got;
-        memcpy(s->bytes + s->bytes_got, data, b);
-        len -= b;
-        data += b;
-
-        { // WINSCP
-        size_t n = 0;
-        unsigned y; // WINSCP
-        for (y = 0; y < 5; y++) {
-            unsigned x; // WINSCP
-            for (x = 0; x < 5; x++) {
-                if (n >= s->bytes_wanted)
-                    break;
-
-                s->A[x][y] ^= GET_64BIT_LSB_FIRST(s->bytes + n);
-                n += 8;
-            }
-        }
-        keccak_transform(s->A);
-
-        s->bytes_got = 0;
-        } // WINSCP
-    }
-
-    memcpy(s->bytes + s->bytes_got, data, len);
-    s->bytes_got += len;
-}
-
-/*
- * Keccak output function.
- */
-static void keccak_output(keccak_state *s, void *voutput)
-{
-    unsigned char *output = (unsigned char *)voutput;
-
-    /*
-     * Add message padding.
-     */
-    {
-        unsigned char padding[25*8];
-        size_t len = s->bytes_wanted - s->bytes_got;
-        if (len == 0)
-            len = s->bytes_wanted;
-        memset(padding, 0, len);
-        padding[0] |= s->first_pad_byte;
-        padding[len-1] |= 0x80;
-        keccak_accumulate(s, padding, len);
-    }
-
-    { // WINSCP
-    size_t n = 0;
-    unsigned y; // WINSCP
-    for (y = 0; y < 5; y++) {
-        unsigned x; // WINSCP
-        for (x = 0; x < 5; x++) {
-            size_t to_copy = s->hash_bytes - n;
-            if (to_copy == 0)
-                break;
-            if (to_copy > 8)
-                to_copy = 8;
-            { // WINSCP
-            unsigned char outbytes[8];
-            PUT_64BIT_LSB_FIRST(outbytes, s->A[x][y]);
-            memcpy(output + n, outbytes, to_copy);
-            n += to_copy;
-            } // WINSCP
-        }
-    }
-    } // WINSCP
-}
-
-static void keccak_init(keccak_state *s, unsigned hashbits, unsigned ratebits,
-                        unsigned char first_pad_byte)
-{
-    int x, y;
-
-    assert(hashbits % 8 == 0);
-    assert(ratebits % 8 == 0);
-
-    s->hash_bytes = hashbits / 8;
-    s->bytes_wanted = (25 * 64 - ratebits) / 8;
-    s->bytes_got = 0;
-    s->first_pad_byte = first_pad_byte;
-
-    assert(s->bytes_wanted % 8 == 0);
-
-    for (y = 0; y < 5; y++)
-        for (x = 0; x < 5; x++)
-            s->A[x][y] = 0;
-}
-
-static void keccak_sha3_init(keccak_state *s, int hashbits)
-{
-    keccak_init(s, hashbits, hashbits * 2, 0x06);
-}
-
-static void keccak_shake_init(keccak_state *s, int parambits, int hashbits)
-{
-    keccak_init(s, hashbits, parambits * 2, 0x1f);
-}
-
-/*
- * Keccak round constants, generated via the LFSR specified in the
- * Keccak reference by the following piece of Python:
-
-import textwrap
-from functools import reduce
-
-rbytes = [1]
-while len(rbytes) < 7*24:
-    k = rbytes[-1] * 2
-    rbytes.append(k ^ (0x171 * (k >> 8)))
-
-rbits = [byte & 1 for byte in rbytes]
-
-rwords = [sum(rbits[i+j] << ((1 << j) - 1) for j in range(7))
-          for i in range(0, len(rbits), 7)]
-
-print(textwrap.indent("\n".join(textwrap.wrap(", ".join(
-    map("0x{:016x}".format, rwords)))), " "*4))
-
-*/
-
-static const uint64_t round_constants[24] = {
-    // WINSCP (ULL)
-    0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
-    0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
-    0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
-    0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
-    0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
-    0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
-    0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
-    0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL
-};
-
-/*
- * Keccak per-element rotation counts, generated from the matrix
- * formula in the Keccak reference by the following piece of Python:
-
-coords = [1, 0]
-while len(coords) < 26:
-    coords.append((2*coords[-2] + 3*coords[-1]) % 5)
-
-matrix = { (coords[i], coords[i+1]) : i for i in range(24) }
-matrix[0,0] = -1
-
-f = lambda t: (t+1) * (t+2) // 2 % 64
-
-for y in range(5):
-    print("    {{{}}},".format(", ".join("{:2d}".format(f(matrix[y,x]))
-                                         for x in range(5))))
-
-*/
-static const unsigned rotation_counts[5][5] = {
-    { 0, 36,  3, 41, 18},
-    { 1, 44, 10, 45,  2},
-    {62,  6, 43, 15, 61},
-    {28, 55, 25, 21, 56},
-    {27, 20, 39,  8, 14},
-};
-
-/*
- * The PuTTY ssh_hashalg abstraction.
- */
-struct keccak_hash {
-    keccak_state state;
-    ssh_hash hash;
-    BinarySink_IMPLEMENTATION;
-};
-
-static void keccak_BinarySink_write(BinarySink *bs, const void *p, size_t len)
-{
-    struct keccak_hash *kh = BinarySink_DOWNCAST(bs, struct keccak_hash);
-    keccak_accumulate(&kh->state, p, len);
-}
-
-static ssh_hash *keccak_new(const ssh_hashalg *alg)
-{
-    struct keccak_hash *kh = snew(struct keccak_hash);
-    kh->hash.vt = alg;
-    BinarySink_INIT(kh, keccak_BinarySink_write);
-    BinarySink_DELEGATE_INIT(&kh->hash, kh);
-    return ssh_hash_reset(&kh->hash);
-}
-
-static void keccak_free(ssh_hash *hash)
-{
-    struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash);
-    smemclr(kh, sizeof(*kh));
-    sfree(kh);
-}
-
-static void keccak_copyfrom(ssh_hash *hnew, ssh_hash *hold)
-{
-    struct keccak_hash *khold = container_of(hold, struct keccak_hash, hash);
-    struct keccak_hash *khnew = container_of(hnew, struct keccak_hash, hash);
-    khnew->state = khold->state;
-}
-
-static void keccak_digest(ssh_hash *hash, unsigned char *output)
-{
-    struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash);
-    keccak_output(&kh->state, output);
-}
-
-static void sha3_reset(ssh_hash *hash)
-{
-    struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash);
-    keccak_sha3_init(&kh->state, hash->vt->hlen * 8);
-}
-
-#define DEFINE_SHA3(bits)                       \
-    const ssh_hashalg ssh_sha3_##bits = {       \
-        /* WINSCP */ \
-        /*.new =*/ keccak_new,                      \
-        /*.reset =*/ sha3_reset,                    \
-        /*.copyfrom =*/ keccak_copyfrom,            \
-        /*.digest =*/ keccak_digest,                \
-        /*.free =*/ keccak_free,                    \
-        /*.hlen =*/ bits/8,                         \
-        /*.blocklen =*/ 200 - 2*(bits/8),           \
-        HASHALG_NAMES_BARE("SHA3-" #bits),      \
-        NULL, /* WINSCP */ \
-    }
-
-DEFINE_SHA3(224);
-DEFINE_SHA3(256);
-DEFINE_SHA3(384);
-DEFINE_SHA3(512);
-
-static void shake256_reset(ssh_hash *hash)
-{
-    struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash);
-    keccak_shake_init(&kh->state, 256, hash->vt->hlen * 8);
-}
-
-/*
- * There is some confusion over the output length parameter for the
- * SHAKE functions. By my reading, FIPS PUB 202 defines SHAKE256(M,d)
- * to generate d _bits_ of output. But RFC 8032 (defining Ed448) talks
- * about "SHAKE256(x,114)" in a context where it definitely means
- * generating 114 _bytes_ of output.
- *
- * Our internal ID therefore suffixes the output length with "bytes",
- * to be clear which we're talking about
- */
-
-#define DEFINE_SHAKE(param, hashbytes)                          \
-    const ssh_hashalg ssh_shake##param##_##hashbytes##bytes = { \
-        /* WINSCP */ \
-        /*.new =*/ keccak_new,                                      \
-        /*.reset =*/ shake##param##_reset,                          \
-        /*.copyfrom =*/ keccak_copyfrom,                            \
-        /*.digest =*/ keccak_digest,                                \
-        /*.free =*/ keccak_free,                                    \
-        /*.hlen =*/ hashbytes,                                      \
-        /*.blocklen =*/ 0,                                          \
-        HASHALG_NAMES_BARE("SHAKE" #param),                     \
-        NULL, /*NULL*/ \
-    }
-
-DEFINE_SHAKE(256, 114);

+ 0 - 2187
source/putty/sshshare.c

@@ -1,2187 +0,0 @@
-/*
- * Support for SSH connection sharing, i.e. permitting one PuTTY to
- * open its own channels over the SSH session being run by another.
- */
-
-/*
- * Discussion and technical documentation
- * ======================================
- *
- * The basic strategy for PuTTY's implementation of SSH connection
- * sharing is to have a single 'upstream' PuTTY process, which manages
- * the real SSH connection and all the cryptography, and then zero or
- * more 'downstream' PuTTYs, which never talk to the real host but
- * only talk to the upstream through local IPC (Unix-domain sockets or
- * Windows named pipes).
- *
- * The downstreams communicate with the upstream using a protocol
- * derived from SSH itself, which I'll document in detail below. In
- * brief, though: the downstream->upstream protocol uses a trivial
- * binary packet protocol (just length/type/data) to encapsulate
- * unencrypted SSH messages, and downstreams talk to the upstream more
- * or less as if it was an SSH server itself. (So downstreams can
- * themselves open multiple SSH channels, for example, by sending
- * multiple SSH2_MSG_CHANNEL_OPENs; they can send CHANNEL_REQUESTs of
- * their choice within each channel, and they handle their own
- * WINDOW_ADJUST messages.)
- *
- * The upstream would ideally handle these downstreams by just putting
- * their messages into the queue for proper SSH-2 encapsulation and
- * encryption and sending them straight on to the server. However,
- * that's not quite feasible as written, because client-side channel
- * IDs could easily conflict (between multiple downstreams, or between
- * a downstream and the upstream). To protect against that, the
- * upstream rewrites the client-side channel IDs in messages it passes
- * on to the server, so that it's performing what you might describe
- * as 'channel-number NAT'. Then the upstream remembers which of its
- * own channel IDs are channels it's managing itself, and which are
- * placeholders associated with a particular downstream, so that when
- * replies come in from the server they can be sent on to the relevant
- * downstream (after un-NATting the channel number, of course).
- *
- * Global requests from downstreams are only accepted if the upstream
- * knows what to do about them; currently the only such requests are
- * the ones having to do with remote-to-local port forwarding (in
- * which, again, the upstream remembers that some of the forwardings
- * it's asked the server to set up were on behalf of particular
- * downstreams, and sends the incoming CHANNEL_OPENs to those
- * downstreams when connections come in).
- *
- * Other fiddly pieces of this mechanism are X forwarding and
- * (OpenSSH-style) agent forwarding. Both of these have a fundamental
- * problem arising from the protocol design: that the CHANNEL_OPEN
- * from the server introducing a forwarded connection does not carry
- * any indication of which session channel gave rise to it; so if
- * session channels from multiple downstreams enable those forwarding
- * methods, it's hard for the upstream to know which downstream to
- * send the resulting connections back to.
- *
- * For X forwarding, we can work around this in a really painful way
- * by using the fake X11 authorisation data sent to the server as part
- * of the forwarding setup: upstream ensures that every X forwarding
- * request carries distinguishable fake auth data, and then when X
- * connections come in it waits to see the auth data in the X11 setup
- * message before it decides which downstream to pass the connection
- * on to.
- *
- * For agent forwarding, that workaround is unavailable. As a result,
- * this system (and, as far as I can think of, any other system too)
- * has the fundamental constraint that it can only forward one SSH
- * agent - it can't forward two agents to different session channels.
- * So downstreams can request agent forwarding if they like, but if
- * they do, they'll get whatever SSH agent is known to the upstream
- * (if any) forwarded to their sessions.
- *
- * Downstream-to-upstream protocol
- * -------------------------------
- *
- * Here I document in detail the protocol spoken between PuTTY
- * downstreams and upstreams over local IPC. The IPC mechanism can
- * vary between host platforms, but the protocol is the same.
- *
- * The protocol commences with a version exchange which is exactly
- * like the SSH-2 one, in that each side sends a single line of text
- * of the form
- *
- *   <protocol>-<version>-<softwareversion> [comments] \r\n
- *
- * The only difference is that in real SSH-2, <protocol> is the string
- * "SSH", whereas in this protocol the string is
- * "[email protected]".
- *
- * (The SSH RFCs allow many protocol-level identifier namespaces to be
- * extended by implementors without central standardisation as long as
- * they suffix "@" and a domain name they control to their new ids.
- * RFC 4253 does not define this particular name to be changeable at
- * all, but I like to think this is obviously how it would have done
- * so if the working group had foreseen the need :-)
- *
- * Thereafter, all data exchanged consists of a sequence of binary
- * packets concatenated end-to-end, each of which is of the form
- *
- *     uint32     length of packet, N
- *     byte[N]    N bytes of packet data
- *
- * and, since these are SSH-2 messages, the first data byte is taken
- * to be the packet type code.
- *
- * These messages are interpreted as those of an SSH connection, after
- * userauth completes, and without any repeat key exchange.
- * Specifically, any message from the SSH Connection Protocol is
- * permitted, and also SSH_MSG_IGNORE, SSH_MSG_DEBUG,
- * SSH_MSG_DISCONNECT and SSH_MSG_UNIMPLEMENTED from the SSH Transport
- * Protocol.
- *
- * This protocol imposes a few additional requirements, over and above
- * those of the standard SSH Connection Protocol:
- *
- * Message sizes are not permitted to exceed 0x4010 (16400) bytes,
- * including their length header.
- *
- * When the server (i.e. really the PuTTY upstream) sends
- * SSH_MSG_CHANNEL_OPEN with channel type "x11", and the client
- * (downstream) responds with SSH_MSG_CHANNEL_OPEN_CONFIRMATION, that
- * confirmation message MUST include an initial window size of at
- * least 256. (Rationale: this is a bit of a fudge which makes it
- * easier, by eliminating the possibility of nasty edge cases, for an
- * upstream to arrange not to pass the CHANNEL_OPEN on to downstream
- * until after it's seen the X11 auth data to decide which downstream
- * it needs to go to.)
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <limits.h>
-#include <errno.h>
-
-#include "putty.h"
-#include "tree234.h"
-#include "ssh.h"
-#include "sshcr.h"
-
-struct ssh_sharing_state {
-    char *sockname;                  /* the socket name, kept for cleanup */
-    Socket *listensock;              /* the master listening Socket */
-    tree234 *connections;            /* holds ssh_sharing_connstates */
-    unsigned nextid;                 /* preferred id for next connstate */
-    ConnectionLayer *cl;             /* instance of the ssh connection layer */
-    char *server_verstring;          /* server version string after "SSH-" */
-
-    Plug plug;
-};
-
-struct share_globreq;
-
-struct ssh_sharing_connstate {
-    unsigned id;    /* used to identify this downstream in log messages */
-
-    Socket *sock;                     /* the Socket for this connection */
-    struct ssh_sharing_state *parent;
-
-    int crLine;                        /* coroutine state for share_receive */
-
-    bool sent_verstring, got_verstring;
-    int curr_packetlen;
-
-    unsigned char recvbuf[0x4010];
-    size_t recvlen;
-
-    /*
-     * Assorted state we have to remember about this downstream, so
-     * that we can clean it up appropriately when the downstream goes
-     * away.
-     */
-
-    /* Channels which don't have a downstream id, i.e. we've passed a
-     * CHANNEL_OPEN down from the server but not had an
-     * OPEN_CONFIRMATION or OPEN_FAILURE back. If downstream goes
-     * away, we respond to all of these with OPEN_FAILURE. */
-    tree234 *halfchannels;         /* stores 'struct share_halfchannel' */
-
-    /* Channels which do have a downstream id. We need to index these
-     * by both server id and upstream id, so we can find a channel
-     * when handling either an upward or a downward message referring
-     * to it. */
-    tree234 *channels_by_us;       /* stores 'struct share_channel' */
-    tree234 *channels_by_server;   /* stores 'struct share_channel' */
-
-    /* Another class of channel which doesn't have a downstream id.
-     * The difference between these and halfchannels is that xchannels
-     * do have an *upstream* id, because upstream has already accepted
-     * the channel request from the server. This arises in the case of
-     * X forwarding, where we have to accept the request and read the
-     * X authorisation data before we know whether the channel needs
-     * to be forwarded to a downstream. */
-    tree234 *xchannels_by_us;     /* stores 'struct share_xchannel' */
-    tree234 *xchannels_by_server; /* stores 'struct share_xchannel' */
-
-    /* Remote port forwarding requests in force. */
-    tree234 *forwardings;          /* stores 'struct share_forwarding' */
-
-    /* Global requests we've sent on to the server, pending replies. */
-    struct share_globreq *globreq_head, *globreq_tail;
-
-    Plug plug;
-};
-
-struct share_halfchannel {
-    unsigned server_id;
-};
-
-/* States of a share_channel. */
-enum {
-    OPEN,
-    SENT_CLOSE,
-    RCVD_CLOSE,
-    /* Downstream has sent CHANNEL_OPEN but server hasn't replied yet.
-     * If downstream goes away when a channel is in this state, we
-     * must wait for the server's response before starting to send
-     * CLOSE. Channels in this state are also not held in
-     * channels_by_server, because their server_id field is
-     * meaningless. */
-    UNACKNOWLEDGED
-};
-
-struct share_channel {
-    unsigned downstream_id, upstream_id, server_id;
-    int downstream_maxpkt;
-    int state;
-    /*
-     * Some channels (specifically, channels on which downstream has
-     * sent "x11-req") have the additional function of storing a set
-     * of downstream X authorisation data and a handle to an upstream
-     * fake set.
-     */
-    struct X11FakeAuth *x11_auth_upstream;
-    int x11_auth_proto;
-    char *x11_auth_data;
-    int x11_auth_datalen;
-    bool x11_one_shot;
-};
-
-struct share_forwarding {
-    char *host;
-    int port;
-    bool active;            /* has the server sent REQUEST_SUCCESS? */
-    struct ssh_rportfwd *rpf;
-};
-
-struct share_xchannel_message {
-    struct share_xchannel_message *next;
-    int type;
-    unsigned char *data;
-    int datalen;
-};
-
-struct share_xchannel {
-    unsigned upstream_id, server_id;
-
-    /*
-     * xchannels come in two flavours: live and dead. Live ones are
-     * waiting for an OPEN_CONFIRMATION or OPEN_FAILURE from
-     * downstream; dead ones have had an OPEN_FAILURE, so they only
-     * exist as a means of letting us conveniently respond to further
-     * channel messages from the server until such time as the server
-     * sends us CHANNEL_CLOSE.
-     */
-    bool live;
-
-    /*
-     * When we receive OPEN_CONFIRMATION, we will need to send a
-     * WINDOW_ADJUST to the server to synchronise the windows. For
-     * this purpose we need to know what window we have so far offered
-     * the server. We record this as exactly the value in the
-     * OPEN_CONFIRMATION that upstream sent us, adjusted by the amount
-     * by which the two X greetings differed in length.
-     */
-    int window;
-
-    /*
-     * Linked list of SSH messages from the server relating to this
-     * channel, which we queue up until downstream sends us an
-     * OPEN_CONFIRMATION and we can belatedly send them all on.
-     */
-    struct share_xchannel_message *msghead, *msgtail;
-};
-
-enum {
-    GLOBREQ_TCPIP_FORWARD,
-    GLOBREQ_CANCEL_TCPIP_FORWARD
-};
-
-struct share_globreq {
-    struct share_globreq *next;
-    int type;
-    bool want_reply;
-    struct share_forwarding *fwd;
-};
-
-static int share_connstate_cmp(void *av, void *bv)
-{
-    const struct ssh_sharing_connstate *a =
-        (const struct ssh_sharing_connstate *)av;
-    const struct ssh_sharing_connstate *b =
-        (const struct ssh_sharing_connstate *)bv;
-
-    if (a->id < b->id)
-        return -1;
-    else if (a->id > b->id)
-        return +1;
-    else
-        return 0;
-}
-
-static unsigned share_find_unused_id
-(struct ssh_sharing_state *sharestate, unsigned first)
-{
-    int low_orig, low, mid, high, high_orig;
-    struct ssh_sharing_connstate *cs;
-    unsigned ret;
-
-    /*
-     * Find the lowest unused downstream ID greater or equal to
-     * 'first'.
-     *
-     * Begin by seeing if 'first' itself is available. If it is, we'll
-     * just return it; if it's already in the tree, we'll find the
-     * tree index where it appears and use that for the next stage.
-     */
-    {
-        struct ssh_sharing_connstate dummy;
-        dummy.id = first;
-        cs = findrelpos234(sharestate->connections, &dummy, NULL,
-                           REL234_GE, &low_orig);
-        if (!cs)
-            return first;
-    }
-
-    /*
-     * Now binary-search using the counted B-tree, to find the largest
-     * ID which is in a contiguous sequence from the beginning of that
-     * range.
-     */
-    low = low_orig;
-    high = high_orig = count234(sharestate->connections);
-    while (high - low > 1) {
-        mid = (high + low) / 2;
-        cs = index234(sharestate->connections, mid);
-        if (cs->id == first + (mid - low_orig))
-            low = mid;                 /* this one is still in the sequence */
-        else
-            high = mid;                /* this one is past the end */
-    }
-
-    /*
-     * Now low is the tree index of the largest ID in the initial
-     * sequence. So the return value is one more than low's id, and we
-     * know low's id is given by the formula in the binary search loop
-     * above.
-     *
-     * (If an SSH connection went on for _enormously_ long, we might
-     * reach a point where all ids from 'first' to UINT_MAX were in
-     * use. In that situation the formula below would wrap round by
-     * one and return zero, which is conveniently the right way to
-     * signal 'no id available' from this function.)
-     */
-    ret = first + (low - low_orig) + 1;
-    {
-        struct ssh_sharing_connstate dummy;
-        dummy.id = ret;
-        assert(NULL == find234(sharestate->connections, &dummy, NULL));
-    }
-    return ret;
-}
-
-static int share_halfchannel_cmp(void *av, void *bv)
-{
-    const struct share_halfchannel *a = (const struct share_halfchannel *)av;
-    const struct share_halfchannel *b = (const struct share_halfchannel *)bv;
-
-    if (a->server_id < b->server_id)
-        return -1;
-    else if (a->server_id > b->server_id)
-        return +1;
-    else
-        return 0;
-}
-
-static int share_channel_us_cmp(void *av, void *bv)
-{
-    const struct share_channel *a = (const struct share_channel *)av;
-    const struct share_channel *b = (const struct share_channel *)bv;
-
-    if (a->upstream_id < b->upstream_id)
-        return -1;
-    else if (a->upstream_id > b->upstream_id)
-        return +1;
-    else
-        return 0;
-}
-
-static int share_channel_server_cmp(void *av, void *bv)
-{
-    const struct share_channel *a = (const struct share_channel *)av;
-    const struct share_channel *b = (const struct share_channel *)bv;
-
-    if (a->server_id < b->server_id)
-        return -1;
-    else if (a->server_id > b->server_id)
-        return +1;
-    else
-        return 0;
-}
-
-static int share_xchannel_us_cmp(void *av, void *bv)
-{
-    const struct share_xchannel *a = (const struct share_xchannel *)av;
-    const struct share_xchannel *b = (const struct share_xchannel *)bv;
-
-    if (a->upstream_id < b->upstream_id)
-        return -1;
-    else if (a->upstream_id > b->upstream_id)
-        return +1;
-    else
-        return 0;
-}
-
-static int share_xchannel_server_cmp(void *av, void *bv)
-{
-    const struct share_xchannel *a = (const struct share_xchannel *)av;
-    const struct share_xchannel *b = (const struct share_xchannel *)bv;
-
-    if (a->server_id < b->server_id)
-        return -1;
-    else if (a->server_id > b->server_id)
-        return +1;
-    else
-        return 0;
-}
-
-static int share_forwarding_cmp(void *av, void *bv)
-{
-    const struct share_forwarding *a = (const struct share_forwarding *)av;
-    const struct share_forwarding *b = (const struct share_forwarding *)bv;
-    int i;
-
-    if ((i = strcmp(a->host, b->host)) != 0)
-        return i;
-    else if (a->port < b->port)
-        return -1;
-    else if (a->port > b->port)
-        return +1;
-    else
-        return 0;
-}
-
-static void share_xchannel_free(struct share_xchannel *xc)
-{
-    while (xc->msghead) {
-        struct share_xchannel_message *tmp = xc->msghead;
-        xc->msghead = tmp->next;
-        sfree(tmp);
-    }
-    sfree(xc);
-}
-
-static void share_connstate_free(struct ssh_sharing_connstate *cs)
-{
-    struct share_halfchannel *hc;
-    struct share_xchannel *xc;
-    struct share_channel *chan;
-    struct share_forwarding *fwd;
-
-    while ((hc = (struct share_halfchannel *)
-            delpos234(cs->halfchannels, 0)) != NULL)
-        sfree(hc);
-    freetree234(cs->halfchannels);
-
-    /* All channels live in 'channels_by_us' but only some in
-     * 'channels_by_server', so we use the former to find the list of
-     * ones to free */
-    freetree234(cs->channels_by_server);
-    while ((chan = (struct share_channel *)
-            delpos234(cs->channels_by_us, 0)) != NULL)
-        sfree(chan);
-    freetree234(cs->channels_by_us);
-
-    /* But every xchannel is in both trees, so it doesn't matter which
-     * we use to free them. */
-    while ((xc = (struct share_xchannel *)
-            delpos234(cs->xchannels_by_us, 0)) != NULL)
-        share_xchannel_free(xc);
-    freetree234(cs->xchannels_by_us);
-    freetree234(cs->xchannels_by_server);
-
-    while ((fwd = (struct share_forwarding *)
-            delpos234(cs->forwardings, 0)) != NULL)
-        sfree(fwd);
-    freetree234(cs->forwardings);
-
-    while (cs->globreq_head) {
-        struct share_globreq *globreq = cs->globreq_head;
-        cs->globreq_head = cs->globreq_head->next;
-        sfree(globreq);
-    }
-
-    if (cs->sock)
-        sk_close(cs->sock);
-
-    sfree(cs);
-}
-
-void sharestate_free(ssh_sharing_state *sharestate)
-{
-    struct ssh_sharing_connstate *cs;
-
-    platform_ssh_share_cleanup(sharestate->sockname);
-
-    while ((cs = (struct ssh_sharing_connstate *)
-            delpos234(sharestate->connections, 0)) != NULL) {
-        share_connstate_free(cs);
-    }
-    freetree234(sharestate->connections);
-    if (sharestate->listensock) {
-        sk_close(sharestate->listensock);
-        sharestate->listensock = NULL;
-    }
-    sfree(sharestate->server_verstring);
-    sfree(sharestate->sockname);
-    sfree(sharestate);
-}
-
-static struct share_halfchannel *share_add_halfchannel
-    (struct ssh_sharing_connstate *cs, unsigned server_id)
-{
-    struct share_halfchannel *hc = snew(struct share_halfchannel);
-    hc->server_id = server_id;
-    if (add234(cs->halfchannels, hc) != hc) {
-        /* Duplicate?! */
-        sfree(hc);
-        return NULL;
-    } else {
-        return hc;
-    }
-}
-
-static struct share_halfchannel *share_find_halfchannel
-    (struct ssh_sharing_connstate *cs, unsigned server_id)
-{
-    struct share_halfchannel dummyhc;
-    dummyhc.server_id = server_id;
-    return find234(cs->halfchannels, &dummyhc, NULL);
-}
-
-static void share_remove_halfchannel(struct ssh_sharing_connstate *cs,
-                                     struct share_halfchannel *hc)
-{
-    del234(cs->halfchannels, hc);
-    sfree(hc);
-}
-
-static struct share_channel *share_add_channel
-    (struct ssh_sharing_connstate *cs, unsigned downstream_id,
-     unsigned upstream_id, unsigned server_id, int state, int maxpkt)
-{
-    struct share_channel *chan = snew(struct share_channel);
-    chan->downstream_id = downstream_id;
-    chan->upstream_id = upstream_id;
-    chan->server_id = server_id;
-    chan->state = state;
-    chan->downstream_maxpkt = maxpkt;
-    chan->x11_auth_upstream = NULL;
-    chan->x11_auth_data = NULL;
-    chan->x11_auth_proto = -1;
-    chan->x11_auth_datalen = 0;
-    chan->x11_one_shot = false;
-    if (add234(cs->channels_by_us, chan) != chan) {
-        sfree(chan);
-        return NULL;
-    }
-    if (chan->state != UNACKNOWLEDGED) {
-        if (add234(cs->channels_by_server, chan) != chan) {
-            del234(cs->channels_by_us, chan);
-            sfree(chan);
-            return NULL;
-        }
-    }
-    return chan;
-}
-
-static void share_channel_set_server_id(struct ssh_sharing_connstate *cs,
-                                        struct share_channel *chan,
-                                        unsigned server_id, int newstate)
-{
-    chan->server_id = server_id;
-    chan->state = newstate;
-    assert(newstate != UNACKNOWLEDGED);
-    add234(cs->channels_by_server, chan);
-}
-
-static struct share_channel *share_find_channel_by_upstream
-    (struct ssh_sharing_connstate *cs, unsigned upstream_id)
-{
-    struct share_channel dummychan;
-    dummychan.upstream_id = upstream_id;
-    return find234(cs->channels_by_us, &dummychan, NULL);
-}
-
-static struct share_channel *share_find_channel_by_server
-    (struct ssh_sharing_connstate *cs, unsigned server_id)
-{
-    struct share_channel dummychan;
-    dummychan.server_id = server_id;
-    return find234(cs->channels_by_server, &dummychan, NULL);
-}
-
-static void share_remove_channel(struct ssh_sharing_connstate *cs,
-                                 struct share_channel *chan)
-{
-    del234(cs->channels_by_us, chan);
-    del234(cs->channels_by_server, chan);
-    if (chan->x11_auth_upstream)
-        ssh_remove_sharing_x11_display(cs->parent->cl,
-                                       chan->x11_auth_upstream);
-    sfree(chan->x11_auth_data);
-    sfree(chan);
-}
-
-static struct share_xchannel *share_add_xchannel
-    (struct ssh_sharing_connstate *cs,
-     unsigned upstream_id, unsigned server_id)
-{
-    struct share_xchannel *xc = snew(struct share_xchannel);
-    xc->upstream_id = upstream_id;
-    xc->server_id = server_id;
-    xc->live = true;
-    xc->msghead = xc->msgtail = NULL;
-    if (add234(cs->xchannels_by_us, xc) != xc) {
-        sfree(xc);
-        return NULL;
-    }
-    if (add234(cs->xchannels_by_server, xc) != xc) {
-        del234(cs->xchannels_by_us, xc);
-        sfree(xc);
-        return NULL;
-    }
-    return xc;
-}
-
-static struct share_xchannel *share_find_xchannel_by_upstream
-    (struct ssh_sharing_connstate *cs, unsigned upstream_id)
-{
-    struct share_xchannel dummyxc;
-    dummyxc.upstream_id = upstream_id;
-    return find234(cs->xchannels_by_us, &dummyxc, NULL);
-}
-
-static struct share_xchannel *share_find_xchannel_by_server
-    (struct ssh_sharing_connstate *cs, unsigned server_id)
-{
-    struct share_xchannel dummyxc;
-    dummyxc.server_id = server_id;
-    return find234(cs->xchannels_by_server, &dummyxc, NULL);
-}
-
-static void share_remove_xchannel(struct ssh_sharing_connstate *cs,
-                                 struct share_xchannel *xc)
-{
-    del234(cs->xchannels_by_us, xc);
-    del234(cs->xchannels_by_server, xc);
-    share_xchannel_free(xc);
-}
-
-static struct share_forwarding *share_add_forwarding
-    (struct ssh_sharing_connstate *cs,
-     const char *host, int port)
-{
-    struct share_forwarding *fwd = snew(struct share_forwarding);
-    fwd->host = dupstr(host);
-    fwd->port = port;
-    fwd->active = false;
-    if (add234(cs->forwardings, fwd) != fwd) {
-        /* Duplicate?! */
-        sfree(fwd);
-        return NULL;
-    }
-    return fwd;
-}
-
-static struct share_forwarding *share_find_forwarding
-    (struct ssh_sharing_connstate *cs, const char *host, int port)
-{
-    struct share_forwarding dummyfwd, *ret;
-    dummyfwd.host = dupstr(host);
-    dummyfwd.port = port;
-    ret = find234(cs->forwardings, &dummyfwd, NULL);
-    sfree(dummyfwd.host);
-    return ret;
-}
-
-static void share_remove_forwarding(struct ssh_sharing_connstate *cs,
-                                    struct share_forwarding *fwd)
-{
-    del234(cs->forwardings, fwd);
-    sfree(fwd);
-}
-
-static PRINTF_LIKE(2, 3) void log_downstream(struct ssh_sharing_connstate *cs,
-                                             const char *logfmt, ...)
-{
-    va_list ap;
-    char *buf;
-
-    va_start(ap, logfmt);
-    buf = dupvprintf(logfmt, ap);
-    va_end(ap);
-    logeventf(cs->parent->cl->logctx,
-              "Connection sharing downstream #%u: %s", cs->id, buf);
-    sfree(buf);
-}
-
-static PRINTF_LIKE(2, 3) void log_general(struct ssh_sharing_state *sharestate,
-                                          const char *logfmt, ...)
-{
-    va_list ap;
-    char *buf;
-
-    va_start(ap, logfmt);
-    buf = dupvprintf(logfmt, ap);
-    va_end(ap);
-    logeventf(sharestate->cl->logctx, "Connection sharing: %s", buf);
-    sfree(buf);
-}
-
-static void send_packet_to_downstream(struct ssh_sharing_connstate *cs,
-                                      int type, const void *pkt, int pktlen,
-                                      struct share_channel *chan)
-{
-    strbuf *packet;
-
-    if (!cs->sock) /* throw away all packets destined for a dead downstream */
-        return;
-
-    if (type == SSH2_MSG_CHANNEL_DATA) {
-        /*
-         * Special case which we take care of at a low level, so as to
-         * be sure to apply it in all cases. On rare occasions we
-         * might find that we have a channel for which the
-         * downstream's maximum packet size exceeds the max packet
-         * size we presented to the server on its behalf. (This can
-         * occur in X11 forwarding, where we have to send _our_
-         * CHANNEL_OPEN_CONFIRMATION before we discover which if any
-         * downstream the channel is destined for, so if that
-         * downstream turns out to present a smaller max packet size
-         * then we're in this situation.)
-         *
-         * If that happens, we just chop up the packet into pieces and
-         * send them as separate CHANNEL_DATA packets.
-         */
-        BinarySource src[1];
-        unsigned channel;
-        ptrlen data;
-
-        BinarySource_BARE_INIT(src, pkt, pktlen);
-        channel = get_uint32(src);
-        data = get_string(src);
-
-        do {
-            int this_len = (data.len > chan->downstream_maxpkt ?
-                            chan->downstream_maxpkt : data.len);
-
-            packet = strbuf_new_nm();
-            put_uint32(packet, 0);     /* placeholder for length field */
-            put_byte(packet, type);
-            put_uint32(packet, channel);
-            put_uint32(packet, this_len);
-            put_data(packet, data.ptr, this_len);
-            data.ptr = (const char *)data.ptr + this_len;
-            data.len -= this_len;
-            PUT_32BIT_MSB_FIRST(packet->s, packet->len-4);
-            sk_write(cs->sock, packet->s, packet->len);
-            strbuf_free(packet);
-        } while (data.len > 0);
-    } else {
-        /*
-         * Just do the obvious thing.
-         */
-        packet = strbuf_new_nm();
-        put_uint32(packet, 0);     /* placeholder for length field */
-        put_byte(packet, type);
-        put_data(packet, pkt, pktlen);
-        PUT_32BIT_MSB_FIRST(packet->s, packet->len-4);
-        sk_write(cs->sock, packet->s, packet->len);
-        strbuf_free(packet);
-    }
-}
-
-static void share_try_cleanup(struct ssh_sharing_connstate *cs)
-{
-    int i;
-    struct share_halfchannel *hc;
-    struct share_channel *chan;
-    struct share_forwarding *fwd;
-
-    /*
-     * Any half-open channels, i.e. those for which we'd received
-     * CHANNEL_OPEN from the server but not passed back a response
-     * from downstream, should be responded to with OPEN_FAILURE.
-     */
-    while ((hc = (struct share_halfchannel *)
-            index234(cs->halfchannels, 0)) != NULL) {
-        static const char reason[] = "PuTTY downstream no longer available";
-        static const char lang[] = "en";
-        strbuf *packet;
-
-        packet = strbuf_new();
-        put_uint32(packet, hc->server_id);
-        put_uint32(packet, SSH2_OPEN_CONNECT_FAILED);
-        put_stringz(packet, reason);
-        put_stringz(packet, lang);
-        ssh_send_packet_from_downstream(
-            cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_OPEN_FAILURE,
-            packet->s, packet->len,
-            "cleanup after downstream went away");
-        strbuf_free(packet);
-
-        share_remove_halfchannel(cs, hc);
-    }
-
-    /*
-     * Any actually open channels should have a CHANNEL_CLOSE sent for
-     * them, unless we've already done so. We won't be able to
-     * actually clean them up until CHANNEL_CLOSE comes back from the
-     * server, though (unless the server happens to have sent a CLOSE
-     * already).
-     *
-     * Another annoying exception is UNACKNOWLEDGED channels, i.e.
-     * we've _sent_ a CHANNEL_OPEN to the server but not received an
-     * OPEN_CONFIRMATION or OPEN_FAILURE. We must wait for a reply
-     * before closing the channel, because until we see that reply we
-     * won't have the server's channel id to put in the close message.
-     */
-    for (i = 0; (chan = (struct share_channel *)
-                 index234(cs->channels_by_us, i)) != NULL; i++) {
-        strbuf *packet;
-
-        if (chan->state != SENT_CLOSE && chan->state != UNACKNOWLEDGED) {
-            packet = strbuf_new();
-            put_uint32(packet, chan->server_id);
-            ssh_send_packet_from_downstream(
-                cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE,
-                packet->s, packet->len,
-                "cleanup after downstream went away");
-            strbuf_free(packet);
-
-            if (chan->state != RCVD_CLOSE) {
-                chan->state = SENT_CLOSE;
-            } else {
-                /* In this case, we _can_ clear up the channel now. */
-                ssh_delete_sharing_channel(cs->parent->cl, chan->upstream_id);
-                share_remove_channel(cs, chan);
-                i--;    /* don't accidentally skip one as a result */
-            }
-        }
-    }
-
-    /*
-     * Any remote port forwardings we're managing on behalf of this
-     * downstream should be cancelled. Again, we must defer those for
-     * which we haven't yet seen REQUEST_SUCCESS/FAILURE.
-     *
-     * We take a fire-and-forget approach during cleanup, not
-     * bothering to set want_reply.
-     */
-    for (i = 0; (fwd = (struct share_forwarding *)
-                 index234(cs->forwardings, i)) != NULL; i++) {
-        if (fwd->active) {
-            strbuf *packet = strbuf_new();
-            put_stringz(packet, "cancel-tcpip-forward");
-            put_bool(packet, false);       /* !want_reply */
-            put_stringz(packet, fwd->host);
-            put_uint32(packet, fwd->port);
-            ssh_send_packet_from_downstream(
-                cs->parent->cl, cs->id, SSH2_MSG_GLOBAL_REQUEST,
-                packet->s, packet->len,
-                "cleanup after downstream went away");
-            strbuf_free(packet);
-
-            ssh_rportfwd_remove(cs->parent->cl, fwd->rpf);
-            share_remove_forwarding(cs, fwd);
-            i--;    /* don't accidentally skip one as a result */
-        }
-    }
-
-    if (count234(cs->halfchannels) == 0 &&
-        count234(cs->channels_by_us) == 0 &&
-        count234(cs->forwardings) == 0) {
-        struct ssh_sharing_state *sharestate = cs->parent;
-
-        /*
-         * Now we're _really_ done, so we can get rid of cs completely.
-         */
-        del234(sharestate->connections, cs);
-        log_downstream(cs, "disconnected");
-        share_connstate_free(cs);
-
-        /*
-         * And if this was the last downstream, notify the connection
-         * layer, because it might now be time to wind up the whole
-         * SSH connection.
-         */
-        if (count234(sharestate->connections) == 0 && sharestate->cl)
-            ssh_sharing_no_more_downstreams(sharestate->cl);
-    }
-}
-
-static void share_begin_cleanup(struct ssh_sharing_connstate *cs)
-{
-
-    sk_close(cs->sock);
-    cs->sock = NULL;
-
-    share_try_cleanup(cs);
-}
-
-static void share_disconnect(struct ssh_sharing_connstate *cs,
-                             const char *message)
-{
-    strbuf *packet = strbuf_new();
-    put_uint32(packet, SSH2_DISCONNECT_PROTOCOL_ERROR);
-    put_stringz(packet, message);
-    put_stringz(packet, "en");         /* language */
-    send_packet_to_downstream(cs, SSH2_MSG_DISCONNECT,
-                              packet->s, packet->len, NULL);
-    strbuf_free(packet);
-
-    share_begin_cleanup(cs);
-}
-
-static void share_closing(Plug *plug, const char *error_msg, int error_code,
-                          bool calling_back)
-{
-    struct ssh_sharing_connstate *cs = container_of(
-        plug, struct ssh_sharing_connstate, plug);
-
-    if (error_msg) {
-#ifdef BROKEN_PIPE_ERROR_CODE
-        /*
-         * Most of the time, we log what went wrong when a downstream
-         * disappears with a socket error. One exception, though, is
-         * receiving EPIPE when we haven't received a protocol version
-         * string from the downstream, because that can happen as a result
-         * of plink -shareexists (opening the connection and instantly
-         * closing it again without bothering to read our version string).
-         * So that one case is not treated as a log-worthy error.
-         */
-        if (error_code == BROKEN_PIPE_ERROR_CODE && !cs->got_verstring)
-            /* do nothing */;
-        else
-#endif
-            log_downstream(cs, "Socket error: %s", error_msg);
-    }
-    share_begin_cleanup(cs);
-}
-
-/*
- * Append a message to the end of an xchannel's queue.
- */
-static void share_xchannel_add_message(
-    struct share_xchannel *xc, int type, const void *data, int len)
-{
-    struct share_xchannel_message *msg;
-
-    /*
-     * Allocate the 'struct share_xchannel_message' and the actual
-     * data in one unit.
-     */
-    msg = snew_plus(struct share_xchannel_message, len);
-    msg->data = snew_plus_get_aux(msg);
-    msg->datalen = len;
-    msg->type = type;
-    memcpy(msg->data, data, len);
-
-    /*
-     * Queue it in the xchannel.
-     */
-    if (xc->msgtail)
-        xc->msgtail->next = msg;
-    else
-        xc->msghead = msg;
-    msg->next = NULL;
-    xc->msgtail = msg;
-}
-
-void share_dead_xchannel_respond(struct ssh_sharing_connstate *cs,
-                                 struct share_xchannel *xc)
-{
-    /*
-     * Handle queued incoming messages from the server destined for an
-     * xchannel which is dead (i.e. downstream sent OPEN_FAILURE).
-     */
-    bool delete = false;
-    while (xc->msghead) {
-        struct share_xchannel_message *msg = xc->msghead;
-        xc->msghead = msg->next;
-
-        if (msg->type == SSH2_MSG_CHANNEL_REQUEST && msg->datalen > 4) {
-            /*
-             * A CHANNEL_REQUEST is responded to by sending
-             * CHANNEL_FAILURE, if it has want_reply set.
-             */
-            BinarySource src[1];
-            BinarySource_BARE_INIT(src, msg->data, msg->datalen);
-            get_uint32(src);           /* skip channel id */
-            get_string(src);           /* skip request type */
-            if (get_bool(src)) {
-                strbuf *packet = strbuf_new();
-                put_uint32(packet, xc->server_id);
-                ssh_send_packet_from_downstream
-                    (cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_FAILURE,
-                     packet->s, packet->len,
-                     "downstream refused X channel open");
-                strbuf_free(packet);
-            }
-        } else if (msg->type == SSH2_MSG_CHANNEL_CLOSE) {
-            /*
-             * On CHANNEL_CLOSE we can discard the channel completely.
-             */
-            delete = true;
-        }
-
-        sfree(msg);
-    }
-    xc->msgtail = NULL;
-    if (delete) {
-        ssh_delete_sharing_channel(cs->parent->cl, xc->upstream_id);
-        share_remove_xchannel(cs, xc);
-    }
-}
-
-void share_xchannel_confirmation(struct ssh_sharing_connstate *cs,
-                                 struct share_xchannel *xc,
-                                 struct share_channel *chan,
-                                 unsigned downstream_window)
-{
-    strbuf *packet;
-
-    /*
-     * Send all the queued messages downstream.
-     */
-    while (xc->msghead) {
-        struct share_xchannel_message *msg = xc->msghead;
-        xc->msghead = msg->next;
-
-        if (msg->datalen >= 4)
-            PUT_32BIT_MSB_FIRST(msg->data, chan->downstream_id);
-        send_packet_to_downstream(cs, msg->type,
-                                  msg->data, msg->datalen, chan);
-
-        sfree(msg);
-    }
-
-    /*
-     * Send a WINDOW_ADJUST back upstream, to synchronise the window
-     * size downstream thinks it's presented with the one we've
-     * actually presented.
-     */
-    packet = strbuf_new();
-    put_uint32(packet, xc->server_id);
-    put_uint32(packet, downstream_window - xc->window);
-    ssh_send_packet_from_downstream(
-        cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_WINDOW_ADJUST,
-        packet->s, packet->len,
-        "window adjustment after downstream accepted X channel");
-    strbuf_free(packet);
-}
-
-void share_xchannel_failure(struct ssh_sharing_connstate *cs,
-                            struct share_xchannel *xc)
-{
-    /*
-     * If downstream refuses to open our X channel at all for some
-     * reason, we must respond by sending an emergency CLOSE upstream.
-     */
-    strbuf *packet = strbuf_new();
-    put_uint32(packet, xc->server_id);
-    ssh_send_packet_from_downstream(
-        cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE,
-        packet->s, packet->len,
-        "downstream refused X channel open");
-    strbuf_free(packet);
-
-    /*
-     * Now mark the xchannel as dead, and respond to anything sent on
-     * it until we see CLOSE for it in turn.
-     */
-    xc->live = false;
-    share_dead_xchannel_respond(cs, xc);
-}
-
-void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan,
-                             unsigned upstream_id, unsigned server_id,
-                             unsigned server_currwin, unsigned server_maxpkt,
-                             unsigned client_adjusted_window,
-                             const char *peer_addr, int peer_port, int endian,
-                             int protomajor, int protominor,
-                             const void *initial_data, int initial_len)
-{
-    struct share_xchannel *xc;
-    void *greeting;
-    int greeting_len;
-    strbuf *packet;
-
-    /*
-     * Create an xchannel containing data we've already received from
-     * the X client, and preload it with a CHANNEL_DATA message
-     * containing our own made-up authorisation greeting and any
-     * additional data sent from the server so far.
-     */
-    xc = share_add_xchannel(cs, upstream_id, server_id);
-    greeting = x11_make_greeting(endian, protomajor, protominor,
-                                 chan->x11_auth_proto,
-                                 chan->x11_auth_data, chan->x11_auth_datalen,
-                                 peer_addr, peer_port, &greeting_len);
-    packet = strbuf_new_nm();
-    put_uint32(packet, 0); /* leave the channel id field unfilled - we
-                            * don't know the downstream id yet */
-    put_uint32(packet, greeting_len + initial_len);
-    put_data(packet, greeting, greeting_len);
-    put_data(packet, initial_data, initial_len);
-    sfree(greeting);
-    share_xchannel_add_message(xc, SSH2_MSG_CHANNEL_DATA,
-                               packet->s, packet->len);
-    strbuf_free(packet);
-
-    xc->window = client_adjusted_window + greeting_len;
-
-    /*
-     * Send on a CHANNEL_OPEN to downstream.
-     */
-    packet = strbuf_new();
-    put_stringz(packet, "x11");
-    put_uint32(packet, server_id);
-    put_uint32(packet, server_currwin);
-    put_uint32(packet, server_maxpkt);
-    put_stringz(packet, peer_addr);
-    put_uint32(packet, peer_port);
-    send_packet_to_downstream(cs, SSH2_MSG_CHANNEL_OPEN,
-                              packet->s, packet->len, NULL);
-    strbuf_free(packet);
-
-    /*
-     * If this was a once-only X forwarding, clean it up now.
-     */
-    if (chan->x11_one_shot) {
-        ssh_remove_sharing_x11_display(cs->parent->cl,
-                                       chan->x11_auth_upstream);
-        chan->x11_auth_upstream = NULL;
-        sfree(chan->x11_auth_data);
-        chan->x11_auth_proto = -1;
-        chan->x11_auth_datalen = 0;
-        chan->x11_one_shot = false;
-    }
-}
-
-void share_got_pkt_from_server(ssh_sharing_connstate *cs, int type,
-                               const void *vpkt, int pktlen)
-{
-    const unsigned char *pkt = (const unsigned char *)vpkt;
-    struct share_globreq *globreq;
-    size_t id_pos;
-    unsigned upstream_id, server_id;
-    struct share_channel *chan;
-    struct share_xchannel *xc;
-    BinarySource src[1];
-
-    BinarySource_BARE_INIT(src, pkt, pktlen);
-
-    switch (type) {
-      case SSH2_MSG_REQUEST_SUCCESS:
-      case SSH2_MSG_REQUEST_FAILURE:
-        globreq = cs->globreq_head;
-        assert(globreq);         /* should match the queue in ssh.c */
-        if (globreq->type == GLOBREQ_TCPIP_FORWARD) {
-            if (type == SSH2_MSG_REQUEST_FAILURE) {
-                share_remove_forwarding(cs, globreq->fwd);
-            } else {
-                globreq->fwd->active = true;
-            }
-        } else if (globreq->type == GLOBREQ_CANCEL_TCPIP_FORWARD) {
-            if (type == SSH2_MSG_REQUEST_SUCCESS) {
-                share_remove_forwarding(cs, globreq->fwd);
-            }
-        }
-        if (globreq->want_reply) {
-            send_packet_to_downstream(cs, type, pkt, pktlen, NULL);
-        }
-        cs->globreq_head = globreq->next;
-        sfree(globreq);
-        if (cs->globreq_head == NULL)
-            cs->globreq_tail = NULL;
-
-        if (!cs->sock) {
-            /* Retry cleaning up this connection, in case that reply
-             * was the last thing we were waiting for. */
-            share_try_cleanup(cs);
-        }
-
-        break;
-
-      case SSH2_MSG_CHANNEL_OPEN:
-        get_string(src);
-        server_id = get_uint32(src);
-        assert(!get_err(src));
-        share_add_halfchannel(cs, server_id);
-
-        send_packet_to_downstream(cs, type, pkt, pktlen, NULL);
-        break;
-
-      case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
-      case SSH2_MSG_CHANNEL_OPEN_FAILURE:
-      case SSH2_MSG_CHANNEL_CLOSE:
-      case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
-      case SSH2_MSG_CHANNEL_DATA:
-      case SSH2_MSG_CHANNEL_EXTENDED_DATA:
-      case SSH2_MSG_CHANNEL_EOF:
-      case SSH2_MSG_CHANNEL_REQUEST:
-      case SSH2_MSG_CHANNEL_SUCCESS:
-      case SSH2_MSG_CHANNEL_FAILURE:
-        /*
-         * All these messages have the recipient channel id as the
-         * first uint32 field in the packet. Substitute the downstream
-         * channel id for our one and pass the packet downstream.
-         */
-        id_pos = src->pos;
-        upstream_id = get_uint32(src);
-        if ((chan = share_find_channel_by_upstream(cs, upstream_id)) != NULL) {
-            /*
-             * The normal case: this id refers to an open channel.
-             */
-            unsigned char *rewritten = snewn(pktlen, unsigned char);
-            memcpy(rewritten, pkt, pktlen);
-            PUT_32BIT_MSB_FIRST(rewritten + id_pos, chan->downstream_id);
-            send_packet_to_downstream(cs, type, rewritten, pktlen, chan);
-            sfree(rewritten);
-
-            /*
-             * Update the channel state, for messages that need it.
-             */
-            if (type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
-                if (chan->state == UNACKNOWLEDGED && pktlen >= 8) {
-                    share_channel_set_server_id(
-                        cs, chan, GET_32BIT_MSB_FIRST(pkt+4), OPEN);
-                    if (!cs->sock) {
-                        /* Retry cleaning up this connection, so that we
-                         * can send an immediate CLOSE on this channel for
-                         * which we now know the server id. */
-                        share_try_cleanup(cs);
-                    }
-                }
-            } else if (type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
-                ssh_delete_sharing_channel(cs->parent->cl, chan->upstream_id);
-                share_remove_channel(cs, chan);
-            } else if (type == SSH2_MSG_CHANNEL_CLOSE) {
-                if (chan->state == SENT_CLOSE) {
-                    ssh_delete_sharing_channel(cs->parent->cl,
-                                               chan->upstream_id);
-                    share_remove_channel(cs, chan);
-                    if (!cs->sock) {
-                        /* Retry cleaning up this connection, in case this
-                         * channel closure was the last thing we were
-                         * waiting for. */
-                        share_try_cleanup(cs);
-                    }
-                } else {
-                    chan->state = RCVD_CLOSE;
-                }
-            }
-        } else if ((xc = share_find_xchannel_by_upstream(cs, upstream_id))
-                   != NULL) {
-            /*
-             * The unusual case: this id refers to an xchannel. Add it
-             * to the xchannel's queue.
-             */
-            share_xchannel_add_message(xc, type, pkt, pktlen);
-
-            /* If the xchannel is dead, then also respond to it (which
-             * may involve deleting the channel). */
-            if (!xc->live)
-                share_dead_xchannel_respond(cs, xc);
-        }
-        break;
-
-      default:
-        unreachable("This packet type should never have come from ssh.c");
-    }
-}
-
-static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
-                                          int type,
-                                          unsigned char *pkt, int pktlen)
-{
-    ptrlen request_name;
-    struct share_forwarding *fwd;
-    size_t id_pos;
-    unsigned maxpkt;
-    unsigned old_id, new_id, server_id;
-    struct share_globreq *globreq;
-    struct share_channel *chan;
-    struct share_halfchannel *hc;
-    struct share_xchannel *xc;
-    strbuf *packet;
-    char *err = NULL;
-    BinarySource src[1];
-    size_t wantreplypos;
-    bool orig_wantreply;
-
-    BinarySource_BARE_INIT(src, pkt, pktlen);
-
-    switch (type) {
-      case SSH2_MSG_DISCONNECT:
-        /*
-         * This message stops here: if downstream is disconnecting
-         * from us, that doesn't mean we want to disconnect from the
-         * SSH server. Close the downstream connection and start
-         * cleanup.
-         */
-        share_begin_cleanup(cs);
-        break;
-
-      case SSH2_MSG_GLOBAL_REQUEST:
-        /*
-         * The only global requests we understand are "tcpip-forward"
-         * and "cancel-tcpip-forward". Since those require us to
-         * maintain state, we must assume that other global requests
-         * will probably require that too, and so we don't forward on
-         * any request we don't understand.
-         */
-        request_name = get_string(src);
-        wantreplypos = src->pos;
-        orig_wantreply = get_bool(src);
-
-        if (ptrlen_eq_string(request_name, "tcpip-forward")) {
-            ptrlen hostpl;
-            char *host;
-            int port;
-            struct ssh_rportfwd *rpf;
-
-            /*
-             * Pick the packet apart to find the want_reply field and
-             * the host/port we're going to ask to listen on.
-             */
-            hostpl = get_string(src);
-            port = toint(get_uint32(src));
-            if (get_err(src)) {
-                err = dupprintf("Truncated GLOBAL_REQUEST packet");
-                goto confused;
-            }
-            host = mkstr(hostpl);
-
-            /*
-             * See if we can allocate space in ssh.c's tree of remote
-             * port forwardings. If we can't, it's because another
-             * client sharing this connection has already allocated
-             * the identical port forwarding, so we take it on
-             * ourselves to manufacture a failure packet and send it
-             * back to downstream.
-             */
-            rpf = ssh_rportfwd_alloc(
-                cs->parent->cl, host, port, NULL, 0, 0, NULL, NULL, cs);
-            if (!rpf) {
-                if (orig_wantreply) {
-                    send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
-                                              "", 0, NULL);
-                }
-            } else {
-                /*
-                 * We've managed to make space for this forwarding
-                 * locally. Pass the request on to the SSH server, but
-                 * set want_reply even if it wasn't originally set, so
-                 * that we know whether this forwarding needs to be
-                 * cleaned up if downstream goes away.
-                 */
-                pkt[wantreplypos] = 1;
-                ssh_send_packet_from_downstream
-                    (cs->parent->cl, cs->id, type, pkt, pktlen,
-                     orig_wantreply ? NULL : "upstream added want_reply flag");
-                fwd = share_add_forwarding(cs, host, port);
-                ssh_sharing_queue_global_request(cs->parent->cl, cs);
-
-                if (fwd) {
-                    globreq = snew(struct share_globreq);
-                    globreq->next = NULL;
-                    if (cs->globreq_tail)
-                        cs->globreq_tail->next = globreq;
-                    else
-                        cs->globreq_head = globreq;
-                    globreq->fwd = fwd;
-                    globreq->want_reply = orig_wantreply;
-                    globreq->type = GLOBREQ_TCPIP_FORWARD;
-
-                    fwd->rpf = rpf;
-                }
-            }
-
-            sfree(host);
-        } else if (ptrlen_eq_string(request_name, "cancel-tcpip-forward")) {
-            ptrlen hostpl;
-            char *host;
-            int port;
-            struct share_forwarding *fwd;
-
-            /*
-             * Pick the packet apart to find the want_reply field and
-             * the host/port we're going to ask to listen on.
-             */
-            hostpl = get_string(src);
-            port = toint(get_uint32(src));
-            if (get_err(src)) {
-                err = dupprintf("Truncated GLOBAL_REQUEST packet");
-                goto confused;
-            }
-            host = mkstr(hostpl);
-
-            /*
-             * Look up the existing forwarding with these details.
-             */
-            fwd = share_find_forwarding(cs, host, port);
-            if (!fwd) {
-                if (orig_wantreply) {
-                    send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
-                                              "", 0, NULL);
-                }
-            } else {
-                /*
-                 * Tell ssh.c to stop sending us channel-opens for
-                 * this forwarding.
-                 */
-                ssh_rportfwd_remove(cs->parent->cl, fwd->rpf);
-
-                /*
-                 * Pass the cancel request on to the SSH server, but
-                 * set want_reply even if it wasn't originally set, so
-                 * that _we_ know whether the forwarding has been
-                 * deleted even if downstream doesn't want to know.
-                 */
-                pkt[wantreplypos] = 1;
-                ssh_send_packet_from_downstream
-                    (cs->parent->cl, cs->id, type, pkt, pktlen,
-                     orig_wantreply ? NULL : "upstream added want_reply flag");
-                ssh_sharing_queue_global_request(cs->parent->cl, cs);
-
-                /*
-                 * And queue a globreq so that when the reply comes
-                 * back we know to cancel it.
-                 */
-                globreq = snew(struct share_globreq);
-                globreq->next = NULL;
-                if (cs->globreq_tail)
-                    cs->globreq_tail->next = globreq;
-                else
-                    cs->globreq_head = globreq;
-                globreq->fwd = fwd;
-                globreq->want_reply = orig_wantreply;
-                globreq->type = GLOBREQ_CANCEL_TCPIP_FORWARD;
-            }
-
-            sfree(host);
-        } else {
-            /*
-             * Request we don't understand. Manufacture a failure
-             * message if an answer was required.
-             */
-            if (orig_wantreply)
-                send_packet_to_downstream(cs, SSH2_MSG_REQUEST_FAILURE,
-                                          "", 0, NULL);
-        }
-        break;
-
-      case SSH2_MSG_CHANNEL_OPEN:
-        /* Sender channel id comes after the channel type string */
-        get_string(src);
-        id_pos = src->pos;
-        old_id = get_uint32(src);
-        new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs);
-        get_uint32(src);               /* skip initial window size */
-        maxpkt = get_uint32(src);
-        if (get_err(src)) {
-            err = dupprintf("Truncated CHANNEL_OPEN packet");
-            goto confused;
-        }
-        share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED, maxpkt);
-        PUT_32BIT_MSB_FIRST(pkt + id_pos, new_id);
-        ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
-                                        type, pkt, pktlen, NULL);
-        break;
-
-      case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
-        if (pktlen < 16) {
-            err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet");
-            goto confused;
-        }
-
-        server_id = get_uint32(src);
-        id_pos = src->pos;
-        old_id = get_uint32(src);
-        get_uint32(src);               /* skip initial window size */
-        maxpkt = get_uint32(src);
-        if (get_err(src)) {
-            err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet");
-            goto confused;
-        }
-
-        /* This server id may refer to either a halfchannel or an xchannel. */
-        hc = NULL, xc = NULL;          /* placate optimiser */
-        if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
-            new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs);
-        } else if ((xc = share_find_xchannel_by_server(cs, server_id))
-                   != NULL) {
-            new_id = xc->upstream_id;
-        } else {
-            err = dupprintf("CHANNEL_OPEN_CONFIRMATION packet cited unknown channel %u", (unsigned)server_id);
-            goto confused;
-        }
-
-        PUT_32BIT_MSB_FIRST(pkt + id_pos, new_id);
-
-        chan = share_add_channel(cs, old_id, new_id, server_id, OPEN, maxpkt);
-
-        if (hc) {
-            ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
-                                            type, pkt, pktlen, NULL);
-            share_remove_halfchannel(cs, hc);
-        } else if (xc) {
-            unsigned downstream_window = GET_32BIT_MSB_FIRST(pkt + 8);
-            if (downstream_window < 256) {
-                err = dupprintf("Initial window size for x11 channel must be at least 256 (got %u)", downstream_window);
-                goto confused;
-            }
-            share_xchannel_confirmation(cs, xc, chan, downstream_window);
-            share_remove_xchannel(cs, xc);
-        }
-
-        break;
-
-      case SSH2_MSG_CHANNEL_OPEN_FAILURE:
-        server_id = get_uint32(src);
-        if (get_err(src)) {
-            err = dupprintf("Truncated CHANNEL_OPEN_FAILURE packet");
-            goto confused;
-        }
-
-        /* This server id may refer to either a halfchannel or an xchannel. */
-        if ((hc = share_find_halfchannel(cs, server_id)) != NULL) {
-            ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
-                                            type, pkt, pktlen, NULL);
-            share_remove_halfchannel(cs, hc);
-        } else if ((xc = share_find_xchannel_by_server(cs, server_id))
-                   != NULL) {
-            share_xchannel_failure(cs, xc);
-        } else {
-            err = dupprintf("CHANNEL_OPEN_FAILURE packet cited unknown channel %u", (unsigned)server_id);
-            goto confused;
-        }
-
-        break;
-
-      case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
-      case SSH2_MSG_CHANNEL_DATA:
-      case SSH2_MSG_CHANNEL_EXTENDED_DATA:
-      case SSH2_MSG_CHANNEL_EOF:
-      case SSH2_MSG_CHANNEL_CLOSE:
-      case SSH2_MSG_CHANNEL_REQUEST:
-      case SSH2_MSG_CHANNEL_SUCCESS:
-      case SSH2_MSG_CHANNEL_FAILURE:
-      case SSH2_MSG_IGNORE:
-      case SSH2_MSG_DEBUG:
-        server_id = get_uint32(src);
-
-        if (type == SSH2_MSG_CHANNEL_REQUEST) {
-            request_name = get_string(src);
-
-            /*
-             * Agent forwarding requests from downstream are treated
-             * specially. Because OpenSSHD doesn't let us enable agent
-             * forwarding independently per session channel, and in
-             * particular because the OpenSSH-defined agent forwarding
-             * protocol does not mark agent-channel requests with the
-             * id of the session channel they originate from, the only
-             * way we can implement agent forwarding in a
-             * connection-shared PuTTY is to forward the _upstream_
-             * agent. Hence, we unilaterally deny agent forwarding
-             * requests from downstreams if we aren't prepared to
-             * forward an agent ourselves.
-             *
-             * (If we are, then we dutifully pass agent forwarding
-             * requests upstream. OpenSSHD has the curious behaviour
-             * that all but the first such request will be rejected,
-             * but all session channels opened after the first request
-             * get agent forwarding enabled whether they ask for it or
-             * not; but that's not our concern, since other SSH
-             * servers supporting the same piece of protocol might in
-             * principle at least manage to enable agent forwarding on
-             * precisely the channels that requested it, even if the
-             * subsequent CHANNEL_OPENs still can't be associated with
-             * a parent session channel.)
-             */
-            if (ptrlen_eq_string(request_name, "[email protected]") &&
-                !ssh_agent_forwarding_permitted(cs->parent->cl)) {
-
-                chan = share_find_channel_by_server(cs, server_id);
-                if (chan) {
-                    packet = strbuf_new();
-                    put_uint32(packet, chan->downstream_id);
-                    send_packet_to_downstream(
-                        cs, SSH2_MSG_CHANNEL_FAILURE,
-                        packet->s, packet->len, NULL);
-                    strbuf_free(packet);
-                } else {
-                    char *buf = dupprintf("Agent forwarding request for "
-                                          "unrecognised channel %u", server_id);
-                    share_disconnect(cs, buf);
-                    sfree(buf);
-                    return;
-                }
-                break;
-            }
-
-            /*
-             * Another thing we treat specially is X11 forwarding
-             * requests. For these, we have to make up another set of
-             * X11 auth data, and enter it into our SSH connection's
-             * list of possible X11 authorisation credentials so that
-             * when we see an X11 channel open request we can know
-             * whether it's one to handle locally or one to pass on to
-             * a downstream, and if the latter, which one.
-             */
-            if (ptrlen_eq_string(request_name, "x11-req")) {
-                bool want_reply, single_connection;
-                int screen;
-                ptrlen auth_data;
-                int auth_proto;
-
-                chan = share_find_channel_by_server(cs, server_id);
-                if (!chan) {
-                    char *buf = dupprintf("X11 forwarding request for "
-                                          "unrecognised channel %u", server_id);
-                    share_disconnect(cs, buf);
-                    sfree(buf);
-                    return;
-                }
-
-                /*
-                 * Pick apart the whole message to find the downstream
-                 * auth details.
-                 */
-                want_reply = get_bool(src);
-                single_connection = get_bool(src);
-                auth_proto = x11_identify_auth_proto(get_string(src));
-                auth_data = get_string(src);
-                screen = toint(get_uint32(src));
-                if (get_err(src)) {
-                    err = dupprintf("Truncated CHANNEL_REQUEST(\"x11-req\")"
-                                    " packet");
-                    goto confused;
-                }
-
-                if (auth_proto < 0) {
-                    /* Reject due to not understanding downstream's
-                     * requested authorisation method. */
-                    packet = strbuf_new();
-                    put_uint32(packet, chan->downstream_id);
-                    send_packet_to_downstream(
-                        cs, SSH2_MSG_CHANNEL_FAILURE,
-                        packet->s, packet->len, NULL);
-                    strbuf_free(packet);
-                    break;
-                }
-
-                chan->x11_auth_proto = auth_proto;
-                chan->x11_auth_data = x11_dehexify(auth_data,
-                                                   &chan->x11_auth_datalen);
-                chan->x11_auth_upstream =
-                    ssh_add_sharing_x11_display(cs->parent->cl, auth_proto,
-                                                cs, chan);
-                chan->x11_one_shot = single_connection;
-
-                /*
-                 * Now construct a replacement X forwarding request,
-                 * containing our own auth data, and send that to the
-                 * server.
-                 */
-                packet = strbuf_new_nm();
-                put_uint32(packet, server_id);
-                put_stringz(packet, "x11-req");
-                put_bool(packet, want_reply);
-                put_bool(packet, single_connection);
-                put_stringz(packet, chan->x11_auth_upstream->protoname);
-                put_stringz(packet, chan->x11_auth_upstream->datastring);
-                put_uint32(packet, screen);
-                ssh_send_packet_from_downstream(
-                    cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_REQUEST,
-                    packet->s, packet->len, NULL);
-                strbuf_free(packet);
-
-                break;
-            }
-        }
-
-        ssh_send_packet_from_downstream(cs->parent->cl, cs->id,
-                                        type, pkt, pktlen, NULL);
-        if (type == SSH2_MSG_CHANNEL_CLOSE && pktlen >= 4) {
-            chan = share_find_channel_by_server(cs, server_id);
-            if (chan) {
-                if (chan->state == RCVD_CLOSE) {
-                    ssh_delete_sharing_channel(cs->parent->cl,
-                                               chan->upstream_id);
-                    share_remove_channel(cs, chan);
-                } else {
-                    chan->state = SENT_CLOSE;
-                }
-            }
-        }
-        break;
-
-      default:
-        err = dupprintf("Unexpected packet type %d\n", type);
-        goto confused;
-
-        /*
-         * Any other packet type is unexpected. In particular, we
-         * never pass GLOBAL_REQUESTs downstream, so we never expect
-         * to see SSH2_MSG_REQUEST_{SUCCESS,FAILURE}.
-         */
-      confused:
-        assert(err != NULL);
-        share_disconnect(cs, err);
-        sfree(err);
-        break;
-    }
-}
-
-/*
- * An extra coroutine macro, specific to this code which is consuming
- * 'const char *data'.
- */
-#define crGetChar(c) do                                         \
-    {                                                           \
-        while (len == 0) {                                      \
-            *crLine =__LINE__; return; case __LINE__:;          \
-        }                                                       \
-        len--;                                                  \
-        (c) = (unsigned char)*data++;                           \
-    } while (0)
-
-static void share_receive(Plug *plug, int urgent, const char *data, size_t len)
-{
-    ssh_sharing_connstate *cs = container_of(
-        plug, ssh_sharing_connstate, plug);
-    static const char expected_verstring_prefix[] =
-        "[email protected]";
-    unsigned char c;
-
-    crBegin(cs->crLine);
-
-    /*
-     * First read the version string from downstream.
-     */
-    cs->recvlen = 0;
-    while (1) {
-        crGetChar(c);
-        if (c == '\012')
-            break;
-        if (cs->recvlen >= sizeof(cs->recvbuf)) {
-            char *buf = dupprintf("Version string far too long\n");
-            share_disconnect(cs, buf);
-            sfree(buf);
-            goto dead;
-        }
-        cs->recvbuf[cs->recvlen++] = c;
-    }
-
-    /*
-     * Now parse the version string to make sure it's at least vaguely
-     * sensible, and log it.
-     */
-    if (cs->recvlen < sizeof(expected_verstring_prefix)-1 ||
-        memcmp(cs->recvbuf, expected_verstring_prefix,
-               sizeof(expected_verstring_prefix) - 1)) {
-        char *buf = dupprintf("Version string did not have expected prefix\n");
-        share_disconnect(cs, buf);
-        sfree(buf);
-        goto dead;
-    }
-    if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015')
-        cs->recvlen--;                 /* trim off \r before \n */
-    { // WINSCP
-    ptrlen verstring = make_ptrlen(cs->recvbuf, cs->recvlen);
-    log_downstream(cs, "Downstream version string: %.*s",
-                   PTRLEN_PRINTF(verstring));
-    } // WINSCP
-    cs->got_verstring = true;
-
-    /*
-     * Loop round reading packets.
-     */
-    while (1) {
-        cs->recvlen = 0;
-        while (cs->recvlen < 4) {
-            crGetChar(c);
-            cs->recvbuf[cs->recvlen++] = c;
-        }
-        cs->curr_packetlen = toint(GET_32BIT_MSB_FIRST(cs->recvbuf) + 4);
-        if (cs->curr_packetlen < 5 ||
-            cs->curr_packetlen > sizeof(cs->recvbuf)) {
-            char *buf = dupprintf("Bad packet length %u\n",
-                                  (unsigned)cs->curr_packetlen);
-            share_disconnect(cs, buf);
-            sfree(buf);
-            goto dead;
-        }
-        while (cs->recvlen < cs->curr_packetlen) {
-            crGetChar(c);
-            cs->recvbuf[cs->recvlen++] = c;
-        }
-
-        share_got_pkt_from_downstream(cs, cs->recvbuf[4],
-                                      cs->recvbuf + 5, cs->recvlen - 5);
-    }
-
-  dead:;
-    crFinishV;
-}
-
-static void share_sent(Plug *plug, size_t bufsize)
-{
-    /* ssh_sharing_connstate *cs = container_of(
-        plug, ssh_sharing_connstate, plug); */
-
-    /*
-     * We do nothing here, because we expect that there won't be a
-     * need to throttle and unthrottle the connection to a downstream.
-     * It should automatically throttle itself: if the SSH server
-     * sends huge amounts of data on all channels then it'll run out
-     * of window until our downstream sends it back some
-     * WINDOW_ADJUSTs.
-     */
-}
-
-static void share_listen_closing(Plug *plug, const char *error_msg,
-                                 int error_code, bool calling_back)
-{
-    ssh_sharing_state *sharestate =
-        container_of(plug, ssh_sharing_state, plug);
-    if (error_msg)
-        log_general(sharestate, "listening socket: %s", error_msg);
-    sk_close(sharestate->listensock);
-    sharestate->listensock = NULL;
-}
-
-static void share_send_verstring(ssh_sharing_connstate *cs)
-{
-    char *fullstring = dupcat("[email protected]",
-                              cs->parent->server_verstring, "\015\012");
-    sk_write(cs->sock, fullstring, strlen(fullstring));
-    sfree(fullstring);
-
-    cs->sent_verstring = true;
-}
-
-int share_ndownstreams(ssh_sharing_state *sharestate)
-{
-    return count234(sharestate->connections);
-}
-
-void share_activate(ssh_sharing_state *sharestate,
-                    const char *server_verstring)
-{
-    /*
-     * Indication from ssh.c that we are now ready to begin serving
-     * any downstreams that have already connected to us.
-     */
-    struct ssh_sharing_connstate *cs;
-    int i;
-
-    /*
-     * Trim the server's version string down to just the software
-     * version component, removing "SSH-2.0-" or whatever at the
-     * front.
-     */
-    for (i = 0; i < 2; i++) {
-        server_verstring += strcspn(server_verstring, "-");
-        if (*server_verstring)
-            server_verstring++;
-    }
-
-    sharestate->server_verstring = dupstr(server_verstring);
-
-    for (i = 0; (cs = (struct ssh_sharing_connstate *)
-                 index234(sharestate->connections, i)) != NULL; i++) {
-        assert(!cs->sent_verstring);
-        share_send_verstring(cs);
-    }
-}
-
-static const PlugVtable ssh_sharing_conn_plugvt = {
-    NULL, // WINSCP
-    /*.closing =*/ share_closing,
-    /*.receive =*/ share_receive,
-    /*.sent =*/ share_sent,
-    NULL, // WINSCP
-};
-
-static int share_listen_accepting(Plug *plug,
-                                  accept_fn_t constructor, accept_ctx_t ctx)
-{
-    struct ssh_sharing_state *sharestate = container_of(
-        plug, struct ssh_sharing_state, plug);
-    struct ssh_sharing_connstate *cs;
-    const char *err;
-    SocketPeerInfo *peerinfo;
-
-    /*
-     * A new downstream has connected to us.
-     */
-    cs = snew(struct ssh_sharing_connstate);
-    cs->plug.vt = &ssh_sharing_conn_plugvt;
-    cs->parent = sharestate;
-
-    if ((cs->id = share_find_unused_id(sharestate, sharestate->nextid)) == 0 &&
-        (cs->id = share_find_unused_id(sharestate, 1)) == 0) {
-        sfree(cs);
-        return 1;
-    }
-    sharestate->nextid = cs->id + 1;
-    if (sharestate->nextid == 0)
-        sharestate->nextid++; /* only happens in VERY long-running upstreams */
-
-    cs->sock = constructor(ctx, &cs->plug);
-    if ((err = sk_socket_error(cs->sock)) != NULL) {
-        sfree(cs);
-        return err != NULL;
-    }
-
-    sk_set_frozen(cs->sock, false);
-
-    add234(cs->parent->connections, cs);
-
-    cs->sent_verstring = false;
-    if (sharestate->server_verstring)
-        share_send_verstring(cs);
-
-    cs->got_verstring = false;
-    cs->recvlen = 0;
-    cs->crLine = 0;
-    cs->halfchannels = newtree234(share_halfchannel_cmp);
-    cs->channels_by_us = newtree234(share_channel_us_cmp);
-    cs->channels_by_server = newtree234(share_channel_server_cmp);
-    cs->xchannels_by_us = newtree234(share_xchannel_us_cmp);
-    cs->xchannels_by_server = newtree234(share_xchannel_server_cmp);
-    cs->forwardings = newtree234(share_forwarding_cmp);
-    cs->globreq_head = cs->globreq_tail = NULL;
-
-    peerinfo = sk_peer_info(cs->sock);
-    log_downstream(cs, "connected%s%s",
-                   (peerinfo && peerinfo->log_text ? " from " : ""),
-                   (peerinfo && peerinfo->log_text ? peerinfo->log_text : ""));
-    sk_free_peer_info(peerinfo);
-
-    return 0;
-}
-
-/*
- * Decide on the string used to identify the connection point between
- * upstream and downstream (be it a Windows named pipe or a
- * Unix-domain socket or whatever else).
- *
- * I wondered about making this a SHA hash of all sorts of pieces of
- * the PuTTY configuration - essentially everything PuTTY uses to know
- * where and how to make a connection, including all the proxy details
- * (or rather, all the _relevant_ ones - only including settings that
- * other settings didn't prevent from having any effect), plus the
- * username. However, I think it's better to keep it really simple:
- * the connection point identifier is derived from the hostname and
- * port used to index the host-key cache (not necessarily where we
- * _physically_ connected to, in cases involving proxies or
- * CONF_loghost), plus the username if one is specified.
- *
- * The per-platform code will quite likely hash or obfuscate this name
- * in turn, for privacy from other users; failing that, it might
- * transform it to avoid dangerous filename characters and so on. But
- * that doesn't matter to us: for us, the point is that two session
- * configurations which return the same string from this function will
- * be treated as potentially shareable with each other.
- */
-char *ssh_share_sockname(const char *host, int port, Conf *conf)
-{
-    char *username = NULL;
-    char *sockname;
-
-    /* Include the username we're logging in as in the hash, unless
-     * we're using a protocol for which it's completely irrelevant. */
-    if (conf_get_int(conf, CONF_protocol) != PROT_SSHCONN)
-        username = get_remote_username(conf);
-
-    if (port == 22) {
-        if (username)
-            sockname = dupprintf("%s@%s", username, host);
-        else
-            sockname = dupprintf("%s", host);
-    } else {
-        if (username)
-            sockname = dupprintf("%s@%s:%d", username, host, port);
-        else
-            sockname = dupprintf("%s:%d", host, port);
-    }
-
-    sfree(username);
-    return sockname;
-}
-
-bool ssh_share_test_for_upstream(const char *host, int port, Conf *conf)
-{
-    char *sockname, *logtext, *ds_err, *us_err;
-    int result;
-    Socket *sock;
-
-    sockname = ssh_share_sockname(host, port, conf);
-
-    sock = NULL;
-    logtext = ds_err = us_err = NULL;
-    result = platform_ssh_share(sockname, conf, nullplug, (Plug *)NULL, &sock,
-                                &logtext, &ds_err, &us_err, false, true);
-
-    sfree(logtext);
-    sfree(ds_err);
-    sfree(us_err);
-    sfree(sockname);
-
-    if (result == SHARE_NONE) {
-        assert(sock == NULL);
-        return false;
-    } else {
-        assert(result == SHARE_DOWNSTREAM);
-        sk_close(sock);
-        return true;
-    }
-}
-
-static const PlugVtable ssh_sharing_listen_plugvt = {
-    NULL, // WINSCP
-    // WINSCP
-    /*.closing =*/ share_listen_closing,
-    NULL, NULL, // WINSCP
-    /*.accepting =*/ share_listen_accepting,
-};
-
-void ssh_connshare_provide_connlayer(ssh_sharing_state *sharestate,
-                                     ConnectionLayer *cl)
-{
-    sharestate->cl = cl;
-}
-
-/*
- * Init function for connection sharing. We either open a listening
- * socket and become an upstream, or connect to an existing one and
- * become a downstream, or do neither. We are responsible for deciding
- * which of these to do (including checking the Conf to see if
- * connection sharing is even enabled in the first place). If we
- * become a downstream, we return the Socket with which we connected
- * to the upstream; otherwise (whether or not we have established an
- * upstream) we return NULL.
- */
-Socket *ssh_connection_sharing_init(
-    const char *host, int port, Conf *conf, LogContext *logctx,
-    Plug *sshplug, ssh_sharing_state **state)
-{
-    int result;
-    bool can_upstream, can_downstream;
-    char *logtext, *ds_err, *us_err;
-    char *sockname;
-    Socket *sock, *toret = NULL;
-    struct ssh_sharing_state *sharestate;
-
-    if (!conf_get_bool(conf, CONF_ssh_connection_sharing))
-        return NULL;                   /* do not share anything */
-    can_upstream = share_can_be_upstream &&
-        conf_get_bool(conf, CONF_ssh_connection_sharing_upstream);
-    can_downstream = share_can_be_downstream &&
-        conf_get_bool(conf, CONF_ssh_connection_sharing_downstream);
-    if (!can_upstream && !can_downstream)
-        return NULL;
-
-    sockname = ssh_share_sockname(host, port, conf);
-
-    /*
-     * Create a data structure for the listening plug if we turn out
-     * to be an upstream.
-     */
-    sharestate = snew(struct ssh_sharing_state);
-    sharestate->plug.vt = &ssh_sharing_listen_plugvt;
-    sharestate->listensock = NULL;
-    sharestate->cl = NULL;
-
-    /*
-     * Now hand off to a per-platform routine that either connects to
-     * an existing upstream (using 'ssh' as the plug), establishes our
-     * own upstream (using 'sharestate' as the plug), or forks off a
-     * separate upstream and then connects to that. It will return a
-     * code telling us which kind of socket it put in 'sock'.
-     */
-    sock = NULL;
-    logtext = ds_err = us_err = NULL;
-    result = platform_ssh_share(
-        sockname, conf, sshplug, &sharestate->plug, &sock, &logtext,
-        &ds_err, &us_err, can_upstream, can_downstream);
-    switch (result) {
-      case SHARE_NONE:
-        /*
-         * We aren't sharing our connection at all (e.g. something
-         * went wrong setting the socket up). Free the upstream
-         * structure and return NULL.
-         */
-
-        if (logtext) {
-            /* For this result, if 'logtext' is not NULL then it is an
-             * error message indicating a reason why connection sharing
-             * couldn't be set up _at all_ */
-            logeventf(logctx,
-                      "Could not set up connection sharing: %s", logtext);
-        } else {
-            /* Failing that, ds_err and us_err indicate why we
-             * couldn't be a downstream and an upstream respectively */
-            if (ds_err)
-                logeventf(logctx, "Could not set up connection sharing"
-                          " as downstream: %s", ds_err);
-            if (us_err)
-                logeventf(logctx, "Could not set up connection sharing"
-                          " as upstream: %s", us_err);
-        }
-
-        assert(sock == NULL);
-        *state = NULL;
-        sfree(sharestate);
-        sfree(sockname);
-        break;
-
-      case SHARE_DOWNSTREAM:
-        /*
-         * We are downstream, so free sharestate which it turns out we
-         * don't need after all, and return the downstream socket as a
-         * replacement for an ordinary SSH connection.
-         */
-
-        /* 'logtext' is a local endpoint address */
-        logeventf(logctx, "Using existing shared connection at %s", logtext);
-
-        *state = NULL;
-        sfree(sharestate);
-        sfree(sockname);
-        toret = sock;
-        break;
-
-      case SHARE_UPSTREAM:
-        /*
-         * We are upstream. Set up sharestate properly and pass a copy
-         * to the caller; return NULL, to tell ssh.c that it has to
-         * make an ordinary connection after all.
-         */
-
-        /* 'logtext' is a local endpoint address */
-        logeventf(logctx, "Sharing this connection at %s", logtext);
-
-        *state = sharestate;
-        sharestate->listensock = sock;
-        sharestate->connections = newtree234(share_connstate_cmp);
-        sharestate->server_verstring = NULL;
-        sharestate->sockname = sockname;
-        sharestate->nextid = 1;
-        break;
-    }
-
-    sfree(logtext);
-    sfree(ds_err);
-    sfree(us_err);
-    return toret;
-}

+ 0 - 53
source/putty/sshsignals.h

@@ -1,53 +0,0 @@
-/*
- * List of signal names known to SSH, indicating whether PuTTY's UI
- * for special session commands likes to put them in the main specials
- * menu or in a submenu (and if the former, what title they have).
- *
- * This is a separate header file rather than my usual style of a
- * parametric list macro, because in this case I need to be able to
- * #ifdef out each mode in case it's not defined on a particular
- * target system.
- *
- * If you want only the locally defined signals, #define
- * SIGNALS_LOCAL_ONLY before including this header.
- */
-
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGINT
-SIGNAL_MAIN(INT, "Interrupt")
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGTERM
-SIGNAL_MAIN(TERM, "Terminate")
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGKILL
-SIGNAL_MAIN(KILL, "Kill")
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGQUIT
-SIGNAL_MAIN(QUIT, "Quit")
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGHUP
-SIGNAL_MAIN(HUP, "Hangup")
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGABRT
-SIGNAL_SUB(ABRT)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGALRM
-SIGNAL_SUB(ALRM)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGFPE
-SIGNAL_SUB(FPE)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGILL
-SIGNAL_SUB(ILL)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGPIPE
-SIGNAL_SUB(PIPE)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGSEGV
-SIGNAL_SUB(SEGV)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGUSR1
-SIGNAL_SUB(USR1)
-#endif
-#if !defined SIGNALS_LOCAL_ONLY || defined SIGUSR2
-SIGNAL_SUB(USR2)
-#endif

+ 0 - 179
source/putty/sshttymodes.h

@@ -1,179 +0,0 @@
-/*
- * List of SSH terminal modes, indicating whether SSH types them as
- * char or boolean, and if they're boolean, which POSIX flags field of
- * a termios structure they appear in, and what bit mask removes them
- * (e.g. CS7 and CS8 aren't single bits).
- *
- * Sources: RFC 4254, SSH-1 RFC-1.2.31, POSIX 2017, and the Linux
- * termios manpage for flags not specified by POSIX.
- *
- * This is a separate header file rather than my usual style of a
- * parametric list macro, because in this case I need to be able to
- * #ifdef out each mode in case it's not defined on a particular
- * target system.
- *
- * If you want only the locally defined modes, #define
- * TTYMODES_LOCAL_ONLY before including this header.
- */
-#if !defined TTYMODES_LOCAL_ONLY || defined VINTR
-TTYMODE_CHAR(INTR, 1, VINTR)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VQUIT
-TTYMODE_CHAR(QUIT, 2, VQUIT)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VERASE
-TTYMODE_CHAR(ERASE, 3, VERASE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VKILL
-TTYMODE_CHAR(KILL, 4, VKILL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VEOF
-TTYMODE_CHAR(EOF, 5, VEOF)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VEOL
-TTYMODE_CHAR(EOL, 6, VEOL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VEOL2
-TTYMODE_CHAR(EOL2, 7, VEOL2)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VSTART
-TTYMODE_CHAR(START, 8, VSTART)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VSTOP
-TTYMODE_CHAR(STOP, 9, VSTOP)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VSUSP
-TTYMODE_CHAR(SUSP, 10, VSUSP)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VDSUSP
-TTYMODE_CHAR(DSUSP, 11, VDSUSP)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VREPRINT
-TTYMODE_CHAR(REPRINT, 12, VREPRINT)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VWERASE
-TTYMODE_CHAR(WERASE, 13, VWERASE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VLNEXT
-TTYMODE_CHAR(LNEXT, 14, VLNEXT)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VFLUSH
-TTYMODE_CHAR(FLUSH, 15, VFLUSH)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VSWTCH
-TTYMODE_CHAR(SWTCH, 16, VSWTCH)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VSTATUS
-TTYMODE_CHAR(STATUS, 17, VSTATUS)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined VDISCARD
-TTYMODE_CHAR(DISCARD, 18, VDISCARD)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IGNPAR
-TTYMODE_FLAG(IGNPAR, 30, i, IGNPAR)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined PARMRK
-TTYMODE_FLAG(PARMRK, 31, i, PARMRK)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined INPCK
-TTYMODE_FLAG(INPCK, 32, i, INPCK)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ISTRIP
-TTYMODE_FLAG(ISTRIP, 33, i, ISTRIP)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined INLCR
-TTYMODE_FLAG(INLCR, 34, i, INLCR)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IGNCR
-TTYMODE_FLAG(IGNCR, 35, i, IGNCR)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ICRNL
-TTYMODE_FLAG(ICRNL, 36, i, ICRNL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IUCLC
-TTYMODE_FLAG(IUCLC, 37, i, IUCLC)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IXON
-TTYMODE_FLAG(IXON, 38, i, IXON)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IXANY
-TTYMODE_FLAG(IXANY, 39, i, IXANY)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IXOFF
-TTYMODE_FLAG(IXOFF, 40, i, IXOFF)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IMAXBEL
-TTYMODE_FLAG(IMAXBEL, 41, i, IMAXBEL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IUTF8
-TTYMODE_FLAG(IUTF8, 42, i, IUTF8)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ISIG
-TTYMODE_FLAG(ISIG, 50, l, ISIG)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ICANON
-TTYMODE_FLAG(ICANON, 51, l, ICANON)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined XCASE
-TTYMODE_FLAG(XCASE, 52, l, XCASE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ECHO
-TTYMODE_FLAG(ECHO, 53, l, ECHO)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ECHOE
-TTYMODE_FLAG(ECHOE, 54, l, ECHOE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ECHOK
-TTYMODE_FLAG(ECHOK, 55, l, ECHOK)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ECHONL
-TTYMODE_FLAG(ECHONL, 56, l, ECHONL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined NOFLSH
-TTYMODE_FLAG(NOFLSH, 57, l, NOFLSH)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined TOSTOP
-TTYMODE_FLAG(TOSTOP, 58, l, TOSTOP)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined IEXTEN
-TTYMODE_FLAG(IEXTEN, 59, l, IEXTEN)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ECHOCTL
-TTYMODE_FLAG(ECHOCTL, 60, l, ECHOCTL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ECHOKE
-TTYMODE_FLAG(ECHOKE, 61, l, ECHOKE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined PENDIN
-TTYMODE_FLAG(PENDIN, 62, l, PENDIN)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined OPOST
-TTYMODE_FLAG(OPOST, 70, o, OPOST)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined OLCUC
-TTYMODE_FLAG(OLCUC, 71, o, OLCUC)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ONLCR
-TTYMODE_FLAG(ONLCR, 72, o, ONLCR)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined OCRNL
-TTYMODE_FLAG(OCRNL, 73, o, OCRNL)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ONOCR
-TTYMODE_FLAG(ONOCR, 74, o, ONOCR)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined ONLRET
-TTYMODE_FLAG(ONLRET, 75, o, ONLRET)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined CS7
-TTYMODE_FLAG(CS7, 90, c, CSIZE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined CS8
-TTYMODE_FLAG(CS8, 91, c, CSIZE)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined PARENB
-TTYMODE_FLAG(PARENB, 92, c, PARENB)
-#endif
-#if !defined TTYMODES_LOCAL_ONLY || defined PARODD
-TTYMODE_FLAG(PARODD, 93, c, PARODD)
-#endif

+ 0 - 128
source/putty/sshutils.c

@@ -1,128 +0,0 @@
-/*
- * Supporting routines used in common by all the various components of
- * the SSH system.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshchan.h"
-
-/* ----------------------------------------------------------------------
- * Centralised standard methods for other channel implementations to
- * borrow.
- */
-
-void chan_remotely_opened_confirmation(Channel *chan)
-{
-    unreachable("this channel type should never receive OPEN_CONFIRMATION");
-}
-
-void chan_remotely_opened_failure(Channel *chan, const char *errtext)
-{
-    unreachable("this channel type should never receive OPEN_FAILURE");
-}
-
-bool chan_default_want_close(
-    Channel *chan, bool sent_local_eof, bool rcvd_remote_eof)
-{
-    /*
-     * Default close policy: we start initiating the CHANNEL_CLOSE
-     * procedure as soon as both sides of the channel have seen EOF.
-     */
-    return sent_local_eof && rcvd_remote_eof;
-}
-
-bool chan_no_exit_status(Channel *chan, int status)
-{
-    return false;
-}
-
-bool chan_no_exit_signal(
-    Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg)
-{
-    return false;
-}
-
-bool chan_no_exit_signal_numeric(
-    Channel *chan, int signum, bool core_dumped, ptrlen msg)
-{
-    return false;
-}
-
-bool chan_no_run_shell(Channel *chan)
-{
-    return false;
-}
-
-bool chan_no_run_command(Channel *chan, ptrlen command)
-{
-    return false;
-}
-
-bool chan_no_run_subsystem(Channel *chan, ptrlen subsys)
-{
-    return false;
-}
-
-bool chan_no_enable_x11_forwarding(
-    Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
-    unsigned screen_number)
-{
-    return false;
-}
-
-bool chan_no_enable_agent_forwarding(Channel *chan)
-{
-    return false;
-}
-
-bool chan_no_allocate_pty(
-    Channel *chan, ptrlen termtype, unsigned width, unsigned height,
-    unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes)
-{
-    return false;
-}
-
-bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value)
-{
-    return false;
-}
-
-bool chan_no_send_break(Channel *chan, unsigned length)
-{
-    return false;
-}
-
-bool chan_no_send_signal(Channel *chan, ptrlen signame)
-{
-    return false;
-}
-
-bool chan_no_change_window_size(
-    Channel *chan, unsigned width, unsigned height,
-    unsigned pixwidth, unsigned pixheight)
-{
-    return false;
-}
-
-void chan_no_request_response(Channel *chan, bool success)
-{
-    unreachable("this channel type should never send a want-reply request");
-}
-
-/* ----------------------------------------------------------------------
- * Other miscellaneous utility functions.
- */
-
-void free_rportfwd(struct ssh_rportfwd *rpf)
-{
-    if (rpf) {
-        sfree(rpf->log_description);
-        sfree(rpf->shost);
-        sfree(rpf->dhost);
-        sfree(rpf);
-    }
-}

+ 0 - 629
source/putty/sshverstring.c

@@ -1,629 +0,0 @@
-/*
- * Code to handle the initial SSH version string exchange.
- */
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "putty.h"
-#include "ssh.h"
-#include "sshbpp.h"
-#include "sshcr.h"
-
-#define PREFIX_MAXLEN 64
-
-struct ssh_verstring_state {
-    int crState;
-
-    Conf *conf;
-    ptrlen prefix_wanted;
-    char *our_protoversion;
-    struct ssh_version_receiver *receiver;
-
-    bool send_early;
-
-    bool found_prefix;
-    int major_protoversion;
-    int remote_bugs;
-    char prefix[PREFIX_MAXLEN];
-    char *impl_name;
-    strbuf *vstring;
-    char *protoversion;
-    const char *softwareversion;
-
-    char *our_vstring;
-    int i;
-
-    BinaryPacketProtocol bpp;
-};
-
-static void ssh_verstring_free(BinaryPacketProtocol *bpp);
-static void ssh_verstring_handle_input(BinaryPacketProtocol *bpp);
-static void ssh_verstring_handle_output(BinaryPacketProtocol *bpp);
-static PktOut *ssh_verstring_new_pktout(int type);
-static void ssh_verstring_queue_disconnect(BinaryPacketProtocol *bpp,
-                                          const char *msg, int category);
-
-static const BinaryPacketProtocolVtable ssh_verstring_vtable = {
-    // WINSCP
-    /*.free =*/ ssh_verstring_free,
-    /*.handle_input =*/ ssh_verstring_handle_input,
-    /*.handle_output =*/ ssh_verstring_handle_output,
-    /*.new_pktout =*/ ssh_verstring_new_pktout,
-    /*.queue_disconnect =*/ ssh_verstring_queue_disconnect,
-    /*.packet_size_limit =*/ 0xFFFFFFFF, /* no special limit for this bpp */
-};
-
-static void ssh_detect_bugs(struct ssh_verstring_state *s);
-static bool ssh_version_includes_v1(const char *ver);
-static bool ssh_version_includes_v2(const char *ver);
-
-BinaryPacketProtocol *ssh_verstring_new(
-    Conf *conf, LogContext *logctx, bool bare_connection_mode,
-    const char *protoversion, struct ssh_version_receiver *rcv,
-    bool server_mode, const char *impl_name)
-{
-    struct ssh_verstring_state *s = snew(struct ssh_verstring_state);
-
-    memset(s, 0, sizeof(struct ssh_verstring_state));
-
-    if (!bare_connection_mode) {
-        s->prefix_wanted = PTRLEN_LITERAL("SSH-");
-    } else {
-        /*
-         * Ordinary SSH begins with the banner "SSH-x.y-...". Here,
-         * we're going to be speaking just the ssh-connection
-         * subprotocol, extracted and given a trivial binary packet
-         * protocol, so we need a new banner.
-         *
-         * The new banner is like the ordinary SSH banner, but
-         * replaces the prefix 'SSH-' at the start with a new name. In
-         * proper SSH style (though of course this part of the proper
-         * SSH protocol _isn't_ subject to this kind of
-         * DNS-domain-based extension), we define the new name in our
-         * extension space.
-         */
-        s->prefix_wanted = PTRLEN_LITERAL(
-            "[email protected]");
-    }
-    assert(s->prefix_wanted.len <= PREFIX_MAXLEN);
-
-    s->conf = conf_copy(conf);
-    s->bpp.logctx = logctx;
-    s->our_protoversion = dupstr(protoversion);
-    s->receiver = rcv;
-    s->impl_name = dupstr(impl_name);
-    s->vstring = strbuf_new();
-
-    /*
-     * We send our version string early if we can. But if it includes
-     * SSH-1, we can't, because we have to take the other end into
-     * account too (see below).
-     *
-     * In server mode, we do send early.
-     */
-    s->send_early = server_mode || !ssh_version_includes_v1(protoversion);
-
-    s->bpp.vt = &ssh_verstring_vtable;
-    ssh_bpp_common_setup(&s->bpp);
-    return &s->bpp;
-}
-
-void ssh_verstring_free(BinaryPacketProtocol *bpp)
-{
-    struct ssh_verstring_state *s =
-        container_of(bpp, struct ssh_verstring_state, bpp);
-    conf_free(s->conf);
-    sfree(s->impl_name);
-    strbuf_free(s->vstring);
-    sfree(s->protoversion);
-    sfree(s->our_vstring);
-    sfree(s->our_protoversion);
-    sfree(s);
-}
-
-static int ssh_versioncmp(const char *a, const char *b)
-{
-    char *ae, *be;
-    unsigned long av, bv;
-
-    av = strtoul(a, &ae, 10);
-    bv = strtoul(b, &be, 10);
-    if (av != bv)
-        return (av < bv ? -1 : +1);
-    if (*ae == '.')
-        ae++;
-    if (*be == '.')
-        be++;
-    av = strtoul(ae, &ae, 10);
-    bv = strtoul(be, &be, 10);
-    if (av != bv)
-        return (av < bv ? -1 : +1);
-    return 0;
-}
-
-static bool ssh_version_includes_v1(const char *ver)
-{
-    return ssh_versioncmp(ver, "2.0") < 0;
-}
-
-static bool ssh_version_includes_v2(const char *ver)
-{
-    return ssh_versioncmp(ver, "1.99") >= 0;
-}
-
-static void ssh_verstring_send(struct ssh_verstring_state *s)
-{
-    BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */
-    char *p;
-    int sv_pos;
-
-    /*
-     * Construct our outgoing version string.
-     */
-    s->our_vstring = dupprintf(
-        "%.*s%s-%s%s",
-        (int)s->prefix_wanted.len, (const char *)s->prefix_wanted.ptr,
-        s->our_protoversion, s->impl_name, sshver);
-    sv_pos = s->prefix_wanted.len + strlen(s->our_protoversion) + 1;
-
-    /* Convert minus signs and spaces in the software version string
-     * into underscores. */
-    for (p = s->our_vstring + sv_pos; *p; p++) {
-        if (*p == '-' || *p == ' ')
-            *p = '_';
-    }
-
-#ifdef FUZZING
-    /*
-     * Replace the first character of the string with an "I" if we're
-     * compiling this code for fuzzing - i.e. the protocol prefix
-     * becomes "ISH-" instead of "SSH-".
-     *
-     * This is irrelevant to any real client software (the only thing
-     * reading the output of PuTTY built for fuzzing is the fuzzer,
-     * which can adapt to whatever it sees anyway). But it's a safety
-     * precaution making it difficult to accidentally run such a
-     * version of PuTTY (which would be hugely insecure) against a
-     * live peer implementation.
-     *
-     * (So the replacement prefix "ISH" notionally stands for
-     * 'Insecure Shell', of course.)
-     */
-    s->our_vstring[0] = 'I';
-#endif
-
-    /*
-     * Now send that version string, plus trailing \r\n or just \n
-     * (the latter in SSH-1 mode).
-     */
-    bufchain_add(s->bpp.out_raw, s->our_vstring, strlen(s->our_vstring));
-    if (ssh_version_includes_v2(s->our_protoversion))
-        bufchain_add(s->bpp.out_raw, "\015", 1);
-    bufchain_add(s->bpp.out_raw, "\012", 1);
-
-    bpp_logevent("We claim version: %s", s->our_vstring);
-}
-
-#define BPP_WAITFOR(minlen) do                          \
-    {                                                                   \
-        bool success;                                                   \
-        crMaybeWaitUntilV(                                              \
-            (success = (bufchain_size(s->bpp.in_raw) >= (minlen))) ||   \
-            s->bpp.input_eof);                                          \
-        if (!success)                                                   \
-            goto eof;                                                   \
-    } while (0)
-
-void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
-{
-    struct ssh_verstring_state *s =
-        container_of(bpp, struct ssh_verstring_state, bpp);
-
-    crBegin(s->crState);
-
-    /*
-     * If we're sending our version string up front before seeing the
-     * other side's, then do it now.
-     */
-    if (s->send_early)
-        ssh_verstring_send(s);
-
-    /*
-     * Search for a line beginning with the protocol name prefix in
-     * the input.
-     */
-    s->i = 0;
-    while (1) {
-        /*
-         * Every time round this loop, we're at the start of a new
-         * line, so look for the prefix.
-         */
-        BPP_WAITFOR(s->prefix_wanted.len);
-        bufchain_fetch(s->bpp.in_raw, s->prefix, s->prefix_wanted.len);
-        if (!memcmp(s->prefix, s->prefix_wanted.ptr, s->prefix_wanted.len)) {
-            bufchain_consume(s->bpp.in_raw, s->prefix_wanted.len);
-            ssh_check_frozen(s->bpp.ssh);
-            break;
-        }
-
-        /*
-         * If we didn't find it, consume data until we see a newline.
-         */
-        while (1) {
-            ptrlen data;
-            char *nl;
-
-            /* Wait to receive at least 1 byte, but then consume more
-             * than that if it's there. */
-            BPP_WAITFOR(1);
-            data = bufchain_prefix(s->bpp.in_raw);
-            if ((nl = memchr(data.ptr, '\012', data.len)) != NULL) {
-                bufchain_consume(s->bpp.in_raw, nl - (char *)data.ptr + 1);
-                ssh_check_frozen(s->bpp.ssh);
-                break;
-            } else {
-                bufchain_consume(s->bpp.in_raw, data.len);
-                ssh_check_frozen(s->bpp.ssh);
-            }
-        }
-    }
-
-    s->found_prefix = true;
-
-    /*
-     * Copy the greeting line so far into vstring.
-     */
-    put_data(s->vstring, s->prefix_wanted.ptr, s->prefix_wanted.len);
-
-    /*
-     * Now read the rest of the greeting line.
-     */
-    s->i = 0;
-    do {
-        ptrlen data;
-        char *nl;
-
-        BPP_WAITFOR(1);
-        data = bufchain_prefix(s->bpp.in_raw);
-        if ((nl = memchr(data.ptr, '\012', data.len)) != NULL) {
-            data.len = nl - (char *)data.ptr + 1;
-        }
-
-        put_datapl(s->vstring, data);
-        bufchain_consume(s->bpp.in_raw, data.len);
-        ssh_check_frozen(s->bpp.ssh);
-
-    } while (s->vstring->s[s->vstring->len-1] != '\012');
-
-    /*
-     * Trim \r and \n from the version string, and replace them with
-     * a NUL terminator.
-     */
-    while (s->vstring->len > 0 &&
-           (s->vstring->s[s->vstring->len-1] == '\r' ||
-            s->vstring->s[s->vstring->len-1] == '\n'))
-        strbuf_shrink_by(s->vstring, 1);
-
-    bpp_logevent("Remote version: %s", s->vstring->s);
-
-    /*
-     * Pick out the protocol version and software version. The former
-     * goes in a separately allocated string, so that s->vstring
-     * remains intact for later use in key exchange; the latter is the
-     * tail of s->vstring, so it doesn't need to be allocated.
-     */
-    {
-        const char *pv_start = s->vstring->s + s->prefix_wanted.len;
-        int pv_len = strcspn(pv_start, "-");
-        s->protoversion = dupprintf("%.*s", pv_len, pv_start);
-        s->softwareversion = pv_start + pv_len;
-        if (*s->softwareversion) {
-            assert(*s->softwareversion == '-');
-            s->softwareversion++;
-        }
-    }
-
-    ssh_detect_bugs(s);
-
-    /*
-     * Figure out what actual SSH protocol version we're speaking.
-     */
-    if (ssh_version_includes_v2(s->our_protoversion) &&
-        ssh_version_includes_v2(s->protoversion)) {
-        /*
-         * We're doing SSH-2.
-         */
-        s->major_protoversion = 2;
-    } else if (ssh_version_includes_v1(s->our_protoversion) &&
-               ssh_version_includes_v1(s->protoversion)) {
-        /*
-         * We're doing SSH-1.
-         */
-        s->major_protoversion = 1;
-
-        /*
-         * There are multiple minor versions of SSH-1, and the
-         * protocol does not specify that the minimum of client
-         * and server versions is used. So we must adjust our
-         * outgoing protocol version to be no higher than that of
-         * the other side.
-         */
-        if (!s->send_early &&
-            ssh_versioncmp(s->our_protoversion, s->protoversion) > 0) {
-            sfree(s->our_protoversion);
-            s->our_protoversion = dupstr(s->protoversion);
-        }
-    } else {
-        /*
-         * Unable to agree on a major protocol version at all.
-         */
-        if (!ssh_version_includes_v2(s->our_protoversion)) {
-            ssh_sw_abort(s->bpp.ssh,
-                         "SSH protocol version 1 required by our "
-                         "configuration but not provided by remote");
-        } else {
-            ssh_sw_abort(s->bpp.ssh,
-                         "SSH protocol version 2 required by our "
-                         "configuration but remote only provides "
-                         "(old, insecure) SSH-1");
-        }
-        crStopV;
-    }
-
-    bpp_logevent("Using SSH protocol version %d", s->major_protoversion);
-
-    if (!s->send_early) {
-        /*
-         * If we didn't send our version string early, construct and
-         * send it now, because now we know what it is.
-         */
-        ssh_verstring_send(s);
-    }
-
-    /*
-     * And we're done. Notify our receiver that we now know our
-     * protocol version. This will cause it to disconnect us from the
-     * input stream and ultimately free us, because our job is now
-     * done.
-     */
-    s->receiver->got_ssh_version(s->receiver, s->major_protoversion);
-    return;
-
-  eof:
-    ssh_remote_error(s->bpp.ssh,
-                     "Remote side unexpectedly closed network connection");
-    return;  /* avoid touching s now it's been freed */
-
-    crFinishV;
-}
-
-static PktOut *ssh_verstring_new_pktout(int type)
-{
-    unreachable("Should never try to send packets during SSH version "
-                "string exchange");
-}
-
-static void ssh_verstring_handle_output(BinaryPacketProtocol *bpp)
-{
-    if (pq_peek(&bpp->out_pq)) {
-        unreachable("Should never try to send packets during SSH version "
-                    "string exchange");
-    }
-}
-
-/*
- * Examine the remote side's version string, and compare it against a
- * list of known buggy implementations.
- */
-static void ssh_detect_bugs(struct ssh_verstring_state *s)
-{
-    BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */
-    const char *imp = s->softwareversion;
-
-    s->remote_bugs = 0;
-
-    /*
-     * General notes on server version strings:
-     *  - Not all servers reporting "Cisco-1.25" have all the bugs listed
-     *    here -- in particular, we've heard of one that's perfectly happy
-     *    with SSH1_MSG_IGNOREs -- but this string never seems to change,
-     *    so we can't distinguish them.
-     */
-    if (conf_get_int(s->conf, CONF_sshbug_ignore1) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_ignore1) == AUTO &&
-         (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
-          !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
-          !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
-          !strcmp(imp, "OSU_1.4alpha3") || !strcmp(imp, "OSU_1.5alpha4")))) {
-        /*
-         * These versions don't support SSH1_MSG_IGNORE, so we have
-         * to use a different defence against password length
-         * sniffing.
-         */
-        s->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
-        bpp_logevent("We believe remote version has SSH-1 ignore bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_plainpw1) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_plainpw1) == AUTO &&
-         (!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
-        /*
-         * These versions need a plain password sent; they can't
-         * handle having a null and a random length of data after
-         * the password.
-         */
-        s->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
-        bpp_logevent("We believe remote version needs a "
-                     "plain SSH-1 password");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_rsa1) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_rsa1) == AUTO &&
-         (!strcmp(imp, "Cisco-1.25")))) {
-        /*
-         * These versions apparently have no clue whatever about
-         * RSA authentication and will panic and die if they see
-         * an AUTH_RSA message.
-         */
-        s->remote_bugs |= BUG_CHOKES_ON_RSA;
-        bpp_logevent("We believe remote version can't handle SSH-1 "
-                     "RSA authentication");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_hmac2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_hmac2) == AUTO &&
-         !wc_match("* VShell", imp) &&
-         (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
-          wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
-          wc_match("2.1 *", imp)))) {
-        /*
-         * These versions have the HMAC bug.
-         */
-        s->remote_bugs |= BUG_SSH2_HMAC;
-        bpp_logevent("We believe remote version has SSH-2 HMAC bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_derivekey2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_derivekey2) == AUTO &&
-         !wc_match("* VShell", imp) &&
-         (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
-        /*
-         * These versions have the key-derivation bug (failing to
-         * include the literal shared secret in the hashes that
-         * generate the keys).
-         */
-        s->remote_bugs |= BUG_SSH2_DERIVEKEY;
-        bpp_logevent("We believe remote version has SSH-2 "
-                     "key-derivation bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_rsapad2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_rsapad2) == AUTO &&
-         (wc_match("OpenSSH_2.[5-9]*", imp) ||
-          wc_match("OpenSSH_3.[0-2]*", imp) ||
-          wc_match("mod_sftp/0.[0-8]*", imp) ||
-          wc_match("mod_sftp/0.9.[0-8]", imp)))) {
-        /*
-         * These versions have the SSH-2 RSA padding bug.
-         */
-        s->remote_bugs |= BUG_SSH2_RSA_PADDING;
-        bpp_logevent("We believe remote version has SSH-2 RSA padding bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_pksessid2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_pksessid2) == AUTO &&
-         wc_match("OpenSSH_2.[0-2]*", imp))) {
-        /*
-         * These versions have the SSH-2 session-ID bug in
-         * public-key authentication.
-         */
-        s->remote_bugs |= BUG_SSH2_PK_SESSIONID;
-        bpp_logevent("We believe remote version has SSH-2 "
-                     "public-key-session-ID bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_rekey2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_rekey2) == AUTO &&
-         (wc_match("DigiSSH_2.0", imp) ||
-          wc_match("OpenSSH_2.[0-4]*", imp) ||
-          wc_match("OpenSSH_2.5.[0-3]*", imp) ||
-          wc_match("Sun_SSH_1.0", imp) ||
-          wc_match("Sun_SSH_1.0.1", imp) ||
-          /* All versions <= 1.2.6 (they changed their format in 1.2.7) */
-          wc_match("WeOnlyDo-*", imp)))) {
-        /*
-         * These versions have the SSH-2 rekey bug.
-         */
-        s->remote_bugs |= BUG_SSH2_REKEY;
-        bpp_logevent("We believe remote version has SSH-2 rekey bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == AUTO &&
-         (wc_match("1.36_sshlib GlobalSCAPE", imp) ||
-          wc_match("1.36 sshlib: GlobalScape", imp)))) {
-        /*
-         * This version ignores our makpkt and needs to be throttled.
-         */
-        s->remote_bugs |= BUG_SSH2_MAXPKT;
-        bpp_logevent("We believe remote version ignores SSH-2 "
-                     "maximum packet size");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_ignore2) == FORCE_ON) {
-        /*
-         * Servers that don't support SSH2_MSG_IGNORE. Currently,
-         * none detected automatically.
-         */
-        s->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
-        bpp_logevent("We believe remote version has SSH-2 ignore bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_oldgex2) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_oldgex2) == AUTO &&
-         (wc_match("OpenSSH_2.[235]*", imp)))) {
-        /*
-         * These versions only support the original (pre-RFC4419)
-         * SSH-2 GEX request, and disconnect with a protocol error if
-         * we use the newer version.
-         */
-        s->remote_bugs |= BUG_SSH2_OLDGEX;
-        bpp_logevent("We believe remote version has outdated SSH-2 GEX");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_winadj) == FORCE_ON) {
-        /*
-         * Servers that don't support our winadj request for one
-         * reason or another. Currently, none detected automatically.
-         */
-        s->remote_bugs |= BUG_CHOKES_ON_WINADJ;
-        bpp_logevent("We believe remote version has winadj bug");
-    }
-
-    if (conf_get_int(s->conf, CONF_sshbug_chanreq) == FORCE_ON ||
-        (conf_get_int(s->conf, CONF_sshbug_chanreq) == AUTO &&
-         (wc_match("OpenSSH_[2-5].*", imp) ||
-          wc_match("OpenSSH_6.[0-6]*", imp) ||
-          wc_match("dropbear_0.[2-4][0-9]*", imp) ||
-          wc_match("dropbear_0.5[01]*", imp)))) {
-        /*
-         * These versions have the SSH-2 channel request bug.
-         * OpenSSH 6.7 and above do not:
-         * https://bugzilla.mindrot.org/show_bug.cgi?id=1818
-         * dropbear_0.52 and above do not:
-         * https://secure.ucc.asn.au/hg/dropbear/rev/cd02449b709c
-         */
-        s->remote_bugs |= BUG_SENDS_LATE_REQUEST_REPLY;
-        bpp_logevent("We believe remote version has SSH-2 "
-                     "channel request bug");
-    }
-}
-
-const char *ssh_verstring_get_remote(BinaryPacketProtocol *bpp)
-{
-    struct ssh_verstring_state *s =
-        container_of(bpp, struct ssh_verstring_state, bpp);
-    return s->vstring->s;
-}
-
-const char *ssh_verstring_get_local(BinaryPacketProtocol *bpp)
-{
-    struct ssh_verstring_state *s =
-        container_of(bpp, struct ssh_verstring_state, bpp);
-    return s->our_vstring;
-}
-
-int ssh_verstring_get_bugs(BinaryPacketProtocol *bpp)
-{
-    struct ssh_verstring_state *s =
-        container_of(bpp, struct ssh_verstring_state, bpp);
-    return s->remote_bugs;
-}
-
-static void ssh_verstring_queue_disconnect(BinaryPacketProtocol *bpp,
-                                           const char *msg, int category)
-{
-    /* No way to send disconnect messages at this stage of the protocol! */
-}

+ 0 - 1254
source/putty/sshzlib.c

@@ -1,1254 +0,0 @@
-/*
- * Zlib (RFC1950 / RFC1951) compression for PuTTY.
- *
- * There will no doubt be criticism of my decision to reimplement
- * Zlib compression from scratch instead of using the existing zlib
- * code. People will cry `reinventing the wheel'; they'll claim
- * that the `fundamental basis of OSS' is code reuse; they'll want
- * to see a really good reason for me having chosen not to use the
- * existing code.
- *
- * Well, here are my reasons. Firstly, I don't want to link the
- * whole of zlib into the PuTTY binary; PuTTY is justifiably proud
- * of its small size and I think zlib contains a lot of unnecessary
- * baggage for the kind of compression that SSH requires.
- *
- * Secondly, I also don't like the alternative of using zlib.dll.
- * Another thing PuTTY is justifiably proud of is its ease of
- * installation, and the last thing I want to do is to start
- * mandating DLLs. Not only that, but there are two _kinds_ of
- * zlib.dll kicking around, one with C calling conventions on the
- * exported functions and another with WINAPI conventions, and
- * there would be a significant danger of getting the wrong one.
- *
- * Thirdly, there seems to be a difference of opinion on the IETF
- * secsh mailing list about the correct way to round off a
- * compressed packet and start the next. In particular, there's
- * some talk of switching to a mechanism zlib isn't currently
- * capable of supporting (see below for an explanation). Given that
- * sort of uncertainty, I thought it might be better to have code
- * that will support even the zlib-incompatible worst case.
- *
- * Fourthly, it's a _second implementation_. Second implementations
- * are fundamentally a Good Thing in standardisation efforts. The
- * difference of opinion mentioned above has arisen _precisely_
- * because there has been only one zlib implementation and
- * everybody has used it. I don't intend that this should happen
- * again.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "defs.h"
-#include "ssh.h"
-
-/* ----------------------------------------------------------------------
- * Basic LZ77 code. This bit is designed modularly, so it could be
- * ripped out and used in a different LZ77 compressor. Go to it,
- * and good luck :-)
- */
-
-struct LZ77InternalContext;
-struct LZ77Context {
-    struct LZ77InternalContext *ictx;
-    void *userdata;
-    void (*literal) (struct LZ77Context * ctx, unsigned char c);
-    void (*match) (struct LZ77Context * ctx, int distance, int len);
-};
-
-/*
- * Initialise the private fields of an LZ77Context. It's up to the
- * user to initialise the public fields.
- */
-static int lz77_init(struct LZ77Context *ctx);
-
-/*
- * Supply data to be compressed. Will update the private fields of
- * the LZ77Context, and will call literal() and match() to output.
- * If `compress' is false, it will never emit a match, but will
- * instead call literal() for everything.
- */
-static void lz77_compress(struct LZ77Context *ctx,
-                          const unsigned char *data, int len);
-
-/*
- * Modifiable parameters.
- */
-#define WINSIZE 32768                  /* window size. Must be power of 2! */
-#define HASHMAX 2039                   /* one more than max hash value */
-#define MAXMATCH 32                    /* how many matches we track */
-#define HASHCHARS 3                    /* how many chars make a hash */
-
-/*
- * This compressor takes a less slapdash approach than the
- * gzip/zlib one. Rather than allowing our hash chains to fall into
- * disuse near the far end, we keep them doubly linked so we can
- * _find_ the far end, and then every time we add a new byte to the
- * window (thus rolling round by one and removing the previous
- * byte), we can carefully remove the hash chain entry.
- */
-
-#define INVALID -1                     /* invalid hash _and_ invalid offset */
-struct WindowEntry {
-    short next, prev;                  /* array indices within the window */
-    short hashval;
-};
-
-struct HashEntry {
-    short first;                       /* window index of first in chain */
-};
-
-struct Match {
-    int distance, len;
-};
-
-struct LZ77InternalContext {
-    struct WindowEntry win[WINSIZE];
-    unsigned char data[WINSIZE];
-    int winpos;
-    struct HashEntry hashtab[HASHMAX];
-    unsigned char pending[HASHCHARS];
-    int npending;
-};
-
-static int lz77_hash(const unsigned char *data)
-{
-    return (257 * data[0] + 263 * data[1] + 269 * data[2]) % HASHMAX;
-}
-
-static int lz77_init(struct LZ77Context *ctx)
-{
-    struct LZ77InternalContext *st;
-    int i;
-
-    st = snew(struct LZ77InternalContext);
-    if (!st)
-        return 0;
-
-    ctx->ictx = st;
-
-    for (i = 0; i < WINSIZE; i++)
-        st->win[i].next = st->win[i].prev = st->win[i].hashval = INVALID;
-    for (i = 0; i < HASHMAX; i++)
-        st->hashtab[i].first = INVALID;
-    st->winpos = 0;
-
-    st->npending = 0;
-
-    return 1;
-}
-
-static void lz77_advance(struct LZ77InternalContext *st,
-                         unsigned char c, int hash)
-{
-    int off;
-
-    /*
-     * Remove the hash entry at winpos from the tail of its chain,
-     * or empty the chain if it's the only thing on the chain.
-     */
-    if (st->win[st->winpos].prev != INVALID) {
-        st->win[st->win[st->winpos].prev].next = INVALID;
-    } else if (st->win[st->winpos].hashval != INVALID) {
-        st->hashtab[st->win[st->winpos].hashval].first = INVALID;
-    }
-
-    /*
-     * Create a new entry at winpos and add it to the head of its
-     * hash chain.
-     */
-    st->win[st->winpos].hashval = hash;
-    st->win[st->winpos].prev = INVALID;
-    off = st->win[st->winpos].next = st->hashtab[hash].first;
-    st->hashtab[hash].first = st->winpos;
-    if (off != INVALID)
-        st->win[off].prev = st->winpos;
-    st->data[st->winpos] = c;
-
-    /*
-     * Advance the window pointer.
-     */
-    st->winpos = (st->winpos + 1) & (WINSIZE - 1);
-}
-
-#define CHARAT(k) ( (k)<0 ? st->data[(st->winpos+k)&(WINSIZE-1)] : data[k] )
-
-static void lz77_compress(struct LZ77Context *ctx,
-                          const unsigned char *data, int len)
-{
-    struct LZ77InternalContext *st = ctx->ictx;
-    int i, distance, off, nmatch, matchlen, advance;
-    struct Match defermatch, matches[MAXMATCH];
-    int deferchr;
-
-    assert(st->npending <= HASHCHARS);
-
-    /*
-     * Add any pending characters from last time to the window. (We
-     * might not be able to.)
-     *
-     * This leaves st->pending empty in the usual case (when len >=
-     * HASHCHARS); otherwise it leaves st->pending empty enough that
-     * adding all the remaining 'len' characters will not push it past
-     * HASHCHARS in size.
-     */
-    for (i = 0; i < st->npending; i++) {
-        unsigned char foo[HASHCHARS];
-        int j;
-        if (len + st->npending - i < HASHCHARS) {
-            /* Update the pending array. */
-            for (j = i; j < st->npending; j++)
-                st->pending[j - i] = st->pending[j];
-            break;
-        }
-        for (j = 0; j < HASHCHARS; j++)
-            foo[j] = (i + j < st->npending ? st->pending[i + j] :
-                      data[i + j - st->npending]);
-        lz77_advance(st, foo[0], lz77_hash(foo));
-    }
-    st->npending -= i;
-
-    defermatch.distance = 0; /* appease compiler */
-    defermatch.len = 0;
-    deferchr = '\0';
-    while (len > 0) {
-
-        if (len >= HASHCHARS) {
-            /*
-             * Hash the next few characters.
-             */
-            int hash = lz77_hash(data);
-
-            /*
-             * Look the hash up in the corresponding hash chain and see
-             * what we can find.
-             */
-            nmatch = 0;
-            for (off = st->hashtab[hash].first;
-                 off != INVALID; off = st->win[off].next) {
-                /* distance = 1       if off == st->winpos-1 */
-                /* distance = WINSIZE if off == st->winpos   */
-                distance =
-                    WINSIZE - (off + WINSIZE - st->winpos) % WINSIZE;
-                for (i = 0; i < HASHCHARS; i++)
-                    if (CHARAT(i) != CHARAT(i - distance))
-                        break;
-                if (i == HASHCHARS) {
-                    matches[nmatch].distance = distance;
-                    matches[nmatch].len = 3;
-                    if (++nmatch >= MAXMATCH)
-                        break;
-                }
-            }
-        } else {
-            nmatch = 0;
-        }
-
-        if (nmatch > 0) {
-            /*
-             * We've now filled up matches[] with nmatch potential
-             * matches. Follow them down to find the longest. (We
-             * assume here that it's always worth favouring a
-             * longer match over a shorter one.)
-             */
-            matchlen = HASHCHARS;
-            while (matchlen < len) {
-                int j;
-                for (i = j = 0; i < nmatch; i++) {
-                    if (CHARAT(matchlen) ==
-                        CHARAT(matchlen - matches[i].distance)) {
-                        matches[j++] = matches[i];
-                    }
-                }
-                if (j == 0)
-                    break;
-                matchlen++;
-                nmatch = j;
-            }
-
-            /*
-             * We've now got all the longest matches. We favour the
-             * shorter distances, which means we go with matches[0].
-             * So see if we want to defer it or throw it away.
-             */
-            matches[0].len = matchlen;
-            if (defermatch.len > 0) {
-                if (matches[0].len > defermatch.len + 1) {
-                    /* We have a better match. Emit the deferred char,
-                     * and defer this match. */
-                    ctx->literal(ctx, (unsigned char) deferchr);
-                    defermatch = matches[0];
-                    deferchr = data[0];
-                    advance = 1;
-                } else {
-                    /* We don't have a better match. Do the deferred one. */
-                    ctx->match(ctx, defermatch.distance, defermatch.len);
-                    advance = defermatch.len - 1;
-                    defermatch.len = 0;
-                }
-            } else {
-                /* There was no deferred match. Defer this one. */
-                defermatch = matches[0];
-                deferchr = data[0];
-                advance = 1;
-            }
-        } else {
-            /*
-             * We found no matches. Emit the deferred match, if
-             * any; otherwise emit a literal.
-             */
-            if (defermatch.len > 0) {
-                ctx->match(ctx, defermatch.distance, defermatch.len);
-                advance = defermatch.len - 1;
-                defermatch.len = 0;
-            } else {
-                ctx->literal(ctx, data[0]);
-                advance = 1;
-            }
-        }
-
-        /*
-         * Now advance the position by `advance' characters,
-         * keeping the window and hash chains consistent.
-         */
-        while (advance > 0) {
-            if (len >= HASHCHARS) {
-                lz77_advance(st, *data, lz77_hash(data));
-            } else {
-                assert(st->npending < HASHCHARS);
-                st->pending[st->npending++] = *data;
-            }
-            data++;
-            len--;
-            advance--;
-        }
-    }
-}
-
-/* ----------------------------------------------------------------------
- * Zlib compression. We always use the static Huffman tree option.
- * Mostly this is because it's hard to scan a block in advance to
- * work out better trees; dynamic trees are great when you're
- * compressing a large file under no significant time constraint,
- * but when you're compressing little bits in real time, things get
- * hairier.
- *
- * I suppose it's possible that I could compute Huffman trees based
- * on the frequencies in the _previous_ block, as a sort of
- * heuristic, but I'm not confident that the gain would balance out
- * having to transmit the trees.
- */
-
-struct Outbuf {
-    strbuf *outbuf;
-    unsigned long outbits;
-    int noutbits;
-    bool firstblock;
-};
-
-static void outbits(struct Outbuf *out, unsigned long bits, int nbits)
-{
-    assert(out->noutbits + nbits <= 32);
-    out->outbits |= bits << out->noutbits;
-    out->noutbits += nbits;
-    while (out->noutbits >= 8) {
-        put_byte(out->outbuf, out->outbits & 0xFF);
-        out->outbits >>= 8;
-        out->noutbits -= 8;
-    }
-}
-
-static const unsigned char mirrorbytes[256] = {
-    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
-    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
-    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
-    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
-    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
-    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
-    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
-    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
-    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
-    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
-    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
-    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
-    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
-    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
-    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
-    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
-    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
-    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
-    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
-    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
-    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
-    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
-    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
-    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
-    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
-    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
-    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
-    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
-    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
-    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
-    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
-    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
-};
-
-typedef struct {
-    short code, extrabits;
-    int min, max;
-} coderecord;
-
-static const coderecord lencodes[] = {
-    {257, 0, 3, 3},
-    {258, 0, 4, 4},
-    {259, 0, 5, 5},
-    {260, 0, 6, 6},
-    {261, 0, 7, 7},
-    {262, 0, 8, 8},
-    {263, 0, 9, 9},
-    {264, 0, 10, 10},
-    {265, 1, 11, 12},
-    {266, 1, 13, 14},
-    {267, 1, 15, 16},
-    {268, 1, 17, 18},
-    {269, 2, 19, 22},
-    {270, 2, 23, 26},
-    {271, 2, 27, 30},
-    {272, 2, 31, 34},
-    {273, 3, 35, 42},
-    {274, 3, 43, 50},
-    {275, 3, 51, 58},
-    {276, 3, 59, 66},
-    {277, 4, 67, 82},
-    {278, 4, 83, 98},
-    {279, 4, 99, 114},
-    {280, 4, 115, 130},
-    {281, 5, 131, 162},
-    {282, 5, 163, 194},
-    {283, 5, 195, 226},
-    {284, 5, 227, 257},
-    {285, 0, 258, 258},
-};
-
-static const coderecord distcodes[] = {
-    {0, 0, 1, 1},
-    {1, 0, 2, 2},
-    {2, 0, 3, 3},
-    {3, 0, 4, 4},
-    {4, 1, 5, 6},
-    {5, 1, 7, 8},
-    {6, 2, 9, 12},
-    {7, 2, 13, 16},
-    {8, 3, 17, 24},
-    {9, 3, 25, 32},
-    {10, 4, 33, 48},
-    {11, 4, 49, 64},
-    {12, 5, 65, 96},
-    {13, 5, 97, 128},
-    {14, 6, 129, 192},
-    {15, 6, 193, 256},
-    {16, 7, 257, 384},
-    {17, 7, 385, 512},
-    {18, 8, 513, 768},
-    {19, 8, 769, 1024},
-    {20, 9, 1025, 1536},
-    {21, 9, 1537, 2048},
-    {22, 10, 2049, 3072},
-    {23, 10, 3073, 4096},
-    {24, 11, 4097, 6144},
-    {25, 11, 6145, 8192},
-    {26, 12, 8193, 12288},
-    {27, 12, 12289, 16384},
-    {28, 13, 16385, 24576},
-    {29, 13, 24577, 32768},
-};
-
-static void zlib_literal(struct LZ77Context *ectx, unsigned char c)
-{
-    struct Outbuf *out = (struct Outbuf *) ectx->userdata;
-
-    if (c <= 143) {
-        /* 0 through 143 are 8 bits long starting at 00110000. */
-        outbits(out, mirrorbytes[0x30 + c], 8);
-    } else {
-        /* 144 through 255 are 9 bits long starting at 110010000. */
-        outbits(out, 1 + 2 * mirrorbytes[0x90 - 144 + c], 9);
-    }
-}
-
-static void zlib_match(struct LZ77Context *ectx, int distance, int len)
-{
-    const coderecord *d, *l;
-    int i, j, k;
-    struct Outbuf *out = (struct Outbuf *) ectx->userdata;
-
-    while (len > 0) {
-        int thislen;
-
-        /*
-         * We can transmit matches of lengths 3 through 258
-         * inclusive. So if len exceeds 258, we must transmit in
-         * several steps, with 258 or less in each step.
-         *
-         * Specifically: if len >= 261, we can transmit 258 and be
-         * sure of having at least 3 left for the next step. And if
-         * len <= 258, we can just transmit len. But if len == 259
-         * or 260, we must transmit len-3.
-         */
-        thislen = (len > 260 ? 258 : len <= 258 ? len : len - 3);
-        len -= thislen;
-
-        /*
-         * Binary-search to find which length code we're
-         * transmitting.
-         */
-        i = -1;
-        j = lenof(lencodes);
-        while (1) {
-            assert(j - i >= 2);
-            k = (j + i) / 2;
-            if (thislen < lencodes[k].min)
-                j = k;
-            else if (thislen > lencodes[k].max)
-                i = k;
-            else {
-                l = &lencodes[k];
-                break;                 /* found it! */
-            }
-        }
-
-        /*
-         * Transmit the length code. 256-279 are seven bits
-         * starting at 0000000; 280-287 are eight bits starting at
-         * 11000000.
-         */
-        if (l->code <= 279) {
-            outbits(out, mirrorbytes[(l->code - 256) * 2], 7);
-        } else {
-            outbits(out, mirrorbytes[0xc0 - 280 + l->code], 8);
-        }
-
-        /*
-         * Transmit the extra bits.
-         */
-        if (l->extrabits)
-            outbits(out, thislen - l->min, l->extrabits);
-
-        /*
-         * Binary-search to find which distance code we're
-         * transmitting.
-         */
-        i = -1;
-        j = lenof(distcodes);
-        while (1) {
-            assert(j - i >= 2);
-            k = (j + i) / 2;
-            if (distance < distcodes[k].min)
-                j = k;
-            else if (distance > distcodes[k].max)
-                i = k;
-            else {
-                d = &distcodes[k];
-                break;                 /* found it! */
-            }
-        }
-
-        /*
-         * Transmit the distance code. Five bits starting at 00000.
-         */
-        outbits(out, mirrorbytes[d->code * 8], 5);
-
-        /*
-         * Transmit the extra bits.
-         */
-        if (d->extrabits)
-            outbits(out, distance - d->min, d->extrabits);
-    }
-}
-
-struct ssh_zlib_compressor {
-    struct LZ77Context ectx;
-    ssh_compressor sc;
-};
-
-ssh_compressor *zlib_compress_init(void)
-{
-    struct Outbuf *out;
-    struct ssh_zlib_compressor *comp = snew(struct ssh_zlib_compressor);
-
-    lz77_init(&comp->ectx);
-    comp->sc.vt = &ssh_zlib;
-    comp->ectx.literal = zlib_literal;
-    comp->ectx.match = zlib_match;
-
-    out = snew(struct Outbuf);
-    out->outbuf = NULL;
-    out->outbits = out->noutbits = 0;
-    out->firstblock = true;
-    comp->ectx.userdata = out;
-
-    return &comp->sc;
-}
-
-void zlib_compress_cleanup(ssh_compressor *sc)
-{
-    struct ssh_zlib_compressor *comp =
-        container_of(sc, struct ssh_zlib_compressor, sc);
-    struct Outbuf *out = (struct Outbuf *)comp->ectx.userdata;
-    if (out->outbuf)
-        strbuf_free(out->outbuf);
-    sfree(out);
-    sfree(comp->ectx.ictx);
-    sfree(comp);
-}
-
-void zlib_compress_block(ssh_compressor *sc,
-                         const unsigned char *block, int len,
-                         unsigned char **outblock, int *outlen,
-                         int minlen)
-{
-    struct ssh_zlib_compressor *comp =
-        container_of(sc, struct ssh_zlib_compressor, sc);
-    struct Outbuf *out = (struct Outbuf *) comp->ectx.userdata;
-    bool in_block;
-
-    assert(!out->outbuf);
-    out->outbuf = strbuf_new_nm();
-
-    /*
-     * If this is the first block, output the Zlib (RFC1950) header
-     * bytes 78 9C. (Deflate compression, 32K window size, default
-     * algorithm.)
-     */
-    if (out->firstblock) {
-        outbits(out, 0x9C78, 16);
-        out->firstblock = false;
-
-        in_block = false;
-    } else
-        in_block = true;
-
-    if (!in_block) {
-        /*
-         * Start a Deflate (RFC1951) fixed-trees block. We
-         * transmit a zero bit (BFINAL=0), followed by a zero
-         * bit and a one bit (BTYPE=01). Of course these are in
-         * the wrong order (01 0).
-         */
-        outbits(out, 2, 3);
-    }
-
-    /*
-     * Do the compression.
-     */
-    lz77_compress(&comp->ectx, block, len);
-
-    /*
-     * End the block (by transmitting code 256, which is
-     * 0000000 in fixed-tree mode), and transmit some empty
-     * blocks to ensure we have emitted the byte containing the
-     * last piece of genuine data. There are three ways we can
-     * do this:
-     *
-     *  - Minimal flush. Output end-of-block and then open a
-     *    new static block. This takes 9 bits, which is
-     *    guaranteed to flush out the last genuine code in the
-     *    closed block; but allegedly zlib can't handle it.
-     *
-     *  - Zlib partial flush. Output EOB, open and close an
-     *    empty static block, and _then_ open the new block.
-     *    This is the best zlib can handle.
-     *
-     *  - Zlib sync flush. Output EOB, then an empty
-     *    _uncompressed_ block (000, then sync to byte
-     *    boundary, then send bytes 00 00 FF FF). Then open the
-     *    new block.
-     *
-     * For the moment, we will use Zlib partial flush.
-     */
-    outbits(out, 0, 7);        /* close block */
-    outbits(out, 2, 3 + 7);    /* empty static block */
-    outbits(out, 2, 3);        /* open new block */
-
-    /*
-     * If we've been asked to pad out the compressed data until it's
-     * at least a given length, do so by emitting further empty static
-     * blocks.
-     */
-    while (out->outbuf->len < minlen) {
-        outbits(out, 0, 7);            /* close block */
-        outbits(out, 2, 3);            /* open new static block */
-    }
-
-    *outlen = out->outbuf->len;
-    *outblock = (unsigned char *)strbuf_to_str(out->outbuf);
-    out->outbuf = NULL;
-}
-
-/* ----------------------------------------------------------------------
- * Zlib decompression. Of course, even though our compressor always
- * uses static trees, our _decompressor_ has to be capable of
- * handling dynamic trees if it sees them.
- */
-
-/*
- * The way we work the Huffman decode is to have a table lookup on
- * the first N bits of the input stream (in the order they arrive,
- * of course, i.e. the first bit of the Huffman code is in bit 0).
- * Each table entry lists the number of bits to consume, plus
- * either an output code or a pointer to a secondary table.
- */
-struct zlib_table;
-struct zlib_tableentry;
-
-struct zlib_tableentry {
-    unsigned char nbits;
-    short code;
-    struct zlib_table *nexttable;
-};
-
-struct zlib_table {
-    int mask;                          /* mask applied to input bit stream */
-    struct zlib_tableentry *table;
-};
-
-#define MAXCODELEN 16
-#define MAXSYMS 288
-
-/*
- * Build a single-level decode table for elements
- * [minlength,maxlength) of the provided code/length tables, and
- * recurse to build subtables.
- */
-static struct zlib_table *zlib_mkonetab(int *codes, unsigned char *lengths,
-                                        int nsyms,
-                                        int pfx, int pfxbits, int bits)
-{
-    struct zlib_table *tab = snew(struct zlib_table);
-    int pfxmask = (1 << pfxbits) - 1;
-    int nbits, i, j, code;
-
-    tab->table = snewn((size_t)1 << bits, struct zlib_tableentry);
-    tab->mask = (1 << bits) - 1;
-
-    for (code = 0; code <= tab->mask; code++) {
-        tab->table[code].code = -1;
-        tab->table[code].nbits = 0;
-        tab->table[code].nexttable = NULL;
-    }
-
-    for (i = 0; i < nsyms; i++) {
-        if (lengths[i] <= pfxbits || (codes[i] & pfxmask) != pfx)
-            continue;
-        code = (codes[i] >> pfxbits) & tab->mask;
-        for (j = code; j <= tab->mask; j += 1 << (lengths[i] - pfxbits)) {
-            tab->table[j].code = i;
-            nbits = lengths[i] - pfxbits;
-            if (tab->table[j].nbits < nbits)
-                tab->table[j].nbits = nbits;
-        }
-    }
-    for (code = 0; code <= tab->mask; code++) {
-        if (tab->table[code].nbits <= bits)
-            continue;
-        /* Generate a subtable. */
-        tab->table[code].code = -1;
-        nbits = tab->table[code].nbits - bits;
-        if (nbits > 7)
-            nbits = 7;
-        tab->table[code].nbits = bits;
-        tab->table[code].nexttable = zlib_mkonetab(codes, lengths, nsyms,
-                                                   pfx | (code << pfxbits),
-                                                   pfxbits + bits, nbits);
-    }
-
-    return tab;
-}
-
-/*
- * Build a decode table, given a set of Huffman tree lengths.
- */
-static struct zlib_table *zlib_mktable(unsigned char *lengths,
-                                       int nlengths)
-{
-    int count[MAXCODELEN], startcode[MAXCODELEN], codes[MAXSYMS];
-    int code, maxlen;
-    int i, j;
-
-    /* Count the codes of each length. */
-    maxlen = 0;
-    for (i = 1; i < MAXCODELEN; i++)
-        count[i] = 0;
-    for (i = 0; i < nlengths; i++) {
-        count[lengths[i]]++;
-        if (maxlen < lengths[i])
-            maxlen = lengths[i];
-    }
-    /* Determine the starting code for each length block. */
-    code = 0;
-    for (i = 1; i < MAXCODELEN; i++) {
-        startcode[i] = code;
-        code += count[i];
-        code <<= 1;
-    }
-    /* Determine the code for each symbol. Mirrored, of course. */
-    for (i = 0; i < nlengths; i++) {
-        code = startcode[lengths[i]]++;
-        codes[i] = 0;
-        for (j = 0; j < lengths[i]; j++) {
-            codes[i] = (codes[i] << 1) | (code & 1);
-            code >>= 1;
-        }
-    }
-
-    /*
-     * Now we have the complete list of Huffman codes. Build a
-     * table.
-     */
-    return zlib_mkonetab(codes, lengths, nlengths, 0, 0,
-                         maxlen < 9 ? maxlen : 9);
-}
-
-static int zlib_freetable(struct zlib_table **ztab)
-{
-    struct zlib_table *tab;
-    int code;
-
-    if (ztab == NULL)
-        return -1;
-
-    if (*ztab == NULL)
-        return 0;
-
-    tab = *ztab;
-
-    for (code = 0; code <= tab->mask; code++)
-        if (tab->table[code].nexttable != NULL)
-            zlib_freetable(&tab->table[code].nexttable);
-
-    sfree(tab->table);
-    tab->table = NULL;
-
-    sfree(tab);
-    *ztab = NULL;
-
-    return (0);
-}
-
-struct zlib_decompress_ctx {
-    struct zlib_table *staticlentable, *staticdisttable;
-    struct zlib_table *currlentable, *currdisttable, *lenlentable;
-    enum {
-        START, OUTSIDEBLK,
-        TREES_HDR, TREES_LENLEN, TREES_LEN, TREES_LENREP,
-        INBLK, GOTLENSYM, GOTLEN, GOTDISTSYM,
-        UNCOMP_LEN, UNCOMP_NLEN, UNCOMP_DATA
-    } state;
-    int sym, hlit, hdist, hclen, lenptr, lenextrabits, lenaddon, len,
-        lenrep;
-    int uncomplen;
-    unsigned char lenlen[19];
-
-    /*
-     * Array that accumulates the code lengths sent in the header of a
-     * dynamic-Huffman-tree block.
-     *
-     * There are 286 actual symbols in the literal/length alphabet
-     * (256 literals plus 20 length categories), and 30 symbols in the
-     * distance alphabet. However, the block header transmits the
-     * number of code lengths for the former alphabet as a 5-bit value
-     * HLIT to be added to 257, and the latter as a 5-bit value HDIST
-     * to be added to 1. This means that the number of _code lengths_
-     * can go as high as 288 for the symbol alphabet and 32 for the
-     * distance alphabet - each of those values being 2 more than the
-     * maximum number of actual symbols.
-     *
-     * It's tempting to rule that sending out-of-range HLIT or HDIST
-     * is therefore just illegal, and to fault it when we initially
-     * receive that header. But instead I've chosen to permit the
-     * Huffman-code definition to include code length entries for
-     * those unused symbols; if a header of that form is transmitted,
-     * then the effect will be that in the main body of the block,
-     * some bit sequence(s) will generate an illegal symbol number,
-     * and _that_ will be faulted as a decoding error.
-     *
-     * Rationale: this can already happen! The standard Huffman code
-     * used in a _static_ block for the literal/length alphabet is
-     * defined in such a way that it includes codes for symbols 287
-     * and 288, which are then never actually sent in the body of the
-     * block. And I think that if the standard static tree definition
-     * is willing to include Huffman codes that don't correspond to a
-     * symbol, then it's an excessive restriction on dynamic tables
-     * not to permit them to do the same. In particular, it would be
-     * strange for a dynamic block not to be able to exactly mimic
-     * either or both of the Huffman codes used by a static block for
-     * the corresponding alphabet.
-     *
-     * So we place no constraint on HLIT or HDIST during code
-     * construction, and we make this array large enough to include
-     * the maximum number of code lengths that can possibly arise as a
-     * result. It's only trying to _use_ the junk Huffman codes after
-     * table construction is completed that will provoke a decode
-     * error.
-     */
-    unsigned char lengths[288 + 32];
-
-    unsigned long bits;
-    int nbits;
-    unsigned char window[WINSIZE];
-    int winpos;
-    strbuf *outblk;
-
-    ssh_decompressor dc;
-};
-
-ssh_decompressor *zlib_decompress_init(void)
-{
-    struct zlib_decompress_ctx *dctx = snew(struct zlib_decompress_ctx);
-    unsigned char lengths[288];
-
-    memset(lengths, 8, 144);
-    memset(lengths + 144, 9, 256 - 144);
-    memset(lengths + 256, 7, 280 - 256);
-    memset(lengths + 280, 8, 288 - 280);
-    dctx->staticlentable = zlib_mktable(lengths, 288);
-    memset(lengths, 5, 32);
-    dctx->staticdisttable = zlib_mktable(lengths, 32);
-    dctx->state = START;                       /* even before header */
-    dctx->currlentable = dctx->currdisttable = dctx->lenlentable = NULL;
-    dctx->bits = 0;
-    dctx->nbits = 0;
-    dctx->winpos = 0;
-    dctx->outblk = NULL;
-
-    dctx->dc.vt = &ssh_zlib;
-    return &dctx->dc;
-}
-
-void zlib_decompress_cleanup(ssh_decompressor *dc)
-{
-    struct zlib_decompress_ctx *dctx =
-        container_of(dc, struct zlib_decompress_ctx, dc);
-
-    if (dctx->currlentable && dctx->currlentable != dctx->staticlentable)
-        zlib_freetable(&dctx->currlentable);
-    if (dctx->currdisttable && dctx->currdisttable != dctx->staticdisttable)
-        zlib_freetable(&dctx->currdisttable);
-    if (dctx->lenlentable)
-        zlib_freetable(&dctx->lenlentable);
-    zlib_freetable(&dctx->staticlentable);
-    zlib_freetable(&dctx->staticdisttable);
-    if (dctx->outblk)
-        strbuf_free(dctx->outblk);
-    sfree(dctx);
-}
-
-static int zlib_huflookup(unsigned long *bitsp, int *nbitsp,
-                   struct zlib_table *tab)
-{
-    unsigned long bits = *bitsp;
-    int nbits = *nbitsp;
-    while (1) {
-        struct zlib_tableentry *ent;
-        ent = &tab->table[bits & tab->mask];
-        if (ent->nbits > nbits)
-            return -1;                 /* not enough data */
-        bits >>= ent->nbits;
-        nbits -= ent->nbits;
-        if (ent->code == -1)
-            tab = ent->nexttable;
-        else {
-            *bitsp = bits;
-            *nbitsp = nbits;
-            return ent->code;
-        }
-
-        if (!tab) {
-            /*
-             * There was a missing entry in the table, presumably
-             * due to an invalid Huffman table description, and the
-             * subsequent data has attempted to use the missing
-             * entry. Return a decoding failure.
-             */
-            return -2;
-        }
-    }
-}
-
-static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c)
-{
-    dctx->window[dctx->winpos] = c;
-    dctx->winpos = (dctx->winpos + 1) & (WINSIZE - 1);
-    put_byte(dctx->outblk, c);
-}
-
-#define EATBITS(n) ( dctx->nbits -= (n), dctx->bits >>= (n) )
-
-bool zlib_decompress_block(ssh_decompressor *dc,
-                           const unsigned char *block, int len,
-                           unsigned char **outblock, int *outlen)
-{
-    struct zlib_decompress_ctx *dctx =
-        container_of(dc, struct zlib_decompress_ctx, dc);
-    const coderecord *rec;
-    int code, blktype, rep, dist, nlen, header;
-    static const unsigned char lenlenmap[] = {
-        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
-    };
-
-    assert(!dctx->outblk);
-    dctx->outblk = strbuf_new_nm();
-
-    while (len > 0 || dctx->nbits > 0) {
-        while (dctx->nbits < 24 && len > 0) {
-            dctx->bits |= (*block++) << dctx->nbits;
-            dctx->nbits += 8;
-            len--;
-        }
-        switch (dctx->state) {
-          case START:
-            /* Expect 16-bit zlib header. */
-            if (dctx->nbits < 16)
-                goto finished;         /* done all we can */
-
-            /*
-             * The header is stored as a big-endian 16-bit integer,
-             * in contrast to the general little-endian policy in
-             * the rest of the format :-(
-             */
-            header = (((dctx->bits & 0xFF00) >> 8) |
-                      ((dctx->bits & 0x00FF) << 8));
-            EATBITS(16);
-
-            /*
-             * Check the header:
-             *
-             *  - bits 8-11 should be 1000 (Deflate/RFC1951)
-             *  - bits 12-15 should be at most 0111 (window size)
-             *  - bit 5 should be zero (no dictionary present)
-             *  - we don't care about bits 6-7 (compression rate)
-             *  - bits 0-4 should be set up to make the whole thing
-             *    a multiple of 31 (checksum).
-             */
-            if ((header & 0x0F00) != 0x0800 ||
-                (header & 0xF000) >  0x7000 ||
-                (header & 0x0020) != 0x0000 ||
-                (header % 31) != 0)
-                goto decode_error;
-
-            dctx->state = OUTSIDEBLK;
-            break;
-          case OUTSIDEBLK:
-            /* Expect 3-bit block header. */
-            if (dctx->nbits < 3)
-                goto finished;         /* done all we can */
-            EATBITS(1);
-            blktype = dctx->bits & 3;
-            EATBITS(2);
-            if (blktype == 0) {
-                int to_eat = dctx->nbits & 7;
-                dctx->state = UNCOMP_LEN;
-                EATBITS(to_eat);       /* align to byte boundary */
-            } else if (blktype == 1) {
-                dctx->currlentable = dctx->staticlentable;
-                dctx->currdisttable = dctx->staticdisttable;
-                dctx->state = INBLK;
-            } else if (blktype == 2) {
-                dctx->state = TREES_HDR;
-            }
-            break;
-          case TREES_HDR:
-            /*
-             * Dynamic block header. Five bits of HLIT, five of
-             * HDIST, four of HCLEN.
-             */
-            if (dctx->nbits < 5 + 5 + 4)
-                goto finished;         /* done all we can */
-            dctx->hlit = 257 + (dctx->bits & 31);
-            EATBITS(5);
-            dctx->hdist = 1 + (dctx->bits & 31);
-            EATBITS(5);
-            dctx->hclen = 4 + (dctx->bits & 15);
-            EATBITS(4);
-            dctx->lenptr = 0;
-            dctx->state = TREES_LENLEN;
-            memset(dctx->lenlen, 0, sizeof(dctx->lenlen));
-            break;
-          case TREES_LENLEN:
-            if (dctx->nbits < 3)
-                goto finished;
-            while (dctx->lenptr < dctx->hclen && dctx->nbits >= 3) {
-                dctx->lenlen[lenlenmap[dctx->lenptr++]] =
-                    (unsigned char) (dctx->bits & 7);
-                EATBITS(3);
-            }
-            if (dctx->lenptr == dctx->hclen) {
-                dctx->lenlentable = zlib_mktable(dctx->lenlen, 19);
-                dctx->state = TREES_LEN;
-                dctx->lenptr = 0;
-            }
-            break;
-          case TREES_LEN:
-            if (dctx->lenptr >= dctx->hlit + dctx->hdist) {
-                dctx->currlentable = zlib_mktable(dctx->lengths, dctx->hlit);
-                dctx->currdisttable = zlib_mktable(dctx->lengths + dctx->hlit,
-                                                  dctx->hdist);
-                zlib_freetable(&dctx->lenlentable);
-                dctx->lenlentable = NULL;
-                dctx->state = INBLK;
-                break;
-            }
-            code =
-                zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->lenlentable);
-            if (code == -1)
-                goto finished;
-            if (code == -2)
-                goto decode_error;
-            if (code < 16)
-                dctx->lengths[dctx->lenptr++] = code;
-            else {
-                dctx->lenextrabits = (code == 16 ? 2 : code == 17 ? 3 : 7);
-                dctx->lenaddon = (code == 18 ? 11 : 3);
-                dctx->lenrep = (code == 16 && dctx->lenptr > 0 ?
-                               dctx->lengths[dctx->lenptr - 1] : 0);
-                dctx->state = TREES_LENREP;
-            }
-            break;
-          case TREES_LENREP:
-            if (dctx->nbits < dctx->lenextrabits)
-                goto finished;
-            rep =
-                dctx->lenaddon +
-                (dctx->bits & ((1 << dctx->lenextrabits) - 1));
-            EATBITS(dctx->lenextrabits);
-            while (rep > 0 && dctx->lenptr < dctx->hlit + dctx->hdist) {
-                dctx->lengths[dctx->lenptr] = dctx->lenrep;
-                dctx->lenptr++;
-                rep--;
-            }
-            dctx->state = TREES_LEN;
-            break;
-          case INBLK:
-            code =
-                zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->currlentable);
-            if (code == -1)
-                goto finished;
-            if (code == -2)
-                goto decode_error;
-            if (code < 256)
-                zlib_emit_char(dctx, code);
-            else if (code == 256) {
-                dctx->state = OUTSIDEBLK;
-                if (dctx->currlentable != dctx->staticlentable) {
-                    zlib_freetable(&dctx->currlentable);
-                    dctx->currlentable = NULL;
-                }
-                if (dctx->currdisttable != dctx->staticdisttable) {
-                    zlib_freetable(&dctx->currdisttable);
-                    dctx->currdisttable = NULL;
-                }
-            } else if (code < 286) {
-                dctx->state = GOTLENSYM;
-                dctx->sym = code;
-            } else {
-                /* literal/length symbols 286 and 287 are invalid */
-                goto decode_error;
-            }
-            break;
-          case GOTLENSYM:
-            rec = &lencodes[dctx->sym - 257];
-            if (dctx->nbits < rec->extrabits)
-                goto finished;
-            dctx->len =
-                rec->min + (dctx->bits & ((1 << rec->extrabits) - 1));
-            EATBITS(rec->extrabits);
-            dctx->state = GOTLEN;
-            break;
-          case GOTLEN:
-            code =
-                zlib_huflookup(&dctx->bits, &dctx->nbits,
-                               dctx->currdisttable);
-            if (code == -1)
-                goto finished;
-            if (code == -2)
-                goto decode_error;
-            if (code >= 30)            /* dist symbols 30 and 31 are invalid */
-                goto decode_error;
-            dctx->state = GOTDISTSYM;
-            dctx->sym = code;
-            break;
-          case GOTDISTSYM:
-            rec = &distcodes[dctx->sym];
-            if (dctx->nbits < rec->extrabits)
-                goto finished;
-            dist = rec->min + (dctx->bits & ((1 << rec->extrabits) - 1));
-            EATBITS(rec->extrabits);
-            dctx->state = INBLK;
-            while (dctx->len--)
-                zlib_emit_char(dctx, dctx->window[(dctx->winpos - dist) &
-                                                  (WINSIZE - 1)]);
-            break;
-          case UNCOMP_LEN:
-            /*
-             * Uncompressed block. We expect to see a 16-bit LEN.
-             */
-            if (dctx->nbits < 16)
-                goto finished;
-            dctx->uncomplen = dctx->bits & 0xFFFF;
-            EATBITS(16);
-            dctx->state = UNCOMP_NLEN;
-            break;
-          case UNCOMP_NLEN:
-            /*
-             * Uncompressed block. We expect to see a 16-bit NLEN,
-             * which should be the one's complement of the previous
-             * LEN.
-             */
-            if (dctx->nbits < 16)
-                goto finished;
-            nlen = dctx->bits & 0xFFFF;
-            EATBITS(16);
-            if (dctx->uncomplen != (nlen ^ 0xFFFF))
-                goto decode_error;
-            if (dctx->uncomplen == 0)
-                dctx->state = OUTSIDEBLK;       /* block is empty */
-            else
-                dctx->state = UNCOMP_DATA;
-            break;
-          case UNCOMP_DATA:
-            if (dctx->nbits < 8)
-                goto finished;
-            zlib_emit_char(dctx, dctx->bits & 0xFF);
-            EATBITS(8);
-            if (--dctx->uncomplen == 0)
-                dctx->state = OUTSIDEBLK;       /* end of uncompressed block */
-            break;
-        }
-    }
-
-  finished:
-    *outlen = dctx->outblk->len;
-    *outblock = (unsigned char *)strbuf_to_str(dctx->outblk);
-    dctx->outblk = NULL;
-    return true;
-
-  decode_error:
-    *outblock = NULL;
-    *outlen = 0;
-    return false;
-}
-
-const ssh_compression_alg ssh_zlib = {
-    // WINSCP
-    /*.name =*/ "zlib",
-    /*.delayed_name =*/ "[email protected]", /* delayed version */
-    /*.compress_new =*/ zlib_compress_init,
-    /*.compress_free =*/ zlib_compress_cleanup,
-    /*.compress =*/ zlib_compress_block,
-    /*.decompress_new =*/ zlib_decompress_init,
-    /*.decompress_free =*/ zlib_decompress_cleanup,
-    /*.decompress =*/ zlib_decompress_block,
-    /*.text_name =*/ "zlib (RFC1950)",
-};

+ 2 - 2
source/putty/storage.h

@@ -85,8 +85,8 @@ int retrieve_host_key(const char *hostname, int port,
  * be 0 (entry matches database), 1 (entry is absent in database),
  * or 2 (entry exists in database and is different).
  */
-int verify_host_key(const char *hostname, int port,
-                    const char *keytype, const char *key);
+int check_stored_host_key(const char *hostname, int port,
+                          const char *keytype, const char *key);
 #endif
 
 /*

+ 0 - 488
source/putty/stripctrl.c

@@ -1,488 +0,0 @@
-/*
- * stripctrl.c: a facility for stripping control characters out of a
- * data stream (defined as any multibyte character in the system
- * locale which is neither printable nor \n), using the standard C
- * library multibyte character facilities.
- */
-
-#include <assert.h>
-#include <locale.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#include "putty.h"
-#ifndef WINSCP
-#include "terminal.h"
-#endif
-#include "misc.h"
-#include "marshal.h"
-
-#define SCC_BUFSIZE 64
-#define LINE_LIMIT 77
-
-typedef struct StripCtrlCharsImpl StripCtrlCharsImpl;
-struct StripCtrlCharsImpl {
-    mbstate_t mbs_in, mbs_out;
-
-    bool permit_cr;
-    wchar_t substitution;
-
-    char buf[SCC_BUFSIZE];
-    size_t buflen;
-
-#ifndef WINSCP
-    Terminal *term;
-    bool last_term_utf;
-    struct term_utf8_decode utf8;
-    unsigned long (*translate)(Terminal *, term_utf8_decode *, unsigned char);
-#endif
-
-    bool line_limit;
-    bool line_start;
-    size_t line_chars_remaining;
-
-    BinarySink *bs_out;
-
-    StripCtrlChars public;
-};
-
-static void stripctrl_locale_BinarySink_write(
-    BinarySink *bs, const void *vp, size_t len);
-static void stripctrl_term_BinarySink_write(
-    BinarySink *bs, const void *vp, size_t len);
-
-static StripCtrlCharsImpl *stripctrl_new_common(
-    BinarySink *bs_out, bool permit_cr, wchar_t substitution)
-{
-    StripCtrlCharsImpl *scc = snew(StripCtrlCharsImpl);
-    memset(scc, 0, sizeof(StripCtrlCharsImpl)); /* zeroes mbstates */
-    scc->bs_out = bs_out;
-    scc->permit_cr = permit_cr;
-    scc->substitution = substitution;
-    return scc;
-}
-
-#ifndef WINSCP
-StripCtrlChars *stripctrl_new(
-    BinarySink *bs_out, bool permit_cr, wchar_t substitution)
-{
-    StripCtrlCharsImpl *scc = stripctrl_new_common(
-        bs_out, permit_cr, substitution);
-    BinarySink_INIT(&scc->public, stripctrl_locale_BinarySink_write);
-    return &scc->public;
-}
-
-StripCtrlChars *stripctrl_new_term_fn(
-    BinarySink *bs_out, bool permit_cr, wchar_t substitution,
-    Terminal *term, unsigned long (*translate)(
-        Terminal *, term_utf8_decode *, unsigned char))
-{
-    StripCtrlCharsImpl *scc = stripctrl_new_common(
-        bs_out, permit_cr, substitution);
-    scc->term = term;
-    scc->translate = translate;
-    BinarySink_INIT(&scc->public, stripctrl_term_BinarySink_write);
-    return &scc->public;
-}
-#endif
-
-void stripctrl_retarget(StripCtrlChars *sccpub, BinarySink *new_bs_out)
-{
-    StripCtrlCharsImpl *scc =
-        container_of(sccpub, StripCtrlCharsImpl, public);
-    scc->bs_out = new_bs_out;
-    stripctrl_reset(sccpub);
-}
-
-void stripctrl_reset(StripCtrlChars *sccpub)
-{
-    StripCtrlCharsImpl *scc =
-        container_of(sccpub, StripCtrlCharsImpl, public);
-
-    /*
-     * Clear all the fields that might have been in the middle of a
-     * multibyte character or non-default shift state, so that we can
-     * start converting a fresh piece of data to send to a channel
-     * that hasn't seen the previous output.
-     */
-#ifndef WINSCP
-    memset(&scc->utf8, 0, sizeof(scc->utf8));
-#endif
-    memset(&scc->mbs_in, 0, sizeof(scc->mbs_in));
-    memset(&scc->mbs_out, 0, sizeof(scc->mbs_out));
-
-    /*
-     * Also, reset the line-limiting system to its starting state.
-     */
-    scc->line_start = true;
-}
-
-void stripctrl_free(StripCtrlChars *sccpub)
-{
-    StripCtrlCharsImpl *scc =
-        container_of(sccpub, StripCtrlCharsImpl, public);
-    smemclr(scc, sizeof(StripCtrlCharsImpl));
-    sfree(scc);
-}
-
-void stripctrl_enable_line_limiting(StripCtrlChars *sccpub)
-{
-    StripCtrlCharsImpl *scc =
-        container_of(sccpub, StripCtrlCharsImpl, public);
-    scc->line_limit = true;
-    scc->line_start = true;
-}
-
-#ifndef WINSCP
-static inline bool stripctrl_ctrlchar_ok(StripCtrlCharsImpl *scc, wchar_t wc)
-{
-    return wc == L'\n' || (wc == L'\r' && scc->permit_cr);
-}
-
-static inline void stripctrl_check_line_limit(
-    StripCtrlCharsImpl *scc, wchar_t wc, size_t width)
-{
-    if (!scc->line_limit)
-        return;                        /* nothing to do */
-
-    if (scc->line_start) {
-        put_datapl(scc->bs_out, PTRLEN_LITERAL("| "));
-        scc->line_start = false;
-        scc->line_chars_remaining = LINE_LIMIT;
-    }
-
-    if (wc == '\n') {
-        scc->line_start = true;
-        return;
-    }
-
-    if (scc->line_chars_remaining < width) {
-        put_datapl(scc->bs_out, PTRLEN_LITERAL("\r\n> "));
-        scc->line_chars_remaining = LINE_LIMIT;
-    }
-
-    assert(width <= scc->line_chars_remaining);
-    scc->line_chars_remaining -= width;
-}
-
-static inline void stripctrl_locale_put_wc(StripCtrlCharsImpl *scc, wchar_t wc)
-{
-    int width = mk_wcwidth(wc);
-    if ((iswprint(wc) && width >= 0) || stripctrl_ctrlchar_ok(scc, wc)) {
-        /* Printable character, or one we're going to let through anyway. */
-        if (width < 0)
-            width = 0;   /* sanitise for stripctrl_check_line_limit */
-    } else if (scc->substitution) {
-        wc = scc->substitution;
-        width = mk_wcwidth(wc);
-        assert(width >= 0);
-    } else {
-        /* No defined substitution, so don't write any output wchar_t. */
-        return;
-    }
-
-    stripctrl_check_line_limit(scc, wc, width);
-
-    char outbuf[MB_LEN_MAX];
-    size_t produced = wcrtomb(outbuf, wc, &scc->mbs_out);
-    if (produced > 0)
-        put_data(scc->bs_out, outbuf, produced);
-}
-
-static inline void stripctrl_term_put_wc(
-    StripCtrlCharsImpl *scc, unsigned long wc)
-{
-    ptrlen prefix = PTRLEN_LITERAL("");
-    int width = term_char_width(scc->term, wc);
-
-    if (!(wc & ~0x9F) || width < 0) {
-        /* This is something the terminal interprets as a control
-         * character. */
-        if (!stripctrl_ctrlchar_ok(scc, wc)) {
-            if (!scc->substitution) {
-                return;
-            } else {
-                wc = scc->substitution;
-                width = term_char_width(scc->term, wc);
-                assert(width >= 0);
-            }
-        } else {
-            if (width < 0)
-                width = 0; /* sanitise for stripctrl_check_line_limit */
-        }
-
-        if (wc == '\012') {
-            /* Precede \n with \r, because our terminal will not
-             * generally be in the ONLCR mode where it assumes that
-             * internally, and any \r on input has been stripped
-             * out. */
-            prefix = PTRLEN_LITERAL("\r");
-        }
-    }
-
-    stripctrl_check_line_limit(scc, wc, width);
-
-    if (prefix.len)
-        put_datapl(scc->bs_out, prefix);
-
-    char outbuf[6];
-    size_t produced;
-
-    /*
-     * The Terminal implementation encodes 7-bit ASCII characters in
-     * UTF-8 mode, and all printing characters in non-UTF-8 (i.e.
-     * single-byte character set) mode, as values in the surrogate
-     * range (a conveniently unused piece of space in this context)
-     * whose low byte is the original 1-byte representation of the
-     * character.
-     */
-    if ((wc - 0xD800) < (0xE000 - 0xD800))
-        wc &= 0xFF;
-
-    if (in_utf(scc->term)) {
-        produced = encode_utf8(outbuf, wc);
-    } else {
-        outbuf[0] = wc;
-        produced = 1;
-    }
-
-    if (produced > 0)
-        put_data(scc->bs_out, outbuf, produced);
-}
-
-static inline size_t stripctrl_locale_try_consume(
-    StripCtrlCharsImpl *scc, const char *p, size_t len)
-{
-    wchar_t wc;
-    mbstate_t mbs_orig = scc->mbs_in;
-    size_t consumed = mbrtowc(&wc, p, len, &scc->mbs_in);
-
-    if (consumed == (size_t)-2) {
-        /*
-         * The buffer is too short to see the end of the multibyte
-         * character that it appears to be starting with. We return 0
-         * for 'no data consumed', restore the conversion state from
-         * before consuming the partial character, and our caller will
-         * come back when it has more data available.
-         */
-        scc->mbs_in = mbs_orig;
-        return 0;
-    }
-
-    if (consumed == (size_t)-1) {
-        /*
-         * The buffer contains an illegal multibyte sequence. There's
-         * no really good way to recover from this, so we'll just
-         * reset our input state, consume a single byte without
-         * emitting anything, and hope we can resynchronise to
-         * _something_ sooner or later.
-         */
-        memset(&scc->mbs_in, 0, sizeof(scc->mbs_in));
-        return 1;
-    }
-
-    if (consumed == 0) {
-        /*
-         * A zero wide character is encoded by the data, but mbrtowc
-         * hasn't told us how many input bytes it takes. There isn't
-         * really anything good we can do here, so we just advance by
-         * one byte in the hope that that was the NUL.
-         *
-         * (If it wasn't - that is, if we're in a multibyte encoding
-         * in which the terminator of a normal C string is encoded in
-         * some way other than a single zero byte - then probably lots
-         * of other things will have gone wrong before we get here!)
-         */
-        stripctrl_locale_put_wc(scc, L'\0');
-        return 1;
-    }
-
-    /*
-     * Otherwise, this is the easy case: consumed > 0, and we've eaten
-     * a valid multibyte character.
-     */
-    stripctrl_locale_put_wc(scc, wc);
-    return consumed;
-}
-
-static void stripctrl_locale_BinarySink_write(
-    BinarySink *bs, const void *vp, size_t len)
-{
-    StripCtrlChars *sccpub = BinarySink_DOWNCAST(bs, StripCtrlChars);
-    StripCtrlCharsImpl *scc =
-        container_of(sccpub, StripCtrlCharsImpl, public);
-    const char *p = (const char *)vp;
-
-    const char *previous_locale = setlocale(LC_CTYPE, NULL);
-    setlocale(LC_CTYPE, "");
-
-    /*
-     * Deal with any partial multibyte character buffered from last
-     * time.
-     */
-    while (scc->buflen > 0) {
-        size_t to_copy = SCC_BUFSIZE - scc->buflen;
-        if (to_copy > len)
-            to_copy = len;
-
-        memcpy(scc->buf + scc->buflen, p, to_copy);
-        { // WINSCP
-        size_t consumed = stripctrl_locale_try_consume(
-            scc, scc->buf, scc->buflen + to_copy);
-
-        if (consumed >= scc->buflen) {
-            /*
-             * We've consumed a multibyte character that includes all
-             * the data buffered from last time. So we can clear our
-             * buffer and move on to processing the main input string
-             * in situ, having first discarded whatever initial
-             * segment of it completed our previous character.
-             */
-            size_t consumed_from_main_string = consumed - scc->buflen;
-            assert(consumed_from_main_string <= len);
-            p += consumed_from_main_string;
-            len -= consumed_from_main_string;
-            scc->buflen = 0;
-            break;
-        }
-
-        if (consumed == 0) {
-            /*
-             * If we didn't manage to consume anything, i.e. the whole
-             * buffer contains an incomplete sequence, it had better
-             * be because our entire input string _this_ time plus
-             * whatever leftover data we had from _last_ time still
-             * comes to less than SCC_BUFSIZE. In other words, we've
-             * already copied all the new data on to the end of our
-             * buffer, and it still hasn't helped. So increment buflen
-             * to reflect the new data, and return.
-             */
-            assert(to_copy == len);
-            scc->buflen += to_copy;
-            goto out;
-        }
-
-        /*
-         * Otherwise, we've somehow consumed _less_ data than we had
-         * buffered, and yet we weren't able to consume that data in
-         * the last call to this function. That sounds impossible, but
-         * I can think of one situation in which it could happen: if
-         * we had an incomplete MB sequence last time, and now more
-         * data has arrived, it turns out to be an _illegal_ one, so
-         * we consume one byte in the hope of resynchronising.
-         *
-         * Anyway, in this case we move the buffer up and go back
-         * round this initial loop.
-         */
-        scc->buflen -= consumed;
-        memmove(scc->buf, scc->buf + consumed, scc->buflen);
-        } // WINSCP
-    }
-
-    /*
-     * Now charge along the main string.
-     */
-    while (len > 0) {
-        size_t consumed = stripctrl_locale_try_consume(scc, p, len);
-        if (consumed == 0)
-            break;
-        assert(consumed <= len);
-        p += consumed;
-        len -= consumed;
-    }
-
-    /*
-     * Any data remaining should be copied into our buffer, to keep
-     * for next time.
-     */
-    assert(len <= SCC_BUFSIZE);
-    memcpy(scc->buf, p, len);
-    scc->buflen = len;
-
-  out:
-    setlocale(LC_CTYPE, previous_locale);
-}
-
-static void stripctrl_term_BinarySink_write(
-    BinarySink *bs, const void *vp, size_t len)
-{
-    StripCtrlChars *sccpub = BinarySink_DOWNCAST(bs, StripCtrlChars);
-    StripCtrlCharsImpl *scc =
-        container_of(sccpub, StripCtrlCharsImpl, public);
-
-    bool utf = in_utf(scc->term);
-    if (utf != scc->last_term_utf) {
-        scc->last_term_utf = utf;
-        scc->utf8.state = 0;
-    }
-
-    for (const unsigned char *p = (const unsigned char *)vp;
-         len > 0; len--, p++) {
-        unsigned long t = scc->translate(scc->term, &scc->utf8, *p);
-        if (t == UCSTRUNCATED) {
-            stripctrl_term_put_wc(scc, 0xFFFD);
-            /* go round again */
-            t = scc->translate(scc->term, &scc->utf8, *p);
-        }
-        if (t == UCSINCOMPLETE)
-            continue;
-        if (t == UCSINVALID)
-            t = 0xFFFD;
-
-        stripctrl_term_put_wc(scc, t);
-    }
-}
-
-char *stripctrl_string_ptrlen(StripCtrlChars *sccpub, ptrlen str)
-{
-    strbuf *out = strbuf_new();
-    stripctrl_retarget(sccpub, BinarySink_UPCAST(out));
-    put_datapl(sccpub, str);
-    stripctrl_retarget(sccpub, NULL);
-    return strbuf_to_str(out);
-}
-#endif
-
-#ifdef STRIPCTRL_TEST
-
-/*
-gcc -std=c99 -DSTRIPCTRL_TEST -o scctest stripctrl.c marshal.c utils.c memory.c wcwidth.c -I . -I unix -I charset
-*/
-
-void out_of_memory(void) { fprintf(stderr, "out of memory\n"); abort(); }
-
-void stripctrl_write(BinarySink *bs, const void *vdata, size_t len)
-{
-    const uint8_t *p = vdata;
-    printf("[");
-    for (size_t i = 0; i < len; i++)
-        printf("%*s%02x", i?1:0, "", (unsigned)p[i]);
-    printf("]");
-}
-
-void stripctrl_test(StripCtrlChars *scc, ptrlen pl)
-{
-    stripctrl_write(NULL, pl.ptr, pl.len);
-    printf(" -> ");
-    put_datapl(scc, pl);
-    printf("\n");
-}
-
-int main(void)
-{
-    struct foo { BinarySink_IMPLEMENTATION; } foo;
-    BinarySink_INIT(&foo, stripctrl_write);
-    StripCtrlChars *scc = stripctrl_new(BinarySink_UPCAST(&foo), false, '?');
-    stripctrl_test(scc, PTRLEN_LITERAL("a\033[1mb"));
-    stripctrl_test(scc, PTRLEN_LITERAL("a\xC2\x9B[1mb"));
-    stripctrl_test(scc, PTRLEN_LITERAL("a\xC2\xC2[1mb"));
-    stripctrl_test(scc, PTRLEN_LITERAL("\xC3"));
-    stripctrl_test(scc, PTRLEN_LITERAL("\xA9"));
-    stripctrl_test(scc, PTRLEN_LITERAL("\xE2\x80\x8F"));
-    stripctrl_test(scc, PTRLEN_LITERAL("a\0b"));
-    stripctrl_free(scc);
-    return 0;
-}
-
-#endif /* STRIPCTRL_TEST */

+ 0 - 1611
source/putty/tree234.c

@@ -1,1611 +0,0 @@
-/*
- * tree234.c: reasonably generic counted 2-3-4 tree routines.
- *
- * This file is copyright 1999-2001 Simon Tatham.
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "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 "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 SIMON TATHAM 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "defs.h"
-#include "tree234.h"
-
-#ifdef TEST
-#define LOG(x) (printf x)
-#define snew(type) ((type *)malloc(sizeof(type)))
-#define snewn(n, type) ((type *)malloc((n) * sizeof(type)))
-#define sresize(ptr, n, type)                                         \
-    ((type *)realloc(sizeof((type *)0 == (ptr)) ? (ptr) : (ptr),      \
-                     (n) * sizeof(type)))
-#define sfree(ptr) free(ptr)
-#else
-#include "puttymem.h"
-#define LOG(x)
-#endif
-
-typedef struct node234_Tag node234;
-
-struct tree234_Tag {
-    node234 *root;
-    cmpfn234 cmp;
-};
-
-struct node234_Tag {
-    node234 *parent;
-    node234 *kids[4];
-    int counts[4];
-    void *elems[3];
-};
-
-/*
- * Create a 2-3-4 tree.
- */
-tree234 *newtree234(cmpfn234 cmp)
-{
-    tree234 *ret = snew(tree234);
-    LOG(("created tree %p\n", ret));
-    ret->root = NULL;
-    ret->cmp = cmp;
-    return ret;
-}
-
-/*
- * Free a 2-3-4 tree (not including freeing the elements).
- */
-static void freenode234(node234 * n)
-{
-    if (!n)
-        return;
-    freenode234(n->kids[0]);
-    freenode234(n->kids[1]);
-    freenode234(n->kids[2]);
-    freenode234(n->kids[3]);
-    sfree(n);
-}
-
-void freetree234(tree234 * t)
-{
-    freenode234(t->root);
-    sfree(t);
-}
-
-/*
- * Internal function to count a node.
- */
-static int countnode234(node234 * n)
-{
-    int count = 0;
-    int i;
-    if (!n)
-        return 0;
-    for (i = 0; i < 4; i++)
-        count += n->counts[i];
-    for (i = 0; i < 3; i++)
-        if (n->elems[i])
-            count++;
-    return count;
-}
-
-/*
- * Internal function to return the number of elements in a node.
- */
-static int elements234(node234 *n)
-{
-    int i;
-    for (i = 0; i < 3; i++)
-        if (!n->elems[i])
-            break;
-    return i;
-}
-
-/*
- * Count the elements in a tree.
- */
-int count234(tree234 * t)
-{
-    if (t->root)
-        return countnode234(t->root);
-    else
-        return 0;
-}
-
-/*
- * Add an element e to a 2-3-4 tree t. Returns e on success, or if
- * an existing element compares equal, returns that.
- */
-static void *add234_internal(tree234 * t, void *e, int index)
-{
-    node234 *n, **np, *left, *right;
-    void *orig_e = e;
-    int c, lcount, rcount;
-
-    LOG(("adding node %p to tree %p\n", e, t));
-    if (t->root == NULL) {
-        t->root = snew(node234);
-        t->root->elems[1] = t->root->elems[2] = NULL;
-        t->root->kids[0] = t->root->kids[1] = NULL;
-        t->root->kids[2] = t->root->kids[3] = NULL;
-        t->root->counts[0] = t->root->counts[1] = 0;
-        t->root->counts[2] = t->root->counts[3] = 0;
-        t->root->parent = NULL;
-        t->root->elems[0] = e;
-        LOG(("  created root %p\n", t->root));
-        return orig_e;
-    }
-
-    n = NULL; /* placate gcc; will always be set below since t->root != NULL */
-    np = &t->root;
-    while (*np) {
-        int childnum;
-        n = *np;
-        LOG(("  node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n",
-             n,
-             n->kids[0], n->counts[0], n->elems[0],
-             n->kids[1], n->counts[1], n->elems[1],
-             n->kids[2], n->counts[2], n->elems[2],
-             n->kids[3], n->counts[3]));
-        if (index >= 0) {
-            if (!n->kids[0]) {
-                /*
-                 * Leaf node. We want to insert at kid position
-                 * equal to the index:
-                 *
-                 *   0 A 1 B 2 C 3
-                 */
-                childnum = index;
-            } else {
-                /*
-                 * Internal node. We always descend through it (add
-                 * always starts at the bottom, never in the
-                 * middle).
-                 */
-                do {                   /* this is a do ... while (0) to allow `break' */
-                    if (index <= n->counts[0]) {
-                        childnum = 0;
-                        break;
-                    }
-                    index -= n->counts[0] + 1;
-                    if (index <= n->counts[1]) {
-                        childnum = 1;
-                        break;
-                    }
-                    index -= n->counts[1] + 1;
-                    if (index <= n->counts[2]) {
-                        childnum = 2;
-                        break;
-                    }
-                    index -= n->counts[2] + 1;
-                    if (index <= n->counts[3]) {
-                        childnum = 3;
-                        break;
-                    }
-                    return NULL;       /* error: index out of range */
-                } while (0);
-            }
-        } else {
-            if ((c = t->cmp(e, n->elems[0])) < 0)
-                childnum = 0;
-            else if (c == 0)
-                return n->elems[0];    /* already exists */
-            else if (n->elems[1] == NULL
-                     || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1;
-            else if (c == 0)
-                return n->elems[1];    /* already exists */
-            else if (n->elems[2] == NULL
-                     || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2;
-            else if (c == 0)
-                return n->elems[2];    /* already exists */
-            else
-                childnum = 3;
-        }
-        np = &n->kids[childnum];
-        LOG(("  moving to child %d (%p)\n", childnum, *np));
-    }
-
-    /*
-     * We need to insert the new element in n at position np.
-     */
-    left = NULL;
-    lcount = 0;
-    right = NULL;
-    rcount = 0;
-    while (n) {
-        LOG(("  at %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d\n",
-             n,
-             n->kids[0], n->counts[0], n->elems[0],
-             n->kids[1], n->counts[1], n->elems[1],
-             n->kids[2], n->counts[2], n->elems[2],
-             n->kids[3], n->counts[3]));
-        LOG(("  need to insert %p/%d [%p] %p/%d at position %d\n",
-             left, lcount, e, right, rcount, (int)(np - n->kids)));
-        if (n->elems[1] == NULL) {
-            /*
-             * Insert in a 2-node; simple.
-             */
-            if (np == &n->kids[0]) {
-                LOG(("  inserting on left of 2-node\n"));
-                n->kids[2] = n->kids[1];
-                n->counts[2] = n->counts[1];
-                n->elems[1] = n->elems[0];
-                n->kids[1] = right;
-                n->counts[1] = rcount;
-                n->elems[0] = e;
-                n->kids[0] = left;
-                n->counts[0] = lcount;
-            } else {                   /* np == &n->kids[1] */
-                LOG(("  inserting on right of 2-node\n"));
-                n->kids[2] = right;
-                n->counts[2] = rcount;
-                n->elems[1] = e;
-                n->kids[1] = left;
-                n->counts[1] = lcount;
-            }
-            if (n->kids[0])
-                n->kids[0]->parent = n;
-            if (n->kids[1])
-                n->kids[1]->parent = n;
-            if (n->kids[2])
-                n->kids[2]->parent = n;
-            LOG(("  done\n"));
-            break;
-        } else if (n->elems[2] == NULL) {
-            /*
-             * Insert in a 3-node; simple.
-             */
-            if (np == &n->kids[0]) {
-                LOG(("  inserting on left of 3-node\n"));
-                n->kids[3] = n->kids[2];
-                n->counts[3] = n->counts[2];
-                n->elems[2] = n->elems[1];
-                n->kids[2] = n->kids[1];
-                n->counts[2] = n->counts[1];
-                n->elems[1] = n->elems[0];
-                n->kids[1] = right;
-                n->counts[1] = rcount;
-                n->elems[0] = e;
-                n->kids[0] = left;
-                n->counts[0] = lcount;
-            } else if (np == &n->kids[1]) {
-                LOG(("  inserting in middle of 3-node\n"));
-                n->kids[3] = n->kids[2];
-                n->counts[3] = n->counts[2];
-                n->elems[2] = n->elems[1];
-                n->kids[2] = right;
-                n->counts[2] = rcount;
-                n->elems[1] = e;
-                n->kids[1] = left;
-                n->counts[1] = lcount;
-            } else {                   /* np == &n->kids[2] */
-                LOG(("  inserting on right of 3-node\n"));
-                n->kids[3] = right;
-                n->counts[3] = rcount;
-                n->elems[2] = e;
-                n->kids[2] = left;
-                n->counts[2] = lcount;
-            }
-            if (n->kids[0])
-                n->kids[0]->parent = n;
-            if (n->kids[1])
-                n->kids[1]->parent = n;
-            if (n->kids[2])
-                n->kids[2]->parent = n;
-            if (n->kids[3])
-                n->kids[3]->parent = n;
-            LOG(("  done\n"));
-            break;
-        } else {
-            node234 *m = snew(node234);
-            m->parent = n->parent;
-            LOG(("  splitting a 4-node; created new node %p\n", m));
-            /*
-             * Insert in a 4-node; split into a 2-node and a
-             * 3-node, and move focus up a level.
-             *
-             * I don't think it matters which way round we put the
-             * 2 and the 3. For simplicity, we'll put the 3 first
-             * always.
-             */
-            if (np == &n->kids[0]) {
-                m->kids[0] = left;
-                m->counts[0] = lcount;
-                m->elems[0] = e;
-                m->kids[1] = right;
-                m->counts[1] = rcount;
-                m->elems[1] = n->elems[0];
-                m->kids[2] = n->kids[1];
-                m->counts[2] = n->counts[1];
-                e = n->elems[1];
-                n->kids[0] = n->kids[2];
-                n->counts[0] = n->counts[2];
-                n->elems[0] = n->elems[2];
-                n->kids[1] = n->kids[3];
-                n->counts[1] = n->counts[3];
-            } else if (np == &n->kids[1]) {
-                m->kids[0] = n->kids[0];
-                m->counts[0] = n->counts[0];
-                m->elems[0] = n->elems[0];
-                m->kids[1] = left;
-                m->counts[1] = lcount;
-                m->elems[1] = e;
-                m->kids[2] = right;
-                m->counts[2] = rcount;
-                e = n->elems[1];
-                n->kids[0] = n->kids[2];
-                n->counts[0] = n->counts[2];
-                n->elems[0] = n->elems[2];
-                n->kids[1] = n->kids[3];
-                n->counts[1] = n->counts[3];
-            } else if (np == &n->kids[2]) {
-                m->kids[0] = n->kids[0];
-                m->counts[0] = n->counts[0];
-                m->elems[0] = n->elems[0];
-                m->kids[1] = n->kids[1];
-                m->counts[1] = n->counts[1];
-                m->elems[1] = n->elems[1];
-                m->kids[2] = left;
-                m->counts[2] = lcount;
-                /* e = e; */
-                n->kids[0] = right;
-                n->counts[0] = rcount;
-                n->elems[0] = n->elems[2];
-                n->kids[1] = n->kids[3];
-                n->counts[1] = n->counts[3];
-            } else {                   /* np == &n->kids[3] */
-                m->kids[0] = n->kids[0];
-                m->counts[0] = n->counts[0];
-                m->elems[0] = n->elems[0];
-                m->kids[1] = n->kids[1];
-                m->counts[1] = n->counts[1];
-                m->elems[1] = n->elems[1];
-                m->kids[2] = n->kids[2];
-                m->counts[2] = n->counts[2];
-                n->kids[0] = left;
-                n->counts[0] = lcount;
-                n->elems[0] = e;
-                n->kids[1] = right;
-                n->counts[1] = rcount;
-                e = n->elems[2];
-            }
-            m->kids[3] = n->kids[3] = n->kids[2] = NULL;
-            m->counts[3] = n->counts[3] = n->counts[2] = 0;
-            m->elems[2] = n->elems[2] = n->elems[1] = NULL;
-            if (m->kids[0])
-                m->kids[0]->parent = m;
-            if (m->kids[1])
-                m->kids[1]->parent = m;
-            if (m->kids[2])
-                m->kids[2]->parent = m;
-            if (n->kids[0])
-                n->kids[0]->parent = n;
-            if (n->kids[1])
-                n->kids[1]->parent = n;
-            LOG(("  left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m,
-                 m->kids[0], m->counts[0], m->elems[0],
-                 m->kids[1], m->counts[1], m->elems[1],
-                 m->kids[2], m->counts[2]));
-            LOG(("  right (%p): %p/%d [%p] %p/%d\n", n,
-                 n->kids[0], n->counts[0], n->elems[0],
-                 n->kids[1], n->counts[1]));
-            left = m;
-            lcount = countnode234(left);
-            right = n;
-            rcount = countnode234(right);
-        }
-        if (n->parent)
-            np = (n->parent->kids[0] == n ? &n->parent->kids[0] :
-                  n->parent->kids[1] == n ? &n->parent->kids[1] :
-                  n->parent->kids[2] == n ? &n->parent->kids[2] :
-                  &n->parent->kids[3]);
-        n = n->parent;
-    }
-
-    /*
-     * If we've come out of here by `break', n will still be
-     * non-NULL and all we need to do is go back up the tree
-     * updating counts. If we've come here because n is NULL, we
-     * need to create a new root for the tree because the old one
-     * has just split into two. */
-    if (n) {
-        while (n->parent) {
-            int count = countnode234(n);
-            int childnum;
-            childnum = (n->parent->kids[0] == n ? 0 :
-                        n->parent->kids[1] == n ? 1 :
-                        n->parent->kids[2] == n ? 2 : 3);
-            n->parent->counts[childnum] = count;
-            n = n->parent;
-        }
-    } else {
-        LOG(("  root is overloaded, split into two\n"));
-        t->root = snew(node234);
-        t->root->kids[0] = left;
-        t->root->counts[0] = lcount;
-        t->root->elems[0] = e;
-        t->root->kids[1] = right;
-        t->root->counts[1] = rcount;
-        t->root->elems[1] = NULL;
-        t->root->kids[2] = NULL;
-        t->root->counts[2] = 0;
-        t->root->elems[2] = NULL;
-        t->root->kids[3] = NULL;
-        t->root->counts[3] = 0;
-        t->root->parent = NULL;
-        if (t->root->kids[0])
-            t->root->kids[0]->parent = t->root;
-        if (t->root->kids[1])
-            t->root->kids[1]->parent = t->root;
-        LOG(("  new root is %p/%d [%p] %p/%d\n",
-             t->root->kids[0], t->root->counts[0],
-             t->root->elems[0], t->root->kids[1], t->root->counts[1]));
-    }
-
-    return orig_e;
-}
-
-void *add234(tree234 * t, void *e)
-{
-    if (!t->cmp)                       /* tree is unsorted */
-        return NULL;
-
-    return add234_internal(t, e, -1);
-}
-void *addpos234(tree234 * t, void *e, int index)
-{
-    if (index < 0 ||                   /* index out of range */
-        t->cmp)                        /* tree is sorted */
-        return NULL;                   /* return failure */
-
-    return add234_internal(t, e, index);        /* this checks the upper bound */
-}
-
-/*
- * Look up the element at a given numeric index in a 2-3-4 tree.
- * Returns NULL if the index is out of range.
- */
-void *index234(tree234 * t, int index)
-{
-    node234 *n;
-
-    if (!t->root)
-        return NULL;                   /* tree is empty */
-
-    if (index < 0 || index >= countnode234(t->root))
-        return NULL;                   /* out of range */
-
-    n = t->root;
-
-    while (n) {
-        if (index < n->counts[0])
-            n = n->kids[0];
-        else if (index -= n->counts[0] + 1, index < 0)
-            return n->elems[0];
-        else if (index < n->counts[1])
-            n = n->kids[1];
-        else if (index -= n->counts[1] + 1, index < 0)
-            return n->elems[1];
-        else if (index < n->counts[2])
-            n = n->kids[2];
-        else if (index -= n->counts[2] + 1, index < 0)
-            return n->elems[2];
-        else
-            n = n->kids[3];
-    }
-
-    /* We shouldn't ever get here. I wonder how we did. */
-    return NULL;
-}
-
-/*
- * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not
- * found. e is always passed as the first argument to cmp, so cmp
- * can be an asymmetric function if desired. cmp can also be passed
- * as NULL, in which case the compare function from the tree proper
- * will be used.
- */
-void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp,
-                    int relation, int *index)
-{
-    search234_state ss;
-    int reldir = (relation == REL234_LT || relation == REL234_LE ? -1 :
-                  relation == REL234_GT || relation == REL234_GE ? +1 : 0);
-    bool equal_permitted = (relation != REL234_LT && relation != REL234_GT);
-    void *toret;
-
-    /* Only LT / GT relations are permitted with a null query element. */
-    assert(!(equal_permitted && !e));
-
-    if (cmp == NULL)
-        cmp = t->cmp;
-
-    search234_start(&ss, t);
-    while (ss.element) {
-        int cmpret;
-
-        if (e) {
-            cmpret = cmp(e, ss.element);
-        } else {
-            cmpret = -reldir;          /* invent a fixed compare result */
-        }
-
-        if (cmpret == 0) {
-            /*
-             * We've found an element that compares exactly equal to
-             * the query element.
-             */
-            if (equal_permitted) {
-                /* If our search relation permits equality, we've
-                 * finished already. */
-                if (index)
-                    *index = ss.index;
-                return ss.element;
-            } else {
-                /* Otherwise, pretend this element was slightly too
-                 * big/small, according to the direction of search. */
-                cmpret = reldir;
-            }
-        }
-
-        search234_step(&ss, cmpret);
-    }
-
-    /*
-     * No element compares equal to the one we were after, but
-     * ss.index indicates the index that element would have if it were
-     * inserted.
-     *
-     * So if our search relation is EQ, we must simply return failure.
-     */
-    if (relation == REL234_EQ)
-        return NULL;
-
-    /*
-     * Otherwise, we must do an index lookup for the previous index
-     * (if we're going left - LE or LT) or this index (if we're going
-     * right - GE or GT).
-     */
-    if (relation == REL234_LT || relation == REL234_LE) {
-        ss.index--;
-    }
-
-    /*
-     * We know the index of the element we want; just call index234
-     * to do the rest. This will return NULL if the index is out of
-     * bounds, which is exactly what we want.
-     */
-    toret = index234(t, ss.index);
-    if (toret && index)
-        *index = ss.index;
-    return toret;
-}
-void *find234(tree234 * t, void *e, cmpfn234 cmp)
-{
-    return findrelpos234(t, e, cmp, REL234_EQ, NULL);
-}
-void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation)
-{
-    return findrelpos234(t, e, cmp, relation, NULL);
-}
-void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index)
-{
-    return findrelpos234(t, e, cmp, REL234_EQ, index);
-}
-
-void search234_start(search234_state *state, tree234 *t)
-{
-    state->_node = t->root;
-    state->_base = 0; /* index of first element in this node's subtree */
-    state->_last = -1; /* indicate that this node is not previously visted */
-    search234_step(state, 0);
-}
-void search234_step(search234_state *state, int direction)
-{
-    node234 *node = state->_node;
-    int i;
-
-    if (!node) {
-        state->element = NULL;
-        state->index = 0;
-        return;
-    }
-
-    if (state->_last != -1) {
-        /*
-         * We're already pointing at some element of a node, so we
-         * should restrict to the elements left or right of it,
-         * depending on the requested search direction.
-         */
-        assert(direction);
-        assert(node);
-
-        if (direction > 0)
-            state->_lo = state->_last + 1;
-        else
-            state->_hi = state->_last - 1;
-
-        if (state->_lo > state->_hi) {
-            /*
-             * We've run out of elements in this node, i.e. we've
-             * narrowed to nothing but a child pointer. Descend to
-             * that child, and update _base to the leftmost index of
-             * its subtree.
-             */
-            for (i = 0; i < state->_lo; i++)
-                state->_base += 1 + node->counts[i];
-            state->_node = node = node->kids[state->_lo];
-            state->_last = -1;
-        }
-    }
-
-    if (state->_last == -1) {
-        /*
-         * We've just entered a new node - either because of the above
-         * code, or because we were called from search234_start - and
-         * anything in that node is a viable answer.
-         */
-        state->_lo = 0;
-        state->_hi = node ? elements234(node)-1 : 0;
-    }
-
-    /*
-     * Now we've got something we can return.
-     */
-    if (!node) {
-        state->element = NULL;
-        state->index = state->_base;
-    } else {
-        state->_last = (state->_lo + state->_hi) / 2;
-        state->element = node->elems[state->_last];
-        state->index = state->_base + state->_last;
-        for (i = 0; i <= state->_last; i++)
-            state->index += node->counts[i];
-    }
-}
-
-/*
- * Delete an element e in a 2-3-4 tree. Does not free the element,
- * merely removes all links to it from the tree nodes.
- */
-static void *delpos234_internal(tree234 * t, int index)
-{
-    node234 *n;
-    void *retval;
-    int ei = -1;
-
-    retval = 0;
-
-    n = t->root;
-    LOG(("deleting item %d from tree %p\n", index, t));
-    while (1) {
-        while (n) {
-            int ki;
-            node234 *sub;
-
-            LOG(
-                ("  node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%d\n",
-                 n, n->kids[0], n->counts[0], n->elems[0], n->kids[1],
-                 n->counts[1], n->elems[1], n->kids[2], n->counts[2],
-                 n->elems[2], n->kids[3], n->counts[3], index));
-            if (index < n->counts[0]) {
-                ki = 0;
-            } else if (index -= n->counts[0] + 1, index < 0) {
-                ei = 0;
-                break;
-            } else if (index < n->counts[1]) {
-                ki = 1;
-            } else if (index -= n->counts[1] + 1, index < 0) {
-                ei = 1;
-                break;
-            } else if (index < n->counts[2]) {
-                ki = 2;
-            } else if (index -= n->counts[2] + 1, index < 0) {
-                ei = 2;
-                break;
-            } else {
-                ki = 3;
-            }
-            /*
-             * Recurse down to subtree ki. If it has only one element,
-             * we have to do some transformation to start with.
-             */
-            LOG(("  moving to subtree %d\n", ki));
-            sub = n->kids[ki];
-            if (!sub->elems[1]) {
-                LOG(("  subtree has only one element!\n"));
-                if (ki > 0 && n->kids[ki - 1]->elems[1]) {
-                    /*
-                     * Case 3a, left-handed variant. Child ki has
-                     * only one element, but child ki-1 has two or
-                     * more. So we need to move a subtree from ki-1
-                     * to ki.
-                     *
-                     *                . C .                     . B .
-                     *               /     \     ->            /     \
-                     * [more] a A b B c   d D e      [more] a A b   c C d D e
-                     */
-                    node234 *sib = n->kids[ki - 1];
-                    int lastelem = (sib->elems[2] ? 2 :
-                                    sib->elems[1] ? 1 : 0);
-                    sub->kids[2] = sub->kids[1];
-                    sub->counts[2] = sub->counts[1];
-                    sub->elems[1] = sub->elems[0];
-                    sub->kids[1] = sub->kids[0];
-                    sub->counts[1] = sub->counts[0];
-                    sub->elems[0] = n->elems[ki - 1];
-                    sub->kids[0] = sib->kids[lastelem + 1];
-                    sub->counts[0] = sib->counts[lastelem + 1];
-                    if (sub->kids[0])
-                        sub->kids[0]->parent = sub;
-                    n->elems[ki - 1] = sib->elems[lastelem];
-                    sib->kids[lastelem + 1] = NULL;
-                    sib->counts[lastelem + 1] = 0;
-                    sib->elems[lastelem] = NULL;
-                    n->counts[ki] = countnode234(sub);
-                    LOG(("  case 3a left\n"));
-                    LOG(
-                        ("  index and left subtree count before adjustment: %d, %d\n",
-                         index, n->counts[ki - 1]));
-                    index += n->counts[ki - 1];
-                    n->counts[ki - 1] = countnode234(sib);
-                    index -= n->counts[ki - 1];
-                    LOG(
-                        ("  index and left subtree count after adjustment: %d, %d\n",
-                         index, n->counts[ki - 1]));
-                } else if (ki < 3 && n->kids[ki + 1]
-                           && n->kids[ki + 1]->elems[1]) {
-                    /*
-                     * Case 3a, right-handed variant. ki has only
-                     * one element but ki+1 has two or more. Move a
-                     * subtree from ki+1 to ki.
-                     *
-                     *      . B .                             . C .
-                     *     /     \                ->         /     \
-                     *  a A b   c C d D e [more]      a A b B c   d D e [more]
-                     */
-                    node234 *sib = n->kids[ki + 1];
-                    int j;
-                    sub->elems[1] = n->elems[ki];
-                    sub->kids[2] = sib->kids[0];
-                    sub->counts[2] = sib->counts[0];
-                    if (sub->kids[2])
-                        sub->kids[2]->parent = sub;
-                    n->elems[ki] = sib->elems[0];
-                    sib->kids[0] = sib->kids[1];
-                    sib->counts[0] = sib->counts[1];
-                    for (j = 0; j < 2 && sib->elems[j + 1]; j++) {
-                        sib->kids[j + 1] = sib->kids[j + 2];
-                        sib->counts[j + 1] = sib->counts[j + 2];
-                        sib->elems[j] = sib->elems[j + 1];
-                    }
-                    sib->kids[j + 1] = NULL;
-                    sib->counts[j + 1] = 0;
-                    sib->elems[j] = NULL;
-                    n->counts[ki] = countnode234(sub);
-                    n->counts[ki + 1] = countnode234(sib);
-                    LOG(("  case 3a right\n"));
-                } else {
-                    /*
-                     * Case 3b. ki has only one element, and has no
-                     * neighbour with more than one. So pick a
-                     * neighbour and merge it with ki, taking an
-                     * element down from n to go in the middle.
-                     *
-                     *      . B .                .
-                     *     /     \     ->        |
-                     *  a A b   c C d      a A b B c C d
-                     *
-                     * (Since at all points we have avoided
-                     * descending to a node with only one element,
-                     * we can be sure that n is not reduced to
-                     * nothingness by this move, _unless_ it was
-                     * the very first node, ie the root of the
-                     * tree. In that case we remove the now-empty
-                     * root and replace it with its single large
-                     * child as shown.)
-                     */
-                    node234 *sib;
-                    int j;
-
-                    if (ki > 0) {
-                        ki--;
-                        index += n->counts[ki] + 1;
-                    }
-                    sib = n->kids[ki];
-                    sub = n->kids[ki + 1];
-
-                    sub->kids[3] = sub->kids[1];
-                    sub->counts[3] = sub->counts[1];
-                    sub->elems[2] = sub->elems[0];
-                    sub->kids[2] = sub->kids[0];
-                    sub->counts[2] = sub->counts[0];
-                    sub->elems[1] = n->elems[ki];
-                    sub->kids[1] = sib->kids[1];
-                    sub->counts[1] = sib->counts[1];
-                    if (sub->kids[1])
-                        sub->kids[1]->parent = sub;
-                    sub->elems[0] = sib->elems[0];
-                    sub->kids[0] = sib->kids[0];
-                    sub->counts[0] = sib->counts[0];
-                    if (sub->kids[0])
-                        sub->kids[0]->parent = sub;
-
-                    n->counts[ki + 1] = countnode234(sub);
-
-                    sfree(sib);
-
-                    /*
-                     * That's built the big node in sub. Now we
-                     * need to remove the reference to sib in n.
-                     */
-                    for (j = ki; j < 3 && n->kids[j + 1]; j++) {
-                        n->kids[j] = n->kids[j + 1];
-                        n->counts[j] = n->counts[j + 1];
-                        n->elems[j] = j < 2 ? n->elems[j + 1] : NULL;
-                    }
-                    n->kids[j] = NULL;
-                    n->counts[j] = 0;
-                    if (j < 3)
-                        n->elems[j] = NULL;
-                    LOG(("  case 3b ki=%d\n", ki));
-
-                    if (!n->elems[0]) {
-                        /*
-                         * The root is empty and needs to be
-                         * removed.
-                         */
-                        LOG(("  shifting root!\n"));
-                        t->root = sub;
-                        sub->parent = NULL;
-                        sfree(n);
-                    }
-                }
-            }
-            n = sub;
-        }
-        if (!retval)
-            retval = n->elems[ei];
-
-        if (ei == -1)
-            return NULL;               /* although this shouldn't happen */
-
-        /*
-         * Treat special case: this is the one remaining item in
-         * the tree. n is the tree root (no parent), has one
-         * element (no elems[1]), and has no kids (no kids[0]).
-         */
-        if (!n->parent && !n->elems[1] && !n->kids[0]) {
-            LOG(("  removed last element in tree\n"));
-            sfree(n);
-            t->root = NULL;
-            return retval;
-        }
-
-        /*
-         * Now we have the element we want, as n->elems[ei], and we
-         * have also arranged for that element not to be the only
-         * one in its node. So...
-         */
-
-        if (!n->kids[0] && n->elems[1]) {
-            /*
-             * Case 1. n is a leaf node with more than one element,
-             * so it's _really easy_. Just delete the thing and
-             * we're done.
-             */
-            int i;
-            LOG(("  case 1\n"));
-            for (i = ei; i < 2 && n->elems[i + 1]; i++)
-                n->elems[i] = n->elems[i + 1];
-            n->elems[i] = NULL;
-            /*
-             * Having done that to the leaf node, we now go back up
-             * the tree fixing the counts.
-             */
-            while (n->parent) {
-                int childnum;
-                childnum = (n->parent->kids[0] == n ? 0 :
-                            n->parent->kids[1] == n ? 1 :
-                            n->parent->kids[2] == n ? 2 : 3);
-                n->parent->counts[childnum]--;
-                n = n->parent;
-            }
-            return retval;             /* finished! */
-        } else if (n->kids[ei]->elems[1]) {
-            /*
-             * Case 2a. n is an internal node, and the root of the
-             * subtree to the left of e has more than one element.
-             * So find the predecessor p to e (ie the largest node
-             * in that subtree), place it where e currently is, and
-             * then start the deletion process over again on the
-             * subtree with p as target.
-             */
-            node234 *m = n->kids[ei];
-            void *target;
-            LOG(("  case 2a\n"));
-            while (m->kids[0]) {
-                m = (m->kids[3] ? m->kids[3] :
-                     m->kids[2] ? m->kids[2] :
-                     m->kids[1] ? m->kids[1] : m->kids[0]);
-            }
-            target = (m->elems[2] ? m->elems[2] :
-                      m->elems[1] ? m->elems[1] : m->elems[0]);
-            n->elems[ei] = target;
-            index = n->counts[ei] - 1;
-            n = n->kids[ei];
-        } else if (n->kids[ei + 1]->elems[1]) {
-            /*
-             * Case 2b, symmetric to 2a but s/left/right/ and
-             * s/predecessor/successor/. (And s/largest/smallest/).
-             */
-            node234 *m = n->kids[ei + 1];
-            void *target;
-            LOG(("  case 2b\n"));
-            while (m->kids[0]) {
-                m = m->kids[0];
-            }
-            target = m->elems[0];
-            n->elems[ei] = target;
-            n = n->kids[ei + 1];
-            index = 0;
-        } else {
-            /*
-             * Case 2c. n is an internal node, and the subtrees to
-             * the left and right of e both have only one element.
-             * So combine the two subnodes into a single big node
-             * with their own elements on the left and right and e
-             * in the middle, then restart the deletion process on
-             * that subtree, with e still as target.
-             */
-            node234 *a = n->kids[ei], *b = n->kids[ei + 1];
-            int j;
-
-            LOG(("  case 2c\n"));
-            a->elems[1] = n->elems[ei];
-            a->kids[2] = b->kids[0];
-            a->counts[2] = b->counts[0];
-            if (a->kids[2])
-                a->kids[2]->parent = a;
-            a->elems[2] = b->elems[0];
-            a->kids[3] = b->kids[1];
-            a->counts[3] = b->counts[1];
-            if (a->kids[3])
-                a->kids[3]->parent = a;
-            sfree(b);
-            n->counts[ei] = countnode234(a);
-            /*
-             * That's built the big node in a, and destroyed b. Now
-             * remove the reference to b (and e) in n.
-             */
-            for (j = ei; j < 2 && n->elems[j + 1]; j++) {
-                n->elems[j] = n->elems[j + 1];
-                n->kids[j + 1] = n->kids[j + 2];
-                n->counts[j + 1] = n->counts[j + 2];
-            }
-            n->elems[j] = NULL;
-            n->kids[j + 1] = NULL;
-            n->counts[j + 1] = 0;
-            /*
-             * It's possible, in this case, that we've just removed
-             * the only element in the root of the tree. If so,
-             * shift the root.
-             */
-            if (n->elems[0] == NULL) {
-                LOG(("  shifting root!\n"));
-                t->root = a;
-                a->parent = NULL;
-                sfree(n);
-            }
-            /*
-             * Now go round the deletion process again, with n
-             * pointing at the new big node and e still the same.
-             */
-            n = a;
-            index = a->counts[0] + a->counts[1] + 1;
-        }
-    }
-}
-void *delpos234(tree234 * t, int index)
-{
-    if (index < 0 || index >= countnode234(t->root))
-        return NULL;
-    return delpos234_internal(t, index);
-}
-void *del234(tree234 * t, void *e)
-{
-    int index;
-    if (!findrelpos234(t, e, NULL, REL234_EQ, &index))
-        return NULL;                   /* it wasn't in there anyway */
-    return delpos234_internal(t, index);        /* it's there; delete it. */
-}
-
-#ifdef TEST
-
-/*
- * Test code for the 2-3-4 tree. This code maintains an alternative
- * representation of the data in the tree, in an array (using the
- * obvious and slow insert and delete functions). After each tree
- * operation, the verify() function is called, which ensures all
- * the tree properties are preserved:
- *  - node->child->parent always equals node
- *  - tree->root->parent always equals NULL
- *  - number of kids == 0 or number of elements + 1;
- *  - tree has the same depth everywhere
- *  - every node has at least one element
- *  - subtree element counts are accurate
- *  - any NULL kid pointer is accompanied by a zero count
- *  - in a sorted tree: ordering property between elements of a
- *    node and elements of its children is preserved
- * and also ensures the list represented by the tree is the same
- * list it should be. (This last check also doubly verifies the
- * ordering properties, because the `same list it should be' is by
- * definition correctly ordered. It also ensures all nodes are
- * distinct, because the enum functions would get caught in a loop
- * if not.)
- */
-
-#include <stdarg.h>
-#include <string.h>
-
-int n_errors = 0;
-
-/*
- * Error reporting function.
- */
-PRINTF_LIKE(1, 2) void error(char *fmt, ...)
-{
-    va_list ap;
-    printf("ERROR: ");
-    va_start(ap, fmt);
-    vfprintf(stdout, fmt, ap);
-    va_end(ap);
-    printf("\n");
-    n_errors++;
-}
-
-/* The array representation of the data. */
-void **array;
-int arraylen, arraysize;
-cmpfn234 cmp;
-
-/* The tree representation of the same data. */
-tree234 *tree;
-
-typedef struct {
-    int treedepth;
-    int elemcount;
-} chkctx;
-
-int chknode(chkctx * ctx, int level, node234 * node,
-            void *lowbound, void *highbound)
-{
-    int nkids, nelems;
-    int i;
-    int count;
-
-    /* Count the non-NULL kids. */
-    for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++);
-    /* Ensure no kids beyond the first NULL are non-NULL. */
-    for (i = nkids; i < 4; i++)
-        if (node->kids[i]) {
-            error("node %p: nkids=%d but kids[%d] non-NULL",
-                  node, nkids, i);
-        } else if (node->counts[i]) {
-            error("node %p: kids[%d] NULL but count[%d]=%d nonzero",
-                  node, i, i, node->counts[i]);
-        }
-
-    /* Count the non-NULL elements. */
-    for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++);
-    /* Ensure no elements beyond the first NULL are non-NULL. */
-    for (i = nelems; i < 3; i++)
-        if (node->elems[i]) {
-            error("node %p: nelems=%d but elems[%d] non-NULL",
-                  node, nelems, i);
-        }
-
-    if (nkids == 0) {
-        /*
-         * If nkids==0, this is a leaf node; verify that the tree
-         * depth is the same everywhere.
-         */
-        if (ctx->treedepth < 0)
-            ctx->treedepth = level;    /* we didn't know the depth yet */
-        else if (ctx->treedepth != level)
-            error("node %p: leaf at depth %d, previously seen depth %d",
-                  node, level, ctx->treedepth);
-    } else {
-        /*
-         * If nkids != 0, then it should be nelems+1, unless nelems
-         * is 0 in which case nkids should also be 0 (and so we
-         * shouldn't be in this condition at all).
-         */
-        int shouldkids = (nelems ? nelems + 1 : 0);
-        if (nkids != shouldkids) {
-            error("node %p: %d elems should mean %d kids but has %d",
-                  node, nelems, shouldkids, nkids);
-        }
-    }
-
-    /*
-     * nelems should be at least 1.
-     */
-    if (nelems == 0) {
-        error("node %p: no elems", node, nkids);
-    }
-
-    /*
-     * Add nelems to the running element count of the whole tree.
-     */
-    ctx->elemcount += nelems;
-
-    /*
-     * Check ordering property: all elements should be strictly >
-     * lowbound, strictly < highbound, and strictly < each other in
-     * sequence. (lowbound and highbound are NULL at edges of tree
-     * - both NULL at root node - and NULL is considered to be <
-     * everything and > everything. IYSWIM.)
-     */
-    if (cmp) {
-        for (i = -1; i < nelems; i++) {
-            void *lower = (i == -1 ? lowbound : node->elems[i]);
-            void *higher =
-                (i + 1 == nelems ? highbound : node->elems[i + 1]);
-            if (lower && higher && cmp(lower, higher) >= 0) {
-                error("node %p: kid comparison [%d=%s,%d=%s] failed",
-                      node, i, lower, i + 1, higher);
-            }
-        }
-    }
-
-    /*
-     * Check parent pointers: all non-NULL kids should have a
-     * parent pointer coming back to this node.
-     */
-    for (i = 0; i < nkids; i++)
-        if (node->kids[i]->parent != node) {
-            error("node %p kid %d: parent ptr is %p not %p",
-                  node, i, node->kids[i]->parent, node);
-        }
-
-
-    /*
-     * Now (finally!) recurse into subtrees.
-     */
-    count = nelems;
-
-    for (i = 0; i < nkids; i++) {
-        void *lower = (i == 0 ? lowbound : node->elems[i - 1]);
-        void *higher = (i >= nelems ? highbound : node->elems[i]);
-        int subcount =
-            chknode(ctx, level + 1, node->kids[i], lower, higher);
-        if (node->counts[i] != subcount) {
-            error("node %p kid %d: count says %d, subtree really has %d",
-                  node, i, node->counts[i], subcount);
-        }
-        count += subcount;
-    }
-
-    return count;
-}
-
-void verify(void)
-{
-    chkctx ctx[1];
-    int i;
-    void *p;
-
-    ctx->treedepth = -1;                /* depth unknown yet */
-    ctx->elemcount = 0;                 /* no elements seen yet */
-    /*
-     * Verify validity of tree properties.
-     */
-    if (tree->root) {
-        if (tree->root->parent != NULL)
-            error("root->parent is %p should be null", tree->root->parent);
-        chknode(&ctx, 0, tree->root, NULL, NULL);
-    }
-    printf("tree depth: %d\n", ctx->treedepth);
-    /*
-     * Enumerate the tree and ensure it matches up to the array.
-     */
-    for (i = 0; NULL != (p = index234(tree, i)); i++) {
-        if (i >= arraylen)
-            error("tree contains more than %d elements", arraylen);
-        if (array[i] != p)
-            error("enum at position %d: array says %s, tree says %s",
-                  i, array[i], p);
-    }
-    if (ctx->elemcount != i) {
-        error("tree really contains %d elements, enum gave %d",
-              ctx->elemcount, i);
-    }
-    if (i < arraylen) {
-        error("enum gave only %d elements, array has %d", i, arraylen);
-    }
-    i = count234(tree);
-    if (ctx->elemcount != i) {
-        error("tree really contains %d elements, count234 gave %d",
-              ctx->elemcount, i);
-    }
-}
-
-void internal_addtest(void *elem, int index, void *realret)
-{
-    int i, j;
-    void *retval;
-
-    if (arraysize < arraylen + 1) {
-        arraysize = arraylen + 1 + 256;
-        array = sresize(array, arraysize, void *);
-    }
-
-    i = index;
-    /* now i points to the first element >= elem */
-    retval = elem;                     /* expect elem returned (success) */
-    for (j = arraylen; j > i; j--)
-        array[j] = array[j - 1];
-    array[i] = elem;                   /* add elem to array */
-    arraylen++;
-
-    if (realret != retval) {
-        error("add: retval was %p expected %p", realret, retval);
-    }
-
-    verify();
-}
-
-void addtest(void *elem)
-{
-    int i;
-    void *realret;
-
-    realret = add234(tree, elem);
-
-    i = 0;
-    while (i < arraylen && cmp(elem, array[i]) > 0)
-        i++;
-    if (i < arraylen && !cmp(elem, array[i])) {
-        void *retval = array[i];       /* expect that returned not elem */
-        if (realret != retval) {
-            error("add: retval was %p expected %p", realret, retval);
-        }
-    } else
-        internal_addtest(elem, i, realret);
-}
-
-void addpostest(void *elem, int i)
-{
-    void *realret;
-
-    realret = addpos234(tree, elem, i);
-
-    internal_addtest(elem, i, realret);
-}
-
-void delpostest(int i)
-{
-    int index = i;
-    void *elem = array[i], *ret;
-
-    /* i points to the right element */
-    while (i < arraylen - 1) {
-        array[i] = array[i + 1];
-        i++;
-    }
-    arraylen--;                        /* delete elem from array */
-
-    if (tree->cmp)
-        ret = del234(tree, elem);
-    else
-        ret = delpos234(tree, index);
-
-    if (ret != elem) {
-        error("del returned %p, expected %p", ret, elem);
-    }
-
-    verify();
-}
-
-void deltest(void *elem)
-{
-    int i;
-
-    i = 0;
-    while (i < arraylen && cmp(elem, array[i]) > 0)
-        i++;
-    if (i >= arraylen || cmp(elem, array[i]) != 0)
-        return;                        /* don't do it! */
-    delpostest(i);
-}
-
-/* A sample data set and test utility. Designed for pseudo-randomness,
- * and yet repeatability. */
-
-/*
- * This random number generator uses the `portable implementation'
- * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits;
- * change it if not.
- */
-int randomnumber(unsigned *seed)
-{
-    *seed *= 1103515245;
-    *seed += 12345;
-    return ((*seed) / 65536) % 32768;
-}
-
-int mycmp(void *av, void *bv)
-{
-    char const *a = (char const *) av;
-    char const *b = (char const *) bv;
-    return strcmp(a, b);
-}
-
-#define lenof(x) ( sizeof((x)) / sizeof(*(x)) )
-
-char *strings[] = {
-    "a", "ab", "absque", "coram", "de",
-    "palam", "clam", "cum", "ex", "e",
-    "sine", "tenus", "pro", "prae",
-    "banana", "carrot", "cabbage", "broccoli", "onion", "zebra",
-    "penguin", "blancmange", "pangolin", "whale", "hedgehog",
-    "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux",
-    "murfl", "spoo", "breen", "flarn", "octothorpe",
-    "snail", "tiger", "elephant", "octopus", "warthog", "armadillo",
-    "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin",
-    "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper",
-    "wand", "ring", "amulet"
-};
-
-#define NSTR lenof(strings)
-
-int findtest(void)
-{
-    const static int rels[] = {
-        REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT
-    };
-    const static char *const relnames[] = {
-        "EQ", "GE", "LE", "LT", "GT"
-    };
-    int i, j, rel, index;
-    char *p, *ret, *realret, *realret2;
-    int lo, hi, mid, c;
-
-    for (i = 0; i < NSTR; i++) {
-        p = strings[i];
-        for (j = 0; j < sizeof(rels) / sizeof(*rels); j++) {
-            rel = rels[j];
-
-            lo = 0;
-            hi = arraylen - 1;
-            while (lo <= hi) {
-                mid = (lo + hi) / 2;
-                c = strcmp(p, array[mid]);
-                if (c < 0)
-                    hi = mid - 1;
-                else if (c > 0)
-                    lo = mid + 1;
-                else
-                    break;
-            }
-
-            if (c == 0) {
-                if (rel == REL234_LT)
-                    ret = (mid > 0 ? array[--mid] : NULL);
-                else if (rel == REL234_GT)
-                    ret = (mid < arraylen - 1 ? array[++mid] : NULL);
-                else
-                    ret = array[mid];
-            } else {
-                assert(lo == hi + 1);
-                if (rel == REL234_LT || rel == REL234_LE) {
-                    mid = hi;
-                    ret = (hi >= 0 ? array[hi] : NULL);
-                } else if (rel == REL234_GT || rel == REL234_GE) {
-                    mid = lo;
-                    ret = (lo < arraylen ? array[lo] : NULL);
-                } else
-                    ret = NULL;
-            }
-
-            realret = findrelpos234(tree, p, NULL, rel, &index);
-            if (realret != ret) {
-                error("find(\"%s\",%s) gave %s should be %s",
-                      p, relnames[j], realret, ret);
-            }
-            if (realret && index != mid) {
-                error("find(\"%s\",%s) gave %d should be %d",
-                      p, relnames[j], index, mid);
-            }
-            if (realret && rel == REL234_EQ) {
-                realret2 = index234(tree, index);
-                if (realret2 != realret) {
-                    error("find(\"%s\",%s) gave %s(%d) but %d -> %s",
-                          p, relnames[j], realret, index, index, realret2);
-                }
-            }
-#if 0
-            printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j],
-                   realret, index);
-#endif
-        }
-    }
-
-    realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index);
-    if (arraylen && (realret != array[0] || index != 0)) {
-        error("find(NULL,GT) gave %s(%d) should be %s(0)",
-              realret, index, array[0]);
-    } else if (!arraylen && (realret != NULL)) {
-        error("find(NULL,GT) gave %s(%d) should be NULL", realret, index);
-    }
-
-    realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index);
-    if (arraylen
-        && (realret != array[arraylen - 1] || index != arraylen - 1)) {
-        error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index,
-              array[arraylen - 1]);
-    } else if (!arraylen && (realret != NULL)) {
-        error("find(NULL,LT) gave %s(%d) should be NULL", realret, index);
-    }
-}
-
-void searchtest_recurse(search234_state ss, int lo, int hi,
-                        char **expected, char *directionbuf,
-                        char *directionptr)
-{
-    *directionptr = '\0';
-
-    if (!ss.element) {
-        if (lo != hi) {
-            error("search234(%s) gave NULL for non-empty interval [%d,%d)",
-                  directionbuf, lo, hi);
-        } else if (ss.index != lo) {
-            error("search234(%s) gave index %d should be %d",
-                  directionbuf, ss.index, lo);
-        } else {
-            printf("%*ssearch234(%s) gave NULL,%d\n",
-                   (int)(directionptr-directionbuf) * 2, "", directionbuf,
-                   ss.index);
-        }
-    } else if (lo == hi) {
-        error("search234(%s) gave %s for empty interval [%d,%d)",
-              directionbuf, (char *)ss.element, lo, hi);
-    } else if (ss.element != expected[ss.index]) {
-        error("search234(%s) gave element %s should be %s",
-              directionbuf, (char *)ss.element, expected[ss.index]);
-    } else if (ss.index < lo || ss.index >= hi) {
-        error("search234(%s) gave index %d should be in [%d,%d)",
-              directionbuf, ss.index, lo, hi);
-        return;
-    } else {
-        search234_state next;
-
-        printf("%*ssearch234(%s) gave %s,%d\n",
-               (int)(directionptr-directionbuf) * 2, "", directionbuf,
-               (char *)ss.element, ss.index);
-
-        next = ss;
-        search234_step(&next, -1);
-        *directionptr = '-';
-        searchtest_recurse(next, lo, ss.index,
-                           expected, directionbuf, directionptr+1);
-
-        next = ss;
-        search234_step(&next, +1);
-        *directionptr = '+';
-        searchtest_recurse(next, ss.index+1, hi,
-                           expected, directionbuf, directionptr+1);
-    }
-}
-
-void searchtest(void)
-{
-    char *expected[NSTR], *p;
-    char directionbuf[NSTR * 10];
-    int n;
-    search234_state ss;
-
-    printf("beginning searchtest:");
-    for (n = 0; (p = index234(tree, n)) != NULL; n++) {
-        expected[n] = p;
-        printf(" %d=%s", n, p);
-    }
-    printf(" count=%d\n", n);
-
-    search234_start(&ss, tree);
-    searchtest_recurse(ss, 0, n, expected, directionbuf, directionbuf);
-}
-
-int main(void)
-{
-    int in[NSTR];
-    int i, j, k;
-    unsigned seed = 0;
-
-    for (i = 0; i < NSTR; i++)
-        in[i] = 0;
-    array = NULL;
-    arraylen = arraysize = 0;
-    tree = newtree234(mycmp);
-    cmp = mycmp;
-
-    verify();
-    searchtest();
-    for (i = 0; i < 10000; i++) {
-        j = randomnumber(&seed);
-        j %= NSTR;
-        printf("trial: %d\n", i);
-        if (in[j]) {
-            printf("deleting %s (%d)\n", strings[j], j);
-            deltest(strings[j]);
-            in[j] = 0;
-        } else {
-            printf("adding %s (%d)\n", strings[j], j);
-            addtest(strings[j]);
-            in[j] = 1;
-        }
-        findtest();
-        searchtest();
-    }
-
-    while (arraylen > 0) {
-        j = randomnumber(&seed);
-        j %= arraylen;
-        deltest(array[j]);
-    }
-
-    freetree234(tree);
-
-    /*
-     * Now try an unsorted tree. We don't really need to test
-     * delpos234 because we know del234 is based on it, so it's
-     * already been tested in the above sorted-tree code; but for
-     * completeness we'll use it to tear down our unsorted tree
-     * once we've built it.
-     */
-    tree = newtree234(NULL);
-    cmp = NULL;
-    verify();
-    for (i = 0; i < 1000; i++) {
-        printf("trial: %d\n", i);
-        j = randomnumber(&seed);
-        j %= NSTR;
-        k = randomnumber(&seed);
-        k %= count234(tree) + 1;
-        printf("adding string %s at index %d\n", strings[j], k);
-        addpostest(strings[j], k);
-    }
-    while (count234(tree) > 0) {
-        printf("cleanup: tree size %d\n", count234(tree));
-        j = randomnumber(&seed);
-        j %= count234(tree);
-        printf("deleting string %s from index %d\n",
-               (const char *)array[j], j);
-        delpostest(j);
-    }
-
-    printf("%d errors found\n", n_errors);
-    return (n_errors != 0);
-}
-
-#endif

+ 0 - 1139
source/putty/utils.c

@@ -1,1139 +0,0 @@
-/*
- * Platform-independent utility routines used throughout this code base.
- *
- * This file is linked into stand-alone test utilities which only want
- * to include the things they really need, so functions in here should
- * avoid depending on any functions outside it. Utility routines that
- * are more tightly integrated into the main code should live in
- * misc.c.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <ctype.h>
-#include <assert.h>
-
-#include "defs.h"
-#include "misc.h"
-#include "ssh.h"
-
-/*
- * Parse a string block size specification. This is approximately a
- * subset of the block size specs supported by GNU fileutils:
- *  "nk" = n kilobytes
- *  "nM" = n megabytes
- *  "nG" = n gigabytes
- * All numbers are decimal, and suffixes refer to powers of two.
- * Case-insensitive.
- */
-unsigned long parse_blocksize(const char *bs)
-{
-    char *suf;
-    unsigned long r = strtoul(bs, &suf, 10);
-    if (*suf != '\0') {
-        while (*suf && isspace((unsigned char)*suf)) suf++;
-        switch (*suf) {
-          case 'k': case 'K':
-            r *= 1024ul;
-            break;
-          case 'm': case 'M':
-            r *= 1024ul * 1024ul;
-            break;
-          case 'g': case 'G':
-            r *= 1024ul * 1024ul * 1024ul;
-            break;
-          case '\0':
-          default:
-            break;
-        }
-    }
-    return r;
-}
-
-/*
- * Parse a ^C style character specification.
- * Returns NULL in `next' if we didn't recognise it as a control character,
- * in which case `c' should be ignored.
- * The precise current parsing is an oddity inherited from the terminal
- * answerback-string parsing code. All sequences start with ^; all except
- * ^<123> are two characters. The ones that are worth keeping are probably:
- *   ^?             127
- *   ^@A-Z[\]^_     0-31
- *   a-z            1-26
- *   <num>          specified by number (decimal, 0octal, 0xHEX)
- *   ~              ^ escape
- */
-char ctrlparse(char *s, char **next)
-{
-    char c = 0;
-    if (*s != '^') {
-        *next = NULL;
-    } else {
-        s++;
-        if (*s == '\0') {
-            *next = NULL;
-        } else if (*s == '<') {
-            s++;
-            c = (char)strtol(s, next, 0);
-            if ((*next == s) || (**next != '>')) {
-                c = 0;
-                *next = NULL;
-            } else
-                (*next)++;
-        } else if (*s >= 'a' && *s <= 'z') {
-            c = (*s - ('a' - 1));
-            *next = s+1;
-        } else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) {
-            c = ('@' ^ *s);
-            *next = s+1;
-        } else if (*s == '~') {
-            c = '^';
-            *next = s+1;
-        }
-    }
-    return c;
-}
-
-/*
- * Find a character in a string, unless it's a colon contained within
- * square brackets. Used for untangling strings of the form
- * 'host:port', where host can be an IPv6 literal.
- *
- * We provide several variants of this function, with semantics like
- * various standard string.h functions.
- */
-static const char *host_strchr_internal(const char *s, const char *set,
-                                        bool first)
-{
-    int brackets = 0;
-    const char *ret = NULL;
-
-    while (1) {
-        if (!*s)
-            return ret;
-
-        if (*s == '[')
-            brackets++;
-        else if (*s == ']' && brackets > 0)
-            brackets--;
-        else if (brackets && *s == ':')
-            /* never match */ ;
-        else if (strchr(set, *s)) {
-            ret = s;
-            if (first)
-                return ret;
-        }
-
-        s++;
-    }
-}
-size_t host_strcspn(const char *s, const char *set)
-{
-    const char *answer = host_strchr_internal(s, set, true);
-    if (answer)
-        return answer - s;
-    else
-        return strlen(s);
-}
-char *host_strchr(const char *s, int c)
-{
-    char set[2];
-    set[0] = c;
-    set[1] = '\0';
-    return (char *) host_strchr_internal(s, set, true);
-}
-char *host_strrchr(const char *s, int c)
-{
-    char set[2];
-    set[0] = c;
-    set[1] = '\0';
-    return (char *) host_strchr_internal(s, set, false);
-}
-
-#ifdef TEST_HOST_STRFOO
-int main(void)
-{
-    int passes = 0, fails = 0;
-
-#define TEST1(func, string, arg2, suffix, result) do                    \
-    {                                                                   \
-        const char *str = string;                                       \
-        unsigned ret = func(string, arg2) suffix;                       \
-        if (ret == result) {                                            \
-            passes++;                                                   \
-        } else {                                                        \
-            printf("fail: %s(%s,%s)%s = %u, expected %u\n",             \
-                   #func, #string, #arg2, #suffix, ret,                 \
-                   (unsigned)result);                                   \
-            fails++;                                                    \
-        }                                                               \
-} while (0)
-
-    TEST1(host_strchr, "[1:2:3]:4:5", ':', -str, 7);
-    TEST1(host_strrchr, "[1:2:3]:4:5", ':', -str, 9);
-    TEST1(host_strcspn, "[1:2:3]:4:5", "/:",, 7);
-    TEST1(host_strchr, "[1:2:3]", ':', == NULL, 1);
-    TEST1(host_strrchr, "[1:2:3]", ':', == NULL, 1);
-    TEST1(host_strcspn, "[1:2:3]", "/:",, 7);
-    TEST1(host_strcspn, "[1:2/3]", "/:",, 4);
-    TEST1(host_strcspn, "[1:2:3]/", "/:",, 7);
-
-    printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
-    return fails != 0 ? 1 : 0;
-}
-
-/* Stubs to stop the rest of this module causing compile failures. */
-static NORETURN void fatal_error(const char *p, ...)
-{
-    va_list ap;
-    fprintf(stderr, "host_string_test: ");
-    va_start(ap, p);
-    vfprintf(stderr, p, ap);
-    va_end(ap);
-    fputc('\n', stderr);
-    exit(1);
-}
-
-void out_of_memory(void) { fatal_error("out of memory"); }
-
-#endif /* TEST_HOST_STRFOO */
-
-/*
- * Trim square brackets off the outside of an IPv6 address literal.
- * Leave all other strings unchanged. Returns a fresh dynamically
- * allocated string.
- */
-char *host_strduptrim(const char *s)
-{
-    if (s[0] == '[') {
-        const char *p = s+1;
-        int colons = 0;
-        while (*p && *p != ']') {
-            if (isxdigit((unsigned char)*p))
-                /* OK */;
-            else if (*p == ':')
-                colons++;
-            else
-                break;
-            p++;
-        }
-        if (*p == '%') {
-            /*
-             * This delimiter character introduces an RFC 4007 scope
-             * id suffix (e.g. suffixing the address literal with
-             * %eth1 or %2 or some such). There's no syntax
-             * specification for the scope id, so just accept anything
-             * except the closing ].
-             */
-            p += strcspn(p, "]");
-        }
-        if (*p == ']' && !p[1] && colons > 1) {
-            /*
-             * This looks like an IPv6 address literal (hex digits and
-             * at least two colons, plus optional scope id, contained
-             * in square brackets). Trim off the brackets.
-             */
-            return dupprintf("%.*s", (int)(p - (s+1)), s+1);
-        }
-    }
-
-    /*
-     * Any other shape of string is simply duplicated.
-     */
-    return dupstr(s);
-}
-
-/* ----------------------------------------------------------------------
- * String handling routines.
- */
-
-char *dupstr(const char *s)
-{
-    char *p = NULL;
-    if (s) {
-        int len = strlen(s);
-        p = snewn(len + 1, char);
-        strcpy(p, s);
-    }
-    return p;
-}
-
-/* Allocate the concatenation of N strings. Terminate arg list with NULL. */
-char *dupcat_fn(const char *s1, ...)
-{
-    int len;
-    char *p, *q, *sn;
-    va_list ap;
-
-    len = strlen(s1);
-    va_start(ap, s1);
-    while (1) {
-        sn = va_arg(ap, char *);
-        if (!sn)
-            break;
-        len += strlen(sn);
-    }
-    va_end(ap);
-
-    p = snewn(len + 1, char);
-    strcpy(p, s1);
-    q = p + strlen(p);
-
-    va_start(ap, s1);
-    while (1) {
-        sn = va_arg(ap, char *);
-        if (!sn)
-            break;
-        strcpy(q, sn);
-        q += strlen(q);
-    }
-    va_end(ap);
-
-    return p;
-}
-
-void burnstr(char *string)             /* sfree(str), only clear it first */
-{
-    if (string) {
-        smemclr(string, strlen(string));
-        sfree(string);
-    }
-}
-
-int string_length_for_printf(size_t s)
-{
-    /* Truncate absurdly long strings (should one show up) to fit
-     * within a positive 'int', which is what the "%.*s" format will
-     * expect. */
-    if (s > INT_MAX)
-        return INT_MAX;
-    return s;
-}
-
-/* Work around lack of va_copy in old MSC */
-#if (defined _MSC_VER || defined WINSCP) && !defined va_copy
-#define va_copy(a, b) TYPECHECK(                        \
-        (va_list *)0 == &(a) && (va_list *)0 == &(b),   \
-        memcpy(&a, &b, sizeof(va_list)))
-#endif
-
-/* Also lack of vsnprintf before VS2015 */
-#if defined _WINDOWS && \
-    !defined __MINGW32__ && \
-    !defined __WINE__ && \
-    _MSC_VER < 1900
-#define vsnprintf _vsnprintf
-#endif
-
-/*
- * Do an sprintf(), but into a custom-allocated buffer.
- *
- * Currently I'm doing this via vsnprintf. This has worked so far,
- * but it's not good, because vsnprintf is not available on all
- * platforms. There's an ifdef to use `_vsnprintf', which seems
- * to be the local name for it on Windows. Other platforms may
- * lack it completely, in which case it'll be time to rewrite
- * this function in a totally different way.
- *
- * The only `properly' portable solution I can think of is to
- * implement my own format string scanner, which figures out an
- * upper bound for the length of each formatting directive,
- * allocates the buffer as it goes along, and calls sprintf() to
- * actually process each directive. If I ever need to actually do
- * this, some caveats:
- *
- *  - It's very hard to find a reliable upper bound for
- *    floating-point values. %f, in particular, when supplied with
- *    a number near to the upper or lower limit of representable
- *    numbers, could easily take several hundred characters. It's
- *    probably feasible to predict this statically using the
- *    constants in <float.h>, or even to predict it dynamically by
- *    looking at the exponent of the specific float provided, but
- *    it won't be fun.
- *
- *  - Don't forget to _check_, after calling sprintf, that it's
- *    used at most the amount of space we had available.
- *
- *  - Fault any formatting directive we don't fully understand. The
- *    aim here is to _guarantee_ that we never overflow the buffer,
- *    because this is a security-critical function. If we see a
- *    directive we don't know about, we should panic and die rather
- *    than run any risk.
- */
-static char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
-                              const char *fmt, va_list ap)
-{
-    size_t size = *sizeptr;
-    sgrowarrayn_nm(buf, size, oldlen, 512);
-
-    while (1) {
-        va_list aq;
-        va_copy(aq, ap);
-        { // WINSCP
-#if defined _DEBUG && defined IDE
-// CodeGuard hangs in v*printf functions. But while it's possible to disable CodeGuard in vsprintf, it's not possible for vsnprintf.
-// We never want to distribute this version of the code, hence the IDE condition.
-// Put this into WinSCP.cgi along with WinSCP.exe
-// [vsprintf]
-// Disable=yes
-        int len = vsprintf(buf + oldlen, fmt, aq);
-#else
-        int len = vsnprintf(buf + oldlen, size - oldlen, fmt, aq);
-#endif
-        va_end(aq);
-
-        if (len >= 0 && len < size) {
-            /* This is the C99-specified criterion for snprintf to have
-             * been completely successful. */
-            *sizeptr = size;
-            return buf;
-        } else if (len > 0) {
-            /* This is the C99 error condition: the returned length is
-             * the required buffer size not counting the NUL. */
-            sgrowarrayn_nm(buf, size, oldlen + 1, len);
-        } else {
-            /* This is the pre-C99 glibc error condition: <0 means the
-             * buffer wasn't big enough, so we enlarge it a bit and hope. */
-            sgrowarray_nm(buf, size, size);
-        }
-        } // WINSCP
-    }
-}
-
-char *dupvprintf(const char *fmt, va_list ap)
-{
-    size_t size = 0;
-    return dupvprintf_inner(NULL, 0, &size, fmt, ap);
-}
-char *dupprintf(const char *fmt, ...)
-{
-    char *ret;
-    va_list ap;
-    va_start(ap, fmt);
-    ret = dupvprintf(fmt, ap);
-    va_end(ap);
-    return ret;
-}
-
-struct strbuf_impl {
-    size_t size;
-    struct strbuf visible;
-    bool nm;          /* true if we insist on non-moving buffer resizes */
-};
-
-#define STRBUF_SET_UPTR(buf)                                    \
-    ((buf)->visible.u = (unsigned char *)(buf)->visible.s)
-#define STRBUF_SET_PTR(buf, ptr)                                \
-    ((buf)->visible.s = (ptr), STRBUF_SET_UPTR(buf))
-
-void *strbuf_append(strbuf *buf_o, size_t len)
-{
-    struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
-    char *toret;
-    sgrowarray_general(
-        buf->visible.s, buf->size, buf->visible.len + 1, len, buf->nm);
-    STRBUF_SET_UPTR(buf);
-    toret = buf->visible.s + buf->visible.len;
-    buf->visible.len += len;
-    buf->visible.s[buf->visible.len] = '\0';
-    return toret;
-}
-
-void strbuf_shrink_to(strbuf *buf, size_t new_len)
-{
-    assert(new_len <= buf->len);
-    buf->len = new_len;
-    buf->s[buf->len] = '\0';
-}
-
-void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove)
-{
-    assert(amount_to_remove <= buf->len);
-    buf->len -= amount_to_remove;
-    buf->s[buf->len] = '\0';
-}
-
-bool strbuf_chomp(strbuf *buf, char char_to_remove)
-{
-    if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) {
-        strbuf_shrink_by(buf, 1);
-        return true;
-    }
-    return false;
-}
-
-static void strbuf_BinarySink_write(
-    BinarySink *bs, const void *data, size_t len)
-{
-    strbuf *buf_o = BinarySink_DOWNCAST(bs, strbuf);
-    memcpy(strbuf_append(buf_o, len), data, len);
-}
-
-static strbuf *strbuf_new_general(bool nm)
-{
-    struct strbuf_impl *buf = snew(struct strbuf_impl);
-    BinarySink_INIT(&buf->visible, strbuf_BinarySink_write);
-    buf->visible.len = 0;
-    buf->size = 512;
-    buf->nm = nm;
-    STRBUF_SET_PTR(buf, snewn(buf->size, char));
-    *buf->visible.s = '\0';
-    return &buf->visible;
-}
-strbuf *strbuf_new(void) { return strbuf_new_general(false); }
-strbuf *strbuf_new_nm(void) { return strbuf_new_general(true); }
-void strbuf_free(strbuf *buf_o)
-{
-    struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
-    if (buf->visible.s) {
-        smemclr(buf->visible.s, buf->size);
-        sfree(buf->visible.s);
-    }
-    sfree(buf);
-}
-char *strbuf_to_str(strbuf *buf_o)
-{
-    struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
-    char *ret = buf->visible.s;
-    sfree(buf);
-    return ret;
-}
-void strbuf_catfv(strbuf *buf_o, const char *fmt, va_list ap)
-{
-    struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
-    STRBUF_SET_PTR(buf, dupvprintf_inner(buf->visible.s, buf->visible.len,
-                                         &buf->size, fmt, ap));
-    buf->visible.len += strlen(buf->visible.s + buf->visible.len);
-}
-void strbuf_catf(strbuf *buf_o, const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    strbuf_catfv(buf_o, fmt, ap);
-    va_end(ap);
-}
-
-strbuf *strbuf_new_for_agent_query(void)
-{
-    strbuf *buf = strbuf_new();
-    strbuf_append(buf, 4);
-    return buf;
-}
-void strbuf_finalise_agent_query(strbuf *buf_o)
-{
-    struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
-    assert(buf->visible.len >= 5);
-    PUT_32BIT_MSB_FIRST(buf->visible.u, buf->visible.len - 4);
-}
-
-/*
- * Read an entire line of text from a file. Return a buffer
- * malloced to be as big as necessary (caller must free).
- */
-char *fgetline(FILE *fp)
-{
-    char *ret = snewn(512, char);
-    size_t size = 512, len = 0;
-    while (fgets(ret + len, size - len, fp)) {
-        len += strlen(ret + len);
-        if (len > 0 && ret[len-1] == '\n')
-            break;                     /* got a newline, we're done */
-        sgrowarrayn_nm(ret, size, len, 512);
-    }
-    if (len == 0) {                    /* first fgets returned NULL */
-        sfree(ret);
-        return NULL;
-    }
-    ret[len] = '\0';
-    return ret;
-}
-
-/*
- * Read an entire file into a BinarySink.
- */
-bool read_file_into(BinarySink *bs, FILE *fp)
-{
-    char buf[4096];
-    while (1) {
-        size_t retd = fread(buf, 1, sizeof(buf), fp);
-        if (retd == 0)
-            return !ferror(fp);
-        put_data(bs, buf, retd);
-    }
-}
-
-/*
- * Perl-style 'chomp', for a line we just read with fgetline. Unlike
- * Perl chomp, however, we're deliberately forgiving of strange
- * line-ending conventions. Also we forgive NULL on input, so you can
- * just write 'line = chomp(fgetline(fp));' and not bother checking
- * for NULL until afterwards.
- */
-char *chomp(char *str)
-{
-    if (str) {
-        int len = strlen(str);
-        while (len > 0 && (str[len-1] == '\r' || str[len-1] == '\n'))
-            len--;
-        str[len] = '\0';
-    }
-    return str;
-}
-
-/* ----------------------------------------------------------------------
- * Core base64 encoding and decoding routines.
- */
-
-void base64_encode_atom(const unsigned char *data, int n, char *out)
-{
-    static const char base64_chars[] =
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-    unsigned word;
-
-    word = data[0] << 16;
-    if (n > 1)
-        word |= data[1] << 8;
-    if (n > 2)
-        word |= data[2];
-    out[0] = base64_chars[(word >> 18) & 0x3F];
-    out[1] = base64_chars[(word >> 12) & 0x3F];
-    if (n > 1)
-        out[2] = base64_chars[(word >> 6) & 0x3F];
-    else
-        out[2] = '=';
-    if (n > 2)
-        out[3] = base64_chars[word & 0x3F];
-    else
-        out[3] = '=';
-}
-
-int base64_decode_atom(const char *atom, unsigned char *out)
-{
-    int vals[4];
-    int i, v, len;
-    unsigned word;
-    char c;
-
-    for (i = 0; i < 4; i++) {
-        c = atom[i];
-        if (c >= 'A' && c <= 'Z')
-            v = c - 'A';
-        else if (c >= 'a' && c <= 'z')
-            v = c - 'a' + 26;
-        else if (c >= '0' && c <= '9')
-            v = c - '0' + 52;
-        else if (c == '+')
-            v = 62;
-        else if (c == '/')
-            v = 63;
-        else if (c == '=')
-            v = -1;
-        else
-            return 0;                  /* invalid atom */
-        vals[i] = v;
-    }
-
-    if (vals[0] == -1 || vals[1] == -1)
-        return 0;
-    if (vals[2] == -1 && vals[3] != -1)
-        return 0;
-
-    if (vals[3] != -1)
-        len = 3;
-    else if (vals[2] != -1)
-        len = 2;
-    else
-        len = 1;
-
-    word = ((vals[0] << 18) |
-            (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
-    out[0] = (word >> 16) & 0xFF;
-    if (len > 1)
-        out[1] = (word >> 8) & 0xFF;
-    if (len > 2)
-        out[2] = word & 0xFF;
-    return len;
-}
-
-/* ----------------------------------------------------------------------
- * Generic routines to deal with send buffers: a linked list of
- * smallish blocks, with the operations
- *
- *  - add an arbitrary amount of data to the end of the list
- *  - remove the first N bytes from the list
- *  - return a (pointer,length) pair giving some initial data in
- *    the list, suitable for passing to a send or write system
- *    call
- *  - retrieve a larger amount of initial data from the list
- *  - return the current size of the buffer chain in bytes
- */
-
-/* WINSCP
-* Default granule of 512 leads to low performance.
-*/
-#define BUFFER_MIN_GRANULE  512*2*32
-
-struct bufchain_granule {
-    struct bufchain_granule *next;
-    char *bufpos, *bufend, *bufmax;
-};
-
-static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic)
-{
-    unreachable("bufchain callback used while uninitialised");
-}
-
-void bufchain_init(bufchain *ch)
-{
-    ch->head = ch->tail = NULL;
-    ch->buffersize = 0;
-    ch->ic = NULL;
-    ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback;
-}
-
-void bufchain_clear(bufchain *ch)
-{
-    struct bufchain_granule *b;
-    while (ch->head) {
-        b = ch->head;
-        ch->head = ch->head->next;
-        smemclr(b, sizeof(*b));
-        sfree(b);
-    }
-    ch->tail = NULL;
-    ch->buffersize = 0;
-}
-
-size_t bufchain_size(bufchain *ch)
-{
-    return ch->buffersize;
-}
-
-void bufchain_set_callback_inner(
-    bufchain *ch, IdempotentCallback *ic,
-    void (*queue_idempotent_callback)(IdempotentCallback *ic))
-{
-    ch->queue_idempotent_callback = queue_idempotent_callback;
-    ch->ic = ic;
-}
-
-void bufchain_add(bufchain *ch, const void *data, size_t len)
-{
-    const char *buf = (const char *)data;
-
-    if (len == 0) return;
-
-    ch->buffersize += len;
-
-    while (len > 0) {
-        if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
-            size_t copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
-            memcpy(ch->tail->bufend, buf, copylen);
-            buf += copylen;
-            len -= copylen;
-            ch->tail->bufend += copylen;
-        }
-        if (len > 0) {
-            size_t grainlen =
-                max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
-            struct bufchain_granule *newbuf;
-            newbuf = smalloc(grainlen);
-            newbuf->bufpos = newbuf->bufend =
-                (char *)newbuf + sizeof(struct bufchain_granule);
-            newbuf->bufmax = (char *)newbuf + grainlen;
-            newbuf->next = NULL;
-            if (ch->tail)
-                ch->tail->next = newbuf;
-            else
-                ch->head = newbuf;
-            ch->tail = newbuf;
-        }
-    }
-
-    if (ch->ic)
-        ch->queue_idempotent_callback(ch->ic);
-}
-
-void bufchain_consume(bufchain *ch, size_t len)
-{
-    struct bufchain_granule *tmp;
-
-    assert(ch->buffersize >= len);
-    while (len > 0) {
-        int remlen = len;
-        assert(ch->head != NULL);
-        if (remlen >= ch->head->bufend - ch->head->bufpos) {
-            remlen = ch->head->bufend - ch->head->bufpos;
-            tmp = ch->head;
-            ch->head = tmp->next;
-            if (!ch->head)
-                ch->tail = NULL;
-            smemclr(tmp, sizeof(*tmp));
-            sfree(tmp);
-        } else
-            ch->head->bufpos += remlen;
-        ch->buffersize -= remlen;
-        len -= remlen;
-    }
-}
-
-ptrlen bufchain_prefix(bufchain *ch)
-{
-    return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos);
-}
-
-void bufchain_fetch(bufchain *ch, void *data, size_t len)
-{
-    struct bufchain_granule *tmp;
-    char *data_c = (char *)data;
-
-    tmp = ch->head;
-
-    assert(ch->buffersize >= len);
-    while (len > 0) {
-        int remlen = len;
-
-        assert(tmp != NULL);
-        if (remlen >= tmp->bufend - tmp->bufpos)
-            remlen = tmp->bufend - tmp->bufpos;
-        memcpy(data_c, tmp->bufpos, remlen);
-
-        tmp = tmp->next;
-        len -= remlen;
-        data_c += remlen;
-    }
-}
-
-void bufchain_fetch_consume(bufchain *ch, void *data, size_t len)
-{
-    bufchain_fetch(ch, data, len);
-    bufchain_consume(ch, len);
-}
-
-bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len)
-{
-    if (ch->buffersize >= len) {
-        bufchain_fetch_consume(ch, data, len);
-        return true;
-    } else {
-        return false;
-    }
-}
-
-size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len)
-{
-    if (len > ch->buffersize)
-        len = ch->buffersize;
-    if (len)
-        bufchain_fetch_consume(ch, data, len);
-    return len;
-}
-
-/* ----------------------------------------------------------------------
- * Debugging routines.
- */
-
-#ifdef DEBUG
-extern void dputs(const char *); /* defined in per-platform *misc.c */
-
-void debug_printf(const char *fmt, ...)
-{
-    char *buf;
-    va_list ap;
-
-    va_start(ap, fmt);
-    buf = dupvprintf(fmt, ap);
-    dputs(buf);
-    sfree(buf);
-    va_end(ap);
-}
-
-void debug_memdump(const void *buf, int len, bool L)
-{
-    int i;
-    const unsigned char *p = buf;
-    char foo[17];
-    if (L) {
-        int delta;
-        debug_printf("\t%d (0x%x) bytes:\n", len, len);
-        delta = 15 & (uintptr_t)p;
-        p -= delta;
-        len += delta;
-    }
-    for (; 0 < len; p += 16, len -= 16) {
-        dputs("  ");
-        if (L)
-            debug_printf("%p: ", p);
-        strcpy(foo, "................");        /* sixteen dots */
-        for (i = 0; i < 16 && i < len; ++i) {
-            if (&p[i] < (unsigned char *) buf) {
-                dputs("   ");          /* 3 spaces */
-                foo[i] = ' ';
-            } else {
-                debug_printf("%c%2.2x",
-                        &p[i] != (unsigned char *) buf
-                        && i % 4 ? '.' : ' ', p[i]
-                    );
-                if (p[i] >= ' ' && p[i] <= '~')
-                    foo[i] = (char) p[i];
-            }
-        }
-        foo[i] = '\0';
-        debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
-    }
-}
-
-#endif                          /* def DEBUG */
-
-#ifndef PLATFORM_HAS_SMEMCLR
-/*
- * Securely wipe memory.
- *
- * The actual wiping is no different from what memset would do: the
- * point of 'securely' is to try to be sure over-clever compilers
- * won't optimise away memsets on variables that are about to be freed
- * or go out of scope. See
- * https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html
- *
- * Some platforms (e.g. Windows) may provide their own version of this
- * function.
- */
-void smemclr(void *b, size_t n) {
-    volatile char *vp;
-
-    if (b && n > 0) {
-        /*
-         * Zero out the memory.
-         */
-        memset(b, 0, n);
-
-        /*
-         * Perform a volatile access to the object, forcing the
-         * compiler to admit that the previous memset was important.
-         *
-         * This while loop should in practice run for zero iterations
-         * (since we know we just zeroed the object out), but in
-         * theory (as far as the compiler knows) it might range over
-         * the whole object. (If we had just written, say, '*vp =
-         * *vp;', a compiler could in principle have 'helpfully'
-         * optimised the memset into only zeroing out the first byte.
-         * This should be robust.)
-         */
-        vp = b;
-        while (*vp) vp++;
-    }
-}
-#endif
-
-bool smemeq(const void *av, const void *bv, size_t len)
-{
-    const unsigned char *a = (const unsigned char *)av;
-    const unsigned char *b = (const unsigned char *)bv;
-    unsigned val = 0;
-
-    while (len-- > 0) {
-        val |= *a++ ^ *b++;
-    }
-    /* Now val is 0 iff we want to return 1, and in the range
-     * 0x01..0xFF iff we want to return 0. So subtracting from 0x100
-     * will clear bit 8 iff we want to return 0, and leave it set iff
-     * we want to return 1, so then we can just shift down. */
-    return (0x100 - val) >> 8;
-}
-
-int nullstrcmp(const char *a, const char *b)
-{
-    if (a == NULL && b == NULL)
-        return 0;
-    if (a == NULL)
-        return -1;
-    if (b == NULL)
-        return +1;
-    return strcmp(a, b);
-}
-
-bool ptrlen_eq_string(ptrlen pl, const char *str)
-{
-    size_t len = strlen(str);
-    return (pl.len == len && !memcmp(pl.ptr, str, len));
-}
-
-bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2)
-{
-    return (pl1.len == pl2.len && !memcmp(pl1.ptr, pl2.ptr, pl1.len));
-}
-
-int ptrlen_strcmp(ptrlen pl1, ptrlen pl2)
-{
-    size_t minlen = pl1.len < pl2.len ? pl1.len : pl2.len;
-    if (minlen) {  /* tolerate plX.ptr==NULL as long as plX.len==0 */
-        int cmp = memcmp(pl1.ptr, pl2.ptr, minlen);
-        if (cmp)
-            return cmp;
-    }
-    return pl1.len < pl2.len ? -1 : pl1.len > pl2.len ? +1 : 0;
-}
-
-bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail)
-{
-    if (whole.len >= prefix.len &&
-        !memcmp(whole.ptr, prefix.ptr, prefix.len)) {
-        if (tail) {
-            tail->ptr = (const char *)whole.ptr + prefix.len;
-            tail->len = whole.len - prefix.len;
-        }
-        return true;
-    }
-    return false;
-}
-
-bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail)
-{
-    if (whole.len >= suffix.len &&
-        !memcmp((char *)whole.ptr + (whole.len - suffix.len),
-                suffix.ptr, suffix.len)) {
-        if (tail) {
-            tail->ptr = whole.ptr;
-            tail->len = whole.len - suffix.len;
-        }
-        return true;
-    }
-    return false;
-}
-
-ptrlen ptrlen_get_word(ptrlen *input, const char *separators)
-{
-    const char *p = input->ptr, *end = p + input->len;
-    ptrlen toret;
-
-    while (p < end && strchr(separators, *p))
-        p++;
-    toret.ptr = p;
-    while (p < end && !strchr(separators, *p))
-        p++;
-    toret.len = p - (const char *)toret.ptr;
-
-    { // WINSCP
-    size_t to_consume = p - (const char *)input->ptr;
-    assert(to_consume <= input->len);
-    input->ptr = (const char *)input->ptr + to_consume;
-    input->len -= to_consume;
-    } // WINSCP
-
-    return toret;
-}
-
-char *mkstr(ptrlen pl)
-{
-    char *p = snewn(pl.len + 1, char);
-    memcpy(p, pl.ptr, pl.len);
-    p[pl.len] = '\0';
-    return p;
-}
-
-bool strstartswith(const char *s, const char *t)
-{
-    return !strncmp(s, t, strlen(t));
-}
-
-bool strendswith(const char *s, const char *t)
-{
-    size_t slen = strlen(s), tlen = strlen(t);
-    return slen >= tlen && !strcmp(s + (slen - tlen), t);
-}
-
-size_t encode_utf8(void *output, unsigned long ch)
-{
-    unsigned char *start = (unsigned char *)output, *p = start;
-
-    if (ch < 0x80) {
-        *p++ = ch;
-    } else if (ch < 0x800) {
-        *p++ = 0xC0 | (ch >> 6);
-        *p++ = 0x80 | (ch & 0x3F);
-    } else if (ch < 0x10000) {
-        *p++ = 0xE0 | (ch >> 12);
-        *p++ = 0x80 | ((ch >> 6) & 0x3F);
-        *p++ = 0x80 | (ch & 0x3F);
-    } else {
-        *p++ = 0xF0 | (ch >> 18);
-        *p++ = 0x80 | ((ch >> 12) & 0x3F);
-        *p++ = 0x80 | ((ch >> 6) & 0x3F);
-        *p++ = 0x80 | (ch & 0x3F);
-    }
-    return p - start;
-}
-
-void write_c_string_literal(FILE *fp, ptrlen str)
-{
-    const char *p; // WINSCP
-    for (p = str.ptr; p < (const char *)str.ptr + str.len; p++) {
-        char c = *p;
-
-        if (c == '\n')
-            fputs("\\n", fp);
-        else if (c == '\r')
-            fputs("\\r", fp);
-        else if (c == '\t')
-            fputs("\\t", fp);
-        else if (c == '\b')
-            fputs("\\b", fp);
-        else if (c == '\\')
-            fputs("\\\\", fp);
-        else if (c == '"')
-            fputs("\\\"", fp);
-        else if (c >= 32 && c <= 126)
-            fputc(c, fp);
-        else
-            fprintf(fp, "\\%03o", (unsigned char)c);
-    }
-}
-
-void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size)
-{
-    switch (size & 15) {
-      case 0:
-        while (size >= 16) {
-            size -= 16;
-                   *out++ = *in1++ ^ *in2++;
-          case 15: *out++ = *in1++ ^ *in2++;
-          case 14: *out++ = *in1++ ^ *in2++;
-          case 13: *out++ = *in1++ ^ *in2++;
-          case 12: *out++ = *in1++ ^ *in2++;
-          case 11: *out++ = *in1++ ^ *in2++;
-          case 10: *out++ = *in1++ ^ *in2++;
-          case 9:  *out++ = *in1++ ^ *in2++;
-          case 8:  *out++ = *in1++ ^ *in2++;
-          case 7:  *out++ = *in1++ ^ *in2++;
-          case 6:  *out++ = *in1++ ^ *in2++;
-          case 5:  *out++ = *in1++ ^ *in2++;
-          case 4:  *out++ = *in1++ ^ *in2++;
-          case 3:  *out++ = *in1++ ^ *in2++;
-          case 2:  *out++ = *in1++ ^ *in2++;
-          case 1:  *out++ = *in1++ ^ *in2++;
-        }
-    }
-}
-
-FingerprintType ssh2_pick_fingerprint(
-    char **fingerprints, FingerprintType preferred_type)
-{
-    /*
-     * Keys are either SSH-2, in which case we have all fingerprint
-     * types, or SSH-1, in which case we have only MD5. So we return
-     * the default type if we can, or MD5 if that's all we have; no
-     * need for a fully general preference-list system.
-     */
-    FingerprintType fptype = fingerprints[preferred_type] ?
-        preferred_type : SSH_FPTYPE_MD5;
-    assert(fingerprints[fptype]);
-    return fptype;
-}
-
-FingerprintType ssh2_pick_default_fingerprint(char **fingerprints)
-{
-    return ssh2_pick_fingerprint(fingerprints, SSH_FPTYPE_DEFAULT);
-}

+ 4 - 5
source/putty/version.h

@@ -1,6 +1,5 @@
 /* Generated by automated build script */
-#define RELEASE 0.76
-#define TEXTVER "Release 0.76"
-#define SSHVER "-Release-0.76"
-#define BINARY_VERSION 0,76,0,0
-#define SOURCE_COMMIT "1fd7baa7344bb38d62a024e5dba3a720c67d05cf"
+#define PRERELEASE 0.77
+#define TEXTVER "Pre-release 0.77:2022-01-23.f11b201"
+#define SSHVER "-Prerelease-0.77:20220123.f11b201"
+#define BINARY_VERSION 0,76,33965,0

+ 0 - 486
source/putty/wildcard.c

@@ -1,486 +0,0 @@
-/*
- * Wildcard matching engine for use with SFTP-based file transfer
- * programs (PSFTP, new-look PSCP): since SFTP has no notion of
- * getting the remote side to do globbing (and rightly so) we have
- * to do it locally, by retrieving all the filenames in a directory
- * and checking each against the wildcard pattern.
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "putty.h"
-
-/*
- * Definition of wildcard syntax:
- *
- *  - * matches any sequence of characters, including zero.
- *  - ? matches exactly one character which can be anything.
- *  - [abc] matches exactly one character which is a, b or c.
- *  - [a-f] matches anything from a through f.
- *  - [^a-f] matches anything _except_ a through f.
- *  - [-_] matches - or _; [^-_] matches anything else. (The - is
- *    non-special if it occurs immediately after the opening
- *    bracket or ^.)
- *  - [a^] matches an a or a ^. (The ^ is non-special if it does
- *    _not_ occur immediately after the opening bracket.)
- *  - \*, \?, \[, \], \\ match the single characters *, ?, [, ], \.
- *  - All other characters are non-special and match themselves.
- */
-
-/*
- * Some notes on differences from POSIX globs (IEEE Std 1003.1, 2003 ed.):
- *  - backslashes act as escapes even within [] bracket expressions
- *  - does not support [!...] for non-matching list (POSIX are weird);
- *    NB POSIX allows [^...] as well via "A bracket expression starting
- *    with an unquoted circumflex character produces unspecified
- *    results". If we wanted to allow [!...] we might want to define
- *    [^!] as having its literal meaning (match '^' or '!').
- *  - none of the scary [[:class:]] stuff, etc
- */
-
-/*
- * The wildcard matching technique we use is very simple and
- * potentially O(N^2) in running time, but I don't anticipate it
- * being that bad in reality (particularly since N will be the size
- * of a filename, which isn't all that much). Perhaps one day, once
- * PuTTY has grown a regexp matcher for some other reason, I might
- * come back and reimplement wildcards by translating them into
- * regexps or directly into NFAs; but for the moment, in the
- * absence of any other need for the NFA->DFA translation engine,
- * anything more than the simplest possible wildcard matcher is
- * vast code-size overkill.
- *
- * Essentially, these wildcards are much simpler than regexps in
- * that they consist of a sequence of rigid fragments (? and [...]
- * can never match more or less than one character) separated by
- * asterisks. It is therefore extremely simple to look at a rigid
- * fragment and determine whether or not it begins at a particular
- * point in the test string; so we can search along the string
- * until we find each fragment, then search for the next. As long
- * as we find each fragment in the _first_ place it occurs, there
- * will never be a danger of having to backpedal and try to find it
- * again somewhere else.
- */
-
-enum {
-    WC_TRAILINGBACKSLASH = 1,
-    WC_UNCLOSEDCLASS,
-    WC_INVALIDRANGE
-};
-
-/*
- * Error reporting is done by returning various negative values
- * from the wildcard routines. Passing any such value to wc_error
- * will give a human-readable message.
- */
-const char *wc_error(int value)
-{
-    value = abs(value);
-    switch (value) {
-      case WC_TRAILINGBACKSLASH:
-        return "'\' occurred at end of string (expected another character)";
-      case WC_UNCLOSEDCLASS:
-        return "expected ']' to close character class";
-      case WC_INVALIDRANGE:
-        return "character range was not terminated (']' just after '-')";
-    }
-    return "INTERNAL ERROR: unrecognised wildcard error number";
-}
-
-/*
- * This is the routine that tests a target string to see if an
- * initial substring of it matches a fragment. If successful, it
- * returns 1, and advances both `fragment' and `target' past the
- * fragment and matching substring respectively. If unsuccessful it
- * returns zero. If the wildcard fragment suffers a syntax error,
- * it returns <0 and the precise value indexes into wc_error.
- */
-static int wc_match_fragment(const char **fragment, const char **target,
-                             const char *target_end)
-{
-    const char *f, *t;
-
-    f = *fragment;
-    t = *target;
-    /*
-     * The fragment terminates at either the end of the string, or
-     * the first (unescaped) *.
-     */
-    while (*f && *f != '*' && t < target_end) {
-        /*
-         * Extract one character from t, and one character's worth
-         * of pattern from f, and step along both. Return 0 if they
-         * fail to match.
-         */
-        if (*f == '\\') {
-            /*
-             * Backslash, which means f[1] is to be treated as a
-             * literal character no matter what it is. It may not
-             * be the end of the string.
-             */
-            if (!f[1])
-                return -WC_TRAILINGBACKSLASH;   /* error */
-            if (f[1] != *t)
-                return 0;              /* failed to match */
-            f += 2;
-        } else if (*f == '?') {
-            /*
-             * Question mark matches anything.
-             */
-            f++;
-        } else if (*f == '[') {
-            bool invert = false;
-            bool matched = false;
-            /*
-             * Open bracket introduces a character class.
-             */
-            f++;
-            if (*f == '^') {
-                invert = true;
-                f++;
-            }
-            while (*f != ']') {
-                if (*f == '\\')
-                    f++;               /* backslashes still work */
-                if (!*f)
-                    return -WC_UNCLOSEDCLASS;   /* error again */
-                if (f[1] == '-') {
-                    int lower, upper, ourchr;
-                    lower = (unsigned char) *f++;
-                    f++;               /* eat the minus */
-                    if (*f == ']')
-                        return -WC_INVALIDRANGE;   /* different error! */
-                    if (*f == '\\')
-                        f++;           /* backslashes _still_ work */
-                    if (!*f)
-                        return -WC_UNCLOSEDCLASS;   /* error again */
-                    upper = (unsigned char) *f++;
-                    ourchr = (unsigned char) *t;
-                    if (lower > upper) {
-                        int t = lower; lower = upper; upper = t;
-                    }
-                    if (ourchr >= lower && ourchr <= upper)
-                        matched = true;
-                } else {
-                    matched |= (*t == *f++);
-                }
-            }
-            if (invert == matched)
-                return 0;              /* failed to match character class */
-            f++;                       /* eat the ] */
-        } else {
-            /*
-             * Non-special character matches itself.
-             */
-            if (*f != *t)
-                return 0;
-            f++;
-        }
-        /*
-         * Now we've done that, increment t past the character we
-         * matched.
-         */
-        t++;
-    }
-    if (!*f || *f == '*') {
-        /*
-         * We have reached the end of f without finding a mismatch;
-         * so we're done. Update the caller pointers and return 1.
-         */
-        *fragment = f;
-        *target = t;
-        return 1;
-    }
-    /*
-     * Otherwise, we must have reached the end of t before we
-     * reached the end of f; so we've failed. Return 0.
-     */
-    return 0;
-}
-
-/*
- * This is the real wildcard matching routine. It returns 1 for a
- * successful match, 0 for an unsuccessful match, and <0 for a
- * syntax error in the wildcard.
- */
-static int wc_match_inner(
-    const char *wildcard, const char *target, size_t target_len)
-{
-    const char *target_end = target + target_len;
-    int ret;
-
-    /*
-     * Every time we see a '*' _followed_ by a fragment, we just
-     * search along the string for a location at which the fragment
-     * matches. The only special case is when we see a fragment
-     * right at the start, in which case we just call the matching
-     * routine once and give up if it fails.
-     */
-    if (*wildcard != '*') {
-        ret = wc_match_fragment(&wildcard, &target, target_end);
-        if (ret <= 0)
-            return ret;                /* pass back failure or error alike */
-    }
-
-    while (*wildcard) {
-        assert(*wildcard == '*');
-        while (*wildcard == '*')
-            wildcard++;
-
-        /*
-         * It's possible we've just hit the end of the wildcard
-         * after seeing a *, in which case there's no need to
-         * bother searching any more because we've won.
-         */
-        if (!*wildcard)
-            return 1;
-
-        /*
-         * Now `wildcard' points at the next fragment. So we
-         * attempt to match it against `target', and if that fails
-         * we increment `target' and try again, and so on. When we
-         * find we're about to try matching against the empty
-         * string, we give up and return 0.
-         */
-        ret = 0;
-        while (*target) {
-            const char *save_w = wildcard, *save_t = target;
-
-            ret = wc_match_fragment(&wildcard, &target, target_end);
-
-            if (ret < 0)
-                return ret;            /* syntax error */
-
-            if (ret > 0 && !*wildcard && target != target_end) {
-                /*
-                 * Final special case - literally.
-                 *
-                 * This situation arises when we are matching a
-                 * _terminal_ fragment of the wildcard (that is,
-                 * there is nothing after it, e.g. "*a"), and it
-                 * has matched _too early_. For example, matching
-                 * "*a" against "parka" will match the "a" fragment
-                 * against the _first_ a, and then (if it weren't
-                 * for this special case) matching would fail
-                 * because we're at the end of the wildcard but not
-                 * at the end of the target string.
-                 *
-                 * In this case what we must do is measure the
-                 * length of the fragment in the target (which is
-                 * why we saved `target'), jump straight to that
-                 * distance from the end of the string using
-                 * strlen, and match the same fragment again there
-                 * (which is why we saved `wildcard'). Then we
-                 * return whatever that operation returns.
-                 */
-                target = target_end - (target - save_t);
-                wildcard = save_w;
-                return wc_match_fragment(&wildcard, &target, target_end);
-            }
-
-            if (ret > 0)
-                break;
-            target++;
-        }
-        if (ret > 0)
-            continue;
-        return 0;
-    }
-
-    /*
-     * If we reach here, it must be because we successfully matched
-     * a fragment and then found ourselves right at the end of the
-     * wildcard. Hence, we return 1 if and only if we are also
-     * right at the end of the target.
-     */
-    return target == target_end;
-}
-
-int wc_match(const char *wildcard, const char *target)
-{
-    return wc_match_inner(wildcard, target, strlen(target));
-}
-
-int wc_match_pl(const char *wildcard, ptrlen target)
-{
-    return wc_match_inner(wildcard, target.ptr, target.len);
-}
-
-/*
- * Another utility routine that translates a non-wildcard string
- * into its raw equivalent by removing any escaping backslashes.
- * Expects a target string buffer of anything up to the length of
- * the original wildcard. You can also pass NULL as the output
- * buffer if you're only interested in the return value.
- *
- * Returns true on success, or false if a wildcard character was
- * encountered. In the latter case the output string MAY not be
- * zero-terminated and you should not use it for anything!
- */
-bool wc_unescape(char *output, const char *wildcard)
-{
-    while (*wildcard) {
-        if (*wildcard == '\\') {
-            wildcard++;
-            /* We are lenient about trailing backslashes in non-wildcards. */
-            if (*wildcard) {
-                if (output)
-                    *output++ = *wildcard;
-                wildcard++;
-            }
-        } else if (*wildcard == '*' || *wildcard == '?' ||
-                   *wildcard == '[' || *wildcard == ']') {
-            return false;              /* it's a wildcard! */
-        } else {
-            if (output)
-                *output++ = *wildcard;
-            wildcard++;
-        }
-    }
-    if (output)
-        *output = '\0';
-    return true;                       /* it's clean */
-}
-
-#ifdef TESTMODE
-
-struct test {
-    const char *wildcard;
-    const char *target;
-    int expected_result;
-};
-
-const struct test fragment_tests[] = {
-    /*
-     * We exhaustively unit-test the fragment matching routine
-     * itself, which should save us the need to test all its
-     * intricacies during the full wildcard tests.
-     */
-    {"abc", "abc", 1},
-    {"abc", "abd", 0},
-    {"abc", "abcd", 1},
-    {"abcd", "abc", 0},
-    {"ab[cd]", "abc", 1},
-    {"ab[cd]", "abd", 1},
-    {"ab[cd]", "abe", 0},
-    {"ab[^cd]", "abc", 0},
-    {"ab[^cd]", "abd", 0},
-    {"ab[^cd]", "abe", 1},
-    {"ab\\", "abc", -WC_TRAILINGBACKSLASH},
-    {"ab\\*", "ab*", 1},
-    {"ab\\?", "ab*", 0},
-    {"ab?", "abc", 1},
-    {"ab?", "ab", 0},
-    {"ab[", "abc", -WC_UNCLOSEDCLASS},
-    {"ab[c-", "abb", -WC_UNCLOSEDCLASS},
-    {"ab[c-]", "abb", -WC_INVALIDRANGE},
-    {"ab[c-e]", "abb", 0},
-    {"ab[c-e]", "abc", 1},
-    {"ab[c-e]", "abd", 1},
-    {"ab[c-e]", "abe", 1},
-    {"ab[c-e]", "abf", 0},
-    {"ab[e-c]", "abb", 0},
-    {"ab[e-c]", "abc", 1},
-    {"ab[e-c]", "abd", 1},
-    {"ab[e-c]", "abe", 1},
-    {"ab[e-c]", "abf", 0},
-    {"ab[^c-e]", "abb", 1},
-    {"ab[^c-e]", "abc", 0},
-    {"ab[^c-e]", "abd", 0},
-    {"ab[^c-e]", "abe", 0},
-    {"ab[^c-e]", "abf", 1},
-    {"ab[^e-c]", "abb", 1},
-    {"ab[^e-c]", "abc", 0},
-    {"ab[^e-c]", "abd", 0},
-    {"ab[^e-c]", "abe", 0},
-    {"ab[^e-c]", "abf", 1},
-    {"ab[a^]", "aba", 1},
-    {"ab[a^]", "ab^", 1},
-    {"ab[a^]", "abb", 0},
-    {"ab[^a^]", "aba", 0},
-    {"ab[^a^]", "ab^", 0},
-    {"ab[^a^]", "abb", 1},
-    {"ab[-c]", "ab-", 1},
-    {"ab[-c]", "abc", 1},
-    {"ab[-c]", "abd", 0},
-    {"ab[^-c]", "ab-", 0},
-    {"ab[^-c]", "abc", 0},
-    {"ab[^-c]", "abd", 1},
-    {"ab[\\[-\\]]", "abZ", 0},
-    {"ab[\\[-\\]]", "ab[", 1},
-    {"ab[\\[-\\]]", "ab\\", 1},
-    {"ab[\\[-\\]]", "ab]", 1},
-    {"ab[\\[-\\]]", "ab^", 0},
-    {"ab[^\\[-\\]]", "abZ", 1},
-    {"ab[^\\[-\\]]", "ab[", 0},
-    {"ab[^\\[-\\]]", "ab\\", 0},
-    {"ab[^\\[-\\]]", "ab]", 0},
-    {"ab[^\\[-\\]]", "ab^", 1},
-    {"ab[a-fA-F]", "aba", 1},
-    {"ab[a-fA-F]", "abF", 1},
-    {"ab[a-fA-F]", "abZ", 0},
-};
-
-const struct test full_tests[] = {
-    {"a", "argh", 0},
-    {"a", "ba", 0},
-    {"a", "a", 1},
-    {"a*", "aardvark", 1},
-    {"a*", "badger", 0},
-    {"*a", "park", 0},
-    {"*a", "pArka", 1},
-    {"*a", "parka", 1},
-    {"*a*", "park", 1},
-    {"*a*", "perk", 0},
-    {"?b*r?", "abracadabra", 1},
-    {"?b*r?", "abracadabr", 0},
-    {"?b*r?", "abracadabzr", 0},
-};
-
-int main(void)
-{
-    int i;
-    int fails, passes;
-
-    fails = passes = 0;
-
-    for (i = 0; i < sizeof(fragment_tests)/sizeof(*fragment_tests); i++) {
-        const char *f, *t;
-        int eret, aret;
-        f = fragment_tests[i].wildcard;
-        t = fragment_tests[i].target;
-        eret = fragment_tests[i].expected_result;
-        aret = wc_match_fragment(&f, &t, t + strlen(t));
-        if (aret != eret) {
-            printf("failed test: /%s/ against /%s/ returned %d not %d\n",
-                   fragment_tests[i].wildcard, fragment_tests[i].target,
-                   aret, eret);
-            fails++;
-        } else
-            passes++;
-    }
-
-    for (i = 0; i < sizeof(full_tests)/sizeof(*full_tests); i++) {
-        const char *f, *t;
-        int eret, aret;
-        f = full_tests[i].wildcard;
-        t = full_tests[i].target;
-        eret = full_tests[i].expected_result;
-        aret = wc_match(f, t);
-        if (aret != eret) {
-            printf("failed test: /%s/ against /%s/ returned %d not %d\n",
-                   full_tests[i].wildcard, full_tests[i].target,
-                   aret, eret);
-            fails++;
-        } else
-            passes++;
-    }
-
-    printf("passed %d, failed %d\n", passes, fails);
-
-    return 0;
-}
-
-#endif

+ 2 - 57
source/putty/windows/winpgntc.c → source/putty/windows/agent-client.c

@@ -9,12 +9,8 @@
 #include "putty.h"
 #include "pageant.h" /* for AGENT_MAX_MSGLEN */
 
-#ifndef NO_SECURITY
-#include "winsecur.h"
-#include "wincapi.h"
-#endif
-
-#define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
+#include "security-api.h"
+#include "cryptoapi.h"
 
 static bool wm_copydata_agent_exists(void)
 {
@@ -50,7 +46,6 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
     mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
 
     psa = NULL;
-#ifndef NO_SECURITY
     if (got_advapi()) {
         /*
          * Make the file mapping we create for communication with
@@ -81,7 +76,6 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
             }
         }
     }
-#endif /* NO_SECURITY */
 
     filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
                                 0, AGENT_MAX_MSGLEN, mapname);
@@ -129,19 +123,6 @@ static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen)
         LocalFree(psd);
 }
 
-#ifndef NO_SECURITY
-
-char *agent_named_pipe_name(void)
-{
-    char *username, *suffix, *pipename;
-    username = get_username();
-    suffix = capi_obfuscate_string("Pageant");
-    pipename = dupprintf("\\\\.\\pipe\\pageant.%s.%s", username, suffix);
-    sfree(username);
-    sfree(suffix);
-    return pipename;
-}
-
 Socket *agent_connect(Plug *plug)
 {
     char *pipename = agent_named_pipe_name();
@@ -319,39 +300,3 @@ agent_pending_query *agent_query(
     wm_copydata_agent_query(query, out, outlen);
     return NULL;
 }
-
-#else /* NO_SECURITY */
-
-Socket *agent_connect(void *vctx, Plug *plug)
-{
-    unreachable("no agent_connect_ctx can be constructed on this platform");
-}
-
-agent_connect_ctx *agent_get_connect_ctx(void)
-{
-    return NULL;
-}
-
-void agent_free_connect_ctx(agent_connect_ctx *ctx)
-{
-}
-
-bool agent_exists(void)
-{
-    return wm_copydata_agent_exists();
-}
-
-agent_pending_query *agent_query(
-    strbuf *query, void **out, int *outlen,
-    void (*callback)(void *, void *, int), void *callback_ctx)
-{
-    wm_copydata_agent_query(query, out, outlen);
-    return NULL;
-}
-
-void agent_cancel_query(agent_pending_query *q)
-{
-    unreachable("Windows agent queries are never asynchronous!");
-}
-
-#endif /* NO_SECURITY */

+ 1 - 5
source/putty/windows/wincapi.h → source/putty/windows/cryptoapi.h

@@ -1,12 +1,10 @@
 /*
- * wincapi.h: Windows Crypto API functions defined in wincapi.c that
+ * cryptoapi.h: Windows Crypto API functions defined in PuTTY that
  * use the crypt32 library. Also centralises the machinery for
  * dynamically loading that library, and our own functions using that
  * in turn.
  */
 
-#if !defined NO_SECURITY
-
 DECL_WINDOWS_FUNCTION(extern, BOOL, CryptProtectMemory, (LPVOID,DWORD,DWORD));
 
 bool got_crypt(void);
@@ -27,5 +25,3 @@ bool got_crypt(void);
  * The returned string is dynamically allocated.
  */
 char *capi_obfuscate_string(const char *realname);
-
-#endif

+ 4 - 4
source/putty/windows/wingss.c → source/putty/windows/gss.c

@@ -6,9 +6,9 @@
 #define SECURITY_WIN32
 #include <security.h>
 
-#include "pgssapi.h"
-#include "sshgss.h"
-#include "sshgssc.h"
+#include "ssh/pgssapi.h"
+#include "ssh/gss.h"
+#include "ssh/gssc.h"
 
 #include "misc.h"
 
@@ -130,7 +130,7 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf, LogContext *logctx) // MPEXT
     if (!kernel32_module) {
         kernel32_module = load_system32_dll("kernel32.dll");
     }
-#if (defined _MSC_VER && _MSC_VER < 1900) || defined MPEXT
+#if !HAVE_ADDDLLDIRECTORY
     /* Omit the type-check because older MSVCs don't have this function */
     GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, AddDllDirectory);
 #else

+ 103 - 155
source/putty/windows/winhandl.c → source/putty/windows/handle-io.c

@@ -1,5 +1,5 @@
 /*
- * winhandl.c: Module to give Windows front ends the general
+ * handle-io.c: Module to give Windows front ends the general
  * ability to deal with consoles, pipes, serial ports, or any other
  * type of data stream accessed through a Windows API HANDLE rather
  * than a WinSock SOCKET.
@@ -37,6 +37,12 @@
  * Generic definitions.
  */
 
+typedef struct handle_list_node handle_list_node;
+struct handle_list_node {
+    handle_list_node *next, *prev;
+};
+static void add_to_ready_list(handle_list_node *node);
+
 /*
  * Maximum amount of backlog we will allow to build up on an input
  * handle before we stop reading from it.
@@ -56,7 +62,7 @@ struct handle_generic {
      * thread.
      */
     HANDLE h;                          /* the handle itself */
-    HANDLE ev_to_main;                 /* event used to signal main thread */
+    handle_list_node ready_node;       /* for linking on to the ready list */
     HANDLE ev_from_main;               /* event used to signal back to us */
     bool moribund;                     /* are we going to kill this soon? */
     bool done;                         /* request subthread to terminate */
@@ -65,7 +71,7 @@ struct handle_generic {
     void *privdata;                    /* for client to remember who they are */
 };
 
-typedef enum { HT_INPUT, HT_OUTPUT, HT_FOREIGN } HandleType;
+typedef enum { HT_INPUT, HT_OUTPUT } HandleType;
 
 /* ----------------------------------------------------------------------
  * Input threads.
@@ -79,7 +85,7 @@ struct handle_input {
      * Copy of the handle_generic structure.
      */
     HANDLE h;                          /* the handle itself */
-    HANDLE ev_to_main;                 /* event used to signal main thread */
+    handle_list_node ready_node;       /* for linking on to the ready list */
     HANDLE ev_from_main;               /* event used to signal back to us */
     bool moribund;                     /* are we going to kill this soon? */
     bool done;                         /* request subthread to terminate */
@@ -93,7 +99,7 @@ struct handle_input {
     int flags;
 
     /*
-     * Data set by the input thread before signalling ev_to_main,
+     * Data set by the input thread before marking the handle ready,
      * and read by the main thread after receiving that signal.
      */
     char buffer[4096];                 /* the data read from the handle */
@@ -176,7 +182,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param)
          */
         finished = (ctx->len == 0);
 
-        SetEvent(ctx->ev_to_main);
+        add_to_ready_list(&ctx->ready_node);
 
         if (finished)
             break;
@@ -189,7 +195,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param)
              * not touch ctx at all, because the main thread might
              * have freed it.
              */
-            SetEvent(ctx->ev_to_main);
+            add_to_ready_list(&ctx->ready_node);
             break;
         }
     }
@@ -240,7 +246,7 @@ struct handle_output {
      * Copy of the handle_generic structure.
      */
     HANDLE h;                          /* the handle itself */
-    HANDLE ev_to_main;                 /* event used to signal main thread */
+    handle_list_node ready_node;       /* for linking on to the ready list */
     HANDLE ev_from_main;               /* event used to signal back to us */
     bool moribund;                     /* are we going to kill this soon? */
     bool done;                         /* request subthread to terminate */
@@ -261,8 +267,8 @@ struct handle_output {
     DWORD len;                         /* how much data there is */
 
     /*
-     * Data set by the input thread before signalling ev_to_main,
-     * and read by the main thread after receiving that signal.
+     * Data set by the input thread before marking this handle as
+     * ready, and read by the main thread after receiving that signal.
      */
     DWORD lenwritten;                  /* how much data we actually wrote */
     int writeerr;                      /* return value from WriteFile */
@@ -278,6 +284,7 @@ struct handle_output {
      * drops.
      */
     handle_outputfn_t sentdata;
+    struct handle *sentdata_param;
 };
 
 static DWORD WINAPI handle_output_threadfunc(void *param)
@@ -303,7 +310,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param)
              * not touch ctx at all, because the main thread might
              * have freed it.
              */
-            SetEvent(ctx->ev_to_main);
+            add_to_ready_list(&ctx->ready_node);
             break;
         }
         if (povl) {
@@ -326,7 +333,7 @@ static DWORD WINAPI handle_output_threadfunc(void *param)
                 ctx->writeerr = 0;
         }
 
-        SetEvent(ctx->ev_to_main);
+        add_to_ready_list(&ctx->ready_node);
         if (!writeret) {
             /*
              * The write operation has suffered an error. Telling that
@@ -355,39 +362,12 @@ static void handle_try_output(struct handle_output *ctx)
         ctx->busy = true;
     } else if (!ctx->busy && bufchain_size(&ctx->queued_data) == 0 &&
                ctx->outgoingeof == EOF_PENDING) {
-        CloseHandle(ctx->h);
+        ctx->sentdata(ctx->sentdata_param, 0, 0, true);
         ctx->h = INVALID_HANDLE_VALUE;
         ctx->outgoingeof = EOF_SENT;
     }
 }
 
-/* ----------------------------------------------------------------------
- * 'Foreign events'. These are handle structures which just contain a
- * single event object passed to us by another module such as
- * winnps.c, so that they can make use of our handle_get_events /
- * handle_got_event mechanism for communicating with application main
- * loops.
- */
-struct handle_foreign {
-    /*
-     * Copy of the handle_generic structure.
-     */
-    HANDLE h;                          /* the handle itself */
-    HANDLE ev_to_main;                 /* event used to signal main thread */
-    HANDLE ev_from_main;               /* event used to signal back to us */
-    bool moribund;                     /* are we going to kill this soon? */
-    bool done;                         /* request subthread to terminate */
-    bool defunct;                      /* has the subthread already gone? */
-    bool busy;                         /* operation currently in progress? */
-    void *privdata;                    /* for client to remember who they are */
-
-    /*
-     * Our own data, just consisting of knowledge of who to call back.
-     */
-    void (*callback)(void *);
-    void *ctx;
-};
-
 /* ----------------------------------------------------------------------
  * Unified code handling both input and output threads.
  */
@@ -398,38 +378,91 @@ struct handle {
         struct handle_generic g;
         struct handle_input i;
         struct handle_output o;
-        struct handle_foreign f;
     } u;
 };
 
-#ifndef WINSCP
-static tree234 *handles_by_evtomain;
-#endif
+/*
+ * Linked list storing the current list of handles ready to have
+ * something done to them by the main thread.
+ */
+static handle_list_node ready_head[1];
+static CRITICAL_SECTION ready_critsec[1];
 
-static int handle_cmp_evtomain(void *av, void *bv)
+/*
+ * Event object used by all subthreads to signal that they've just put
+ * something on the ready list, i.e. that the ready list is non-empty.
+ */
+static HANDLE ready_event = INVALID_HANDLE_VALUE;
+
+static void add_to_ready_list(handle_list_node *node)
 {
-    struct handle *a = (struct handle *)av;
-    struct handle *b = (struct handle *)bv;
+    /*
+     * Called from subthreads, when their handle has done something
+     * that they need the main thread to respond to. We append the
+     * given list node to the end of the ready list, and set
+     * ready_event to signal to the main thread that the ready list is
+     * now non-empty.
+     */
+    EnterCriticalSection(ready_critsec);
+    node->next = ready_head;
+    node->prev = ready_head->prev;
+    node->next->prev = node->prev->next = node;
+    SetEvent(ready_event);
+    LeaveCriticalSection(ready_critsec);
+}
 
-    if ((uintptr_t)a->u.g.ev_to_main < (uintptr_t)b->u.g.ev_to_main)
-        return -1;
-    else if ((uintptr_t)a->u.g.ev_to_main > (uintptr_t)b->u.g.ev_to_main)
-        return +1;
-    else
-        return 0;
+static void remove_from_ready_list(handle_list_node *node)
+{
+    /*
+     * Called from the main thread, just before destroying a 'struct
+     * handle' completely: as a precaution, we make absolutely sure
+     * it's not linked on the ready list, just in case somehow it
+     * still was.
+     */
+    EnterCriticalSection(ready_critsec);
+    node->next->prev = node->prev;
+    node->prev->next = node->next;
+    node->next = node->prev = node;
+    LeaveCriticalSection(ready_critsec);
 }
 
-static int handle_find_evtomain(void *av, void *bv)
+static void handle_ready(struct handle *h); /* process one handle (below) */
+
+static void handle_ready_callback(void *vctx)
 {
-    HANDLE *a = (HANDLE *)av;
-    struct handle *b = (struct handle *)bv;
+    /*
+     * Called when the main thread detects ready_event, indicating
+     * that at least one handle is on the ready list. We empty the
+     * whole list and process the handles one by one.
+     *
+     * It's possible that other handles may be destroyed, and hence
+     * taken _off_ the ready list, during this processing. That
+     * shouldn't cause a deadlock, because according to the API docs,
+     * it's safe to call EnterCriticalSection twice in the same thread
+     * - the second call will return immediately because that thread
+     * already owns the critsec. (And then it takes two calls to
+     * LeaveCriticalSection to release it again, which is just what we
+     * want here.)
+     */
+    EnterCriticalSection(ready_critsec);
+    while (ready_head->next != ready_head) {
+        handle_list_node *node = ready_head->next;
+        node->prev->next = node->next;
+        node->next->prev = node->prev;
+        node->next = node->prev = node;
+        handle_ready(container_of(node, struct handle, u.g.ready_node));
+    }
+    LeaveCriticalSection(ready_critsec);
+}
 
-    if ((uintptr_t)*a < (uintptr_t)b->u.g.ev_to_main)
-        return -1;
-    else if ((uintptr_t)*a > (uintptr_t)b->u.g.ev_to_main)
-        return +1;
-    else
-        return 0;
+static inline void ensure_ready_event_setup(void)
+{
+    if (ready_event == INVALID_HANDLE_VALUE) {
+        ready_head->prev = ready_head->next = ready_head;
+        InitializeCriticalSection(ready_critsec);
+        ready_event = CreateEvent(NULL, false, false, NULL);
+        add_handle_wait(ready_event, handle_ready_callback, NULL);
+    }
 }
 
 struct handle *handle_input_new(tree234 * handles_by_evtomain, HANDLE handle, handle_inputfn_t gotdata,
@@ -440,7 +473,6 @@ struct handle *handle_input_new(tree234 * handles_by_evtomain, HANDLE handle, ha
 
     h->type = HT_INPUT;
     h->u.i.h = handle;
-    h->u.i.ev_to_main = CreateEvent(NULL, false, false, NULL);
     h->u.i.ev_from_main = CreateEvent(NULL, false, false, NULL);
     h->u.i.gotdata = gotdata;
     h->u.i.defunct = false;
@@ -449,12 +481,7 @@ struct handle *handle_input_new(tree234 * handles_by_evtomain, HANDLE handle, ha
     h->u.i.privdata = privdata;
     h->u.i.flags = flags;
 
-    #ifndef WINSCP
-    if (!handles_by_evtomain)
-        handles_by_evtomain = newtree234(handle_cmp_evtomain);
-    #endif
-    add234(handles_by_evtomain, h);
-
+    ensure_ready_event_setup();
     { // WINSCP
     HANDLE hThread = CreateThread(NULL, 0, handle_input_threadfunc,
                                   &h->u.i, 0, &in_threadid);
@@ -474,7 +501,6 @@ struct handle *handle_output_new(tree234 * handles_by_evtomain, HANDLE handle, h
 
     h->type = HT_OUTPUT;
     h->u.o.h = handle;
-    h->u.o.ev_to_main = CreateEvent(NULL, false, false, NULL);
     h->u.o.ev_from_main = CreateEvent(NULL, false, false, NULL);
     h->u.o.busy = false;
     h->u.o.defunct = false;
@@ -484,14 +510,10 @@ struct handle *handle_output_new(tree234 * handles_by_evtomain, HANDLE handle, h
     bufchain_init(&h->u.o.queued_data);
     h->u.o.outgoingeof = EOF_NO;
     h->u.o.sentdata = sentdata;
+    h->u.o.sentdata_param = h;
     h->u.o.flags = flags;
 
-    #ifndef WINSCP
-    if (!handles_by_evtomain)
-        handles_by_evtomain = newtree234(handle_cmp_evtomain);
-    #endif
-    add234(handles_by_evtomain, h);
-
+    ensure_ready_event_setup();
     { // WINSCP
     HANDLE hThread = CreateThread(NULL, 0, handle_output_threadfunc,
                                   &h->u.o, 0, &out_threadid);
@@ -502,32 +524,6 @@ struct handle *handle_output_new(tree234 * handles_by_evtomain, HANDLE handle, h
     } // WINSCP
 }
 
-#ifndef WINSCP
-struct handle *handle_add_foreign_event(HANDLE event,
-                                        void (*callback)(void *), void *ctx)
-{
-    struct handle *h = snew(struct handle);
-
-    h->type = HT_FOREIGN;
-    h->u.f.h = INVALID_HANDLE_VALUE;
-    h->u.f.ev_to_main = event;
-    h->u.f.ev_from_main = INVALID_HANDLE_VALUE;
-    h->u.f.defunct = true;  /* we have no thread in the first place */
-    h->u.f.moribund = false;
-    h->u.f.done = false;
-    h->u.f.privdata = NULL;
-    h->u.f.callback = callback;
-    h->u.f.ctx = ctx;
-    h->u.f.busy = true;
-
-    if (!handles_by_evtomain)
-        handles_by_evtomain = newtree234(handle_cmp_evtomain);
-    add234(handles_by_evtomain, h);
-
-    return h;
-}
-#endif
-
 size_t handle_write(struct handle *h, const void *data, size_t len)
 {
     assert(h->type == HT_OUTPUT);
@@ -553,46 +549,19 @@ void handle_write_eof(struct handle *h)
     }
 }
 
-HANDLE *handle_get_events(tree234 * handles_by_evtomain, int *nevents) // WINSCP
-{
-    HANDLE *ret;
-    struct handle *h;
-    int i;
-    size_t n, size;
-
-    /*
-     * Go through our tree counting the handle objects currently
-     * engaged in useful activity.
-     */
-    ret = NULL;
-    n = size = 0;
-    if (handles_by_evtomain) {
-        for (i = 0; (h = index234(handles_by_evtomain, i)) != NULL; i++) {
-            if (h->u.g.busy) {
-                sgrowarray(ret, size, n);
-                ret[n++] = h->u.g.ev_to_main;
-            }
-        }
-    }
-
-    *nevents = n;
-    return ret;
-}
-
 static void handle_destroy(tree234 * handles_by_evtomain, struct handle *h) // WINSCP
 {
     if (h->type == HT_OUTPUT)
         bufchain_clear(&h->u.o.queued_data);
     CloseHandle(h->u.g.ev_from_main);
-    CloseHandle(h->u.g.ev_to_main);
-    del234(handles_by_evtomain, h);
+    remove_from_ready_list(&h->u.g.ready_node);
     sfree(h);
 }
 
 void handle_free(tree234 * handles_by_evtomain, struct handle *h) // WINSCP
 {
     assert(h && !h->u.g.moribund);
-    if (h->u.g.busy && h->type != HT_FOREIGN) {
+    if (h->u.g.busy) {
         /*
          * If the handle is currently busy, we cannot immediately free
          * it, because its subthread is in the middle of something.
@@ -624,24 +593,8 @@ void handle_free(tree234 * handles_by_evtomain, struct handle *h) // WINSCP
     }
 }
 
-int handle_got_event(tree234 * handles_by_evtomain, HANDLE event) // WINSCP
+static void handle_ready(struct handle *h)
 {
-    struct handle *h;
-
-    assert(handles_by_evtomain);
-    h = find234(handles_by_evtomain, &event, handle_find_evtomain);
-    if (!h) {
-        /*
-         * This isn't an error condition. If two or more event
-         * objects were signalled during the same select operation,
-         * and processing of the first caused the second handle to
-         * be closed, then it will sometimes happen that we receive
-         * an event notification here for a handle which is already
-         * deceased. In that situation we simply do nothing.
-         */
-        return 0; // WINSCP
-    }
-
     if (h->u.g.moribund) {
         /*
          * A moribund handle is one which we have either already
@@ -697,19 +650,14 @@ int handle_got_event(tree234 * handles_by_evtomain, HANDLE event) // WINSCP
              * thread is terminating by now).
              */
             h->u.o.defunct = true;
-            h->u.o.sentdata(h, 0, h->u.o.writeerr);
+            h->u.o.sentdata(h, 0, h->u.o.writeerr, false);
         } else {
             bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten);
             noise_ultralight(NOISE_SOURCE_IOLEN, h->u.o.lenwritten);
-            h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data), 0);
+            h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data), 0, false);
             handle_try_output(&h->u.o);
         }
         return 0; // WINSCP
-
-      case HT_FOREIGN:
-        /* Just call the callback. */
-        h->u.f.callback(h->u.f.ctx);
-        return 0; // WINSCP
     }
 }
 

+ 194 - 32
source/putty/windows/winhsock.c → source/putty/windows/handle-socket.c

@@ -15,35 +15,56 @@
 #define queue_toplevel_callback(FN, CTX) queue_toplevel_callback(get_callback_set(CTX->plug), FN, CTX)
 #endif
 
+/*
+ * Freezing one of these sockets is a slightly fiddly business,
+ * because the reads from the handle are happening in a separate
+ * thread as blocking system calls and so once one is in progress it
+ * can't sensibly be interrupted. Hence, after the user tries to
+ * freeze one of these sockets, it's unavoidable that we may receive
+ * one more load of data before we manage to get handle-io.c to stop
+ * reading.
+ */
+typedef enum HandleSocketFreezeState {
+    UNFROZEN,  /* reading as normal */
+    FREEZING,  /* have been set to frozen but winhandl is still reading */
+    FROZEN,    /* really frozen - winhandl has been throttled */
+    THAWING    /* we're gradually releasing our remaining data */
+} HandleSocketFreezeState;
+
 typedef struct HandleSocket {
-    HANDLE send_H, recv_H, stderr_H;
-    struct handle *send_h, *recv_h, *stderr_h;
+    union {
+        struct {
+            HANDLE send_H, recv_H, stderr_H;
+            struct handle *send_h, *recv_h, *stderr_h;
 
-    /*
-     * Freezing one of these sockets is a slightly fiddly business,
-     * because the reads from the handle are happening in a separate
-     * thread as blocking system calls and so once one is in progress
-     * it can't sensibly be interrupted. Hence, after the user tries
-     * to freeze one of these sockets, it's unavoidable that we may
-     * receive one more load of data before we manage to get
-     * winhandl.c to stop reading.
-     */
-    enum {
-        UNFROZEN,  /* reading as normal */
-        FREEZING,  /* have been set to frozen but winhandl is still reading */
-        FROZEN,    /* really frozen - winhandl has been throttled */
-        THAWING    /* we're gradually releasing our remaining data */
-    } frozen;
-    /* We buffer data here if we receive it from winhandl while frozen. */
-    bufchain inputdata;
+            HandleSocketFreezeState frozen;
+            /* We buffer data here if we receive it from winhandl
+             * while frozen. */
+            bufchain inputdata;
 
-    /* Handle logging proxy error messages from stderr_H, if we have one. */
-    ProxyStderrBuf psb;
+            /* Handle logging proxy error messages from stderr_H, if
+             * we have one */
+            ProxyStderrBuf psb;
 
-    bool defer_close, deferred_close;   /* in case of re-entrance */
+            bool defer_close, deferred_close;   /* in case of re-entrance */
+        };
+        struct {
+            DeferredSocketOpener *opener;
+
+            /* We buffer data here if we receive it via sk_write
+             * before the socket is opened. */
+            bufchain outputdata;
+
+            bool output_eof_pending;
+
+            bool start_frozen;
+        };
+    };
 
     char *error;
 
+    SockAddr *addr;
+    int port;
     Plug *plug;
 
     Socket sock;
@@ -55,17 +76,17 @@ static size_t handle_gotdata(
     HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
 
     if (err) {
-        plug_closing(hs->plug, "Read error from handle", 0, 0);
+        plug_closing_error(hs->plug, "Read error from handle");
         return 0;
     } else if (len == 0) {
-        plug_closing(hs->plug, NULL, 0, 0);
+        plug_closing_normal(hs->plug);
         return 0;
     } else {
         assert(hs->frozen != FROZEN && hs->frozen != THAWING);
         if (hs->frozen == FREEZING) {
             /*
              * If we've received data while this socket is supposed to
-             * be frozen (because the read winhandl.c started before
+             * be frozen (because the read handle-io.c started before
              * sk_set_frozen was called has now returned) then buffer
              * the data for when we unfreeze.
              */
@@ -95,12 +116,21 @@ static size_t handle_stderr(
     return 0;
 }
 
-static void handle_sentdata(struct handle *h, size_t new_backlog, int err)
+static void handle_sentdata(struct handle *h, size_t new_backlog, int err,
+                            bool close)
 {
     HandleSocket *hs = (HandleSocket *)handle_get_privdata(h);
 
+    if (close) {
+        if (hs->send_H != INVALID_HANDLE_VALUE)
+            CloseHandle(hs->send_H);
+        if (hs->recv_H != INVALID_HANDLE_VALUE && hs->recv_H != hs->send_H)
+            CloseHandle(hs->recv_H);
+        hs->send_H = hs->recv_H = INVALID_HANDLE_VALUE;
+    }
+
     if (err) {
-        plug_closing(hs->plug, win_strerror(err), err, 0);
+        plug_closing_system_error(hs->plug, err);
         return;
     }
 
@@ -133,8 +163,9 @@ static void sk_handle_close(Socket *s)
 
     handle_free(handles_by_evtomain, hs->send_h); // WINSCP
     handle_free(handles_by_evtomain, hs->recv_h); // WINSCP
-    CloseHandle(hs->send_H);
-    if (hs->recv_H != hs->send_H)
+    if (hs->send_H != INVALID_HANDLE_VALUE)
+        CloseHandle(hs->send_H);
+    if (hs->recv_H != INVALID_HANDLE_VALUE && hs->recv_H != hs->send_H)
         CloseHandle(hs->recv_H);
     bufchain_clear(&hs->inputdata);
 #ifdef MPEXT
@@ -148,6 +179,9 @@ static void sk_handle_close(Socket *s)
     }
 #endif
 
+    if (hs->addr)
+        sk_addr_free(hs->addr);
+
     delete_callbacks_for_context(get_callback_set(hs->plug), hs);
 
     sfree(hs);
@@ -236,7 +270,7 @@ static void sk_handle_set_frozen(Socket *s, bool is_frozen)
           case THAWING:
             /*
              * We were in the middle of emptying our bufchain, and got
-             * frozen again. In that case, winhandl.c is already
+             * frozen again. In that case, handle-io.c is already
              * throttled, so just return to FROZEN state. The toplevel
              * callback will notice and disable itself.
              */
@@ -294,7 +328,7 @@ static SocketPeerInfo *sk_handle_peer_info(Socket *s)
 
     if (!kernel32_module) {
         kernel32_module = load_system32_dll("kernel32.dll");
-#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__
+#if !HAVE_GETNAMEDPIPECLIENTPROCESSID
         /* For older Visual Studio, and MinGW too (at least as of
          * Ubuntu 16.04), this function isn't available in the header
          * files to type-check. Ditto the toolchain I use for
@@ -337,8 +371,15 @@ static const SocketVtable HandleSocket_sockvt = {
     /*.peer_info =*/ sk_handle_peer_info,
 };
 
+static void sk_handle_connect_success_callback(void *ctx)
+{
+    HandleSocket *hs = (HandleSocket *)ctx;
+    plug_log(hs->plug, PLUGLOG_CONNECT_SUCCESS, hs->addr, hs->port, NULL, 0);
+}
+
 Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
-                           Plug *plug, bool overlapped)
+                           SockAddr *addr, int port, Plug *plug,
+                           bool overlapped)
 {
     HandleSocket *hs;
     int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0);
@@ -346,8 +387,11 @@ Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
 
     hs = snew(HandleSocket);
     hs->sock.vt = &HandleSocket_sockvt;
+    hs->addr = addr;
+    hs->port = port;
     hs->plug = plug;
     hs->error = NULL;
+
     hs->frozen = UNFROZEN;
     bufchain_init(&hs->inputdata);
     psb_init(&hs->psb);
@@ -368,5 +412,123 @@ Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
     do_select(plug, INVALID_SOCKET, 1);
     #endif
 
+    queue_toplevel_callback(sk_handle_connect_success_callback, hs);
+
     return &hs->sock;
 }
+
+static void sk_handle_deferred_close(Socket *s)
+{
+    HandleSocket *hs = container_of(s, HandleSocket, sock);
+
+    deferred_socket_opener_free(hs->opener);
+    bufchain_clear(&hs->outputdata);
+
+    if (hs->addr)
+        sk_addr_free(hs->addr);
+
+    delete_callbacks_for_context(hs);
+
+    sfree(hs);
+}
+
+static size_t sk_handle_deferred_write(Socket *s, const void *data, size_t len)
+{
+    HandleSocket *hs = container_of(s, HandleSocket, sock);
+    assert(!hs->output_eof_pending);
+    bufchain_add(&hs->outputdata, data, len);
+    return bufchain_size(&hs->outputdata);
+}
+
+static void sk_handle_deferred_write_eof(Socket *s)
+{
+    HandleSocket *hs = container_of(s, HandleSocket, sock);
+    assert(!hs->output_eof_pending);
+    hs->output_eof_pending = true;
+}
+
+static void sk_handle_deferred_set_frozen(Socket *s, bool is_frozen)
+{
+    HandleSocket *hs = container_of(s, HandleSocket, sock);
+    hs->frozen = is_frozen;
+}
+
+static SocketPeerInfo *sk_handle_deferred_peer_info(Socket *s)
+{
+    return NULL;
+}
+
+static const SocketVtable HandleSocket_deferred_sockvt = {
+    .plug = sk_handle_plug,
+    .close = sk_handle_deferred_close,
+    .write = sk_handle_deferred_write,
+    .write_oob = sk_handle_deferred_write,
+    .write_eof = sk_handle_deferred_write_eof,
+    .set_frozen = sk_handle_deferred_set_frozen,
+    .socket_error = sk_handle_socket_error,
+    .peer_info = sk_handle_deferred_peer_info,
+};
+
+Socket *make_deferred_handle_socket(DeferredSocketOpener *opener,
+                                    SockAddr *addr, int port, Plug *plug)
+{
+    HandleSocket *hs = snew(HandleSocket);
+    hs->sock.vt = &HandleSocket_deferred_sockvt;
+    hs->addr = addr;
+    hs->port = port;
+    hs->plug = plug;
+    hs->error = NULL;
+
+    hs->opener = opener;
+    bufchain_init(&hs->outputdata);
+    hs->output_eof_pending = false;
+    hs->start_frozen = false;
+
+    return &hs->sock;
+}
+
+void setup_handle_socket(Socket *s, HANDLE send_H, HANDLE recv_H,
+                         HANDLE stderr_H, bool overlapped)
+{
+    HandleSocket *hs = container_of(s, HandleSocket, sock);
+    assert(hs->sock.vt == &HandleSocket_deferred_sockvt);
+
+    int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0);
+
+    struct handle *recv_h = handle_input_new(
+        recv_H, handle_gotdata, hs, flags);
+    struct handle *send_h = handle_output_new(
+        send_H, handle_sentdata, hs, flags);
+    struct handle *stderr_h = !stderr_H ? NULL : handle_input_new(
+        stderr_H, handle_stderr, hs, flags);
+
+    while (bufchain_size(&hs->outputdata)) {
+        ptrlen data = bufchain_prefix(&hs->outputdata);
+        handle_write(send_h, data.ptr, data.len);
+        bufchain_consume(&hs->outputdata, data.len);
+    }
+
+    if (hs->output_eof_pending)
+        handle_write_eof(send_h);
+
+    bool start_frozen = hs->start_frozen;
+
+    deferred_socket_opener_free(hs->opener);
+    bufchain_clear(&hs->outputdata);
+
+    hs->sock.vt = &HandleSocket_sockvt;
+    hs->frozen = start_frozen ? FREEZING : UNFROZEN;
+    bufchain_init(&hs->inputdata);
+    psb_init(&hs->psb);
+
+    hs->recv_H = recv_H;
+    hs->recv_h = recv_h;
+    hs->send_H = send_H;
+    hs->send_h = send_h;
+    hs->stderr_H = stderr_H;
+    hs->stderr_h = stderr_h;
+
+    hs->defer_close = hs->deferred_close = false;
+
+    queue_toplevel_callback(sk_handle_connect_success_callback, hs);
+}

+ 143 - 0
source/putty/windows/handle-wait.c

@@ -0,0 +1,143 @@
+/*
+ * handle-wait.c: Manage a collection of HANDLEs to wait for (in a
+ * WaitFor{Single,Multiple}Objects sense), each with a callback to be
+ * called when it's activated. Tracks the list, and provides an API to
+ * event loops that let them get a list of things to wait for and a
+ * way to call back to here when one of them does something.
+ */
+
+/*
+ * TODO: currently this system can't cope with more than
+ * MAXIMUM_WAIT_OBJECTS (= 64) handles at a time. It enforces that by
+ * assertion, so we'll at least find out if that assumption is ever
+ * violated.
+ *
+ * It should be OK for the moment. As of 2021-05-24, the only uses of
+ * this system are by the ConPTY backend (just once, to watch for its
+ * subprocess terminating); by Pageant (for the event that the
+ * WM_COPYDATA subthread uses to signal the main thread); and by
+ * named-pipe-server.c (once per named-pipe server, of which there is
+ * one in Pageant and one in connection-sharing upstreams). So the
+ * total number of handles has a pretty small upper bound.
+ *
+ * But sooner or later, I'm sure we'll find a reason why we really
+ * need to watch a squillion handles at once. When that happens, I
+ * can't see any alternative to setting up some kind of tree of
+ * subthreads in this module, each one condensing 64 of our handles
+ * into one, by doing its own WaitForMultipleObjects and setting an
+ * event object to indicate that one of them did something. It'll be
+ * horribly ugly.
+ */
+
+#include "putty.h"
+
+struct HandleWait {
+    HANDLE handle;
+    handle_wait_callback_fn_t callback;
+    void *callback_ctx;
+
+    int index;                    /* sort key for tree234 */
+};
+
+struct HandleWaitListInner {
+    HandleWait *hws[MAXIMUM_WAIT_OBJECTS];
+    HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+
+    struct HandleWaitList hwl;
+};
+
+static int handlewait_cmp(void *av, void *bv)
+{
+    HandleWait *a = (HandleWait *)av, *b = (HandleWait *)bv;
+    if (a->index < b->index)
+        return -1;
+    if (a->index > b->index)
+        return +1;
+    return 0;
+}
+
+static tree234 *handlewaits_tree_real;
+
+static inline tree234 *ensure_handlewaits_tree_exists(void)
+{
+    if (!handlewaits_tree_real)
+        handlewaits_tree_real = newtree234(handlewait_cmp);
+    return handlewaits_tree_real;
+}
+
+static int allocate_index(void)
+{
+    tree234 *t = ensure_handlewaits_tree_exists();
+    search234_state st[1];
+
+    search234_start(st, t);
+    while (st->element) {
+        HandleWait *hw = (HandleWait *)st->element;
+        if (st->index < hw->index) {
+            /* There are unused index slots to the left of this element */
+            search234_step(st, -1);
+        } else {
+            assert(st->index == hw->index);
+            search234_step(st, +1);
+        }
+    }
+
+    return st->index;
+}
+
+HandleWait *add_handle_wait(HANDLE h, handle_wait_callback_fn_t callback,
+                            void *callback_ctx)
+{
+    HandleWait *hw = snew(HandleWait);
+    hw->handle = h;
+    hw->callback = callback;
+    hw->callback_ctx = callback_ctx;
+
+    tree234 *t = ensure_handlewaits_tree_exists();
+    hw->index = allocate_index();
+    HandleWait *added = add234(t, hw);
+    assert(added == hw);
+
+    return hw;
+}
+
+void delete_handle_wait(HandleWait *hw)
+{
+    tree234 *t = ensure_handlewaits_tree_exists();
+    HandleWait *deleted = del234(t, hw);
+    assert(deleted == hw);
+    sfree(hw);
+}
+
+HandleWaitList *get_handle_wait_list(void)
+{
+    tree234 *t = ensure_handlewaits_tree_exists();
+    struct HandleWaitListInner *hwli = snew(struct HandleWaitListInner);
+    size_t n = 0;
+    HandleWait *hw;
+    for (int i = 0; (hw = index234(t, i)) != NULL; i++) {
+        assert(n < MAXIMUM_WAIT_OBJECTS);
+        hwli->hws[n] = hw;
+        hwli->hwl.handles[n] = hw->handle;
+        n++;
+    }
+    hwli->hwl.nhandles = n;
+    return &hwli->hwl;
+}
+
+void handle_wait_activate(HandleWaitList *hwl, int index)
+{
+    struct HandleWaitListInner *hwli =
+        container_of(hwl, struct HandleWaitListInner, hwl);
+    assert(0 <= index);
+    assert(index < hwli->hwl.nhandles);
+    HandleWait *hw = hwli->hws[index];
+    hw->callback(hw->callback_ctx);
+}
+
+void handle_wait_list_free(HandleWaitList *hwl)
+{
+    struct HandleWaitListInner *hwli =
+        container_of(hwl, struct HandleWaitListInner, hwl);
+    sfree(hwli);
+}

+ 32 - 39
source/putty/windows/winproxy.c → source/putty/windows/local-proxy.c

@@ -1,6 +1,6 @@
 /*
- * winproxy.c: Windows implementation of platform_new_connection(),
- * supporting an OpenSSH-like proxy command via the winhandl.c
+ * local-proxy.c: Windows implementation of platform_new_connection(),
+ * supporting an OpenSSH-like proxy command via the handle-io.c
  * mechanism.
  */
 
@@ -10,14 +10,10 @@
 #include "tree234.h"
 #include "putty.h"
 #include "network.h"
-#include "proxy.h"
+#include "proxy/proxy.h"
 
-Socket *platform_new_connection(SockAddr *addr, const char *hostname,
-                                int port, bool privport,
-                                bool oobinline, bool nodelay, bool keepalive,
-                                Plug *plug, Conf *conf)
+char *platform_setup_local_proxy(Socket *socket, const char *cmd)
 {
-    char *cmd;
     HANDLE us_to_cmd, cmd_from_us;
     HANDLE us_from_cmd, cmd_to_us;
     HANDLE us_from_cmd_err, cmd_err_to_us;
@@ -25,20 +21,6 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
 
-    if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
-        return NULL;
-
-    cmd = format_telnet_command(addr, port, conf);
-
-    /* We are responsible for this and don't need it any more */
-    sk_addr_free(addr);
-
-    {
-        char *msg = dupprintf("Starting local proxy command: %s", cmd);
-        plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0);
-        sfree(msg);
-    }
-
     /*
      * Create the pipes to the proxy command, and spawn the proxy
      * command process.
@@ -47,30 +29,24 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
     sa.lpSecurityDescriptor = NULL;    /* default */
     sa.bInheritHandle = true;
     if (!CreatePipe(&us_from_cmd, &cmd_to_us, &sa, 0)) {
-        sfree(cmd);
-        return new_error_socket_fmt(
-            plug, "Unable to create pipes for proxy command: %s",
-            win_strerror(GetLastError()));
+        return dupprintf("Unable to create pipes for proxy command: %s",
+                         win_strerror(GetLastError()));
     }
 
     if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) {
-        sfree(cmd);
         CloseHandle(us_from_cmd);
         CloseHandle(cmd_to_us);
-        return new_error_socket_fmt(
-            plug, "Unable to create pipes for proxy command: %s",
-            win_strerror(GetLastError()));
+        return dupprintf("Unable to create pipes for proxy command: %s",
+                         win_strerror(GetLastError()));
     }
 
     if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
-        sfree(cmd);
         CloseHandle(us_from_cmd);
         CloseHandle(cmd_to_us);
         CloseHandle(us_to_cmd);
         CloseHandle(cmd_from_us);
-        return new_error_socket_fmt(
-            plug, "Unable to create pipes for proxy command: %s",
-            win_strerror(GetLastError()));
+        return dupprintf("Unable to create pipes for proxy command: %s",
+                         win_strerror(GetLastError()));
     }
 
     SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);
@@ -88,20 +64,37 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname,
     si.hStdInput = cmd_from_us;
     si.hStdOutput = cmd_to_us;
     si.hStdError = cmd_err_to_us;
-    CreateProcess(NULL, cmd, NULL, NULL, true,
+    char *cmd_mutable = dupstr(cmd); /* CreateProcess needs non-const char * */
+    CreateProcess(NULL, cmd_mutable, NULL, NULL, true,
                   CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
                   NULL, NULL, &si, &pi);
+    sfree(cmd_mutable);
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
 
-    sfree(cmd);
-
     CloseHandle(cmd_from_us);
     CloseHandle(cmd_to_us);
 
     if (cmd_err_to_us != NULL)
         CloseHandle(cmd_err_to_us);
 
-    return make_handle_socket(us_to_cmd, us_from_cmd, us_from_cmd_err,
-                              plug, false);
+    setup_handle_socket(socket, us_to_cmd, us_from_cmd, us_from_cmd_err,
+                        false);
+
+    return NULL;
+}
+
+Socket *platform_new_connection(SockAddr *addr, const char *hostname,
+                                int port, bool privport,
+                                bool oobinline, bool nodelay, bool keepalive,
+                                Plug *plug, Conf *conf, Interactor *itr)
+{
+    if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
+        return NULL;
+
+    DeferredSocketOpener *opener = local_proxy_opener(
+        addr, port, plug, conf, itr);
+    Socket *socket = make_deferred_handle_socket(opener, addr, port, plug);
+    local_proxy_opener_set_socket(opener, socket);
+    return socket;
 }

+ 9 - 12
source/putty/windows/winnpc.c → source/putty/windows/named-pipe-client.c

@@ -8,12 +8,10 @@
 #include "tree234.h"
 #include "putty.h"
 #include "network.h"
-#include "proxy.h"
+#include "proxy/proxy.h"
 #include "ssh.h"
 
-#if !defined NO_SECURITY
-
-#include "winsecur.h"
+#include "security-api.h"
 
 HANDLE connect_to_named_pipe(const char *pipename, char **err)
 {
@@ -40,11 +38,11 @@ HANDLE connect_to_named_pipe(const char *pipename, char **err)
         }
 
         /*
-         * If we got ERROR_PIPE_BUSY, wait for the server to
-         * create a new pipe instance. (Since the server is
-         * expected to be winnps.c, which will do that immediately
-         * after a previous connection is accepted, that shouldn't
-         * take excessively long.)
+         * If we got ERROR_PIPE_BUSY, wait for the server to create a
+         * new pipe instance. (Since the server is expected to be
+         * named-pipe-server.c, which will do that immediately after a
+         * previous connection is accepted, that shouldn't take
+         * excessively long.)
          */
         if (!WaitNamedPipe(pipename, NMPWAIT_USE_DEFAULT_WAIT)) {
             *err = dupprintf(
@@ -92,7 +90,6 @@ Socket *new_named_pipe_client(const char *pipename, Plug *plug)
     if (pipehandle == INVALID_HANDLE_VALUE)
         return new_error_socket_consume_string(plug, err);
     else
-        return make_handle_socket(pipehandle, pipehandle, NULL, plug, true);
+        return make_handle_socket(pipehandle, pipehandle, NULL, NULL, 0,
+                                  plug, true);
 }
-
-#endif /* !defined NO_SECURITY */

+ 21 - 9
source/putty/windows/winnet.c → source/putty/windows/network.c

@@ -1449,6 +1449,19 @@ static void sk_net_close(Socket *sock)
     sfree(s);
 }
 
+void plug_closing_system_error(Plug *plug, DWORD error)
+{
+    PlugCloseType type = PLUGCLOSE_ERROR;
+    if (error == ERROR_BROKEN_PIPE)
+        type = PLUGCLOSE_BROKEN_PIPE;
+    plug_closing(plug, type, win_strerror(error));
+}
+
+void plug_closing_winsock_error(Plug *plug, DWORD error)
+{
+    plug_closing(plug, PLUGCLOSE_ERROR, winsock_error_string(error));
+}
+
 /*
  * Deal with socket errors detected in try_send().
  */
@@ -1466,8 +1479,7 @@ static void socket_error_callback(void *vs)
     /*
      * An error has occurred on this socket. Pass it to the plug.
      */
-    plug_closing(s->plug, winsock_error_string(s->pending_error),
-                 s->pending_error, 0);
+    plug_closing_winsock_error(s->plug, s->pending_error);
 }
 
 /*
@@ -1649,7 +1661,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
         }
         if (err != 0)
         {
-            plug_closing(s->plug, winsock_error_string(err), err, 0);
+            plug_closing_winsock_error(s->plug, err);
         }
         return;
     }
@@ -1712,9 +1724,9 @@ void select_result(WPARAM wParam, LPARAM lParam)
             }
         }
         if (ret < 0) {
-            plug_closing(s->plug, winsock_error_string(err), err, 0);
+            plug_closing_winsock_error(s->plug, err);
         } else if (0 == ret) {
-            plug_closing(s->plug, NULL, 0, 0);
+            plug_closing_normal(s->plug);
         } else {
             plug_receive(s->plug, atmark ? 0 : 1, buf, ret);
         }
@@ -1730,7 +1742,7 @@ void select_result(WPARAM wParam, LPARAM lParam)
         noise_ultralight(NOISE_SOURCE_IOLEN, ret);
         if (ret <= 0) {
             int err = p_WSAGetLastError();
-            plug_closing(s->plug, winsock_error_string(err), err, 0);
+            plug_closing_winsock_error(s->plug, err);
         } else {
             plug_receive(s->plug, 2, buf, ret);
         }
@@ -1753,13 +1765,13 @@ void select_result(WPARAM wParam, LPARAM lParam)
                 err = p_WSAGetLastError();
                 if (err == WSAEWOULDBLOCK)
                     break;
-                plug_closing(s->plug, winsock_error_string(err), err, 0);
+                plug_closing_winsock_error(s->plug, err);
             } else {
                 if (ret)
                     plug_receive(s->plug, 0, buf, ret);
                 else
                 {
-                    plug_closing(s->plug, NULL, 0, 0);
+                    plug_closing_normal(s->plug);
                 }
             }
         } while (ret > 0);
@@ -1929,7 +1941,7 @@ bool socket_writable(SOCKET skt)
         return false;
 }
 
-int net_service_lookup(char *service)
+int net_service_lookup(const char *service)
 {
     struct servent *se;
     se = p_getservbyname(service, NULL);

+ 2 - 2
source/putty/windows/winnojmp.c → source/putty/windows/no-jump-list.c

@@ -1,6 +1,6 @@
 /*
- * winnojmp.c: stub jump list functions for Windows executables that
- * don't update the jump list.
+ * no-jump-list.c: stub jump list functions for Windows executables
+ * that don't update the jump list.
  */
 
 void add_session_to_jumplist(const char * const sessionname) {}

+ 0 - 0
source/putty/windows/winnoise.c → source/putty/windows/noise.c


部分文件因为文件数量过多而无法显示