Browse Source

Merge branch 'thirdparty_dev' into dev

Conflicts:
	source/putty/WINDOWS/winproxy.c
	source/putty/misc.c
	source/putty/proxy.c
	source/putty/ssh.c
	source/putty/sshbn.h
	source/putty/sshpubk.c

Source commit: e85dbbd7b3a324935d9a8f0fb2e38a8e7efd199a
Martin Prikryl 9 years ago
parent
commit
11d0bb9038
47 changed files with 7863 additions and 1293 deletions
  1. 38 4
      source/putty/conf.c
  2. 1 1
      source/putty/cproxy.c
  3. 569 316
      source/putty/import.c
  4. 1 1
      source/putty/logging.c
  5. 73 10
      source/putty/misc.c
  6. 24 4
      source/putty/misc.h
  7. 25 39
      source/putty/network.h
  8. 3 2
      source/putty/portfwd.c
  9. 98 7
      source/putty/proxy.c
  10. 1 1
      source/putty/proxy.h
  11. 52 31
      source/putty/putty.h
  12. 395 168
      source/putty/ssh.c
  13. 244 43
      source/putty/ssh.h
  14. 21 14
      source/putty/sshaes.c
  15. 6 4
      source/putty/ssharcf.c
  16. 124 0
      source/putty/sshbcrypt.c
  17. 76 19
      source/putty/sshblowf.c
  18. 15 0
      source/putty/sshblowf.h
  19. 584 198
      source/putty/sshbn.c
  20. 209 98
      source/putty/sshbn.h
  21. 1059 0
      source/putty/sshccp.c
  22. 12 8
      source/putty/sshdes.c
  23. 30 6
      source/putty/sshdh.c
  24. 61 81
      source/putty/sshdss.c
  25. 2967 0
      source/putty/sshecc.c
  26. 71 0
      source/putty/sshecdsag.c
  27. 3 3
      source/putty/sshmd5.c
  28. 547 26
      source/putty/sshpubk.c
  29. 17 1
      source/putty/sshrand.c
  30. 32 61
      source/putty/sshrsa.c
  31. 25 7
      source/putty/sshsh256.c
  32. 115 0
      source/putty/sshsh512.c
  33. 30 13
      source/putty/sshsha.c
  34. 115 43
      source/putty/sshshare.c
  35. 1 0
      source/putty/sshzlib.c
  36. 4 4
      source/putty/version.h
  37. 1 1
      source/putty/windows/wingss.c
  38. 4 4
      source/putty/windows/winhandl.c
  39. 23 4
      source/putty/windows/winhsock.c
  40. 5 3
      source/putty/windows/winmisc.c
  41. 8 8
      source/putty/windows/winnet.c
  42. 30 9
      source/putty/windows/winproxy.c
  43. 105 36
      source/putty/windows/winsecur.c
  44. 5 7
      source/putty/windows/winsecur.h
  45. 11 1
      source/putty/windows/winstore.c
  46. 14 0
      source/putty/windows/winstuff.h
  47. 9 7
      source/putty/x11fwd.c

+ 38 - 4
source/putty/conf.c

@@ -39,6 +39,16 @@ struct key {
     } 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 {
 	int intval;
@@ -88,6 +98,29 @@ static int conf_cmp(void *av, void *bv)
     }
 }
 
+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
@@ -286,7 +319,7 @@ char *conf_get_str_str(Conf *conf, int primary, const char *secondary)
 char *conf_get_str_strs(Conf *conf, int primary,
 		       char *subkeyin, char **subkeyout)
 {
-    struct key key;
+    struct constkey key;
     struct conf_entry *entry;
 
     assert(subkeytypes[primary] == TYPE_STR);
@@ -297,7 +330,7 @@ char *conf_get_str_strs(Conf *conf, int primary,
 	entry = findrel234(conf->tree, &key, NULL, REL234_GT);
     } else {
 	key.secondary.s = "";
-	entry = findrel234(conf->tree, &key, NULL, REL234_GE);
+	entry = findrel234(conf->tree, &key, conf_cmp_constkey, REL234_GE);
     }
     if (!entry || entry->key.primary != primary)
 	return NULL;
@@ -307,7 +340,7 @@ char *conf_get_str_strs(Conf *conf, int primary,
 
 char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
 {
-    struct key key;
+    struct constkey key;
     struct conf_entry *entry;
     int index;
 
@@ -315,7 +348,8 @@ char *conf_get_str_nthstrkey(Conf *conf, int primary, int n)
     assert(valuetypes[primary] == TYPE_STR);
     key.primary = primary;
     key.secondary.s = "";
-    entry = findrelpos234(conf->tree, &key, NULL, REL234_GE, &index);
+    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);

+ 1 - 1
source/putty/cproxy.c

@@ -21,7 +21,7 @@ static void hmacmd5_chap(const unsigned char *challenge, int challen,
     void *hmacmd5_ctx;
     int pwlen;
 
-    hmacmd5_ctx = hmacmd5_make_context();
+    hmacmd5_ctx = hmacmd5_make_context(NULL);
 
     pwlen = strlen(passwd);
     if (pwlen>64) {

File diff suppressed because it is too large
+ 569 - 316
source/putty/import.c


+ 1 - 1
source/putty/logging.c

@@ -256,7 +256,7 @@ void log_eventlog(void *handle, const char *event)
  * Set of blanking areas must be in increasing order.
  */
 void log_packet(void *handle, int direction, int type,
-		char *texttype, const void *data, int len,
+		const char *texttype, const void *data, int len,
 		int n_blanks, const struct logblank_t *blanks,
 		const unsigned long *seq,
                 unsigned downstream_id, const char *additional_log_text)

+ 73 - 10
source/putty/misc.c

@@ -9,6 +9,7 @@
 #include <ctype.h>
 #include <assert.h>
 #include "putty.h"
+#include "misc.h"
 
 /*
  * Parse a string block size specification. This is approximately a
@@ -179,7 +180,7 @@ int main(void)
     return fails != 0 ? 1 : 0;
 }
 /* Stubs to stop the rest of this module causing compile failures. */
-void modalfatalbox(char *fmt, ...) {}
+void modalfatalbox(const char *fmt, ...) {}
 int conf_get_int(Conf *conf, int primary) { return 0; }
 char *conf_get_str(Conf *conf, int primary) { return NULL; }
 #endif /* TEST_HOST_STRFOO */
@@ -416,7 +417,7 @@ char *dupvprintf(const char *fmt, va_list ap)
     size = 512;
 
     while (1) {
-#ifdef _WINDOWS
+#if defined _WINDOWS && _MSC_VER < 1900 /* 1900 == VS2015 has real snprintf */
 #define vsnprintf _vsnprintf
 #endif
 #ifdef va_copy
@@ -464,7 +465,7 @@ char *fgetline(FILE *fp)
     int size = 512, len = 0;
     while (fgets(ret + len, size - len, fp)) {
 	len += strlen(ret + len);
-	if (ret[len-1] == '\n')
+	if (len > 0 && ret[len-1] == '\n')
 	    break;		       /* got a newline, we're done */
 	size = len + 512;
 	ret = sresize(ret, size, char);
@@ -477,11 +478,29 @@ char *fgetline(FILE *fp)
     return ret;
 }
 
+/*
+ * 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(unsigned char *data, int n, char *out)
+void base64_encode_atom(const unsigned char *data, int n, char *out)
 {
     static const char base64_chars[] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -505,7 +524,7 @@ void base64_encode_atom(unsigned char *data, int n, char *out)
 	out[3] = '=';
 }
 
-int base64_decode_atom(char *atom, unsigned char *out)
+int base64_decode_atom(const char *atom, unsigned char *out)
 {
     int vals[4];
     int i, v, len;
@@ -819,9 +838,9 @@ void safefree(void *ptr)
  */
 
 #ifdef DEBUG
-extern void dputs(char *);             /* defined in per-platform *misc.c */
+extern void dputs(const char *); /* defined in per-platform *misc.c */
 
-void debug_printf(char *fmt, ...)
+void debug_printf(const char *fmt, ...)
 {
     char *buf;
     va_list ap;
@@ -834,15 +853,15 @@ void debug_printf(char *fmt, ...)
 }
 
 
-void debug_memdump(void *buf, int len, int L)
+void debug_memdump(const void *buf, int len, int L)
 {
     int i;
-    unsigned char *p = buf;
+    const unsigned char *p = buf;
     char foo[17];
     if (L) {
 	int delta;
 	debug_printf("\t%d (0x%x) bytes:\n", len, len);
-	delta = 15 & (unsigned long int) p;
+	delta = 15 & (uintptr_t)p;
 	p -= delta;
 	len += delta;
     }
@@ -1044,6 +1063,50 @@ int smemeq(const void *av, const void *bv, size_t len)
     return (0x100 - val) >> 8;
 }
 
+int match_ssh_id(int stringlen, const void *string, const char *id)
+{
+    int idlen = strlen(id);
+    return (idlen == stringlen && !memcmp(string, id, idlen));
+}
+
+void *get_ssh_string(int *datalen, const void **data, int *stringlen)
+{
+    void *ret;
+    unsigned int len;
+
+    if (*datalen < 4)
+        return NULL;
+    len = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
+    if (*datalen < len+4)
+        return NULL;
+    ret = (void *)((const char *)*data + 4);
+    *datalen -= len + 4;
+    *data = (const char *)*data + len + 4;
+    *stringlen = len;
+    return ret;
+}
+
+int get_ssh_uint32(int *datalen, const void **data, unsigned *ret)
+{
+    if (*datalen < 4)
+        return FALSE;
+    *ret = GET_32BIT_MSB_FIRST((const unsigned char *)*data);
+    *datalen -= 4;
+    *data = (const char *)*data + 4;
+    return TRUE;
+}
+
+int strstartswith(const char *s, const char *t)
+{
+    return !memcmp(s, t, strlen(t));
+}
+
+int strendswith(const char *s, const char *t)
+{
+    size_t slen = strlen(s), tlen = strlen(t);
+    return slen >= tlen && !strcmp(s + (slen - tlen), t);
+}
+
 #ifdef MPEXT
 
 #include "version.h"

+ 24 - 4
source/putty/misc.h

@@ -54,9 +54,12 @@ wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string);
 int toint(unsigned);
 
 char *fgetline(FILE *fp);
+char *chomp(char *str);
+int strstartswith(const char *s, const char *t);
+int strendswith(const char *s, const char *t);
 
-void base64_encode_atom(unsigned char *data, int n, char *out);
-int base64_decode_atom(char *atom, unsigned char *out);
+void base64_encode_atom(const unsigned char *data, int n, char *out);
+int base64_decode_atom(const char *atom, unsigned char *out);
 
 struct bufchain_granule;
 typedef struct bufchain_tag {
@@ -90,6 +93,23 @@ void smemclr(void *b, size_t len);
  * by the 'eq' in the name. */
 int smemeq(const void *av, const void *bv, size_t len);
 
+/* Extracts an SSH-marshalled string from the start of *data. If
+ * successful (*datalen is not too small), advances data/datalen past
+ * the string and returns a pointer to the string itself and its
+ * length in *stringlen. Otherwise does nothing and returns NULL.
+ *
+ * Like strchr, this function can discard const from its parameter.
+ * Treat it as if it was a family of two functions, one returning a
+ * non-const string given a non-const pointer, and one taking and
+ * returning const. */
+void *get_ssh_string(int *datalen, const void **data, int *stringlen);
+/* Extracts an SSH uint32, similarly. Returns TRUE on success, and
+ * leaves the extracted value in *ret. */
+int get_ssh_uint32(int *datalen, const void **data, unsigned *ret);
+/* Given a not-necessarily-zero-terminated string in (length,data)
+ * form, check if it equals an ordinary C zero-terminated string. */
+int match_ssh_id(int stringlen, const void *string, const char *id);
+
 /*
  * Debugging functions.
  *
@@ -104,8 +124,8 @@ int smemeq(const void *av, const void *bv, size_t len);
  */
 
 #ifdef DEBUG
-void debug_printf(char *fmt, ...);
-void debug_memdump(void *buf, int len, int L);
+void debug_printf(const char *fmt, ...);
+void debug_memdump(const void *buf, int len, int L);
 #define debug(x) (debug_printf x)
 #define dmemdump(buf,len) debug_memdump (buf, len, 0);
 #define dmemdumpl(buf,len) debug_memdump (buf, len, 1);

+ 25 - 39
source/putty/network.h

@@ -58,6 +58,11 @@ struct plug_function_table {
      * 	  fatal error - we may well have other candidate addresses
      * 	  to fall back to. When it _is_ fatal, the closing()
      * 	  function will be called.
+     *  - type==2 means that error_msg contains a line of generic
+     *    logging information about setting up the connection. This
+     *    will typically be a wodge of standard-error output from a
+     *    proxy command, so the receiver should probably prefix it to
+     *    indicate this.
      */
     int (*closing)
      (Plug p, const char *error_msg, int error_code, int calling_back);
@@ -93,20 +98,21 @@ struct plug_function_table {
 /* proxy indirection layer */
 /* NB, control of 'addr' is passed via new_connection, which takes
  * responsibility for freeing it */
-Socket new_connection(SockAddr addr, char *hostname,
+Socket new_connection(SockAddr addr, const char *hostname,
 		      int port, int privport,
 		      int oobinline, int nodelay, int keepalive,
 		      Plug plug, Conf *conf);
-Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
-		    Conf *conf, int addressfamily);
-SockAddr name_lookup(char *host, int port, char **canonicalname,
-		     Conf *conf, int addressfamily);
+Socket new_listener(const char *srcaddr, int port, Plug plug,
+                    int local_host_only, Conf *conf, int addressfamily);
+SockAddr name_lookup(const char *host, int port, char **canonicalname,
+		     Conf *conf, int addressfamily, void *frontend_for_logging,
+                     const char *lookup_reason_for_logging);
 int proxy_for_destination (SockAddr addr, const char *hostname, int port,
                            Conf *conf);
 
 /* platform-dependent callback from new_connection() */
 /* (same caveat about addr as new_connection()) */
-Socket platform_new_connection(SockAddr addr, char *hostname,
+Socket platform_new_connection(SockAddr addr, const char *hostname,
 			       int port, int privport,
 			       int oobinline, int nodelay, int keepalive,
 			       Plug plug, Conf *conf);
@@ -148,7 +154,8 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 #endif
 	      );
 
-Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int address_family);
+Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
+                      int local_host_only, int address_family);
 
 #define sk_plug(s,p) (((*s)->plug) (s, p))
 #define sk_close(s) (((*s)->close) (s))
@@ -220,40 +227,19 @@ char *get_hostname(void);
  */
 Socket new_error_socket(const char *errmsg, Plug plug);
 
-/********** SSL stuff **********/
+/* ----------------------------------------------------------------------
+ * 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.
+ */
 
 /*
- * This section is subject to change, but you get the general idea
- * of what it will eventually look like.
+ * Exports from be_misc.c.
  */
-
-typedef struct certificate *Certificate;
-typedef struct our_certificate *Our_Certificate;
-    /* to be defined somewhere else, somehow */
-
-typedef struct ssl_client_socket_function_table **SSL_Client_Socket;
-typedef struct ssl_client_plug_function_table **SSL_Client_Plug;
-
-struct ssl_client_socket_function_table {
-    struct socket_function_table base;
-    void (*renegotiate) (SSL_Client_Socket s);
-    /* renegotiate the cipher spec */
-};
-
-struct ssl_client_plug_function_table {
-    struct plug_function_table base;
-    int (*refuse_cert) (SSL_Client_Plug p, Certificate cert[]);
-    /* do we accept this certificate chain?  If not, why not? */
-    /* cert[0] is the server's certificate, cert[] is NULL-terminated */
-    /* the last certificate may or may not be the root certificate */
-     Our_Certificate(*client_cert) (SSL_Client_Plug p);
-    /* the server wants us to identify ourselves */
-    /* may return NULL if we want anonymity */
-};
-
-SSL_Client_Socket sk_ssl_client_over(Socket s,	/* pre-existing (tcp) connection */
-				     SSL_Client_Plug p);
-
-#define sk_renegotiate(s) (((*s)->renegotiate) (s))
+void backend_socket_log(void *frontend, int type, SockAddr addr, int port,
+                        const char *error_msg, int error_code, Conf *conf,
+                        int session_started);
+typedef struct bufchain_tag bufchain;  /* rest of declaration in misc.c */
+void log_proxy_stderr(Plug plug, bufchain *buf, const void *vdata, int len);
 
 #endif

+ 3 - 2
source/putty/portfwd.c

@@ -157,7 +157,7 @@ static int pfl_closing(Plug plug, const char *error_msg, int error_code,
     return 1;
 }
 
-static void wrap_send_port_open(void *channel, char *hostname, int port,
+static void wrap_send_port_open(void *channel, const char *hostname, int port,
                                 Socket s)
 {
     char *peerinfo, *description;
@@ -443,7 +443,8 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
     /*
      * Try to find host.
      */
-    addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);
+    addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily,
+                       NULL, NULL);
     if ((err = sk_addr_error(addr)) != NULL) {
         char *err_ret = dupstr(err);
 	sk_addr_free(addr);

+ 98 - 7
source/putty/proxy.c

@@ -362,20 +362,45 @@ int proxy_for_destination (SockAddr addr, const char *hostname,
     return 1;
 }
 
-SockAddr name_lookup(char *host, int port, char **canonicalname,
-		     Conf *conf, int addressfamily)
+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, void *frontend,
+                     const char *reason)
+{
+    char *logmsg;
     if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&
 	do_proxy_dns(conf) &&
 	proxy_for_destination(NULL, host, port, conf)) {
+
+        if (frontend) {
+            logmsg = dupprintf("Leaving host lookup to proxy of \"%s\""
+                               " (for %s)", host, reason);
+            logevent(frontend, logmsg);
+            sfree(logmsg);
+        }
+
 	*canonicalname = dupstr(host);
 	return sk_nonamelookup(host);
+    } else {
+        if (frontend) {
+            logmsg = dns_log_msg(host, addressfamily, reason);
+            logevent(frontend, logmsg);
+            sfree(logmsg);
+        }
+
+        return sk_namelookup(host, canonicalname, addressfamily);
     }
-
-    return sk_namelookup(host, canonicalname, addressfamily);
 }
 
-Socket new_connection(SockAddr addr, char *hostname,
+Socket new_connection(SockAddr addr, const char *hostname,
 		      int port, int privport,
 		      int oobinline, int nodelay, int keepalive,
 		      Plug plug, Conf *conf)
@@ -407,6 +432,7 @@ Socket new_connection(SockAddr addr, char *hostname,
 	Proxy_Plug pplug;
 	SockAddr proxy_addr;
 	char *proxy_canonical_name;
+        const char *proxy_type;
 	Socket sret;
 	int type;
 
@@ -439,23 +465,45 @@ Socket new_connection(SockAddr addr, char *hostname,
 	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 (Socket) ret;
 	}
 
+        {
+            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, 2, NULL, 0, logmsg, 0);
+            sfree(logmsg);
+        }
+
 	/* create the proxy plug to map calls from the actual
 	 * socket into our proxy socket layer */
 	pplug = snew(struct Plug_proxy_tag);
 	pplug->fn = &plug_fn_table;
 	pplug->proxy_socket = ret;
 
+        {
+            char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host),
+                                       conf_get_int(conf, CONF_addressfamily),
+                                       "proxy");
+            plug_log(plug, 2, NULL, 0, logmsg, 0);
+            sfree(logmsg);
+        }
+
 	/* look-up proxy */
 	proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),
 				   &proxy_canonical_name,
@@ -468,6 +516,16 @@ Socket new_connection(SockAddr addr, char *hostname,
 	}
 	sfree(proxy_canonical_name);
 
+        {
+            char addrbuf[256], *logmsg;
+            sk_getaddr(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, 2, NULL, 0, logmsg, 0);
+            sfree(logmsg);
+        }
+
 	/* create the actual socket we will be using,
 	 * connected to our proxy server and port.
 	 */
@@ -497,8 +555,8 @@ Socket new_connection(SockAddr addr, char *hostname,
       );
 }
 
-Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,
-		    Conf *conf, int addressfamily)
+Socket new_listener(const char *srcaddr, int port, Plug plug,
+                    int local_host_only, Conf *conf, int addressfamily)
 {
     /* TODO: SOCKS (and potentially others) support inbound
      * TODO: connections via the proxy. support them.
@@ -1462,6 +1520,39 @@ int proxy_telnet_negotiate (Proxy_Socket p, int change)
 	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, 2, NULL, 0, logmsg, 0);
+            sfree(logmsg);
+            sfree(reescaped);
+        }
+
 	sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));
 	sfree(formatted_cmd);
 

+ 1 - 1
source/putty/proxy.h

@@ -19,7 +19,7 @@ struct Socket_proxy_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
 
-    char * error;
+    const char *error;
 
     Socket sub_socket;
     Plug plug;

+ 52 - 31
source/putty/putty.h

@@ -141,7 +141,7 @@ typedef struct terminal_tag Terminal;
 
 struct sesslist {
     int nsessions;
-    char **sessions;
+    const char **sessions;
     char *buffer;		       /* so memory can be freed later */
 };
 
@@ -255,6 +255,7 @@ enum {
     KEX_DHGROUP14,
     KEX_DHGEX,
     KEX_RSA,
+    KEX_ECDH,
     KEX_MAX
 };
 
@@ -268,6 +269,7 @@ enum {
     CIPHER_AES,			       /* (SSH-2 only) */
     CIPHER_DES,
     CIPHER_ARCFOUR,
+    CIPHER_CHACHA20,
     CIPHER_MAX			       /* no. ciphers (inc warn) */
 };
 
@@ -277,9 +279,9 @@ enum {
      * three-way settings whose values are `always yes', `always
      * no', and `decide by some more complex automated means'. This
      * is true of line discipline options (local echo and line
-     * editing), proxy DNS, Close On Exit, and SSH server bug
-     * workarounds. Accordingly I supply a single enum here to deal
-     * with them all.
+     * editing), proxy DNS, proxy terminal logging, Close On Exit, and
+     * SSH server bug workarounds. Accordingly I supply a single enum
+     * here to deal with them all.
      */
     FORCE_ON, FORCE_OFF, AUTO
 };
@@ -289,7 +291,7 @@ enum {
      * Proxy types.
      */
     PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5,
-    PROXY_HTTP, PROXY_TELNET, PROXY_CMD
+    PROXY_HTTP, PROXY_TELNET, PROXY_CMD, PROXY_FUZZ
 };
 
 enum {
@@ -360,7 +362,7 @@ struct keyvalwhere {
      * Two fields which define a string and enum value to be
      * equivalent to each other.
      */
-    char *s;
+    const char *s;
     int v;
 
     /*
@@ -417,13 +419,13 @@ enum {
 
 struct backend_tag {
     const char *(*init) (void *frontend_handle, void **backend_handle,
-			 Conf *conf, char *host, int port, char **realhost,
-			 int nodelay, int keepalive);
+			 Conf *conf, const char *host, int port,
+                         char **realhost, int nodelay, int keepalive);
     void (*free) (void *handle);
     /* back->reconfig() passes in a replacement configuration. */
     void (*reconfig) (void *handle, Conf *conf);
     /* back->send() returns the current amount of buffered data. */
-    int (*send) (void *handle, char *buf, int len);
+    int (*send) (void *handle, const char *buf, int len);
     /* back->sendbuffer() does the same thing but without attempting a send */
     int (*sendbuffer) (void *handle);
     void (*size) (void *handle, int width, int height);
@@ -443,7 +445,10 @@ struct backend_tag {
      */
     void (*unthrottle) (void *handle, int);
     int (*cfg_info) (void *handle);
-    char *name;
+    /* Only implemented in the SSH protocol: check whether a
+     * connection-sharing upstream exists for a given configuration. */
+    int (*test_for_upstream)(const char *host, int port, Conf *conf);
+    const char *name;
     int protocol;
     int default_port;
 };
@@ -590,10 +595,10 @@ void write_clip(void *frontend, wchar_t *, int *, int, int);
 void get_clip(void *frontend, wchar_t **, int *);
 void optimised_move(void *frontend, int, int, int);
 void set_raw_mouse_mode(void *frontend, int);
-void connection_fatal(void *frontend, char *, ...);
-void nonfatal(char *, ...);
-void fatalbox(char *, ...);
-void modalfatalbox(char *, ...);
+void connection_fatal(void *frontend, const char *, ...);
+void nonfatal(const char *, ...);
+void fatalbox(const char *, ...);
+void modalfatalbox(const char *, ...);
 #ifdef macintosh
 #pragma noreturn(fatalbox)
 #pragma noreturn(modalfatalbox)
@@ -603,7 +608,7 @@ void begin_session(void *frontend);
 void sys_cursor(void *frontend, int x, int y);
 void request_paste(void *frontend);
 void frontend_keypress(void *frontend);
-void ldisc_update(void *frontend, int echo, int edit);
+void frontend_echoedit_update(void *frontend, int echo, int edit);
 /* It's the backend's responsibility to invoke this at the start of a
  * connection, if necessary; it can also invoke it later if the set of
  * special commands changes. It does not need to invoke it at session
@@ -625,7 +630,7 @@ char *get_ttymode(void *frontend, const char *mode);
  * 0  = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?)
  * <0 = `please call back later with more in/inlen'
  */
-int get_userpass_input(prompts_t *p, unsigned char *in, int inlen);
+int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen);
 #define OPTIMISE_IS_SCROLL 1
 
 void set_iconic(void *frontend, int iconic);
@@ -676,6 +681,7 @@ void cleanup_exit(int);
     X(STR, NONE, proxy_username) \
     X(STR, NONE, proxy_password) \
     X(STR, NONE, proxy_telnet_command) \
+    X(INT, NONE, proxy_log_to_term) \
     /* SSH options */ \
     X(STR, NONE, remote_cmd) \
     X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \
@@ -751,6 +757,8 @@ void cleanup_exit(int);
     X(INT, NONE, erase_to_scrollback) \
     X(INT, NONE, compose_key) \
     X(INT, NONE, ctrlaltkeys) \
+    X(INT, NONE, osx_option_meta) \
+    X(INT, NONE, osx_command_meta) \
     X(STR, NONE, wintitle) /* initial window title */ \
     /* Terminal options */ \
     X(INT, NONE, savelines) \
@@ -946,12 +954,12 @@ void random_destroy_seed(void);
 Backend *backend_from_name(const char *name);
 Backend *backend_from_proto(int proto);
 char *get_remote_username(Conf *conf); /* dynamically allocated */
-char *save_settings(char *section, Conf *conf);
+char *save_settings(const char *section, Conf *conf);
 void save_open_settings(void *sesskey, Conf *conf);
-void load_settings(char *section, Conf *conf);
+void load_settings(const char *section, Conf *conf);
 void load_open_settings(void *sesskey, Conf *conf);
 void get_sesslist(struct sesslist *, int allocate);
-void do_defaults(char *, Conf *);
+void do_defaults(const char *, Conf *);
 void registry_cleanup(void);
 
 /*
@@ -1009,7 +1017,7 @@ void term_provide_logctx(Terminal *term, void *logctx);
 void term_set_focus(Terminal *term, int has_focus);
 char *term_get_ttymode(Terminal *term, const char *mode);
 int term_get_userpass_input(Terminal *term, prompts_t *p,
-			    unsigned char *in, int inlen);
+			    const unsigned char *in, int inlen);
 
 int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
 
@@ -1032,7 +1040,7 @@ struct logblank_t {
     int type;
 };
 void log_packet(void *logctx, int direction, int type,
-		char *texttype, const void *data, int len,
+		const char *texttype, const void *data, int len,
 		int n_blanks, const struct logblank_t *blanks,
 		const unsigned long *sequence,
                 unsigned downstream_id, const char *additional_log_text);
@@ -1073,13 +1081,15 @@ extern Backend ssh_backend;
 void *ldisc_create(Conf *, Terminal *, Backend *, void *, void *);
 void ldisc_configure(void *, Conf *);
 void ldisc_free(void *);
-void ldisc_send(void *handle, char *buf, int len, int interactive);
+void ldisc_send(void *handle, const char *buf, int len, int interactive);
+void ldisc_echoedit_update(void *handle);
 
 /*
  * Exports from ldiscucs.c.
  */
-void lpage_send(void *, int codepage, char *buf, int len, int interactive);
-void luni_send(void *, wchar_t * widebuf, int len, int interactive);
+void lpage_send(void *, int codepage, const char *buf, int len,
+                int interactive);
+void luni_send(void *, const wchar_t * widebuf, int len, int interactive);
 
 /*
  * Exports from sshrand.c.
@@ -1133,7 +1143,7 @@ int is_dbcs_leadbyte(int codepage, char byte);
 int mb_to_wc(int codepage, int flags, const char *mbstr, int mblen,
 	     wchar_t *wcstr, int wclen);
 int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen,
-	     char *mbstr, int mblen, char *defchr, int *defused,
+	     char *mbstr, int mblen, const char *defchr, int *defused,
 	     struct unicode_data *ucsdata);
 wchar_t xlat_uskbd2cyrllic(int ch);
 int check_compose(int first, int second);
@@ -1198,9 +1208,14 @@ void pgp_fingerprints(void);
  *    back via the provided function with a result that's either 0
  *    or +1'.
  */
-int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
-                        char *keystr, char *fingerprint,
+int verify_ssh_host_key(void *frontend, char *host, int port,
+                        const char *keytype, char *keystr, char *fingerprint,
                         void (*callback)(void *ctx, int result), void *ctx);
+/*
+ * have_ssh_host_key() just returns true if a key of that type is
+ * already chached and false otherwise.
+ */
+int have_ssh_host_key(const char *host, int port, const char *keytype);
 /*
  * askalg has the same set of return values as verify_ssh_host_key.
  */
@@ -1225,7 +1240,8 @@ void display_banner(void *frontend, const char* banner, int size);
  * that aren't equivalents to things in windlg.c et al.
  */
 extern int console_batch_mode;
-int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen);
+int console_get_userpass_input(prompts_t *p, const unsigned char *in,
+                               int inlen);
 void console_provide_logctx(void *logctx);
 int is_interactive(void);
 
@@ -1245,16 +1261,21 @@ void printer_finish_job(printer_job *);
  * Exports from cmdline.c (and also cmdline_error(), which is
  * defined differently in various places and required _by_
  * cmdline.c).
+ *
+ * Note that cmdline_process_param takes a const option string, but a
+ * writable argument string. That's not a mistake - that's so it can
+ * zero out password arguments in the hope of not having them show up
+ * avoidably in Unix 'ps'.
  */
-int cmdline_process_param(char *, char *, int, Conf *);
+int cmdline_process_param(const char *, char *, int, Conf *);
 void cmdline_run_saved(Conf *);
 void cmdline_cleanup(void);
-int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen);
+int cmdline_get_passwd_input(prompts_t *p, const unsigned char *in, int inlen);
 #define TOOLTYPE_FILETRANSFER 1
 #define TOOLTYPE_NONNETWORK 2
 extern int cmdline_tooltype;
 
-void cmdline_error(char *, ...);
+void cmdline_error(const char *, ...);
 
 /*
  * Exports from config.c.

File diff suppressed because it is too large
+ 395 - 168
source/putty/ssh.c


+ 244 - 43
source/putty/ssh.h

@@ -25,6 +25,7 @@ void sshfwd_x11_is_local(struct ssh_channel *c);
 
 extern Socket ssh_connection_sharing_init(const char *host, int port,
                                           Conf *conf, Ssh ssh, void **state);
+int ssh_share_test_for_upstream(const char *host, int port, Conf *conf);
 void share_got_pkt_from_server(void *ctx, int type,
                                unsigned char *pkt, int pktlen);
 void share_activate(void *state, const char *server_verstring);
@@ -101,9 +102,80 @@ struct dss_key {
     Bignum p, q, g, y, x;
 };
 
-int makekey(unsigned char *data, int len, struct RSAKey *result,
-	    unsigned char **keystr, int order);
-int makeprivate(unsigned char *data, int len, struct RSAKey *result);
+struct ec_curve;
+
+struct ec_point {
+    const struct ec_curve *curve;
+    Bignum x, y;
+    Bignum z;  /* Jacobian denominator */
+    unsigned char infinity;
+};
+
+void ec_point_free(struct ec_point *point);
+
+/* Weierstrass form curve */
+struct ec_wcurve
+{
+    Bignum a, b, n;
+    struct ec_point G;
+};
+
+/* Montgomery form curve */
+struct ec_mcurve
+{
+    Bignum a, b;
+    struct ec_point G;
+};
+
+/* Edwards form curve */
+struct ec_ecurve
+{
+    Bignum l, d;
+    struct ec_point B;
+};
+
+struct ec_curve {
+    enum { EC_WEIERSTRASS, EC_MONTGOMERY, EC_EDWARDS } type;
+    /* 'name' is the identifier of the curve when it has to appear in
+     * wire protocol encodings, as it does in e.g. the public key and
+     * signature formats for NIST curves. Curves which do not format
+     * their keys or signatures in this way just have name==NULL.
+     *
+     * 'textname' is non-NULL for all curves, and is a human-readable
+     * identification suitable for putting in log messages. */
+    const char *name, *textname;
+    unsigned int fieldBits;
+    Bignum p;
+    union {
+        struct ec_wcurve w;
+        struct ec_mcurve m;
+        struct ec_ecurve e;
+    };
+};
+
+const struct ssh_signkey *ec_alg_by_oid(int len, const void *oid,
+                                        const struct ec_curve **curve);
+const unsigned char *ec_alg_oid(const struct ssh_signkey *alg, int *oidlen);
+const int ec_nist_alg_and_curve_by_bits(int bits,
+                                        const struct ec_curve **curve,
+                                        const struct ssh_signkey **alg);
+const int ec_ed_alg_and_curve_by_bits(int bits,
+                                      const struct ec_curve **curve,
+                                      const struct ssh_signkey **alg);
+
+struct ssh_signkey;
+
+struct ec_key {
+    const struct ssh_signkey *signalg;
+    struct ec_point publicKey;
+    Bignum privateKey;
+};
+
+struct ec_point *ec_public(const Bignum privateKey, const struct ec_curve *curve);
+
+int makekey(const unsigned char *data, int len, struct RSAKey *result,
+	    const unsigned char **keystr, int order);
+int makeprivate(const unsigned char *data, int len, struct RSAKey *result);
 int rsaencrypt(unsigned char *data, int length, struct RSAKey *key);
 Bignum rsadecrypt(Bignum input, struct RSAKey *key);
 void rsasign(unsigned char *data, int length, struct RSAKey *key);
@@ -145,6 +217,22 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
                         unsigned char *out, int outlen,
                         void *key);
 
+/*
+ * SSH2 ECDH key exchange functions
+ */
+struct ssh_kex;
+const char *ssh_ecdhkex_curve_textname(const struct ssh_kex *kex);
+void *ssh_ecdhkex_newkey(const struct ssh_kex *kex);
+void ssh_ecdhkex_freekey(void *key);
+char *ssh_ecdhkex_getpublic(void *key, int *len);
+Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen);
+
+/*
+ * Helper function for k generation in DSA, reused in ECDSA
+ */
+Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
+                  unsigned char *digest, int digest_len);
+
 typedef struct {
     uint32 h[4];
 } MD5_Core_State;
@@ -166,7 +254,7 @@ void MD5Update(struct MD5Context *context, unsigned char const *buf,
 void MD5Final(unsigned char digest[16], struct MD5Context *context);
 void MD5Simple(void const *p, unsigned len, unsigned char output[16]);
 
-void *hmacmd5_make_context(void);
+void *hmacmd5_make_context(void *);
 void hmacmd5_free_context(void *handle);
 void hmacmd5_key(void *handle, void const *key, int len);
 void hmacmd5_do_hmac(void *handle, unsigned char const *blk, int len,
@@ -214,11 +302,17 @@ typedef struct {
     int blkused;
     uint32 len[4];
 } SHA512_State;
+#define SHA384_State SHA512_State
 void SHA512_Init(SHA512_State * s);
 void SHA512_Bytes(SHA512_State * s, const void *p, int len);
 void SHA512_Final(SHA512_State * s, unsigned char *output);
 void SHA512_Simple(const void *p, int len, unsigned char *output);
+void SHA384_Init(SHA384_State * s);
+#define SHA384_Bytes(s, p, len) SHA512_Bytes(s, p, len)
+void SHA384_Final(SHA384_State * s, unsigned char *output);
+void SHA384_Simple(const void *p, int len, unsigned char *output);
 
+struct ssh_mac;
 struct ssh_cipher {
     void *(*make_context)(void);
     void (*free_context)(void *);
@@ -226,7 +320,7 @@ struct ssh_cipher {
     void (*encrypt) (void *, unsigned char *blk, int len);
     void (*decrypt) (void *, unsigned char *blk, int len);
     int blksize;
-    char *text_name;
+    const char *text_name;
 };
 
 struct ssh2_cipher {
@@ -236,12 +330,30 @@ struct ssh2_cipher {
     void (*setkey) (void *, unsigned char *key);/* for SSH-2 */
     void (*encrypt) (void *, unsigned char *blk, int len);
     void (*decrypt) (void *, unsigned char *blk, int len);
-    char *name;
+    /* Ignored unless SSH_CIPHER_SEPARATE_LENGTH flag set */
+    void (*encrypt_length) (void *, unsigned char *blk, int len, unsigned long seq);
+    void (*decrypt_length) (void *, unsigned char *blk, int len, unsigned long seq);
+    const char *name;
     int blksize;
-    int keylen;
+    /* real_keybits is the number of bits of entropy genuinely used by
+     * the cipher scheme; it's used for deciding how big a
+     * Diffie-Hellman group is needed to exchange a key for the
+     * cipher. */
+    int real_keybits;
+    /* padded_keybytes is the number of bytes of key data expected as
+     * input to the setkey function; it's used for deciding how much
+     * data needs to be generated from the post-kex generation of key
+     * material. In a sensible cipher which uses all its key bytes for
+     * real work, this will just be real_keybits/8, but in DES-type
+     * ciphers which ignore one bit in each byte, it'll be slightly
+     * different. */
+    int padded_keybytes;
     unsigned int flags;
 #define SSH_CIPHER_IS_CBC	1
-    char *text_name;
+#define SSH_CIPHER_SEPARATE_LENGTH      2
+    const char *text_name;
+    /* If set, this takes priority over other MAC. */
+    const struct ssh_mac *required_mac;
 };
 
 struct ssh2_ciphers {
@@ -250,7 +362,8 @@ struct ssh2_ciphers {
 };
 
 struct ssh_mac {
-    void *(*make_context)(void);
+    /* Passes in the cipher context */
+    void *(*make_context)(void *);
     void (*free_context)(void *);
     void (*setkey) (void *, unsigned char *key);
     /* whole-packet operations */
@@ -261,26 +374,26 @@ struct ssh_mac {
     void (*bytes) (void *, unsigned char const *, int);
     void (*genresult) (void *, unsigned char *);
     int (*verresult) (void *, unsigned char const *);
-    char *name;
-    int len;
-    char *text_name;
+    const char *name, *etm_name;
+    int len, keylen;
+    const char *text_name;
 };
 
 struct ssh_hash {
     void *(*init)(void); /* also allocates context */
-    void (*bytes)(void *, void *, int);
+    void *(*copy)(const void *);
+    void (*bytes)(void *, const void *, int);
     void (*final)(void *, unsigned char *); /* also frees context */
+    void (*free)(void *);
     int hlen; /* output length in bytes */
-    char *text_name;
+    const char *text_name;
 };   
 
 struct ssh_kex {
-    char *name, *groupname;
-    enum { KEXTYPE_DH, KEXTYPE_RSA } main_type;
-    /* For DH */
-    const unsigned char *pdata, *gdata; /* NULL means group exchange */
-    int plen, glen;
+    const char *name, *groupname;
+    enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH } main_type;
     const struct ssh_hash *hash;
+    const void *extra;                 /* private to the kex methods */
 };
 
 struct ssh_kexes {
@@ -289,30 +402,43 @@ struct ssh_kexes {
 };
 
 struct ssh_signkey {
-    void *(*newkey) (char *data, int len);
+    void *(*newkey) (const struct ssh_signkey *self,
+                     const char *data, int len);
     void (*freekey) (void *key);
     char *(*fmtkey) (void *key);
     unsigned char *(*public_blob) (void *key, int *len);
     unsigned char *(*private_blob) (void *key, int *len);
-    void *(*createkey) (unsigned char *pub_blob, int pub_len,
-			unsigned char *priv_blob, int priv_len);
-    void *(*openssh_createkey) (unsigned char **blob, int *len);
+    void *(*createkey) (const struct ssh_signkey *self,
+                        const unsigned char *pub_blob, int pub_len,
+			const unsigned char *priv_blob, int priv_len);
+    void *(*openssh_createkey) (const struct ssh_signkey *self,
+                                const unsigned char **blob, int *len);
     int (*openssh_fmtkey) (void *key, unsigned char *blob, int len);
-    int (*pubkey_bits) (void *blob, int len);
-    char *(*fingerprint) (void *key);
-    int (*verifysig) (void *key, char *sig, int siglen,
-		      char *data, int datalen);
-    unsigned char *(*sign) (void *key, char *data, int datalen,
+    /* OpenSSH private key blobs, as created by openssh_fmtkey and
+     * consumed by openssh_createkey, always (at least so far...) take
+     * the form of a number of SSH-2 strings / mpints concatenated
+     * end-to-end. Because the new-style OpenSSH private key format
+     * stores those blobs without a containing string wrapper, we need
+     * to know how many strings each one consists of, so that we can
+     * skip over the right number to find the next key in the file.
+     * openssh_private_npieces gives that information. */
+    int openssh_private_npieces;
+    int (*pubkey_bits) (const struct ssh_signkey *self,
+                        const void *blob, int len);
+    int (*verifysig) (void *key, const char *sig, int siglen,
+		      const char *data, int datalen);
+    unsigned char *(*sign) (void *key, const char *data, int datalen,
 			    int *siglen);
-    char *name;
-    char *keytype;		       /* for host key cache */
+    const char *name;
+    const char *keytype;               /* for host key cache */
+    const void *extra;                 /* private to the public key methods */
 };
 
 struct ssh_compress {
-    char *name;
+    const char *name;
     /* For [email protected]: if non-NULL, this name will be considered once
      * userauth has completed successfully. */
-    char *delayed_name;
+    const char *delayed_name;
     void *(*compress_init) (void);
     void (*compress_cleanup) (void *);
     int (*compress) (void *, unsigned char *block, int len,
@@ -322,7 +448,7 @@ struct ssh_compress {
     int (*decompress) (void *, unsigned char *block, int len,
 		       unsigned char **outblock, int *outlen);
     int (*disable_compression) (void *);
-    char *text_name;
+    const char *text_name;
 };
 
 struct ssh2_userkey {
@@ -332,7 +458,7 @@ struct ssh2_userkey {
 };
 
 /* The maximum length of any hash algorithm used in kex. (bytes) */
-#define SSH2_KEX_MAX_HASH_LEN (32) /* SHA-256 */
+#define SSH2_KEX_MAX_HASH_LEN (64) /* SHA-512 */
 
 extern const struct ssh_cipher ssh_3des;
 extern const struct ssh_cipher ssh_des;
@@ -342,14 +468,22 @@ extern const struct ssh2_ciphers ssh2_des;
 extern const struct ssh2_ciphers ssh2_aes;
 extern const struct ssh2_ciphers ssh2_blowfish;
 extern const struct ssh2_ciphers ssh2_arcfour;
+extern const struct ssh2_ciphers ssh2_ccp;
 extern const struct ssh_hash ssh_sha1;
 extern const struct ssh_hash ssh_sha256;
+extern const struct ssh_hash ssh_sha384;
+extern const struct ssh_hash ssh_sha512;
 extern const struct ssh_kexes ssh_diffiehellman_group1;
 extern const struct ssh_kexes ssh_diffiehellman_group14;
 extern const struct ssh_kexes ssh_diffiehellman_gex;
 extern const struct ssh_kexes ssh_rsa_kex;
+extern const struct ssh_kexes ssh_ecdh_kex;
 extern const struct ssh_signkey ssh_dss;
 extern const struct ssh_signkey ssh_rsa;
+extern const struct ssh_signkey ssh_ecdsa_ed25519;
+extern const struct ssh_signkey ssh_ecdsa_nistp256;
+extern const struct ssh_signkey ssh_ecdsa_nistp384;
+extern const struct ssh_signkey ssh_ecdsa_nistp521;
 extern const struct ssh_mac ssh_hmac_md5;
 extern const struct ssh_mac ssh_hmac_sha1;
 extern const struct ssh_mac ssh_hmac_sha1_buggy;
@@ -392,7 +526,8 @@ struct PortForwarding;
 
 /* Allocate and register a new channel for port forwarding */
 void *new_sock_channel(void *handle, struct PortForwarding *pf);
-void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
+void ssh_send_port_open(void *channel, const char *hostname, int port,
+                        const char *org);
 
 /* Exports from portfwd.c */
 extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port,
@@ -471,7 +606,7 @@ int x11_authcmp(void *av, void *bv); /* for putting X11FakeAuth in a tree234 */
  * authorisation protocol to use at the remote end. The local auth
  * details are looked up by calling platform_get_x11_auth.
  */
-extern struct X11Display *x11_setup_display(char *display, Conf *);
+extern struct X11Display *x11_setup_display(const char *display, Conf *);
 void x11_free_display(struct X11Display *disp);
 struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype);
 void x11_free_fake_auth(struct X11FakeAuth *auth);
@@ -518,9 +653,12 @@ Bignum bignum_from_long(unsigned long n);
 void freebn(Bignum b);
 Bignum modpow(Bignum base, Bignum exp, Bignum mod);
 Bignum modmul(Bignum a, Bignum b, Bignum mod);
+Bignum modsub(const Bignum a, const Bignum b, const Bignum n);
 void decbn(Bignum n);
 extern Bignum Zero, One;
 Bignum bignum_from_bytes(const unsigned char *data, int nbytes);
+Bignum bignum_from_bytes_le(const unsigned char *data, int nbytes);
+Bignum bignum_random_in_range(const Bignum lower, const Bignum upper);
 int ssh1_read_bignum(const unsigned char *data, int len, Bignum * result);
 int bignum_bitcount(Bignum bn);
 int ssh1_bignum_length(Bignum bn);
@@ -541,13 +679,16 @@ Bignum bigmod(Bignum a, Bignum b);
 Bignum modinv(Bignum number, Bignum modulus);
 Bignum bignum_bitmask(Bignum number);
 Bignum bignum_rshift(Bignum number, int shift);
+Bignum bignum_lshift(Bignum number, int shift);
 int bignum_cmp(Bignum a, Bignum b);
 char *bignum_decimal(Bignum x);
+Bignum bignum_from_decimal(const char *decimal);
 
 #ifdef DEBUG
 void diagbn(char *prefix, Bignum md);
 #endif
 
+int dh_is_gex(const struct ssh_kex *kex);
 void *dh_setup_group(const struct ssh_kex *kex);
 void *dh_setup_gex(Bignum pval, Bignum gval);
 void dh_cleanup(void *);
@@ -556,17 +697,18 @@ const char *dh_validate_f(void *handle, Bignum f);
 Bignum dh_find_K(void *, Bignum f);
 
 int loadrsakey(const Filename *filename, struct RSAKey *key,
-	       char *passphrase, const char **errorstr);
+	       const char *passphrase, const char **errorstr);
 int rsakey_encrypted(const Filename *filename, char **comment);
 int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
 		   char **commentptr, const char **errorstr);
 
 int saversakey(const Filename *filename, struct RSAKey *key, char *passphrase);
 
-extern int base64_decode_atom(char *atom, unsigned char *out);
+extern int base64_decode_atom(const char *atom, unsigned char *out);
 extern int base64_lines(int datalen);
-extern void base64_encode_atom(unsigned char *data, int n, char *out);
-extern void base64_encode(FILE *fp, unsigned char *data, int datalen, int cpl);
+extern void base64_encode_atom(const unsigned char *data, int n, char *out);
+extern void base64_encode(FILE *fp, const unsigned char *data, int datalen,
+                          int cpl);
 
 /* ssh2_load_userkey can return this as an error */
 extern struct ssh2_userkey ssh2_wrong_passphrase;
@@ -574,22 +716,69 @@ extern struct ssh2_userkey ssh2_wrong_passphrase;
 
 int ssh2_userkey_encrypted(const Filename *filename, char **comment);
 struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
-				       char *passphrase, const char **errorstr);
+				       const char *passphrase,
+                                       const char **errorstr);
 unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
 				    int *pub_blob_len, char **commentptr,
 				    const char **errorstr);
 int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
 		      char *passphrase);
 const struct ssh_signkey *find_pubkey_alg(const char *name);
+const struct ssh_signkey *find_pubkey_alg_len(int namelen, const char *name);
 
 enum {
     SSH_KEYTYPE_UNOPENABLE,
     SSH_KEYTYPE_UNKNOWN,
     SSH_KEYTYPE_SSH1, SSH_KEYTYPE_SSH2,
-    SSH_KEYTYPE_OPENSSH, SSH_KEYTYPE_SSHCOM
+    /*
+     * The OpenSSH key types deserve a little explanation. OpenSSH has
+     * two physical formats for private key storage: an old PEM-based
+     * one largely dictated by their use of OpenSSL and full of ASN.1,
+     * and a new one using the same private key formats used over the
+     * wire for talking to ssh-agent. The old format can only support
+     * a subset of the key types, because it needs redesign for each
+     * key type, and after a while they decided to move to the new
+     * format so as not to have to do that.
+     *
+     * On input, key files are identified as either
+     * SSH_KEYTYPE_OPENSSH_PEM or SSH_KEYTYPE_OPENSSH_NEW, describing
+     * accurately which actual format the keys are stored in.
+     *
+     * On output, however, we default to following OpenSSH's own
+     * policy of writing out PEM-style keys for maximum backwards
+     * compatibility if the key type supports it, and otherwise
+     * switching to the new format. So the formats you can select for
+     * output are SSH_KEYTYPE_OPENSSH_NEW (forcing the new format for
+     * any key type), and SSH_KEYTYPE_OPENSSH_AUTO to use the oldest
+     * format supported by whatever key type you're writing out.
+     *
+     * So we have three type codes, but only two of them usable in any
+     * given circumstance. An input key file will never be identified
+     * as AUTO, only PEM or NEW; key export UIs should not be able to
+     * select PEM, only AUTO or NEW.
+     */
+    SSH_KEYTYPE_OPENSSH_AUTO,
+    SSH_KEYTYPE_OPENSSH_PEM,
+    SSH_KEYTYPE_OPENSSH_NEW,
+    SSH_KEYTYPE_SSHCOM,
+    /*
+     * Public-key-only formats, which we still want to be able to read
+     * for various purposes.
+     */
+    SSH_KEYTYPE_SSH1_PUBLIC,
+    SSH_KEYTYPE_SSH2_PUBLIC_RFC4716,
+    SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH
 };
+char *ssh1_pubkey_str(struct RSAKey *ssh1key);
+void ssh1_write_pubkey(FILE *fp, struct RSAKey *ssh1key);
+char *ssh2_pubkey_openssh_str(struct ssh2_userkey *key);
+void ssh2_write_pubkey(FILE *fp, const char *comment,
+                       const void *v_pub_blob, int pub_len,
+                       int keytype);
+char *ssh2_fingerprint_blob(const void *blob, int bloblen);
+char *ssh2_fingerprint(const struct ssh_signkey *alg, void *data);
 int key_type(const Filename *filename);
-char *key_type_to_str(int type);
+const char *key_type_to_str(int type);
 
 int import_possible(int type);
 int import_target_type(int type);
@@ -619,6 +808,10 @@ void des_encrypt_xdmauth(const unsigned char *key,
 void des_decrypt_xdmauth(const unsigned char *key,
                          unsigned char *blk, int len);
 
+void openssh_bcrypt(const char *passphrase,
+                    const unsigned char *salt, int saltbytes,
+                    int rounds, unsigned char *out, int outbytes);
+
 /*
  * For progress updates in the key generation utility.
  */
@@ -634,6 +827,10 @@ int rsa_generate(struct RSAKey *key, int bits, progfn_t pfn,
 		 void *pfnparam);
 int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
 		 void *pfnparam);
+int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
+                void *pfnparam);
+int ec_edgenerate(struct ec_key *key, int bits, progfn_t pfn,
+                  void *pfnparam);
 Bignum primegen(int bits, int modulus, int residue, Bignum factor,
 		int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
 void invent_firstbits(unsigned *one, unsigned *two);
@@ -744,6 +941,10 @@ void platform_ssh_share_cleanup(const char *name);
 #define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
 #define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
 #define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
+#define SSH2_MSG_KEX_ECDH_INIT                    30    /* 0x1e */
+#define SSH2_MSG_KEX_ECDH_REPLY                   31    /* 0x1f */
+#define SSH2_MSG_KEX_ECMQV_INIT                   30    /* 0x1e */
+#define SSH2_MSG_KEX_ECMQV_REPLY                  31    /* 0x1f */
 #define SSH2_MSG_USERAUTH_REQUEST                 50	/* 0x32 */
 #define SSH2_MSG_USERAUTH_FAILURE                 51	/* 0x33 */
 #define SSH2_MSG_USERAUTH_SUCCESS                 52	/* 0x34 */

+ 21 - 14
source/putty/sshaes.c

@@ -1198,51 +1198,58 @@ void aes256_decrypt_pubkey(unsigned char *key, unsigned char *blk, int len)
 
 static const struct ssh2_cipher ssh_aes128_ctr = {
     aes_make_context, aes_free_context, aes_iv, aes128_key,
-    aes_ssh2_sdctr, aes_ssh2_sdctr,
+    aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
     "aes128-ctr",
-    16, 128, 0, "AES-128 SDCTR"
+    16, 128, 16, 0, "AES-128 SDCTR",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_aes192_ctr = {
     aes_make_context, aes_free_context, aes_iv, aes192_key,
-    aes_ssh2_sdctr, aes_ssh2_sdctr,
+    aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
     "aes192-ctr",
-    16, 192, 0, "AES-192 SDCTR"
+    16, 192, 24, 0, "AES-192 SDCTR",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_aes256_ctr = {
     aes_make_context, aes_free_context, aes_iv, aes256_key,
-    aes_ssh2_sdctr, aes_ssh2_sdctr,
+    aes_ssh2_sdctr, aes_ssh2_sdctr, NULL, NULL,
     "aes256-ctr",
-    16, 256, 0, "AES-256 SDCTR"
+    16, 256, 32, 0, "AES-256 SDCTR",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_aes128 = {
     aes_make_context, aes_free_context, aes_iv, aes128_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
     "aes128-cbc",
-    16, 128, SSH_CIPHER_IS_CBC, "AES-128 CBC"
+    16, 128, 16, SSH_CIPHER_IS_CBC, "AES-128 CBC",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_aes192 = {
     aes_make_context, aes_free_context, aes_iv, aes192_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
     "aes192-cbc",
-    16, 192, SSH_CIPHER_IS_CBC, "AES-192 CBC"
+    16, 192, 24, SSH_CIPHER_IS_CBC, "AES-192 CBC",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_aes256 = {
     aes_make_context, aes_free_context, aes_iv, aes256_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
     "aes256-cbc",
-    16, 256, SSH_CIPHER_IS_CBC, "AES-256 CBC"
+    16, 256, 32, SSH_CIPHER_IS_CBC, "AES-256 CBC",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_rijndael_lysator = {
     aes_make_context, aes_free_context, aes_iv, aes256_key,
-    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk,
+    aes_ssh2_encrypt_blk, aes_ssh2_decrypt_blk, NULL, NULL,
     "[email protected]",
-    16, 256, SSH_CIPHER_IS_CBC, "AES-256 CBC"
+    16, 256, 32, SSH_CIPHER_IS_CBC, "AES-256 CBC",
+    NULL
 };
 
 static const struct ssh2_cipher *const aes_list[] = {

+ 6 - 4
source/putty/ssharcf.c

@@ -100,16 +100,18 @@ static void arcfour_iv(void *handle, unsigned char *key)
 
 const struct ssh2_cipher ssh_arcfour128_ssh2 = {
     arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour128_key,
-    arcfour_block, arcfour_block,
+    arcfour_block, arcfour_block, NULL, NULL,
     "arcfour128",
-    1, 128, 0, "Arcfour-128"
+    1, 128, 16, 0, "Arcfour-128",
+    NULL
 };
 
 const struct ssh2_cipher ssh_arcfour256_ssh2 = {
     arcfour_make_context, arcfour_free_context, arcfour_iv, arcfour256_key,
-    arcfour_block, arcfour_block,
+    arcfour_block, arcfour_block, NULL, NULL,
     "arcfour256",
-    1, 256, 0, "Arcfour-256"
+    1, 256, 32, 0, "Arcfour-256",
+    NULL
 };
 
 static const struct ssh2_cipher *const arcfour_list[] = {

+ 124 - 0
source/putty/sshbcrypt.c

@@ -0,0 +1,124 @@
+/*
+ * '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])
+{
+    SHA512_State shastate;
+    unsigned char hashed_salt[64];
+    unsigned char countbuf[4];
+
+    /* Hash the input salt with the counter value optionally suffixed
+     * to get our real 32-byte salt */
+    SHA512_Init(&shastate);
+    SHA512_Bytes(&shastate, salt, saltbytes);
+    if (counter) {
+        PUT_32BIT_MSB_FIRST(countbuf, counter);
+        SHA512_Bytes(&shastate, countbuf, 4);
+    }
+    SHA512_Final(&shastate, hashed_salt);
+
+    bcrypt_hash(hashed_passphrase, 64, hashed_salt, 64, output);
+
+    smemclr(&shastate, sizeof(shastate));
+    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 */
+    SHA512_Simple(passphrase, strlen(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));
+}

+ 76 - 19
source/putty/sshblowf.c

@@ -7,11 +7,12 @@
 #include <assert.h>
 #include <stdio.h>
 #include "ssh.h"
+#include "sshblowf.h"
 
-typedef struct {
+struct BlowfishContext {
     word32 S0[256], S1[256], S2[256], S3[256], P[18];
     word32 iv0, iv1;		       /* for CBC mode */
-} BlowfishContext;
+};
 
 /*
  * The Blowfish init data: hex digits of the fractional part of pi.
@@ -326,6 +327,24 @@ static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len,
     ctx->iv1 = iv1;
 }
 
+void blowfish_lsb_encrypt_ecb(unsigned char *blk, int len,
+                              BlowfishContext * ctx)
+{
+    word32 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)
 {
@@ -436,8 +455,25 @@ static void blowfish_msb_sdctr(unsigned char *blk, int len,
     ctx->iv1 = iv1;
 }
 
-static void blowfish_setkey(BlowfishContext * ctx,
-			    const unsigned char *key, short keybytes)
+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 unsigned char *key, short keybytes,
+                        const unsigned char *salt, short saltbytes)
 {
     word32 *S0 = ctx->S0;
     word32 *S1 = ctx->S1;
@@ -445,10 +481,18 @@ static void blowfish_setkey(BlowfishContext * ctx,
     word32 *S3 = ctx->S3;
     word32 *P = ctx->P;
     word32 str[2];
-    int i;
+    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] = parray[i];
 	P[i] ^=
 	    ((word32) (unsigned char) (key[(i * 4 + 0) % keybytes])) << 24;
 	P[i] ^=
@@ -458,48 +502,59 @@ static void blowfish_setkey(BlowfishContext * ctx,
 	P[i] ^= ((word32) (unsigned char) (key[(i * 4 + 3) % keybytes]));
     }
 
-    for (i = 0; i < 256; i++) {
-	S0[i] = sbox0[i];
-	S1[i] = sbox1[i];
-	S2[i] = sbox2[i];
-	S3[i] = sbox3[i];
-    }
-
     str[0] = str[1] = 0;
 
     for (i = 0; i < 18; i += 2) {
+        for (j = 0; j < 8; j++)
+            str[j/4] ^= ((word32)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] ^= ((word32)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] ^= ((word32)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] ^= ((word32)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] ^= ((word32)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 SSH_SESSION_KEY_LENGTH	32
 
-static void *blowfish_make_context(void)
+void *blowfish_make_context(void)
 {
     return snew(BlowfishContext);
 }
@@ -510,7 +565,7 @@ static void *blowfish_ssh1_make_context(void)
     return snewn(2, BlowfishContext);
 }
 
-static void blowfish_free_context(void *handle)
+void blowfish_free_context(void *handle)
 {
     sfree(handle);
 }
@@ -586,16 +641,18 @@ const struct ssh_cipher ssh_blowfish_ssh1 = {
 
 static const struct ssh2_cipher ssh_blowfish_ssh2 = {
     blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish_key,
-    blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk,
+    blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk, NULL, NULL,
     "blowfish-cbc",
-    8, 128, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC"
+    8, 128, 16, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_blowfish_ssh2_ctr = {
     blowfish_make_context, blowfish_free_context, blowfish_iv, blowfish256_key,
-    blowfish_ssh2_sdctr, blowfish_ssh2_sdctr,
+    blowfish_ssh2_sdctr, blowfish_ssh2_sdctr, NULL, NULL,
     "blowfish-ctr",
-    8, 256, 0, "Blowfish-256 SDCTR"
+    8, 256, 32, 0, "Blowfish-256 SDCTR",
+    NULL
 };
 
 static const struct ssh2_cipher *const blowfish_list[] = {

+ 15 - 0
source/putty/sshblowf.h

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

File diff suppressed because it is too large
+ 584 - 198
source/putty/sshbn.c


+ 209 - 98
source/putty/sshbn.h

@@ -1,63 +1,135 @@
 /*
  * sshbn.h: the assorted conditional definitions of BignumInt and
- * multiply/divide 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.
- */
-
-/*
- * Usage notes:
- *  * Do not call the DIVMOD_WORD macro with expressions such as array
- *    subscripts, as some implementations object to this (see below).
- *  * Note that none of the division methods below will cope if the
- *    quotient won't fit into BIGNUM_INT_BITS. Callers should be careful
- *    to avoid this case.
- *    If this condition occurs, in the case of the x86 DIV instruction,
- *    an overflow exception will occur, which (according to a correspondent)
- *    will manifest on Windows as something like
- *      0xC0000095: Integer overflow
- *    The C variant won't give the right answer, either.
+ * 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 file 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).
+ *
+ *  - four constant macros: BIGNUM_INT_BITS, BIGNUM_INT_BYTES,
+ *    BIGNUM_TOP_BIT, BIGNUM_INT_MASK. These should be more or less
+ *    self-explanatory, but just in case, they give the number of bits
+ *    in BignumInt, the number of bytes that works out to, the
+ *    BignumInt value consisting of only the top bit, and the
+ *    BignumInt value with all bits set.
+ *
+ *  - 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. The other three 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.
  */
 
 #if defined __SIZEOF_INT128__
-/* 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. If we have one of these, we can
- * use a 64-bit BignumInt and a 128-bit BignumDblInt. */
-typedef __uint64_t BignumInt;
-typedef __uint128_t BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFFFFFFFFFULL
-#define BIGNUM_TOP_BIT   0x8000000000000000ULL
-#define BIGNUM_INT_BITS  64
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-#define DIVMOD_WORD(q, r, hi, lo, w) do { \
-    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
-    q = n / w; \
-    r = n % w; \
-} while (0)
-#elif defined __GNUC__ && defined __i386__
-typedef unsigned long BignumInt;
-typedef unsigned long long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFUL
-#define BIGNUM_TOP_BIT   0x80000000UL
-#define BIGNUM_INT_BITS  32
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-#define DIVMOD_WORD(q, r, hi, lo, w) \
-    __asm__("div %2" : \
-	    "=d" (r), "=a" (q) : \
-	    "r" (w), "d" (hi), "a" (lo))
+
+  /*
+   * 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 64
+  #define DEFINE_BIGNUMDBLINT typedef __uint128_t BignumDblInt
+
+#elif defined _MSC_VER && defined _M_AMD64
+
+  /*
+   * 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 64
+  #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
+
+  /* 32-bit BignumInt, using C99 unsigned long long as BignumDblInt */
+
+  typedef unsigned int BignumInt;
+  #define BIGNUM_INT_BITS 32
+  #define DEFINE_BIGNUMDBLINT typedef unsigned long long BignumDblInt
+
 #elif (defined _MSC_VER && defined _M_IX86) || defined(MPEXT)
-typedef unsigned __int32 BignumInt;
-typedef unsigned __int64 BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFUL
-#define BIGNUM_TOP_BIT   0x80000000UL
-#define BIGNUM_INT_BITS  32
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-/* Note: MASM interprets array subscripts in the macro arguments as
- * assembler syntax, which gives the wrong answer. Don't supply them.
- * <http://msdn2.microsoft.com/en-us/library/bf1dw62z.aspx> */
+
+  /* 32-bit BignumInt, using Visual Studio __int64 as BignumDblInt */
+
+  typedef unsigned int BignumInt;
+  #define BIGNUM_INT_BITS  32
+  #define DEFINE_BIGNUMDBLINT typedef unsigned __int64 BignumDblInt
+
 #ifdef MPEXT
 // BCC requires semicolons
 #define DIVMOD_WORD(q, r, hi, lo, w) do { \
@@ -68,53 +140,92 @@ typedef unsigned __int64 BignumDblInt;
     __asm mov q, eax; \
 } while(0)
 #else
-#define DIVMOD_WORD(q, r, hi, lo, w) do { \
-    __asm mov edx, hi \
-    __asm mov eax, lo \
-    __asm div w \
-    __asm mov r, edx \
-    __asm mov q, eax \
-} while(0)
 #endif
 #elif defined _LP64
-/* 64-bit architectures can do 32x32->64 chunks at a time */
-typedef unsigned int BignumInt;
-typedef unsigned long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFU
-#define BIGNUM_TOP_BIT   0x80000000U
-#define BIGNUM_INT_BITS  32
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-#define DIVMOD_WORD(q, r, hi, lo, w) do { \
-    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
-    q = n / w; \
-    r = n % w; \
-} while (0)
-#elif defined _LLP64
-/* 64-bit architectures in which unsigned long is 32 bits, not 64 */
-typedef unsigned long BignumInt;
-typedef unsigned long long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFFFFFUL
-#define BIGNUM_TOP_BIT   0x80000000UL
-#define BIGNUM_INT_BITS  32
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-#define DIVMOD_WORD(q, r, hi, lo, w) do { \
-    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
-    q = n / w; \
-    r = n % w; \
-} while (0)
+
+  /*
+   * 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  32
+  #define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt
+
 #else
-/* Fallback for all other cases */
-typedef unsigned short BignumInt;
-typedef unsigned long BignumDblInt;
-#define BIGNUM_INT_MASK  0xFFFFU
-#define BIGNUM_TOP_BIT   0x8000U
-#define BIGNUM_INT_BITS  16
-#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
-#define DIVMOD_WORD(q, r, hi, lo, w) do { \
-    BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \
-    q = n / w; \
-    r = n % w; \
-} while (0)
+
+  /*
+   * 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  16
+  #define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt
+
 #endif
 
+/*
+ * Common code across all branches of that ifdef: define the three
+ * easy constant macros in terms of BIGNUM_INT_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))
+
+/*
+ * 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 */

+ 1059 - 0
source/putty/sshccp.c

@@ -0,0 +1,1059 @@
+/*
+ * 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 ssh2_cipher 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 "sshbn.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 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 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)(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, v12, v13, v14, v15;
+    BignumInt v16, v17, v18, v19, v20, v21, v22, v24, v25, v26, v27, v28, v29;
+    BignumInt v30, v31, v32, v33;
+    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 = 5 * v9;
+    BignumADC(v12, carry, v0, v10, 0);
+    (void)v12;
+    BignumADC(v13, carry, v1, 0, carry);
+    (void)v13;
+    BignumADC(v14, carry, v2, 0, carry);
+    (void)v14;
+    BignumADC(v15, carry, v3, 0, carry);
+    (void)v15;
+    BignumADC(v16, carry, v4, 0, carry);
+    (void)v16;
+    BignumADC(v17, carry, v5, 0, carry);
+    (void)v17;
+    BignumADC(v18, carry, v6, 0, carry);
+    (void)v18;
+    BignumADC(v19, carry, v7, 0, carry);
+    (void)v19;
+    v20 = v8 + 0 + carry;
+    v21 = (v20) >> 2;
+    v22 = 5 * v21;
+    BignumADC(v24, carry, v0, v22, 0);
+    BignumADC(v25, carry, v1, 0, carry);
+    BignumADC(v26, carry, v2, 0, carry);
+    BignumADC(v27, carry, v3, 0, carry);
+    BignumADC(v28, carry, v4, 0, carry);
+    BignumADC(v29, carry, v5, 0, carry);
+    BignumADC(v30, carry, v6, 0, carry);
+    BignumADC(v31, carry, v7, 0, carry);
+    v32 = v8 + 0 + carry;
+    v33 = (v32) & ((((BignumInt)1) << 2)-1);
+    n->w[0] = v24;
+    n->w[1] = v25;
+    n->w[2] = v26;
+    n->w[3] = v27;
+    n->w[4] = v28;
+    n->w[5] = v29;
+    n->w[6] = v30;
+    n->w[7] = v31;
+    n->w[8] = v33;
+}
+
+#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, v8, v9, v10, v11, v12, v13, v14;
+    BignumInt v16, v17, v18, v19, v20, v21;
+    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 = 5 * v5;
+    BignumADC(v8, carry, v0, v6, 0);
+    (void)v8;
+    BignumADC(v9, carry, v1, 0, carry);
+    (void)v9;
+    BignumADC(v10, carry, v2, 0, carry);
+    (void)v10;
+    BignumADC(v11, carry, v3, 0, carry);
+    (void)v11;
+    v12 = v4 + 0 + carry;
+    v13 = (v12) >> 2;
+    v14 = 5 * v13;
+    BignumADC(v16, carry, v0, v14, 0);
+    BignumADC(v17, carry, v1, 0, carry);
+    BignumADC(v18, carry, v2, 0, carry);
+    BignumADC(v19, carry, v3, 0, carry);
+    v20 = v4 + 0 + carry;
+    v21 = (v20) & ((((BignumInt)1) << 2)-1);
+    n->w[0] = v16;
+    n->w[1] = v17;
+    n->w[2] = v18;
+    n->w[3] = v19;
+    n->w[4] = v21;
+}
+
+#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, v6, v7, v8, v9, v10, v12, v13, v14, v15;
+    BignumCarry carry;
+
+    v0 = n->w[0];
+    v1 = n->w[1];
+    v2 = n->w[2];
+    v3 = (v2) >> 2;
+    v4 = 5 * v3;
+    BignumADC(v6, carry, v0, v4, 0);
+    (void)v6;
+    BignumADC(v7, carry, v1, 0, carry);
+    (void)v7;
+    v8 = v2 + 0 + carry;
+    v9 = (v8) >> 2;
+    v10 = 5 * v9;
+    BignumADC(v12, carry, v0, v10, 0);
+    BignumADC(v13, carry, v1, 0, carry);
+    v14 = v2 + 0 + carry;
+    v15 = (v14) & ((((BignumInt)1) << 2)-1);
+    n->w[0] = v12;
+    n->w[1] = v13;
+    n->w[2] = v15;
+}
+
+#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);
+}
+
+/* Takes a 256 bit key */
+static void poly1305_key(struct poly1305 *ctx, const unsigned char *key)
+{
+    unsigned char key_copy[16];
+    memcpy(key_copy, key, 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 are the nonce */
+    memcpy(ctx->nonce, key+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;
+};
+
+static void *poly_make_context(void *ctx)
+{
+    return ctx;
+}
+
+static void poly_free_context(void *ctx)
+{
+    /* Not allocated, just forwarded, no need to free */
+}
+
+static void poly_setkey(void *ctx, unsigned char *key)
+{
+    /* Uses the same context as ChaCha20, so ignore */
+}
+
+static void poly_start(void *handle)
+{
+    struct ccp_context *ctx = (struct ccp_context *)handle;
+
+    ctx->mac_initialised = 0;
+    memset(ctx->mac_iv, 0, 8);
+    poly1305_init(&ctx->mac);
+}
+
+static void poly_bytes(void *handle, unsigned char const *blk, int len)
+{
+    struct ccp_context *ctx = (struct ccp_context *)handle;
+
+    /* 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, ctx->b_cipher.current);
+
+        /* 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(void *handle, unsigned char *blk)
+{
+    struct ccp_context *ctx = (struct ccp_context *)handle;
+    poly1305_finalise(&ctx->mac, blk);
+}
+
+static int poly_verresult(void *handle, unsigned char const *blk)
+{
+    struct ccp_context *ctx = (struct ccp_context *)handle;
+    int res;
+    unsigned char mac[16];
+    poly1305_finalise(&ctx->mac, mac);
+    res = smemeq(blk, mac, 16);
+    return res;
+}
+
+/* The generic poly operation used before generate and verify */
+static void poly_op(void *handle, unsigned char *blk, int len, unsigned long seq)
+{
+    unsigned char iv[4];
+    poly_start(handle);
+    PUT_32BIT_MSB_FIRST(iv, seq);
+    /* poly_bytes expects the first 4 bytes to be the IV */
+    poly_bytes(handle, iv, 4);
+    smemclr(iv, sizeof(iv));
+    poly_bytes(handle, blk, len);
+}
+
+static void poly_generate(void *handle, unsigned char *blk, int len, unsigned long seq)
+{
+    poly_op(handle, blk, len, seq);
+    poly_genresult(handle, blk+len);
+}
+
+static int poly_verify(void *handle, unsigned char *blk, int len, unsigned long seq)
+{
+    poly_op(handle, blk, len, seq);
+    return poly_verresult(handle, blk+len);
+}
+
+static const struct ssh_mac ssh2_poly1305 = {
+    poly_make_context, poly_free_context,
+    poly_setkey,
+
+    /* whole-packet operations */
+    poly_generate, poly_verify,
+
+    /* partial-packet operations */
+    poly_start, poly_bytes, poly_genresult, poly_verresult,
+
+    "", "", /* Not selectable individually, just part of ChaCha20-Poly1305 */
+    16, 0, "Poly1305"
+};
+
+static void *ccp_make_context(void)
+{
+    struct ccp_context *ctx = snew(struct ccp_context);
+    if (ctx) {
+        poly1305_init(&ctx->mac);
+    }
+    return ctx;
+}
+
+static void ccp_free_context(void *vctx)
+{
+    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    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(void *vctx, unsigned char *iv)
+{
+    /* struct ccp_context *ctx = (struct ccp_context *)vctx; */
+    /* IV is set based on the sequence number */
+}
+
+static void ccp_key(void *vctx, unsigned char *key)
+{
+    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    /* 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(void *vctx, unsigned char *blk, int len)
+{
+    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    chacha20_encrypt(&ctx->b_cipher, blk, len);
+}
+
+static void ccp_decrypt(void *vctx, unsigned char *blk, int len)
+{
+    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    chacha20_decrypt(&ctx->b_cipher, blk, len);
+}
+
+static void ccp_length_op(struct ccp_context *ctx, unsigned char *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(void *vctx, unsigned char *blk, int len,
+                               unsigned long seq)
+{
+    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    ccp_length_op(ctx, blk, len, seq);
+    chacha20_encrypt(&ctx->a_cipher, blk, len);
+}
+
+static void ccp_decrypt_length(void *vctx, unsigned char *blk, int len,
+                               unsigned long seq)
+{
+    struct ccp_context *ctx = (struct ccp_context *)vctx;
+    ccp_length_op(ctx, blk, len, seq);
+    chacha20_decrypt(&ctx->a_cipher, blk, len);
+}
+
+static const struct ssh2_cipher ssh2_chacha20_poly1305 = {
+
+    ccp_make_context,
+    ccp_free_context,
+    ccp_iv,
+    ccp_key,
+    ccp_encrypt,
+    ccp_decrypt,
+    ccp_encrypt_length,
+    ccp_decrypt_length,
+
+    "[email protected]",
+    1, 512, 64, SSH_CIPHER_SEPARATE_LENGTH, "ChaCha20",
+
+    &ssh2_poly1305
+};
+
+static const struct ssh2_cipher *const ccp_list[] = {
+    &ssh2_chacha20_poly1305
+};
+
+const struct ssh2_ciphers ssh2_ccp = {
+    sizeof(ccp_list) / sizeof(*ccp_list),
+    ccp_list
+};

+ 12 - 8
source/putty/sshdes.c

@@ -947,16 +947,18 @@ void des_decrypt_xdmauth(const unsigned char *keydata,
 
 static const struct ssh2_cipher ssh_3des_ssh2 = {
     des3_make_context, des3_free_context, des3_iv, des3_key,
-    des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk,
+    des3_ssh2_encrypt_blk, des3_ssh2_decrypt_blk, NULL, NULL,
     "3des-cbc",
-    8, 168, SSH_CIPHER_IS_CBC, "triple-DES CBC"
+    8, 168, 24, SSH_CIPHER_IS_CBC, "triple-DES CBC",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
     des3_make_context, des3_free_context, des3_iv, des3_key,
-    des3_ssh2_sdctr, des3_ssh2_sdctr,
+    des3_ssh2_sdctr, des3_ssh2_sdctr, NULL, NULL,
     "3des-ctr",
-    8, 168, 0, "triple-DES SDCTR"
+    8, 168, 24, 0, "triple-DES SDCTR",
+    NULL
 };
 
 /*
@@ -969,16 +971,18 @@ static const struct ssh2_cipher ssh_3des_ssh2_ctr = {
  */
 static const struct ssh2_cipher ssh_des_ssh2 = {
     des_make_context, des3_free_context, des3_iv, des_key,
-    des_ssh2_encrypt_blk, des_ssh2_decrypt_blk,
+    des_ssh2_encrypt_blk, des_ssh2_decrypt_blk, NULL, NULL,
     "des-cbc",
-    8, 56, SSH_CIPHER_IS_CBC, "single-DES CBC"
+    8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
+    NULL
 };
 
 static const struct ssh2_cipher ssh_des_sshcom_ssh2 = {
     des_make_context, des3_free_context, des3_iv, des_key,
-    des_ssh2_encrypt_blk, des_ssh2_decrypt_blk,
+    des_ssh2_encrypt_blk, des_ssh2_decrypt_blk, NULL, NULL,
     "[email protected]",
-    8, 56, SSH_CIPHER_IS_CBC, "single-DES CBC"
+    8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC",
+    NULL
 };
 
 static const struct ssh2_cipher *const des3_list[] = {

+ 30 - 6
source/putty/sshdh.c

@@ -50,9 +50,18 @@ static const unsigned char P14[] = {
  */
 static const unsigned char G[] = { 2 };
 
+struct dh_extra {
+    const unsigned char *pdata, *gdata; /* NULL means group exchange */
+    int plen, glen;
+};
+
+static const struct dh_extra extra_group1 = {
+    P1, G, lenof(P1), lenof(G),
+};
+
 static const struct ssh_kex ssh_diffiehellman_group1_sha1 = {
     "diffie-hellman-group1-sha1", "group1",
-    KEXTYPE_DH, P1, G, lenof(P1), lenof(G), &ssh_sha1
+    KEXTYPE_DH, &ssh_sha1, &extra_group1,
 };
 
 static const struct ssh_kex *const group1_list[] = {
@@ -64,9 +73,13 @@ const struct ssh_kexes ssh_diffiehellman_group1 = {
     group1_list
 };
 
+static const struct dh_extra extra_group14 = {
+    P14, G, lenof(P14), lenof(G),
+};
+
 static const struct ssh_kex ssh_diffiehellman_group14_sha1 = {
     "diffie-hellman-group14-sha1", "group14",
-    KEXTYPE_DH, P14, G, lenof(P14), lenof(G), &ssh_sha1
+    KEXTYPE_DH, &ssh_sha1, &extra_group14,
 };
 
 static const struct ssh_kex *const group14_list[] = {
@@ -78,14 +91,18 @@ const struct ssh_kexes ssh_diffiehellman_group14 = {
     group14_list
 };
 
+static const struct dh_extra extra_gex = {
+    NULL, NULL, 0, 0,
+};
+
 static const struct ssh_kex ssh_diffiehellman_gex_sha256 = {
     "diffie-hellman-group-exchange-sha256", NULL,
-    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha256
+    KEXTYPE_DH, &ssh_sha256, &extra_gex,
 };
 
 static const struct ssh_kex ssh_diffiehellman_gex_sha1 = {
     "diffie-hellman-group-exchange-sha1", NULL,
-    KEXTYPE_DH, NULL, NULL, 0, 0, &ssh_sha1
+    KEXTYPE_DH, &ssh_sha1, &extra_gex,
 };
 
 static const struct ssh_kex *const gex_list[] = {
@@ -115,14 +132,21 @@ static void dh_init(struct dh_ctx *ctx)
     ctx->x = ctx->e = NULL;
 }
 
+int dh_is_gex(const struct ssh_kex *kex)
+{
+    const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
+    return extra->pdata == NULL;
+}
+
 /*
  * Initialise DH for a standard group.
  */
 void *dh_setup_group(const struct ssh_kex *kex)
 {
+    const struct dh_extra *extra = (const struct dh_extra *)kex->extra;
     struct dh_ctx *ctx = snew(struct dh_ctx);
-    ctx->p = bignum_from_bytes(kex->pdata, kex->plen);
-    ctx->g = bignum_from_bytes(kex->gdata, kex->glen);
+    ctx->p = bignum_from_bytes(extra->pdata, extra->plen);
+    ctx->g = bignum_from_bytes(extra->gdata, extra->glen);
     dh_init(ctx);
     return ctx;
 }

+ 61 - 81
source/putty/sshdss.c

@@ -37,7 +37,8 @@ static void sha512_mpint(SHA512_State * s, Bignum b)
     smemclr(lenbuf, sizeof(lenbuf));
 }
 
-static void getstring(char **data, int *datalen, char **p, int *length)
+static void getstring(const char **data, int *datalen,
+                      const char **p, int *length)
 {
     *p = NULL;
     if (*datalen < 4)
@@ -53,9 +54,9 @@ static void getstring(char **data, int *datalen, char **p, int *length)
     *data += *length;
     *datalen -= *length;
 }
-static Bignum getmp(char **data, int *datalen)
+static Bignum getmp(const char **data, int *datalen)
 {
-    char *p;
+    const char *p;
     int length;
     Bignum b;
 
@@ -64,18 +65,18 @@ static Bignum getmp(char **data, int *datalen)
 	return NULL;
     if (p[0] & 0x80)
 	return NULL;		       /* negative mp */
-    b = bignum_from_bytes((unsigned char *)p, length);
+    b = bignum_from_bytes((const unsigned char *)p, length);
     return b;
 }
 
-static Bignum get160(char **data, int *datalen)
+static Bignum get160(const char **data, int *datalen)
 {
     Bignum b;
 
     if (*datalen < 20)
         return NULL;
 
-    b = bignum_from_bytes((unsigned char *)*data, 20);
+    b = bignum_from_bytes((const unsigned char *)*data, 20);
     *data += 20;
     *datalen -= 20;
 
@@ -84,9 +85,10 @@ static Bignum get160(char **data, int *datalen)
 
 static void dss_freekey(void *key);    /* forward reference */
 
-static void *dss_newkey(char *data, int len)
+static void *dss_newkey(const struct ssh_signkey *self,
+                        const char *data, int len)
 {
-    char *p;
+    const char *p;
     int slen;
     struct dss_key *dss;
 
@@ -189,48 +191,11 @@ static char *dss_fmtkey(void *key)
     return p;
 }
 
-static char *dss_fingerprint(void *key)
-{
-    struct dss_key *dss = (struct dss_key *) key;
-    struct MD5Context md5c;
-    unsigned char digest[16], lenbuf[4];
-    char buffer[16 * 3 + 40];
-    char *ret;
-    int numlen, i;
-
-    MD5Init(&md5c);
-    MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-dss", 11);
-
-#define ADD_BIGNUM(bignum) \
-    numlen = (bignum_bitcount(bignum)+8)/8; \
-    PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \
-    for (i = numlen; i-- ;) { \
-        unsigned char c = bignum_byte(bignum, i); \
-        MD5Update(&md5c, &c, 1); \
-    }
-    ADD_BIGNUM(dss->p);
-    ADD_BIGNUM(dss->q);
-    ADD_BIGNUM(dss->g);
-    ADD_BIGNUM(dss->y);
-#undef ADD_BIGNUM
-
-    MD5Final(digest, &md5c);
-
-    sprintf(buffer, "ssh-dss %d ", bignum_bitcount(dss->p));
-    for (i = 0; i < 16; i++)
-	sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",
-		digest[i]);
-    ret = snewn(strlen(buffer) + 1, char);
-    if (ret)
-	strcpy(ret, buffer);
-    return ret;
-}
-
-static int dss_verifysig(void *key, char *sig, int siglen,
-			 char *data, int datalen)
+static int dss_verifysig(void *key, const char *sig, int siglen,
+			 const char *data, int datalen)
 {
     struct dss_key *dss = (struct dss_key *) key;
-    char *p;
+    const char *p;
     int slen;
     char hash[20];
     Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;
@@ -402,18 +367,19 @@ static unsigned char *dss_private_blob(void *key, int *len)
     return blob;
 }
 
-static void *dss_createkey(unsigned char *pub_blob, int pub_len,
-			   unsigned char *priv_blob, int priv_len)
+static void *dss_createkey(const struct ssh_signkey *self,
+                           const unsigned char *pub_blob, int pub_len,
+			   const unsigned char *priv_blob, int priv_len)
 {
     struct dss_key *dss;
-    char *pb = (char *) priv_blob;
-    char *hash;
+    const char *pb = (const char *) priv_blob;
+    const char *hash;
     int hashlen;
     SHA_State s;
     unsigned char digest[20];
     Bignum ytest;
 
-    dss = dss_newkey((char *) pub_blob, pub_len);
+    dss = dss_newkey(self, (char *) pub_blob, pub_len);
     if (!dss)
         return NULL;
     dss->x = getmp(&pb, &priv_len);
@@ -453,9 +419,10 @@ static void *dss_createkey(unsigned char *pub_blob, int pub_len,
     return dss;
 }
 
-static void *dss_openssh_createkey(unsigned char **blob, int *len)
+static void *dss_openssh_createkey(const struct ssh_signkey *self,
+                                   const unsigned char **blob, int *len)
 {
-    char **b = (char **) blob;
+    const char **b = (const char **) blob;
     struct dss_key *dss;
 
     dss = snew(struct dss_key);
@@ -504,12 +471,13 @@ static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len)
     return bloblen;
 }
 
-static int dss_pubkey_bits(void *blob, int len)
+static int dss_pubkey_bits(const struct ssh_signkey *self,
+                           const void *blob, int len)
 {
     struct dss_key *dss;
     int ret;
 
-    dss = dss_newkey((char *) blob, len);
+    dss = dss_newkey(self, (const char *) blob, len);
     if (!dss)
         return -1;
     ret = bignum_bitcount(dss->p);
@@ -518,7 +486,8 @@ static int dss_pubkey_bits(void *blob, int len)
     return ret;
 }
 
-static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
+Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,
+                  unsigned char *digest, int digest_len)
 {
     /*
      * The basic DSS signing algorithm is:
@@ -591,21 +560,16 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
      * Computer Security Group for helping to argue out all the
      * fine details.
      */
-    struct dss_key *dss = (struct dss_key *) key;
     SHA512_State ss;
-    unsigned char digest[20], digest512[64];
-    Bignum proto_k, k, gkp, hash, kinv, hxr, r, s;
-    unsigned char *bytes;
-    int nbytes, i;
-
-    SHA_Simple(data, datalen, digest);
+    unsigned char digest512[64];
+    Bignum proto_k, k;
 
     /*
      * Hash some identifying text plus x.
      */
     SHA512_Init(&ss);
-    SHA512_Bytes(&ss, "DSA deterministic k generator", 30);
-    sha512_mpint(&ss, dss->x);
+    SHA512_Bytes(&ss, id_string, strlen(id_string) + 1);
+    sha512_mpint(&ss, private_key);
     SHA512_Final(&ss, digest512);
 
     /*
@@ -613,7 +577,7 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
      */
     SHA512_Init(&ss);
     SHA512_Bytes(&ss, digest512, sizeof(digest512));
-    SHA512_Bytes(&ss, digest, sizeof(digest));
+    SHA512_Bytes(&ss, digest, digest_len);
 
     while (1) {
         SHA512_State ss2 = ss;         /* structure copy */
@@ -625,23 +589,38 @@ static unsigned char *dss_sign(void *key, char *data, int datalen, int *siglen)
          * Now convert the result into a bignum, and reduce it mod q.
          */
         proto_k = bignum_from_bytes(digest512, 64);
-        k = bigmod(proto_k, dss->q);
+        k = bigmod(proto_k, modulus);
         freebn(proto_k);
-        kinv = modinv(k, dss->q);	       /* k^-1 mod q */
-        if (!kinv) {                           /* very unlikely */
-            freebn(k);
-            /* Perturb the hash to think of a different k. */
-            SHA512_Bytes(&ss, "x", 1);
-            /* Go round and try again. */
-            continue;
+
+        if (bignum_cmp(k, One) != 0 && bignum_cmp(k, Zero) != 0) {
+            smemclr(&ss, sizeof(ss));
+            smemclr(digest512, sizeof(digest512));
+            return k;
         }
 
-        break;
+        /* Very unlikely we get here, but if so, k was unsuitable. */
+        freebn(k);
+        /* Perturb the hash to think of a different k. */
+        SHA512_Bytes(&ss, "x", 1);
+        /* Go round and try again. */
     }
+}
 
-    smemclr(&ss, sizeof(ss));
+static unsigned char *dss_sign(void *key, const char *data, int datalen,
+                               int *siglen)
+{
+    struct dss_key *dss = (struct dss_key *) key;
+    Bignum k, gkp, hash, kinv, hxr, r, s;
+    unsigned char digest[20];
+    unsigned char *bytes;
+    int nbytes, i;
+
+    SHA_Simple(data, datalen, digest);
 
-    smemclr(digest512, sizeof(digest512));
+    k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x,
+                  digest, sizeof(digest));
+    kinv = modinv(k, dss->q);	       /* k^-1 mod q */
+    assert(kinv);
 
     /*
      * Now we have k, so just go ahead and compute the signature.
@@ -691,10 +670,11 @@ const struct ssh_signkey ssh_dss = {
     dss_createkey,
     dss_openssh_createkey,
     dss_openssh_fmtkey,
+    5 /* p,q,g,y,x */,
     dss_pubkey_bits,
-    dss_fingerprint,
     dss_verifysig,
     dss_sign,
     "ssh-dss",
-    "dss"
+    "dss",
+    NULL,
 };

+ 2967 - 0
source/putty/sshecc.c

@@ -0,0 +1,2967 @@
+/*
+ * 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)
+ */
+
+/*
+ * 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"
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve definitions
+ */
+
+static void initialise_wcurve(struct ec_curve *curve, int bits,
+                              const unsigned char *p,
+                              const unsigned char *a, const unsigned char *b,
+                              const unsigned char *n, const unsigned char *Gx,
+                              const unsigned char *Gy)
+{
+    int length = bits / 8;
+    if (bits % 8) ++length;
+
+    curve->type = EC_WEIERSTRASS;
+
+    curve->fieldBits = bits;
+    curve->p = bignum_from_bytes(p, length);
+
+    /* Curve co-efficients */
+    curve->w.a = bignum_from_bytes(a, length);
+    curve->w.b = bignum_from_bytes(b, length);
+
+    /* Group order and generator */
+    curve->w.n = bignum_from_bytes(n, length);
+    curve->w.G.x = bignum_from_bytes(Gx, length);
+    curve->w.G.y = bignum_from_bytes(Gy, length);
+    curve->w.G.curve = curve;
+    curve->w.G.infinity = 0;
+}
+
+static void initialise_mcurve(struct ec_curve *curve, int bits,
+                              const unsigned char *p,
+                              const unsigned char *a, const unsigned char *b,
+                              const unsigned char *Gx)
+{
+    int length = bits / 8;
+    if (bits % 8) ++length;
+
+    curve->type = EC_MONTGOMERY;
+
+    curve->fieldBits = bits;
+    curve->p = bignum_from_bytes(p, length);
+
+    /* Curve co-efficients */
+    curve->m.a = bignum_from_bytes(a, length);
+    curve->m.b = bignum_from_bytes(b, length);
+
+    /* Generator */
+    curve->m.G.x = bignum_from_bytes(Gx, length);
+    curve->m.G.y = NULL;
+    curve->m.G.z = NULL;
+    curve->m.G.curve = curve;
+    curve->m.G.infinity = 0;
+}
+
+static void initialise_ecurve(struct ec_curve *curve, int bits,
+                              const unsigned char *p,
+                              const unsigned char *l, const unsigned char *d,
+                              const unsigned char *Bx, const unsigned char *By)
+{
+    int length = bits / 8;
+    if (bits % 8) ++length;
+
+    curve->type = EC_EDWARDS;
+
+    curve->fieldBits = bits;
+    curve->p = bignum_from_bytes(p, length);
+
+    /* Curve co-efficients */
+    curve->e.l = bignum_from_bytes(l, length);
+    curve->e.d = bignum_from_bytes(d, length);
+
+    /* Group order and generator */
+    curve->e.B.x = bignum_from_bytes(Bx, length);
+    curve->e.B.y = bignum_from_bytes(By, length);
+    curve->e.B.curve = curve;
+    curve->e.B.infinity = 0;
+}
+
+static struct ec_curve *ec_p256(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        static const unsigned char p[] = {
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+        };
+        static const unsigned char a[] = {
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc
+        };
+        static const unsigned char b[] = {
+            0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7,
+            0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86, 0xbc,
+            0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6,
+            0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b
+        };
+        static const unsigned char n[] = {
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
+            0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
+        };
+        static const unsigned char Gx[] = {
+            0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
+            0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+            0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+            0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96
+        };
+        static const unsigned char Gy[] = {
+            0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
+            0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+            0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+            0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5
+        };
+
+        initialise_wcurve(&curve, 256, p, a, b, n, Gx, Gy);
+        curve.textname = curve.name = "nistp256";
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+static struct ec_curve *ec_p384(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        static const unsigned char p[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+        };
+        static const unsigned char a[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+            0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc
+        };
+        static const unsigned char b[] = {
+            0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4,
+            0x98, 0x8e, 0x05, 0x6b, 0xe3, 0xf8, 0x2d, 0x19,
+            0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, 0x41, 0x12,
+            0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a,
+            0xc6, 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d,
+            0x2a, 0x85, 0xc8, 0xed, 0xd3, 0xec, 0x2a, 0xef
+        };
+        static const unsigned char n[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
+            0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
+            0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73
+        };
+        static const unsigned char Gx[] = {
+            0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37,
+            0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74,
+            0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
+            0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38,
+            0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c,
+            0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7
+        };
+        static const unsigned char Gy[] = {
+            0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f,
+            0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29,
+            0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+            0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0,
+            0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
+            0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f
+        };
+
+        initialise_wcurve(&curve, 384, p, a, b, n, Gx, Gy);
+        curve.textname = curve.name = "nistp384";
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+static struct ec_curve *ec_p521(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        static const unsigned char p[] = {
+            0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff
+        };
+        static const unsigned char a[] = {
+            0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xfc
+        };
+        static const unsigned char b[] = {
+            0x00, 0x51, 0x95, 0x3e, 0xb9, 0x61, 0x8e, 0x1c,
+            0x9a, 0x1f, 0x92, 0x9a, 0x21, 0xa0, 0xb6, 0x85,
+            0x40, 0xee, 0xa2, 0xda, 0x72, 0x5b, 0x99, 0xb3,
+            0x15, 0xf3, 0xb8, 0xb4, 0x89, 0x91, 0x8e, 0xf1,
+            0x09, 0xe1, 0x56, 0x19, 0x39, 0x51, 0xec, 0x7e,
+            0x93, 0x7b, 0x16, 0x52, 0xc0, 0xbd, 0x3b, 0xb1,
+            0xbf, 0x07, 0x35, 0x73, 0xdf, 0x88, 0x3d, 0x2c,
+            0x34, 0xf1, 0xef, 0x45, 0x1f, 0xd4, 0x6b, 0x50,
+            0x3f, 0x00
+        };
+        static const unsigned char n[] = {
+            0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f,
+            0x96, 0x6b, 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09,
+            0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c,
+            0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38,
+            0x64, 0x09
+        };
+        static const unsigned char Gx[] = {
+            0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04,
+            0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
+            0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
+            0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
+            0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
+            0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
+            0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
+            0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
+            0xbd, 0x66
+        };
+        static const unsigned char Gy[] = {
+            0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b,
+            0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
+            0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+            0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
+            0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
+            0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+            0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
+            0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
+            0x66, 0x50
+        };
+
+        initialise_wcurve(&curve, 521, p, a, b, n, Gx, Gy);
+        curve.textname = curve.name = "nistp521";
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+static struct ec_curve *ec_curve25519(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        static const unsigned char p[] = {
+            0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed
+        };
+        static const unsigned char a[] = {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x6d, 0x06
+        };
+        static const unsigned char b[] = {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+        };
+        static const unsigned char gx[32] = {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09
+        };
+
+        initialise_mcurve(&curve, 256, p, a, b, gx);
+        /* 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 = 1;
+    }
+
+    return &curve;
+}
+
+static struct ec_curve *ec_ed25519(void)
+{
+    static struct ec_curve curve = { 0 };
+    static unsigned char initialised = 0;
+
+    if (!initialised)
+    {
+        static const unsigned char q[] = {
+            0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed
+        };
+        static const unsigned char l[32] = {
+            0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6,
+            0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed
+        };
+        static const unsigned char d[32] = {
+            0x52, 0x03, 0x6c, 0xee, 0x2b, 0x6f, 0xfe, 0x73,
+            0x8c, 0xc7, 0x40, 0x79, 0x77, 0x79, 0xe8, 0x98,
+            0x00, 0x70, 0x0a, 0x4d, 0x41, 0x41, 0xd8, 0xab,
+            0x75, 0xeb, 0x4d, 0xca, 0x13, 0x59, 0x78, 0xa3
+        };
+        static const unsigned char Bx[32] = {
+            0x21, 0x69, 0x36, 0xd3, 0xcd, 0x6e, 0x53, 0xfe,
+            0xc0, 0xa4, 0xe2, 0x31, 0xfd, 0xd6, 0xdc, 0x5c,
+            0x69, 0x2c, 0xc7, 0x60, 0x95, 0x25, 0xa7, 0xb2,
+            0xc9, 0x56, 0x2d, 0x60, 0x8f, 0x25, 0xd5, 0x1a
+        };
+        static const unsigned char By[32] = {
+            0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+            0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+            0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+            0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x58
+        };
+
+        /* This curve doesn't need a name, because it's never used in
+         * any format that embeds the curve name */
+        curve.name = NULL;
+
+        initialise_ecurve(&curve, 256, q, l, d, Bx, By);
+        curve.textname = "Ed25519";
+
+        /* Now initialised, no need to do it again */
+        initialised = 1;
+    }
+
+    return &curve;
+}
+
+/* Return 1 if a is -3 % p, otherwise return 0
+ * This is used because there are some maths optimisations */
+static int ec_aminus3(const struct ec_curve *curve)
+{
+    int ret;
+    Bignum _p;
+
+    if (curve->type != EC_WEIERSTRASS) {
+        return 0;
+    }
+
+    _p = bignum_add_long(curve->w.a, 3);
+
+    ret = !bignum_cmp(curve->p, _p);
+    freebn(_p);
+    return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve field maths
+ */
+
+static Bignum ecf_add(const Bignum a, const Bignum b,
+                      const struct ec_curve *curve)
+{
+    Bignum a1, b1, ab, ret;
+
+    a1 = bigmod(a, curve->p);
+    b1 = bigmod(b, curve->p);
+
+    ab = bigadd(a1, b1);
+    freebn(a1);
+    freebn(b1);
+
+    ret = bigmod(ab, curve->p);
+    freebn(ab);
+
+    return ret;
+}
+
+static Bignum ecf_square(const Bignum a, const struct ec_curve *curve)
+{
+    return modmul(a, a, curve->p);
+}
+
+static Bignum ecf_treble(const Bignum a, const struct ec_curve *curve)
+{
+    Bignum ret, tmp;
+
+    /* Double */
+    tmp = bignum_lshift(a, 1);
+
+    /* Add itself (i.e. treble) */
+    ret = bigadd(tmp, a);
+    freebn(tmp);
+
+    /* Normalise */
+    while (bignum_cmp(ret, curve->p) >= 0)
+    {
+        tmp = bigsub(ret, curve->p);
+        assert(tmp);
+        freebn(ret);
+        ret = tmp;
+    }
+
+    return ret;
+}
+
+static Bignum ecf_double(const Bignum a, const struct ec_curve *curve)
+{
+    Bignum ret = bignum_lshift(a, 1);
+    if (bignum_cmp(ret, curve->p) >= 0)
+    {
+        Bignum tmp = bigsub(ret, curve->p);
+        assert(tmp);
+        freebn(ret);
+        return tmp;
+    }
+    else
+    {
+        return ret;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Memory functions
+ */
+
+void ec_point_free(struct ec_point *point)
+{
+    if (point == NULL) return;
+    point->curve = 0;
+    if (point->x) freebn(point->x);
+    if (point->y) freebn(point->y);
+    if (point->z) freebn(point->z);
+    point->infinity = 0;
+    sfree(point);
+}
+
+static struct ec_point *ec_point_new(const struct ec_curve *curve,
+                                     const Bignum x, const Bignum y, const Bignum z,
+                                     unsigned char infinity)
+{
+    struct ec_point *point = snewn(1, struct ec_point);
+    point->curve = curve;
+    point->x = x;
+    point->y = y;
+    point->z = z;
+    point->infinity = infinity ? 1 : 0;
+    return point;
+}
+
+static struct ec_point *ec_point_copy(const struct ec_point *a)
+{
+    if (a == NULL) return NULL;
+    return ec_point_new(a->curve,
+                        a->x ? copybn(a->x) : NULL,
+                        a->y ? copybn(a->y) : NULL,
+                        a->z ? copybn(a->z) : NULL,
+                        a->infinity);
+}
+
+static int ec_point_verify(const struct ec_point *a)
+{
+    if (a->infinity) {
+        return 1;
+    } else if (a->curve->type == EC_EDWARDS) {
+        /* Check y^2 - x^2 - 1 - d * x^2 * y^2 == 0 */
+        Bignum y2, x2, tmp, tmp2, tmp3;
+        int ret;
+
+        y2 = ecf_square(a->y, a->curve);
+        x2 = ecf_square(a->x, a->curve);
+        tmp = modmul(a->curve->e.d, x2, a->curve->p);
+        tmp2 = modmul(tmp, y2, a->curve->p);
+        freebn(tmp);
+        tmp = modsub(y2, x2, a->curve->p);
+        freebn(y2);
+        freebn(x2);
+        tmp3 = modsub(tmp, tmp2, a->curve->p);
+        freebn(tmp);
+        freebn(tmp2);
+        ret = !bignum_cmp(tmp3, One);
+        freebn(tmp3);
+        return ret;
+    } else if (a->curve->type == EC_WEIERSTRASS) {
+        /* Verify y^2 = x^3 + ax + b */
+        int ret = 0;
+
+        Bignum lhs = NULL, x3 = NULL, ax = NULL, x3ax = NULL, x3axm = NULL, x3axb = NULL, rhs = NULL;
+
+        Bignum Three = bignum_from_long(3);
+
+        lhs = modmul(a->y, a->y, a->curve->p);
+
+        /* This uses montgomery multiplication to optimise */
+        x3 = modpow(a->x, Three, a->curve->p);
+        freebn(Three);
+        ax = modmul(a->curve->w.a, a->x, a->curve->p);
+        x3ax = bigadd(x3, ax);
+        freebn(x3); x3 = NULL;
+        freebn(ax); ax = NULL;
+        x3axm = bigmod(x3ax, a->curve->p);
+        freebn(x3ax); x3ax = NULL;
+        x3axb = bigadd(x3axm, a->curve->w.b);
+        freebn(x3axm); x3axm = NULL;
+        rhs = bigmod(x3axb, a->curve->p);
+        freebn(x3axb);
+
+        ret = bignum_cmp(lhs, rhs) ? 0 : 1;
+        freebn(lhs);
+        freebn(rhs);
+
+        return ret;
+    } else {
+        return 0;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Elliptic curve point maths
+ */
+
+/* Returns 1 on success and 0 on memory error */
+static int ecp_normalise(struct ec_point *a)
+{
+    if (!a) {
+        /* No point */
+        return 0;
+    }
+
+    if (a->infinity) {
+        /* Point is at infinity - i.e. normalised */
+        return 1;
+    }
+
+    if (a->curve->type == EC_WEIERSTRASS) {
+        /* In Jacobian Coordinates the triple (X, Y, Z) represents
+           the affine point (X / Z^2, Y / Z^3) */
+
+        Bignum Z2, Z2inv, Z3, Z3inv, tx, ty;
+
+        if (!a->x || !a->y) {
+            /* No point defined */
+            return 0;
+        } else if (!a->z) {
+            /* Already normalised */
+            return 1;
+        }
+
+        Z2 = ecf_square(a->z, a->curve);
+        Z2inv = modinv(Z2, a->curve->p);
+        if (!Z2inv) {
+            freebn(Z2);
+            return 0;
+        }
+        tx = modmul(a->x, Z2inv, a->curve->p);
+        freebn(Z2inv);
+
+        Z3 = modmul(Z2, a->z, a->curve->p);
+        freebn(Z2);
+        Z3inv = modinv(Z3, a->curve->p);
+        freebn(Z3);
+        if (!Z3inv) {
+            freebn(tx);
+            return 0;
+        }
+        ty = modmul(a->y, Z3inv, a->curve->p);
+        freebn(Z3inv);
+
+        freebn(a->x);
+        a->x = tx;
+        freebn(a->y);
+        a->y = ty;
+        freebn(a->z);
+        a->z = NULL;
+        return 1;
+    } else if (a->curve->type == EC_MONTGOMERY) {
+        /* In Montgomery (X : Z) represents the x co-ord (X / Z, ?) */
+
+        Bignum tmp, tmp2;
+
+        if (!a->x) {
+            /* No point defined */
+            return 0;
+        } else if (!a->z) {
+            /* Already normalised */
+            return 1;
+        }
+
+        tmp = modinv(a->z, a->curve->p);
+        if (!tmp) {
+            return 0;
+        }
+        tmp2 = modmul(a->x, tmp, a->curve->p);
+        freebn(tmp);
+
+        freebn(a->z);
+        a->z = NULL;
+        freebn(a->x);
+        a->x = tmp2;
+        return 1;
+    } else if (a->curve->type == EC_EDWARDS) {
+        /* Always normalised */
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static struct ec_point *ecp_doublew(const struct ec_point *a, const int aminus3)
+{
+    Bignum S, M, outx, outy, outz;
+
+    if (bignum_cmp(a->y, Zero) == 0)
+    {
+        /* Identity */
+        return ec_point_new(a->curve, NULL, NULL, NULL, 1);
+    }
+
+    /* S = 4*X*Y^2 */
+    {
+        Bignum Y2, XY2, _2XY2;
+
+        Y2 = ecf_square(a->y, a->curve);
+        XY2 = modmul(a->x, Y2, a->curve->p);
+        freebn(Y2);
+
+        _2XY2 = ecf_double(XY2, a->curve);
+        freebn(XY2);
+        S = ecf_double(_2XY2, a->curve);
+        freebn(_2XY2);
+    }
+
+    /* Faster calculation if a = -3 */
+    if (aminus3) {
+        /* if a = -3, then M can also be calculated as M = 3*(X + Z^2)*(X - Z^2) */
+        Bignum Z2, XpZ2, XmZ2, second;
+
+        if (a->z == NULL) {
+            Z2 = copybn(One);
+        } else {
+            Z2 = ecf_square(a->z, a->curve);
+        }
+
+        XpZ2 = ecf_add(a->x, Z2, a->curve);
+        XmZ2 = modsub(a->x, Z2, a->curve->p);
+        freebn(Z2);
+
+        second = modmul(XpZ2, XmZ2, a->curve->p);
+        freebn(XpZ2);
+        freebn(XmZ2);
+
+        M = ecf_treble(second, a->curve);
+        freebn(second);
+    } else {
+        /* M = 3*X^2 + a*Z^4 */
+        Bignum _3X2, X2, aZ4;
+
+        if (a->z == NULL) {
+            aZ4 = copybn(a->curve->w.a);
+        } else {
+            Bignum Z2, Z4;
+
+            Z2 = ecf_square(a->z, a->curve);
+            Z4 = ecf_square(Z2, a->curve);
+            freebn(Z2);
+            aZ4 = modmul(a->curve->w.a, Z4, a->curve->p);
+            freebn(Z4);
+        }
+
+        X2 = modmul(a->x, a->x, a->curve->p);
+        _3X2 = ecf_treble(X2, a->curve);
+        freebn(X2);
+        M = ecf_add(_3X2, aZ4, a->curve);
+        freebn(_3X2);
+        freebn(aZ4);
+    }
+
+    /* X' = M^2 - 2*S */
+    {
+        Bignum M2, _2S;
+
+        M2 = ecf_square(M, a->curve);
+        _2S = ecf_double(S, a->curve);
+        outx = modsub(M2, _2S, a->curve->p);
+        freebn(M2);
+        freebn(_2S);
+    }
+
+    /* Y' = M*(S - X') - 8*Y^4 */
+    {
+        Bignum SX, MSX, Eight, Y2, Y4, _8Y4;
+
+        SX = modsub(S, outx, a->curve->p);
+        freebn(S);
+        MSX = modmul(M, SX, a->curve->p);
+        freebn(SX);
+        freebn(M);
+        Y2 = ecf_square(a->y, a->curve);
+        Y4 = ecf_square(Y2, a->curve);
+        freebn(Y2);
+        Eight = bignum_from_long(8);
+        _8Y4 = modmul(Eight, Y4, a->curve->p);
+        freebn(Eight);
+        freebn(Y4);
+        outy = modsub(MSX, _8Y4, a->curve->p);
+        freebn(MSX);
+        freebn(_8Y4);
+    }
+
+    /* Z' = 2*Y*Z */
+    {
+        Bignum YZ;
+
+        if (a->z == NULL) {
+            YZ = copybn(a->y);
+        } else {
+            YZ = modmul(a->y, a->z, a->curve->p);
+        }
+
+        outz = ecf_double(YZ, a->curve);
+        freebn(YZ);
+    }
+
+    return ec_point_new(a->curve, outx, outy, outz, 0);
+}
+
+static struct ec_point *ecp_doublem(const struct ec_point *a)
+{
+    Bignum z, outx, outz, xpz, xmz;
+
+    z = a->z;
+    if (!z) {
+        z = One;
+    }
+
+    /* 4xz = (x + z)^2 - (x - z)^2 */
+    {
+        Bignum tmp;
+
+        tmp = ecf_add(a->x, z, a->curve);
+        xpz = ecf_square(tmp, a->curve);
+        freebn(tmp);
+
+        tmp = modsub(a->x, z, a->curve->p);
+        xmz = ecf_square(tmp, a->curve);
+        freebn(tmp);
+    }
+
+    /* outx = (x + z)^2 * (x - z)^2 */
+    outx = modmul(xpz, xmz, a->curve->p);
+
+    /* outz = 4xz * ((x - z)^2 + ((A + 2) / 4)*4xz) */
+    {
+        Bignum _4xz, tmp, tmp2, tmp3;
+
+        tmp = bignum_from_long(2);
+        tmp2 = ecf_add(a->curve->m.a, tmp, a->curve);
+        freebn(tmp);
+
+        _4xz = modsub(xpz, xmz, a->curve->p);
+        freebn(xpz);
+        tmp = modmul(tmp2, _4xz, a->curve->p);
+        freebn(tmp2);
+
+        tmp2 = bignum_from_long(4);
+        tmp3 = modinv(tmp2, a->curve->p);
+        freebn(tmp2);
+        if (!tmp3) {
+            freebn(tmp);
+            freebn(_4xz);
+            freebn(outx);
+            freebn(xmz);
+            return NULL;
+        }
+        tmp2 = modmul(tmp, tmp3, a->curve->p);
+        freebn(tmp);
+        freebn(tmp3);
+
+        tmp = ecf_add(xmz, tmp2, a->curve);
+        freebn(xmz);
+        freebn(tmp2);
+        outz = modmul(_4xz, tmp, a->curve->p);
+        freebn(_4xz);
+        freebn(tmp);
+    }
+
+    return ec_point_new(a->curve, outx, NULL, outz, 0);
+}
+
+/* Forward declaration for Edwards curve doubling */
+static struct ec_point *ecp_add(const struct ec_point *a,
+                                const struct ec_point *b,
+                                const int aminus3);
+
+static struct ec_point *ecp_double(const struct ec_point *a, const int aminus3)
+{
+    if (a->infinity)
+    {
+        /* Identity */
+        return ec_point_new(a->curve, NULL, NULL, NULL, 1);
+    }
+
+    if (a->curve->type == EC_EDWARDS)
+    {
+        return ecp_add(a, a, aminus3);
+    }
+    else if (a->curve->type == EC_WEIERSTRASS)
+    {
+        return ecp_doublew(a, aminus3);
+    }
+    else
+    {
+        return ecp_doublem(a);
+    }
+}
+
+static struct ec_point *ecp_addw(const struct ec_point *a,
+                                 const struct ec_point *b,
+                                 const int aminus3)
+{
+    Bignum U1, U2, S1, S2, outx, outy, outz;
+
+    /* U1 = X1*Z2^2 */
+    /* S1 = Y1*Z2^3 */
+    if (b->z) {
+        Bignum Z2, Z3;
+
+        Z2 = ecf_square(b->z, a->curve);
+        U1 = modmul(a->x, Z2, a->curve->p);
+        Z3 = modmul(Z2, b->z, a->curve->p);
+        freebn(Z2);
+        S1 = modmul(a->y, Z3, a->curve->p);
+        freebn(Z3);
+    } else {
+        U1 = copybn(a->x);
+        S1 = copybn(a->y);
+    }
+
+    /* U2 = X2*Z1^2 */
+    /* S2 = Y2*Z1^3 */
+    if (a->z) {
+        Bignum Z2, Z3;
+
+        Z2 = ecf_square(a->z, b->curve);
+        U2 = modmul(b->x, Z2, b->curve->p);
+        Z3 = modmul(Z2, a->z, b->curve->p);
+        freebn(Z2);
+        S2 = modmul(b->y, Z3, b->curve->p);
+        freebn(Z3);
+    } else {
+        U2 = copybn(b->x);
+        S2 = copybn(b->y);
+    }
+
+    /* Check if multiplying by self */
+    if (bignum_cmp(U1, U2) == 0)
+    {
+        freebn(U1);
+        freebn(U2);
+        if (bignum_cmp(S1, S2) == 0)
+        {
+            freebn(S1);
+            freebn(S2);
+            return ecp_double(a, aminus3);
+        }
+        else
+        {
+            freebn(S1);
+            freebn(S2);
+            /* Infinity */
+            return ec_point_new(a->curve, NULL, NULL, NULL, 1);
+        }
+    }
+
+    {
+        Bignum H, R, UH2, H3;
+
+        /* H = U2 - U1 */
+        H = modsub(U2, U1, a->curve->p);
+        freebn(U2);
+
+        /* R = S2 - S1 */
+        R = modsub(S2, S1, a->curve->p);
+        freebn(S2);
+
+        /* X3 = R^2 - H^3 - 2*U1*H^2 */
+        {
+            Bignum R2, H2, _2UH2, first;
+
+            H2 = ecf_square(H, a->curve);
+            UH2 = modmul(U1, H2, a->curve->p);
+            freebn(U1);
+            H3 = modmul(H2, H, a->curve->p);
+            freebn(H2);
+            R2 = ecf_square(R, a->curve);
+            _2UH2 = ecf_double(UH2, a->curve);
+            first = modsub(R2, H3, a->curve->p);
+            freebn(R2);
+            outx = modsub(first, _2UH2, a->curve->p);
+            freebn(first);
+            freebn(_2UH2);
+        }
+
+        /* Y3 = R*(U1*H^2 - X3) - S1*H^3 */
+        {
+            Bignum RUH2mX, UH2mX, SH3;
+
+            UH2mX = modsub(UH2, outx, a->curve->p);
+            freebn(UH2);
+            RUH2mX = modmul(R, UH2mX, a->curve->p);
+            freebn(UH2mX);
+            freebn(R);
+            SH3 = modmul(S1, H3, a->curve->p);
+            freebn(S1);
+            freebn(H3);
+
+            outy = modsub(RUH2mX, SH3, a->curve->p);
+            freebn(RUH2mX);
+            freebn(SH3);
+        }
+
+        /* Z3 = H*Z1*Z2 */
+        if (a->z && b->z) {
+            Bignum ZZ;
+
+            ZZ = modmul(a->z, b->z, a->curve->p);
+            outz = modmul(H, ZZ, a->curve->p);
+            freebn(H);
+            freebn(ZZ);
+        } else if (a->z) {
+            outz = modmul(H, a->z, a->curve->p);
+            freebn(H);
+        } else if (b->z) {
+            outz = modmul(H, b->z, a->curve->p);
+            freebn(H);
+        } else {
+            outz = H;
+        }
+    }
+
+    return ec_point_new(a->curve, outx, outy, outz, 0);
+}
+
+static struct ec_point *ecp_addm(const struct ec_point *a,
+                                 const struct ec_point *b,
+                                 const struct ec_point *base)
+{
+    Bignum outx, outz, az, bz;
+
+    az = a->z;
+    if (!az) {
+        az = One;
+    }
+    bz = b->z;
+    if (!bz) {
+        bz = One;
+    }
+
+    /* a-b is maintained at 1 due to Montgomery ladder implementation */
+    /* Xa+b = Za-b * ((Xa - Za)*(Xb + Zb) + (Xa + Za)*(Xb - Zb))^2 */
+    /* Za+b = Xa-b * ((Xa - Za)*(Xb + Zb) - (Xa + Za)*(Xb - Zb))^2 */
+    {
+        Bignum tmp, tmp2, tmp3, tmp4;
+
+        /* (Xa + Za) * (Xb - Zb) */
+        tmp = ecf_add(a->x, az, a->curve);
+        tmp2 = modsub(b->x, bz, a->curve->p);
+        tmp3 = modmul(tmp, tmp2, a->curve->p);
+        freebn(tmp);
+        freebn(tmp2);
+
+        /* (Xa - Za) * (Xb + Zb) */
+        tmp = modsub(a->x, az, a->curve->p);
+        tmp2 = ecf_add(b->x, bz, a->curve);
+        tmp4 = modmul(tmp, tmp2, a->curve->p);
+        freebn(tmp);
+        freebn(tmp2);
+
+        tmp = ecf_add(tmp3, tmp4, a->curve);
+        outx = ecf_square(tmp, a->curve);
+        freebn(tmp);
+
+        tmp = modsub(tmp3, tmp4, a->curve->p);
+        freebn(tmp3);
+        freebn(tmp4);
+        tmp2 = ecf_square(tmp, a->curve);
+        freebn(tmp);
+        outz = modmul(base->x, tmp2, a->curve->p);
+        freebn(tmp2);
+    }
+
+    return ec_point_new(a->curve, outx, NULL, outz, 0);
+}
+
+static struct ec_point *ecp_adde(const struct ec_point *a,
+                                 const struct ec_point *b)
+{
+    Bignum outx, outy, dmul;
+
+    /* outx = (a->x * b->y + b->x * a->y) /
+     *        (1 + a->curve->e.d * a->x * b->x * a->y * b->y) */
+    {
+        Bignum tmp, tmp2, tmp3, tmp4;
+
+        tmp = modmul(a->x, b->y, a->curve->p);
+        tmp2 = modmul(b->x, a->y, a->curve->p);
+        tmp3 = ecf_add(tmp, tmp2, a->curve);
+
+        tmp4 = modmul(tmp, tmp2, a->curve->p);
+        freebn(tmp);
+        freebn(tmp2);
+        dmul = modmul(a->curve->e.d, tmp4, a->curve->p);
+        freebn(tmp4);
+
+        tmp = ecf_add(One, dmul, a->curve);
+        tmp2 = modinv(tmp, a->curve->p);
+        freebn(tmp);
+        if (!tmp2)
+        {
+            freebn(tmp3);
+            freebn(dmul);
+            return NULL;
+        }
+
+        outx = modmul(tmp3, tmp2, a->curve->p);
+        freebn(tmp3);
+        freebn(tmp2);
+    }
+
+    /* outy = (a->y * b->y + a->x * b->x) /
+     *        (1 - a->curve->e.d * a->x * b->x * a->y * b->y) */
+    {
+        Bignum tmp, tmp2, tmp3, tmp4;
+
+        tmp = modsub(One, dmul, a->curve->p);
+        freebn(dmul);
+
+        tmp2 = modinv(tmp, a->curve->p);
+        freebn(tmp);
+        if (!tmp2)
+        {
+            freebn(outx);
+            return NULL;
+        }
+
+        tmp = modmul(a->y, b->y, a->curve->p);
+        tmp3 = modmul(a->x, b->x, a->curve->p);
+        tmp4 = ecf_add(tmp, tmp3, a->curve);
+        freebn(tmp);
+        freebn(tmp3);
+
+        outy = modmul(tmp4, tmp2, a->curve->p);
+        freebn(tmp4);
+        freebn(tmp2);
+    }
+
+    return ec_point_new(a->curve, outx, outy, NULL, 0);
+}
+
+static struct ec_point *ecp_add(const struct ec_point *a,
+                                const struct ec_point *b,
+                                const int aminus3)
+{
+    if (a->curve != b->curve) {
+        return NULL;
+    }
+
+    /* Check if multiplying by infinity */
+    if (a->infinity) return ec_point_copy(b);
+    if (b->infinity) return ec_point_copy(a);
+
+    if (a->curve->type == EC_EDWARDS)
+    {
+        return ecp_adde(a, b);
+    }
+
+    if (a->curve->type == EC_WEIERSTRASS)
+    {
+        return ecp_addw(a, b, aminus3);
+    }
+
+    return NULL;
+}
+
+static struct ec_point *ecp_mul_(const struct ec_point *a, const Bignum b, int aminus3)
+{
+    struct ec_point *A, *ret;
+    int bits, i;
+
+    A = ec_point_copy(a);
+    ret = ec_point_new(a->curve, NULL, NULL, NULL, 1);
+
+    bits = bignum_bitcount(b);
+    for (i = 0; i < bits; ++i)
+    {
+        if (bignum_bit(b, i))
+        {
+            struct ec_point *tmp = ecp_add(ret, A, aminus3);
+            ec_point_free(ret);
+            ret = tmp;
+        }
+        if (i+1 != bits)
+        {
+            struct ec_point *tmp = ecp_double(A, aminus3);
+            ec_point_free(A);
+            A = tmp;
+        }
+    }
+
+    ec_point_free(A);
+    return ret;
+}
+
+static struct ec_point *ecp_mulw(const struct ec_point *a, const Bignum b)
+{
+    struct ec_point *ret = ecp_mul_(a, b, ec_aminus3(a->curve));
+
+    if (!ecp_normalise(ret)) {
+        ec_point_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+
+static struct ec_point *ecp_mule(const struct ec_point *a, const Bignum b)
+{
+    int i;
+    struct ec_point *ret;
+
+    ret = ec_point_new(a->curve, NULL, NULL, NULL, 1);
+
+    for (i = bignum_bitcount(b); i >= 0 && ret; --i)
+    {
+        {
+            struct ec_point *tmp = ecp_double(ret, 0);
+            ec_point_free(ret);
+            ret = tmp;
+        }
+        if (ret && bignum_bit(b, i))
+        {
+            struct ec_point *tmp = ecp_add(ret, a, 0);
+            ec_point_free(ret);
+            ret = tmp;
+        }
+    }
+
+    return ret;
+}
+
+static struct ec_point *ecp_mulm(const struct ec_point *p, const Bignum n)
+{
+    struct ec_point *P1, *P2;
+    int bits, i;
+
+    /* P1 <- P and P2 <- [2]P */
+    P2 = ecp_double(p, 0);
+    P1 = ec_point_copy(p);
+
+    /* for i = bits − 2 down to 0 */
+    bits = bignum_bitcount(n);
+    for (i = bits - 2; i >= 0; --i)
+    {
+        if (!bignum_bit(n, i))
+        {
+            /* P2 <- P1 + P2 */
+            struct ec_point *tmp = ecp_addm(P1, P2, p);
+            ec_point_free(P2);
+            P2 = tmp;
+
+            /* P1 <- [2]P1 */
+            tmp = ecp_double(P1, 0);
+            ec_point_free(P1);
+            P1 = tmp;
+        }
+        else
+        {
+            /* P1 <- P1 + P2 */
+            struct ec_point *tmp = ecp_addm(P1, P2, p);
+            ec_point_free(P1);
+            P1 = tmp;
+
+            /* P2 <- [2]P2 */
+            tmp = ecp_double(P2, 0);
+            ec_point_free(P2);
+            P2 = tmp;
+        }
+    }
+
+    ec_point_free(P2);
+
+    if (!ecp_normalise(P1)) {
+        ec_point_free(P1);
+        return NULL;
+    }
+
+    return P1;
+}
+
+/* Not static because it is used by sshecdsag.c to generate a new key */
+struct ec_point *ecp_mul(const struct ec_point *a, const Bignum b)
+{
+    if (a->curve->type == EC_WEIERSTRASS) {
+        return ecp_mulw(a, b);
+    } else if (a->curve->type == EC_EDWARDS) {
+        return ecp_mule(a, b);
+    } else {
+        return ecp_mulm(a, b);
+    }
+}
+
+static struct ec_point *ecp_summul(const Bignum a, const Bignum b,
+                                   const struct ec_point *point)
+{
+    struct ec_point *aG, *bP, *ret;
+    int aminus3;
+
+    if (point->curve->type != EC_WEIERSTRASS) {
+        return NULL;
+    }
+
+    aminus3 = ec_aminus3(point->curve);
+
+    aG = ecp_mul_(&point->curve->w.G, a, aminus3);
+    if (!aG) return NULL;
+    bP = ecp_mul_(point, b, aminus3);
+    if (!bP) {
+        ec_point_free(aG);
+        return NULL;
+    }
+
+    ret = ecp_add(aG, bP, aminus3);
+
+    ec_point_free(aG);
+    ec_point_free(bP);
+
+    if (!ecp_normalise(ret)) {
+        ec_point_free(ret);
+        return NULL;
+    }
+
+    return ret;
+}
+static Bignum *ecp_edx(const struct ec_curve *curve, const Bignum y)
+{
+    /* Get the x value on the given Edwards curve for a given y */
+    Bignum x, xx;
+
+    /* xx = (y^2 - 1) / (d * y^2 + 1) */
+    {
+        Bignum tmp, tmp2, tmp3;
+
+        tmp = ecf_square(y, curve);
+        tmp2 = modmul(curve->e.d, tmp, curve->p);
+        tmp3 = ecf_add(tmp2, One, curve);
+        freebn(tmp2);
+        tmp2 = modinv(tmp3, curve->p);
+        freebn(tmp3);
+        if (!tmp2) {
+            freebn(tmp);
+            return NULL;
+        }
+
+        tmp3 = modsub(tmp, One, curve->p);
+        freebn(tmp);
+        xx = modmul(tmp3, tmp2, curve->p);
+        freebn(tmp3);
+        freebn(tmp2);
+    }
+
+    /* x = xx^((p + 3) / 8) */
+    {
+        Bignum tmp, tmp2;
+
+        tmp = bignum_add_long(curve->p, 3);
+        tmp2 = bignum_rshift(tmp, 3);
+        freebn(tmp);
+        x = modpow(xx, tmp2, curve->p);
+        freebn(tmp2);
+    }
+
+    /* if x^2 - xx != 0 then x = x*(2^((p - 1) / 4)) */
+    {
+        Bignum tmp, tmp2;
+
+        tmp = ecf_square(x, curve);
+        tmp2 = modsub(tmp, xx, curve->p);
+        freebn(tmp);
+        freebn(xx);
+        if (bignum_cmp(tmp2, Zero)) {
+            Bignum tmp3;
+
+            freebn(tmp2);
+
+            tmp = modsub(curve->p, One, curve->p);
+            tmp2 = bignum_rshift(tmp, 2);
+            freebn(tmp);
+            tmp = bignum_from_long(2);
+            tmp3 = modpow(tmp, tmp2, curve->p);
+            freebn(tmp);
+            freebn(tmp2);
+
+            tmp = modmul(x, tmp3, curve->p);
+            freebn(x);
+            freebn(tmp3);
+            x = tmp;
+        } else {
+            freebn(tmp2);
+        }
+    }
+
+    /* if x % 2 != 0 then x = p - x */
+    if (bignum_bit(x, 0)) {
+        Bignum tmp = modsub(curve->p, x, curve->p);
+        freebn(x);
+        x = tmp;
+    }
+
+    return x;
+}
+
+/* ----------------------------------------------------------------------
+ * Public point from private
+ */
+
+struct ec_point *ec_public(const Bignum privateKey, const struct ec_curve *curve)
+{
+    if (curve->type == EC_WEIERSTRASS) {
+        return ecp_mul(&curve->w.G, privateKey);
+    } else if (curve->type == EC_EDWARDS) {
+        /* hash = H(sk) (where hash creates 2 * fieldBits)
+         * b = fieldBits
+         * a = 2^(b-2) + SUM(2^i * h_i) for i = 2 -> b-2
+         * publicKey = aB */
+        struct ec_point *ret;
+        unsigned char hash[512/8];
+        Bignum a;
+        int i, keylen;
+        SHA512_State s;
+        SHA512_Init(&s);
+
+        keylen = curve->fieldBits / 8;
+        for (i = 0; i < keylen; ++i) {
+            unsigned char b = bignum_byte(privateKey, i);
+            SHA512_Bytes(&s, &b, 1);
+        }
+        SHA512_Final(&s, hash);
+
+        /* The second part is simply turning the hash into a Bignum,
+         * however the 2^(b-2) bit *must* be set, and the bottom 3
+         * bits *must* not be */
+        hash[0] &= 0xf8; /* Unset bottom 3 bits (if set) */
+        hash[31] &= 0x7f; /* Unset above (b-2) */
+        hash[31] |= 0x40; /* Set 2^(b-2) */
+        /* Chop off the top part and convert to int */
+        a = bignum_from_bytes_le(hash, 32);
+
+        ret = ecp_mul(&curve->e.B, a);
+        freebn(a);
+        return ret;
+    } else {
+        return NULL;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Basic sign and verify routines
+ */
+
+static int _ecdsa_verify(const struct ec_point *publicKey,
+                         const unsigned char *data, const int dataLen,
+                         const Bignum r, const Bignum s)
+{
+    int z_bits, n_bits;
+    Bignum z;
+    int valid = 0;
+
+    if (publicKey->curve->type != EC_WEIERSTRASS) {
+        return 0;
+    }
+
+    /* Sanity checks */
+    if (bignum_cmp(r, Zero) == 0 || bignum_cmp(r, publicKey->curve->w.n) >= 0
+        || bignum_cmp(s, Zero) == 0 || bignum_cmp(s, publicKey->curve->w.n) >= 0)
+    {
+        return 0;
+    }
+
+    /* z = left most bitlen(curve->n) of data */
+    z = bignum_from_bytes(data, dataLen);
+    n_bits = bignum_bitcount(publicKey->curve->w.n);
+    z_bits = bignum_bitcount(z);
+    if (z_bits > n_bits)
+    {
+        Bignum tmp = bignum_rshift(z, z_bits - n_bits);
+        freebn(z);
+        z = tmp;
+    }
+
+    /* Ensure z in range of n */
+    {
+        Bignum tmp = bigmod(z, publicKey->curve->w.n);
+        freebn(z);
+        z = tmp;
+    }
+
+    /* Calculate signature */
+    {
+        Bignum w, x, u1, u2;
+        struct ec_point *tmp;
+
+        w = modinv(s, publicKey->curve->w.n);
+        if (!w) {
+            freebn(z);
+            return 0;
+        }
+        u1 = modmul(z, w, publicKey->curve->w.n);
+        u2 = modmul(r, w, publicKey->curve->w.n);
+        freebn(w);
+
+        tmp = ecp_summul(u1, u2, publicKey);
+        freebn(u1);
+        freebn(u2);
+        if (!tmp) {
+            freebn(z);
+            return 0;
+        }
+
+        x = bigmod(tmp->x, publicKey->curve->w.n);
+        ec_point_free(tmp);
+
+        valid = (bignum_cmp(r, x) == 0) ? 1 : 0;
+        freebn(x);
+    }
+
+    freebn(z);
+
+    return valid;
+}
+
+static void _ecdsa_sign(const Bignum privateKey, const struct ec_curve *curve,
+                        const unsigned char *data, const int dataLen,
+                        Bignum *r, Bignum *s)
+{
+    unsigned char digest[20];
+    int z_bits, n_bits;
+    Bignum z, k;
+    struct ec_point *kG;
+
+    *r = NULL;
+    *s = NULL;
+
+    if (curve->type != EC_WEIERSTRASS) {
+        return;
+    }
+
+    /* z = left most bitlen(curve->n) of data */
+    z = bignum_from_bytes(data, dataLen);
+    n_bits = bignum_bitcount(curve->w.n);
+    z_bits = bignum_bitcount(z);
+    if (z_bits > n_bits)
+    {
+        Bignum tmp;
+        tmp = bignum_rshift(z, z_bits - n_bits);
+        freebn(z);
+        z = tmp;
+    }
+
+    /* Generate k between 1 and curve->n, using the same deterministic
+     * k generation system we use for conventional DSA. */
+    SHA_Simple(data, dataLen, digest);
+    k = dss_gen_k("ECDSA deterministic k generator", curve->w.n, privateKey,
+                  digest, sizeof(digest));
+
+    kG = ecp_mul(&curve->w.G, k);
+    if (!kG) {
+        freebn(z);
+        freebn(k);
+        return;
+    }
+
+    /* r = kG.x mod n */
+    *r = bigmod(kG->x, curve->w.n);
+    ec_point_free(kG);
+
+    /* s = (z + r * priv)/k mod n */
+    {
+        Bignum rPriv, zMod, first, firstMod, kInv;
+        rPriv = modmul(*r, privateKey, curve->w.n);
+        zMod = bigmod(z, curve->w.n);
+        freebn(z);
+        first = bigadd(rPriv, zMod);
+        freebn(rPriv);
+        freebn(zMod);
+        firstMod = bigmod(first, curve->w.n);
+        freebn(first);
+        kInv = modinv(k, curve->w.n);
+        freebn(k);
+        if (!kInv) {
+            freebn(firstMod);
+            freebn(*r);
+            return;
+        }
+        *s = modmul(firstMod, kInv, curve->w.n);
+        freebn(firstMod);
+        freebn(kInv);
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Misc functions
+ */
+
+static void getstring(const char **data, int *datalen,
+                      const char **p, int *length)
+{
+    *p = NULL;
+    if (*datalen < 4)
+        return;
+    *length = toint(GET_32BIT(*data));
+    if (*length < 0)
+        return;
+    *datalen -= 4;
+    *data += 4;
+    if (*datalen < *length)
+        return;
+    *p = *data;
+    *data += *length;
+    *datalen -= *length;
+}
+
+static Bignum getmp(const char **data, int *datalen)
+{
+    const char *p;
+    int length;
+
+    getstring(data, datalen, &p, &length);
+    if (!p)
+        return NULL;
+    if (p[0] & 0x80)
+        return NULL;                   /* negative mp */
+    return bignum_from_bytes((unsigned char *)p, length);
+}
+
+static Bignum getmp_le(const char **data, int *datalen)
+{
+    const char *p;
+    int length;
+
+    getstring(data, datalen, &p, &length);
+    if (!p)
+        return NULL;
+    return bignum_from_bytes_le((const unsigned char *)p, length);
+}
+
+static int decodepoint_ed(const char *p, int length, struct ec_point *point)
+{
+    /* Got some conversion to do, first read in the y co-ord */
+    int negative;
+
+    point->y = bignum_from_bytes_le((const unsigned char*)p, length);
+    if ((unsigned)bignum_bitcount(point->y) > point->curve->fieldBits) {
+        freebn(point->y);
+        point->y = NULL;
+        return 0;
+    }
+    /* Read x bit and then reset it */
+    negative = bignum_bit(point->y, point->curve->fieldBits - 1);
+    bignum_set_bit(point->y, point->curve->fieldBits - 1, 0);
+    bn_restore_invariant(point->y);
+
+    /* Get the x from the y */
+    point->x = ecp_edx(point->curve, point->y);
+    if (!point->x) {
+        freebn(point->y);
+        point->y = NULL;
+        return 0;
+    }
+    if (negative) {
+        Bignum tmp = modsub(point->curve->p, point->x, point->curve->p);
+        freebn(point->x);
+        point->x = tmp;
+    }
+
+    /* Verify the point is on the curve */
+    if (!ec_point_verify(point)) {
+        freebn(point->x);
+        point->x = NULL;
+        freebn(point->y);
+        point->y = NULL;
+        return 0;
+    }
+
+    return 1;
+}
+
+static int decodepoint(const char *p, int length, struct ec_point *point)
+{
+    if (point->curve->type == EC_EDWARDS) {
+        return decodepoint_ed(p, length, point);
+    }
+
+    if (length < 1 || p[0] != 0x04) /* Only support uncompressed point */
+        return 0;
+    /* Skip compression flag */
+    ++p;
+    --length;
+    /* The two values must be equal length */
+    if (length % 2 != 0) {
+        point->x = NULL;
+        point->y = NULL;
+        point->z = NULL;
+        return 0;
+    }
+    length = length / 2;
+    point->x = bignum_from_bytes((const unsigned char *)p, length);
+    p += length;
+    point->y = bignum_from_bytes((const unsigned char *)p, length);
+    point->z = NULL;
+
+    /* Verify the point is on the curve */
+    if (!ec_point_verify(point)) {
+        freebn(point->x);
+        point->x = NULL;
+        freebn(point->y);
+        point->y = NULL;
+        return 0;
+    }
+
+    return 1;
+}
+
+static int getmppoint(const char **data, int *datalen, struct ec_point *point)
+{
+    const char *p;
+    int length;
+
+    getstring(data, datalen, &p, &length);
+    if (!p) return 0;
+    return decodepoint(p, length, point);
+}
+
+/* ----------------------------------------------------------------------
+ * Exposed ECDSA interface
+ */
+
+struct ecsign_extra {
+    struct ec_curve *(*curve)(void);
+    const struct ssh_hash *hash;
+
+    /* These fields are used by the OpenSSH PEM format importer/exporter */
+    const unsigned char *oid;
+    int oidlen;
+};
+
+static void ecdsa_freekey(void *key)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    if (!ec) return;
+
+    if (ec->publicKey.x)
+        freebn(ec->publicKey.x);
+    if (ec->publicKey.y)
+        freebn(ec->publicKey.y);
+    if (ec->publicKey.z)
+        freebn(ec->publicKey.z);
+    if (ec->privateKey)
+        freebn(ec->privateKey);
+    sfree(ec);
+}
+
+static void *ecdsa_newkey(const struct ssh_signkey *self,
+                          const char *data, int len)
+{
+    const struct ecsign_extra *extra =
+        (const struct ecsign_extra *)self->extra;
+    const char *p;
+    int slen;
+    struct ec_key *ec;
+    struct ec_curve *curve;
+
+    getstring(&data, &len, &p, &slen);
+
+    if (!p) {
+        return NULL;
+    }
+    curve = extra->curve();
+    assert(curve->type == EC_WEIERSTRASS || curve->type == EC_EDWARDS);
+
+    /* Curve name is duplicated for Weierstrass form */
+    if (curve->type == EC_WEIERSTRASS) {
+        getstring(&data, &len, &p, &slen);
+	if (!p) return NULL;
+        if (!match_ssh_id(slen, p, curve->name)) return NULL;
+    }
+
+    ec = snew(struct ec_key);
+
+    ec->signalg = self;
+    ec->publicKey.curve = curve;
+    ec->publicKey.infinity = 0;
+    ec->publicKey.x = NULL;
+    ec->publicKey.y = NULL;
+    ec->publicKey.z = NULL;
+    ec->privateKey = NULL;
+    if (!getmppoint(&data, &len, &ec->publicKey)) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    if (!ec->publicKey.x || !ec->publicKey.y ||
+        bignum_cmp(ec->publicKey.x, curve->p) >= 0 ||
+        bignum_cmp(ec->publicKey.y, curve->p) >= 0)
+    {
+        ecdsa_freekey(ec);
+        ec = NULL;
+    }
+
+    return ec;
+}
+
+static char *ecdsa_fmtkey(void *key)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    char *p;
+    int len, i, pos, nibbles;
+    static const char hex[] = "0123456789abcdef";
+    if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
+        return NULL;
+
+    len = 4 + 2 + 1;                  /* 2 x "0x", punctuation, \0 */
+    if (ec->publicKey.curve->name)
+        len += strlen(ec->publicKey.curve->name); /* Curve name */
+    len += 4 * (bignum_bitcount(ec->publicKey.x) + 15) / 16;
+    len += 4 * (bignum_bitcount(ec->publicKey.y) + 15) / 16;
+    p = snewn(len, char);
+
+    pos = 0;
+    if (ec->publicKey.curve->name)
+        pos += sprintf(p + pos, "%s,", ec->publicKey.curve->name);
+    pos += sprintf(p + pos, "0x");
+    nibbles = (3 + bignum_bitcount(ec->publicKey.x)) / 4;
+    if (nibbles < 1)
+        nibbles = 1;
+    for (i = nibbles; i--;) {
+        p[pos++] =
+            hex[(bignum_byte(ec->publicKey.x, i / 2) >> (4 * (i % 2))) & 0xF];
+    }
+    pos += sprintf(p + pos, ",0x");
+    nibbles = (3 + bignum_bitcount(ec->publicKey.y)) / 4;
+    if (nibbles < 1)
+        nibbles = 1;
+    for (i = nibbles; i--;) {
+        p[pos++] =
+            hex[(bignum_byte(ec->publicKey.y, i / 2) >> (4 * (i % 2))) & 0xF];
+    }
+    p[pos] = '\0';
+    return p;
+}
+
+static unsigned char *ecdsa_public_blob(void *key, int *len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    int pointlen, bloblen, fullnamelen, namelen;
+    int i;
+    unsigned char *blob, *p;
+
+    fullnamelen = strlen(ec->signalg->name);
+
+    if (ec->publicKey.curve->type == EC_EDWARDS) {
+        /* Edwards compressed form "ssh-ed25519" point y[:-1] + x[0:1] */
+
+        pointlen = ec->publicKey.curve->fieldBits / 8;
+
+        /* Can't handle this in our loop */
+        if (pointlen < 2) return NULL;
+
+        bloblen = 4 + fullnamelen + 4 + pointlen;
+        blob = snewn(bloblen, unsigned char);
+
+        p = blob;
+        PUT_32BIT(p, fullnamelen);
+        p += 4;
+        memcpy(p, ec->signalg->name, fullnamelen);
+        p += fullnamelen;
+        PUT_32BIT(p, pointlen);
+        p += 4;
+
+        /* Unset last bit of y and set first bit of x in its place */
+        for (i = 0; i < pointlen - 1; ++i) {
+            *p++ = bignum_byte(ec->publicKey.y, i);
+        }
+        /* Unset last bit of y and set first bit of x in its place */
+        *p = bignum_byte(ec->publicKey.y, i) & 0x7f;
+        *p++ |= bignum_bit(ec->publicKey.x, 0) << 7;
+    } else if (ec->publicKey.curve->type == EC_WEIERSTRASS) {
+        assert(ec->publicKey.curve->name);
+        namelen = strlen(ec->publicKey.curve->name);
+
+        pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+
+        /*
+         * string "ecdsa-sha2-<name>", string "<name>", 0x04 point x, y.
+         */
+        bloblen = 4 + fullnamelen + 4 + namelen + 4 + 1 + (pointlen * 2);
+        blob = snewn(bloblen, unsigned char);
+
+        p = blob;
+        PUT_32BIT(p, fullnamelen);
+        p += 4;
+        memcpy(p, ec->signalg->name, fullnamelen);
+        p += fullnamelen;
+        PUT_32BIT(p, namelen);
+        p += 4;
+        memcpy(p, ec->publicKey.curve->name, namelen);
+        p += namelen;
+        PUT_32BIT(p, (2 * pointlen) + 1);
+        p += 4;
+        *p++ = 0x04;
+        for (i = pointlen; i--;) {
+            *p++ = bignum_byte(ec->publicKey.x, i);
+        }
+        for (i = pointlen; i--;) {
+            *p++ = bignum_byte(ec->publicKey.y, i);
+        }
+    } else {
+        return NULL;
+    }
+
+    assert(p == blob + bloblen);
+    *len = bloblen;
+
+    return blob;
+}
+
+static unsigned char *ecdsa_private_blob(void *key, int *len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    int keylen, bloblen;
+    int i;
+    unsigned char *blob, *p;
+
+    if (!ec->privateKey) return NULL;
+
+    if (ec->publicKey.curve->type == EC_EDWARDS) {
+        /* Unsigned */
+        keylen = (bignum_bitcount(ec->privateKey) + 7) / 8;
+    } else {
+        /* Signed */
+        keylen = (bignum_bitcount(ec->privateKey) + 8) / 8;
+    }
+
+    /*
+     * mpint privateKey. Total 4 + keylen.
+     */
+    bloblen = 4 + keylen;
+    blob = snewn(bloblen, unsigned char);
+
+    p = blob;
+    PUT_32BIT(p, keylen);
+    p += 4;
+    if (ec->publicKey.curve->type == EC_EDWARDS) {
+        /* Little endian */
+        for (i = 0; i < keylen; ++i)
+            *p++ = bignum_byte(ec->privateKey, i);
+    } else {
+        for (i = keylen; i--;)
+            *p++ = bignum_byte(ec->privateKey, i);
+    }
+
+    assert(p == blob + bloblen);
+    *len = bloblen;
+    return blob;
+}
+
+static void *ecdsa_createkey(const struct ssh_signkey *self,
+                             const unsigned char *pub_blob, int pub_len,
+                             const unsigned char *priv_blob, int priv_len)
+{
+    struct ec_key *ec;
+    struct ec_point *publicKey;
+    const char *pb = (const char *) priv_blob;
+
+    ec = (struct ec_key*)ecdsa_newkey(self, (const char *) pub_blob, pub_len);
+    if (!ec) {
+        return NULL;
+    }
+
+    if (ec->publicKey.curve->type != EC_WEIERSTRASS
+        && ec->publicKey.curve->type != EC_EDWARDS) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    if (ec->publicKey.curve->type == EC_EDWARDS) {
+        ec->privateKey = getmp_le(&pb, &priv_len);
+    } else {
+        ec->privateKey = getmp(&pb, &priv_len);
+    }
+    if (!ec->privateKey) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    /* Check that private key generates public key */
+    publicKey = ec_public(ec->privateKey, ec->publicKey.curve);
+
+    if (!publicKey ||
+        bignum_cmp(publicKey->x, ec->publicKey.x) ||
+        bignum_cmp(publicKey->y, ec->publicKey.y))
+    {
+        ecdsa_freekey(ec);
+        ec = NULL;
+    }
+    ec_point_free(publicKey);
+
+    return ec;
+}
+
+static void *ed25519_openssh_createkey(const struct ssh_signkey *self,
+                                       const unsigned char **blob, int *len)
+{
+    struct ec_key *ec;
+    struct ec_point *publicKey;
+    const char *p, *q;
+    int plen, qlen;
+
+    getstring((const char**)blob, len, &p, &plen);
+    if (!p)
+    {
+        return NULL;
+    }
+
+    ec = snew(struct ec_key);
+
+    ec->signalg = self;
+    ec->publicKey.curve = ec_ed25519();
+    ec->publicKey.infinity = 0;
+    ec->privateKey = NULL;
+    ec->publicKey.x = NULL;
+    ec->publicKey.z = NULL;
+    ec->publicKey.y = NULL;
+
+    if (!decodepoint_ed(p, plen, &ec->publicKey))
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    getstring((const char**)blob, len, &q, &qlen);
+    if (!q)
+        return NULL;
+    if (qlen != 64)
+        return NULL;
+
+    ec->privateKey = bignum_from_bytes_le((const unsigned char *)q, 32);
+
+    /* Check that private key generates public key */
+    publicKey = ec_public(ec->privateKey, ec->publicKey.curve);
+
+    if (!publicKey ||
+        bignum_cmp(publicKey->x, ec->publicKey.x) ||
+        bignum_cmp(publicKey->y, ec->publicKey.y))
+    {
+        ecdsa_freekey(ec);
+        ec = NULL;
+    }
+    ec_point_free(publicKey);
+
+    /* 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. */
+    if (plen != 32 || 0 != memcmp(q + 32, p, 32)) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    return ec;
+}
+
+static int ed25519_openssh_fmtkey(void *key, unsigned char *blob, int len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+
+    int pointlen;
+    int keylen;
+    int bloblen;
+    int i;
+
+    if (ec->publicKey.curve->type != EC_EDWARDS) {
+        return 0;
+    }
+
+    pointlen = (bignum_bitcount(ec->publicKey.y) + 7) / 8;
+    keylen = (bignum_bitcount(ec->privateKey) + 7) / 8;
+    bloblen = 4 + pointlen + 4 + keylen + pointlen;
+
+    if (bloblen > len)
+        return bloblen;
+
+    /* Encode the public point */
+    PUT_32BIT(blob, pointlen);
+    blob += 4;
+
+    for (i = 0; i < pointlen - 1; ++i) {
+         *blob++ = bignum_byte(ec->publicKey.y, i);
+    }
+    /* Unset last bit of y and set first bit of x in its place */
+    *blob = bignum_byte(ec->publicKey.y, i) & 0x7f;
+    *blob++ |= bignum_bit(ec->publicKey.x, 0) << 7;
+
+    PUT_32BIT(blob, keylen + pointlen);
+    blob += 4;
+    for (i = 0; i < keylen; ++i) {
+         *blob++ = bignum_byte(ec->privateKey, i);
+    }
+    /* Now encode an extra copy of the public point as the second half
+     * of the private key string, as the OpenSSH format for some
+     * reason requires */
+    for (i = 0; i < pointlen - 1; ++i) {
+         *blob++ = bignum_byte(ec->publicKey.y, i);
+    }
+    /* Unset last bit of y and set first bit of x in its place */
+    *blob = bignum_byte(ec->publicKey.y, i) & 0x7f;
+    *blob++ |= bignum_bit(ec->publicKey.x, 0) << 7;
+
+    return bloblen;
+}
+
+static void *ecdsa_openssh_createkey(const struct ssh_signkey *self,
+                                     const unsigned char **blob, int *len)
+{
+    const struct ecsign_extra *extra =
+        (const struct ecsign_extra *)self->extra;
+    const char **b = (const char **) blob;
+    const char *p;
+    int slen;
+    struct ec_key *ec;
+    struct ec_curve *curve;
+    struct ec_point *publicKey;
+
+    getstring(b, len, &p, &slen);
+
+    if (!p) {
+        return NULL;
+    }
+    curve = extra->curve();
+    assert(curve->type == EC_WEIERSTRASS);
+
+    ec = snew(struct ec_key);
+
+    ec->signalg = self;
+    ec->publicKey.curve = curve;
+    ec->publicKey.infinity = 0;
+    ec->publicKey.x = NULL;
+    ec->publicKey.y = NULL;
+    ec->publicKey.z = NULL;
+    if (!getmppoint(b, len, &ec->publicKey)) {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+    ec->privateKey = NULL;
+
+    if (!ec->publicKey.x || !ec->publicKey.y ||
+        bignum_cmp(ec->publicKey.x, curve->p) >= 0 ||
+        bignum_cmp(ec->publicKey.y, curve->p) >= 0)
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    ec->privateKey = getmp(b, len);
+    if (ec->privateKey == NULL)
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    /* Now check that the private key makes the public key */
+    publicKey = ec_public(ec->privateKey, ec->publicKey.curve);
+    if (!publicKey)
+    {
+        ecdsa_freekey(ec);
+        return NULL;
+    }
+
+    if (bignum_cmp(ec->publicKey.x, publicKey->x) ||
+        bignum_cmp(ec->publicKey.y, publicKey->y))
+    {
+        /* Private key doesn't make the public key on the given curve */
+        ecdsa_freekey(ec);
+        ec_point_free(publicKey);
+        return NULL;
+    }
+
+    ec_point_free(publicKey);
+
+    return ec;
+}
+
+static int ecdsa_openssh_fmtkey(void *key, unsigned char *blob, int len)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+
+    int pointlen;
+    int namelen;
+    int bloblen;
+    int i;
+
+    if (ec->publicKey.curve->type != EC_WEIERSTRASS) {
+        return 0;
+    }
+
+    pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+    namelen = strlen(ec->publicKey.curve->name);
+    bloblen =
+        4 + namelen /* <LEN> nistpXXX */
+        + 4 + 1 + (pointlen * 2) /* <LEN> 0x04 pX pY */
+        + ssh2_bignum_length(ec->privateKey);
+
+    if (bloblen > len)
+        return bloblen;
+
+    bloblen = 0;
+
+    PUT_32BIT(blob+bloblen, namelen);
+    bloblen += 4;
+    memcpy(blob+bloblen, ec->publicKey.curve->name, namelen);
+    bloblen += namelen;
+
+    PUT_32BIT(blob+bloblen, 1 + (pointlen * 2));
+    bloblen += 4;
+    blob[bloblen++] = 0x04;
+    for (i = pointlen; i--; )
+        blob[bloblen++] = bignum_byte(ec->publicKey.x, i);
+    for (i = pointlen; i--; )
+        blob[bloblen++] = bignum_byte(ec->publicKey.y, i);
+
+    pointlen = (bignum_bitcount(ec->privateKey) + 8) / 8;
+    PUT_32BIT(blob+bloblen, pointlen);
+    bloblen += 4;
+    for (i = pointlen; i--; )
+        blob[bloblen++] = bignum_byte(ec->privateKey, i);
+
+    return bloblen;
+}
+
+static int ecdsa_pubkey_bits(const struct ssh_signkey *self,
+                             const void *blob, int len)
+{
+    struct ec_key *ec;
+    int ret;
+
+    ec = (struct ec_key*)ecdsa_newkey(self, (const char *) blob, len);
+    if (!ec)
+        return -1;
+    ret = ec->publicKey.curve->fieldBits;
+    ecdsa_freekey(ec);
+
+    return ret;
+}
+
+static int ecdsa_verifysig(void *key, const char *sig, int siglen,
+                           const char *data, int datalen)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    const struct ecsign_extra *extra =
+        (const struct ecsign_extra *)ec->signalg->extra;
+    const char *p;
+    int slen;
+    int digestLen;
+    int ret;
+
+    if (!ec->publicKey.x || !ec->publicKey.y || !ec->publicKey.curve)
+        return 0;
+
+    /* Check the signature starts with the algorithm name */
+    getstring(&sig, &siglen, &p, &slen);
+    if (!p) {
+        return 0;
+    }
+    if (!match_ssh_id(slen, p, ec->signalg->name)) {
+        return 0;
+    }
+
+    getstring(&sig, &siglen, &p, &slen);
+    if (!p) return 0;
+    if (ec->publicKey.curve->type == EC_EDWARDS) {
+        struct ec_point *r;
+        Bignum s, h;
+
+        /* Check that the signature is two times the length of a point */
+        if (slen != (ec->publicKey.curve->fieldBits / 8) * 2) {
+            return 0;
+        }
+
+        /* Check it's the 256 bit field so that SHA512 is the correct hash */
+        if (ec->publicKey.curve->fieldBits != 256) {
+            return 0;
+        }
+
+        /* Get the signature */
+        r = ec_point_new(ec->publicKey.curve, NULL, NULL, NULL, 0);
+        if (!r) {
+            return 0;
+        }
+        if (!decodepoint(p, ec->publicKey.curve->fieldBits / 8, r)) {
+            ec_point_free(r);
+            return 0;
+        }
+        s = bignum_from_bytes_le((unsigned char*)p + (ec->publicKey.curve->fieldBits / 8),
+                                 ec->publicKey.curve->fieldBits / 8);
+
+        /* Get the hash of the encoded value of R + encoded value of pk + message */
+        {
+            int i, pointlen;
+            unsigned char b;
+            unsigned char digest[512 / 8];
+            SHA512_State hs;
+            SHA512_Init(&hs);
+
+            /* Add encoded r (no need to encode it again, it was in the signature) */
+            SHA512_Bytes(&hs, p, ec->publicKey.curve->fieldBits / 8);
+
+            /* Encode pk and add it */
+            pointlen = ec->publicKey.curve->fieldBits / 8;
+            for (i = 0; i < pointlen - 1; ++i) {
+                b = bignum_byte(ec->publicKey.y, i);
+                SHA512_Bytes(&hs, &b, 1);
+            }
+            /* Unset last bit of y and set first bit of x in its place */
+            b = bignum_byte(ec->publicKey.y, i) & 0x7f;
+            b |= bignum_bit(ec->publicKey.x, 0) << 7;
+            SHA512_Bytes(&hs, &b, 1);
+
+            /* Add the message itself */
+            SHA512_Bytes(&hs, data, datalen);
+
+            /* Get the hash */
+            SHA512_Final(&hs, digest);
+
+            /* Convert to Bignum */
+            h = bignum_from_bytes_le(digest, sizeof(digest));
+        }
+
+        /* Verify sB == r + h*publicKey */
+        {
+            struct ec_point *lhs, *rhs, *tmp;
+
+            /* lhs = sB */
+            lhs = ecp_mul(&ec->publicKey.curve->e.B, s);
+            freebn(s);
+            if (!lhs) {
+                ec_point_free(r);
+                freebn(h);
+                return 0;
+            }
+
+            /* rhs = r + h*publicKey */
+            tmp = ecp_mul(&ec->publicKey, h);
+            freebn(h);
+            if (!tmp) {
+                ec_point_free(lhs);
+                ec_point_free(r);
+                return 0;
+            }
+            rhs = ecp_add(r, tmp, 0);
+            ec_point_free(r);
+            ec_point_free(tmp);
+            if (!rhs) {
+                ec_point_free(lhs);
+                return 0;
+            }
+
+            /* Check the point is the same */
+            ret = !bignum_cmp(lhs->x, rhs->x);
+            if (ret) {
+                ret = !bignum_cmp(lhs->y, rhs->y);
+                if (ret) {
+                    ret = 1;
+                }
+            }
+            ec_point_free(lhs);
+            ec_point_free(rhs);
+        }
+    } else {
+        Bignum r, s;
+        unsigned char digest[512 / 8];
+        void *hashctx;
+
+        r = getmp(&p, &slen);
+        if (!r) return 0;
+        s = getmp(&p, &slen);
+        if (!s) {
+            freebn(r);
+            return 0;
+        }
+
+        digestLen = extra->hash->hlen;
+        assert(digestLen <= sizeof(digest));
+        hashctx = extra->hash->init();
+        extra->hash->bytes(hashctx, data, datalen);
+        extra->hash->final(hashctx, digest);
+
+        /* Verify the signature */
+        ret = _ecdsa_verify(&ec->publicKey, digest, digestLen, r, s);
+
+        freebn(r);
+        freebn(s);
+    }
+
+    return ret;
+}
+
+static unsigned char *ecdsa_sign(void *key, const char *data, int datalen,
+                                 int *siglen)
+{
+    struct ec_key *ec = (struct ec_key *) key;
+    const struct ecsign_extra *extra =
+        (const struct ecsign_extra *)ec->signalg->extra;
+    unsigned char digest[512 / 8];
+    int digestLen;
+    Bignum r = NULL, s = NULL;
+    unsigned char *buf, *p;
+    int rlen, slen, namelen;
+    int i;
+
+    if (!ec->privateKey || !ec->publicKey.curve) {
+        return NULL;
+    }
+
+    if (ec->publicKey.curve->type == EC_EDWARDS) {
+        struct ec_point *rp;
+        int pointlen = ec->publicKey.curve->fieldBits / 8;
+
+        /* hash = H(sk) (where hash creates 2 * fieldBits)
+         * b = fieldBits
+         * a = 2^(b-2) + SUM(2^i * h_i) for i = 2 -> b-2
+         * r = H(h[b/8:b/4] + m)
+         * R = rB
+         * S = (r + H(encodepoint(R) + encodepoint(pk) + m) * a) % l */
+        {
+            unsigned char hash[512/8];
+            unsigned char b;
+            Bignum a;
+            SHA512_State hs;
+            SHA512_Init(&hs);
+
+            for (i = 0; i < pointlen; ++i) {
+                unsigned char b = (unsigned char)bignum_byte(ec->privateKey, i);
+                SHA512_Bytes(&hs, &b, 1);
+            }
+
+            SHA512_Final(&hs, hash);
+
+            /* The second part is simply turning the hash into a
+             * Bignum, however the 2^(b-2) bit *must* be set, and the
+             * bottom 3 bits *must* not be */
+            hash[0] &= 0xf8; /* Unset bottom 3 bits (if set) */
+            hash[31] &= 0x7f; /* Unset above (b-2) */
+            hash[31] |= 0x40; /* Set 2^(b-2) */
+            /* Chop off the top part and convert to int */
+            a = bignum_from_bytes_le(hash, 32);
+
+            SHA512_Init(&hs);
+            SHA512_Bytes(&hs,
+                         hash+(ec->publicKey.curve->fieldBits / 8),
+                         (ec->publicKey.curve->fieldBits / 4)
+                         - (ec->publicKey.curve->fieldBits / 8));
+            SHA512_Bytes(&hs, data, datalen);
+            SHA512_Final(&hs, hash);
+
+            r = bignum_from_bytes_le(hash, 512/8);
+            rp = ecp_mul(&ec->publicKey.curve->e.B, r);
+            if (!rp) {
+                freebn(r);
+                freebn(a);
+                return NULL;
+            }
+
+            /* Now calculate s */
+            SHA512_Init(&hs);
+            /* Encode the point R */
+            for (i = 0; i < pointlen - 1; ++i) {
+                b = bignum_byte(rp->y, i);
+                SHA512_Bytes(&hs, &b, 1);
+            }
+            /* Unset last bit of y and set first bit of x in its place */
+            b = bignum_byte(rp->y, i) & 0x7f;
+            b |= bignum_bit(rp->x, 0) << 7;
+            SHA512_Bytes(&hs, &b, 1);
+
+            /* Encode the point pk */
+            for (i = 0; i < pointlen - 1; ++i) {
+                b = bignum_byte(ec->publicKey.y, i);
+                SHA512_Bytes(&hs, &b, 1);
+            }
+            /* Unset last bit of y and set first bit of x in its place */
+            b = bignum_byte(ec->publicKey.y, i) & 0x7f;
+            b |= bignum_bit(ec->publicKey.x, 0) << 7;
+            SHA512_Bytes(&hs, &b, 1);
+
+            /* Add the message */
+            SHA512_Bytes(&hs, data, datalen);
+            SHA512_Final(&hs, hash);
+
+            {
+                Bignum tmp, tmp2;
+
+                tmp = bignum_from_bytes_le(hash, 512/8);
+                tmp2 = modmul(tmp, a, ec->publicKey.curve->e.l);
+                freebn(a);
+                freebn(tmp);
+                tmp = bigadd(r, tmp2);
+                freebn(r);
+                freebn(tmp2);
+                s = bigmod(tmp, ec->publicKey.curve->e.l);
+                freebn(tmp);
+            }
+        }
+
+        /* Format the output */
+        namelen = strlen(ec->signalg->name);
+        *siglen = 4+namelen+4+((ec->publicKey.curve->fieldBits / 8)*2);
+        buf = snewn(*siglen, unsigned char);
+        p = buf;
+        PUT_32BIT(p, namelen);
+        p += 4;
+        memcpy(p, ec->signalg->name, namelen);
+        p += namelen;
+        PUT_32BIT(p, ((ec->publicKey.curve->fieldBits / 8)*2));
+        p += 4;
+
+        /* Encode the point */
+        pointlen = ec->publicKey.curve->fieldBits / 8;
+        for (i = 0; i < pointlen - 1; ++i) {
+            *p++ = bignum_byte(rp->y, i);
+        }
+        /* Unset last bit of y and set first bit of x in its place */
+        *p = bignum_byte(rp->y, i) & 0x7f;
+        *p++ |= bignum_bit(rp->x, 0) << 7;
+        ec_point_free(rp);
+
+        /* Encode the int */
+        for (i = 0; i < pointlen; ++i) {
+            *p++ = bignum_byte(s, i);
+        }
+        freebn(s);
+    } else {
+        void *hashctx;
+
+        digestLen = extra->hash->hlen;
+        assert(digestLen <= sizeof(digest));
+        hashctx = extra->hash->init();
+        extra->hash->bytes(hashctx, data, datalen);
+        extra->hash->final(hashctx, digest);
+
+        /* Do the signature */
+        _ecdsa_sign(ec->privateKey, ec->publicKey.curve, digest, digestLen, &r, &s);
+        if (!r || !s) {
+            if (r) freebn(r);
+            if (s) freebn(s);
+            return NULL;
+        }
+
+        rlen = (bignum_bitcount(r) + 8) / 8;
+        slen = (bignum_bitcount(s) + 8) / 8;
+
+        namelen = strlen(ec->signalg->name);
+
+        /* Format the output */
+        *siglen = 8+namelen+rlen+slen+8;
+        buf = snewn(*siglen, unsigned char);
+        p = buf;
+        PUT_32BIT(p, namelen);
+        p += 4;
+        memcpy(p, ec->signalg->name, namelen);
+        p += namelen;
+        PUT_32BIT(p, rlen + slen + 8);
+        p += 4;
+        PUT_32BIT(p, rlen);
+        p += 4;
+        for (i = rlen; i--;)
+            *p++ = bignum_byte(r, i);
+        PUT_32BIT(p, slen);
+        p += 4;
+        for (i = slen; i--;)
+            *p++ = bignum_byte(s, i);
+
+        freebn(r);
+        freebn(s);
+    }
+
+    return buf;
+}
+
+const struct ecsign_extra sign_extra_ed25519 = {
+    ec_ed25519, NULL,
+    NULL, 0,
+};
+const struct ssh_signkey ssh_ecdsa_ed25519 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ed25519_openssh_createkey,
+    ed25519_openssh_fmtkey,
+    2 /* point, private exponent */,
+    ecdsa_pubkey_bits,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ssh-ed25519",
+    "ssh-ed25519",
+    &sign_extra_ed25519,
+};
+
+/* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */
+static const unsigned char nistp256_oid[] = {
+    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
+};
+const struct ecsign_extra sign_extra_nistp256 = {
+    ec_p256, &ssh_sha256,
+    nistp256_oid, lenof(nistp256_oid),
+};
+const struct ssh_signkey ssh_ecdsa_nistp256 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ecdsa_openssh_createkey,
+    ecdsa_openssh_fmtkey,
+    3 /* curve name, point, private exponent */,
+    ecdsa_pubkey_bits,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ecdsa-sha2-nistp256",
+    "ecdsa-sha2-nistp256",
+    &sign_extra_nistp256,
+};
+
+/* OID: 1.3.132.0.34 (secp384r1) */
+static const unsigned char nistp384_oid[] = {
+    0x2b, 0x81, 0x04, 0x00, 0x22
+};
+const struct ecsign_extra sign_extra_nistp384 = {
+    ec_p384, &ssh_sha384,
+    nistp384_oid, lenof(nistp384_oid),
+};
+const struct ssh_signkey ssh_ecdsa_nistp384 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ecdsa_openssh_createkey,
+    ecdsa_openssh_fmtkey,
+    3 /* curve name, point, private exponent */,
+    ecdsa_pubkey_bits,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ecdsa-sha2-nistp384",
+    "ecdsa-sha2-nistp384",
+    &sign_extra_nistp384,
+};
+
+/* OID: 1.3.132.0.35 (secp521r1) */
+static const unsigned char nistp521_oid[] = {
+    0x2b, 0x81, 0x04, 0x00, 0x23
+};
+const struct ecsign_extra sign_extra_nistp521 = {
+    ec_p521, &ssh_sha512,
+    nistp521_oid, lenof(nistp521_oid),
+};
+const struct ssh_signkey ssh_ecdsa_nistp521 = {
+    ecdsa_newkey,
+    ecdsa_freekey,
+    ecdsa_fmtkey,
+    ecdsa_public_blob,
+    ecdsa_private_blob,
+    ecdsa_createkey,
+    ecdsa_openssh_createkey,
+    ecdsa_openssh_fmtkey,
+    3 /* curve name, point, private exponent */,
+    ecdsa_pubkey_bits,
+    ecdsa_verifysig,
+    ecdsa_sign,
+    "ecdsa-sha2-nistp521",
+    "ecdsa-sha2-nistp521",
+    &sign_extra_nistp521,
+};
+
+/* ----------------------------------------------------------------------
+ * Exposed ECDH interface
+ */
+
+struct eckex_extra {
+    struct ec_curve *(*curve)(void);
+};
+
+static Bignum ecdh_calculate(const Bignum private,
+                             const struct ec_point *public)
+{
+    struct ec_point *p;
+    Bignum ret;
+    p = ecp_mul(public, private);
+    if (!p) return NULL;
+    ret = p->x;
+    p->x = NULL;
+
+    if (p->curve->type == EC_MONTGOMERY) {
+        /*
+         * 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
+         * p->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.
+         */
+        int i;
+        int bytes = (p->curve->fieldBits+7) / 8;
+        unsigned char *byteorder = snewn(bytes, unsigned char);
+        for (i = 0; i < bytes; ++i) {
+            byteorder[i] = bignum_byte(ret, i);
+        }
+        freebn(ret);
+        ret = bignum_from_bytes(byteorder, bytes);
+        smemclr(byteorder, bytes);
+        sfree(byteorder);
+    }
+
+    ec_point_free(p);
+    return ret;
+}
+
+const char *ssh_ecdhkex_curve_textname(const struct ssh_kex *kex)
+{
+    const struct eckex_extra *extra = (const struct eckex_extra *)kex->extra;
+    struct ec_curve *curve = extra->curve();
+    return curve->textname;
+}
+
+void *ssh_ecdhkex_newkey(const struct ssh_kex *kex)
+{
+    const struct eckex_extra *extra = (const struct eckex_extra *)kex->extra;
+    struct ec_curve *curve;
+    struct ec_key *key;
+    struct ec_point *publicKey;
+
+    curve = extra->curve();
+
+    key = snew(struct ec_key);
+
+    key->signalg = NULL;
+    key->publicKey.curve = curve;
+
+    if (curve->type == EC_MONTGOMERY) {
+        unsigned char bytes[32] = {0};
+        int i;
+
+        for (i = 0; i < sizeof(bytes); ++i)
+        {
+            bytes[i] = (unsigned char)random_byte();
+        }
+        bytes[0] &= 248;
+        bytes[31] &= 127;
+        bytes[31] |= 64;
+        key->privateKey = bignum_from_bytes(bytes, sizeof(bytes));
+        for (i = 0; i < sizeof(bytes); ++i)
+        {
+            ((volatile char*)bytes)[i] = 0;
+        }
+        if (!key->privateKey) {
+            sfree(key);
+            return NULL;
+        }
+        publicKey = ecp_mul(&key->publicKey.curve->m.G, key->privateKey);
+        if (!publicKey) {
+            freebn(key->privateKey);
+            sfree(key);
+            return NULL;
+        }
+        key->publicKey.x = publicKey->x;
+        key->publicKey.y = publicKey->y;
+        key->publicKey.z = NULL;
+        sfree(publicKey);
+    } else {
+        key->privateKey = bignum_random_in_range(One, key->publicKey.curve->w.n);
+        if (!key->privateKey) {
+            sfree(key);
+            return NULL;
+        }
+        publicKey = ecp_mul(&key->publicKey.curve->w.G, key->privateKey);
+        if (!publicKey) {
+            freebn(key->privateKey);
+            sfree(key);
+            return NULL;
+        }
+        key->publicKey.x = publicKey->x;
+        key->publicKey.y = publicKey->y;
+        key->publicKey.z = NULL;
+        sfree(publicKey);
+    }
+    return key;
+}
+
+char *ssh_ecdhkex_getpublic(void *key, int *len)
+{
+    struct ec_key *ec = (struct ec_key*)key;
+    char *point, *p;
+    int i;
+    int pointlen;
+
+    pointlen = (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8;
+
+    if (ec->publicKey.curve->type == EC_WEIERSTRASS) {
+        *len = 1 + pointlen * 2;
+    } else {
+        *len = pointlen;
+    }
+    point = (char*)snewn(*len, char);
+
+    p = point;
+    if (ec->publicKey.curve->type == EC_WEIERSTRASS) {
+        *p++ = 0x04;
+        for (i = pointlen; i--;) {
+            *p++ = bignum_byte(ec->publicKey.x, i);
+        }
+        for (i = pointlen; i--;) {
+            *p++ = bignum_byte(ec->publicKey.y, i);
+        }
+    } else {
+        for (i = 0; i < pointlen; ++i) {
+            *p++ = bignum_byte(ec->publicKey.x, i);
+        }
+    }
+
+    return point;
+}
+
+Bignum ssh_ecdhkex_getkey(void *key, char *remoteKey, int remoteKeyLen)
+{
+    struct ec_key *ec = (struct ec_key*) key;
+    struct ec_point remote;
+    Bignum ret;
+
+    if (ec->publicKey.curve->type == EC_WEIERSTRASS) {
+        remote.curve = ec->publicKey.curve;
+        remote.infinity = 0;
+        if (!decodepoint(remoteKey, remoteKeyLen, &remote)) {
+            return NULL;
+        }
+    } else {
+        /* Point length has to be the same length */
+        if (remoteKeyLen != (bignum_bitcount(ec->publicKey.curve->p) + 7) / 8) {
+            return NULL;
+        }
+
+        remote.curve = ec->publicKey.curve;
+        remote.infinity = 0;
+        remote.x = bignum_from_bytes_le((unsigned char*)remoteKey, remoteKeyLen);
+        remote.y = NULL;
+        remote.z = NULL;
+    }
+
+    ret = ecdh_calculate(ec->privateKey, &remote);
+    if (remote.x) freebn(remote.x);
+    if (remote.y) freebn(remote.y);
+    return ret;
+}
+
+void ssh_ecdhkex_freekey(void *key)
+{
+    ecdsa_freekey(key);
+}
+
+static const struct eckex_extra kex_extra_curve25519 = { ec_curve25519 };
+static const struct ssh_kex ssh_ec_kex_curve25519 = {
+    "[email protected]", NULL, KEXTYPE_ECDH,
+    &ssh_sha256, &kex_extra_curve25519,
+};
+
+const struct eckex_extra kex_extra_nistp256 = { ec_p256 };
+static const struct ssh_kex ssh_ec_kex_nistp256 = {
+    "ecdh-sha2-nistp256", NULL, KEXTYPE_ECDH,
+    &ssh_sha256, &kex_extra_nistp256,
+};
+
+const struct eckex_extra kex_extra_nistp384 = { ec_p384 };
+static const struct ssh_kex ssh_ec_kex_nistp384 = {
+    "ecdh-sha2-nistp384", NULL, KEXTYPE_ECDH,
+    &ssh_sha384, &kex_extra_nistp384,
+};
+
+const struct eckex_extra kex_extra_nistp521 = { ec_p521 };
+static const struct ssh_kex ssh_ec_kex_nistp521 = {
+    "ecdh-sha2-nistp521", NULL, KEXTYPE_ECDH,
+    &ssh_sha512, &kex_extra_nistp521,
+};
+
+static const struct ssh_kex *const ec_kex_list[] = {
+    &ssh_ec_kex_curve25519,
+    &ssh_ec_kex_nistp256,
+    &ssh_ec_kex_nistp384,
+    &ssh_ec_kex_nistp521,
+};
+
+const struct ssh_kexes ssh_ecdh_kex = {
+    sizeof(ec_kex_list) / sizeof(*ec_kex_list),
+    ec_kex_list
+};
+
+/* ----------------------------------------------------------------------
+ * Helper functions for finding key algorithms and returning auxiliary
+ * data.
+ */
+
+const struct ssh_signkey *ec_alg_by_oid(int len, const void *oid,
+                                        const struct ec_curve **curve)
+{
+    static const struct ssh_signkey *algs_with_oid[] = {
+        &ssh_ecdsa_nistp256,
+        &ssh_ecdsa_nistp384,
+        &ssh_ecdsa_nistp521,
+    };
+    int i;
+
+    for (i = 0; i < lenof(algs_with_oid); i++) {
+        const struct ssh_signkey *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 struct ssh_signkey *alg,
+                                int *oidlen)
+{
+    const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra;
+    *oidlen = extra->oidlen;
+    return extra->oid;
+}
+
+const int ec_nist_alg_and_curve_by_bits(int bits,
+                                        const struct ec_curve **curve,
+                                        const struct ssh_signkey **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;
+}
+
+const int ec_ed_alg_and_curve_by_bits(int bits,
+                                      const struct ec_curve **curve,
+                                      const struct ssh_signkey **alg)
+{
+    switch (bits) {
+      case 256: *alg = &ssh_ecdsa_ed25519; break;
+      default: return FALSE;
+    }
+    *curve = ((struct ecsign_extra *)(*alg)->extra)->curve();
+    return TRUE;
+}

+ 71 - 0
source/putty/sshecdsag.c

@@ -0,0 +1,71 @@
+/*
+ * EC key generation.
+ */
+
+#include "ssh.h"
+
+/* Forward reference from sshecc.c */
+struct ec_point *ecp_mul(const struct ec_point *a, const Bignum b);
+
+int ec_generate(struct ec_key *key, int bits, progfn_t pfn,
+                void *pfnparam)
+{
+    struct ec_point *publicKey;
+
+    if (!ec_nist_alg_and_curve_by_bits(bits, &key->publicKey.curve,
+                                       &key->signalg))
+        return 0;
+
+    key->privateKey = bignum_random_in_range(One, key->publicKey.curve->w.n);
+    if (!key->privateKey) return 0;
+
+    publicKey = ec_public(key->privateKey, key->publicKey.curve);
+    if (!publicKey) {
+        freebn(key->privateKey);
+        key->privateKey = NULL;
+        return 0;
+    }
+
+    key->publicKey.x = publicKey->x;
+    key->publicKey.y = publicKey->y;
+    key->publicKey.z = NULL;
+    sfree(publicKey);
+
+    return 1;
+}
+
+int ec_edgenerate(struct ec_key *key, int bits, progfn_t pfn,
+                  void *pfnparam)
+{
+    struct ec_point *publicKey;
+
+    if (!ec_ed_alg_and_curve_by_bits(bits, &key->publicKey.curve,
+                                     &key->signalg))
+        return 0;
+
+    {
+        /* EdDSA secret keys are just 32 bytes of hash preimage; the
+         * 64-byte SHA-512 hash of that key will be used when signing,
+         * but the form of the key stored on disk is the preimage
+         * only. */
+        Bignum privMax = bn_power_2(bits);
+        if (!privMax) return 0;
+        key->privateKey = bignum_random_in_range(Zero, privMax);
+        freebn(privMax);
+        if (!key->privateKey) return 0;
+    }
+
+    publicKey = ec_public(key->privateKey, key->publicKey.curve);
+    if (!publicKey) {
+        freebn(key->privateKey);
+        key->privateKey = NULL;
+        return 0;
+    }
+
+    key->publicKey.x = publicKey->x;
+    key->publicKey.y = publicKey->y;
+    key->publicKey.z = NULL;
+    sfree(publicKey);
+
+    return 1;
+}

+ 3 - 3
source/putty/sshmd5.c

@@ -224,7 +224,7 @@ void MD5Simple(void const *p, unsigned len, unsigned char output[16])
  * useful elsewhere (SOCKS5 CHAP authentication uses HMAC-MD5).
  */
 
-void *hmacmd5_make_context(void)
+void *hmacmd5_make_context(void *cipher_ctx)
 {
     return snewn(3, struct MD5Context);
 }
@@ -339,7 +339,7 @@ const struct ssh_mac ssh_hmac_md5 = {
     hmacmd5_make_context, hmacmd5_free_context, hmacmd5_key_16,
     hmacmd5_generate, hmacmd5_verify,
     hmacmd5_start, hmacmd5_bytes, hmacmd5_genresult, hmacmd5_verresult,
-    "hmac-md5",
-    16,
+    "hmac-md5", "[email protected]",
+    16, 16,
     "HMAC-MD5"
 };

+ 547 - 26
source/putty/sshpubk.c

@@ -21,8 +21,10 @@
                           (x)=='+' ? 62 : \
                           (x)=='/' ? 63 : 0 )
 
+static int key_type_fp(FILE *fp);
+
 static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
-			   char **commentptr, char *passphrase,
+			   char **commentptr, const char *passphrase,
 			   const char **error)
 {
     unsigned char buf[16384];
@@ -155,8 +157,8 @@ static int loadrsakey_main(FILE * fp, struct RSAKey *key, int pub_only,
     return ret;
 }
 
-int loadrsakey(const Filename *filename, struct RSAKey *key, char *passphrase,
-	       const char **errorstr)
+int loadrsakey(const Filename *filename, struct RSAKey *key,
+               const char *passphrase, const char **errorstr)
 {
     FILE *fp;
     char buf[64];
@@ -261,6 +263,56 @@ int rsakey_pubblob(const Filename *filename, void **blob, int *bloblen,
 	}
 	fp = NULL; /* loadrsakey_main unconditionally closes fp */
     } else {
+        /*
+         * Try interpreting the file as an SSH-1 public key.
+         */
+        char *line, *p, *bitsp, *expp, *modp, *commentp;
+
+        rewind(fp);
+        line = chomp(fgetline(fp));
+        p = line;
+
+        bitsp = p;
+        p += strspn(p, "0123456789");
+        if (*p != ' ')
+            goto not_public_either;
+        *p++ = '\0';
+
+        expp = p;
+        p += strspn(p, "0123456789");
+        if (*p != ' ')
+            goto not_public_either;
+        *p++ = '\0';
+
+        modp = p;
+        p += strspn(p, "0123456789");
+        if (*p) {
+            if (*p != ' ')
+                goto not_public_either;
+            *p++ = '\0';
+            commentp = p;
+        } else {
+            commentp = NULL;
+        }
+
+	memset(&key, 0, sizeof(key));
+        key.exponent = bignum_from_decimal(expp);
+        key.modulus = bignum_from_decimal(modp);
+        if (atoi(bitsp) != bignum_bitcount(key.modulus)) {
+            freebn(key.exponent);
+            freebn(key.modulus);
+            sfree(line);
+            error = "key bit count does not match in SSH-1 public key file";
+            goto end;
+        }
+        if (commentptr)
+            *commentptr = commentp ? dupstr(commentp) : NULL;
+        *blob = rsa_public_blob(&key, bloblen);
+        freersakey(&key);
+        return 1;
+
+      not_public_either:
+        sfree(line);
 	error = "not an SSH-1 RSA file";
     }
 
@@ -557,18 +609,32 @@ struct ssh2_userkey ssh2_wrong_passphrase = {
     NULL, NULL, NULL
 };
 
-const struct ssh_signkey *find_pubkey_alg(const char *name)
+const struct ssh_signkey *find_pubkey_alg_len(int namelen, const char *name)
 {
-    if (!strcmp(name, "ssh-rsa"))
+    if (match_ssh_id(namelen, name, "ssh-rsa"))
 	return &ssh_rsa;
-    else if (!strcmp(name, "ssh-dss"))
+    else if (match_ssh_id(namelen, name, "ssh-dss"))
 	return &ssh_dss;
+    else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp256"))
+        return &ssh_ecdsa_nistp256;
+    else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp384"))
+        return &ssh_ecdsa_nistp384;
+    else if (match_ssh_id(namelen, name, "ecdsa-sha2-nistp521"))
+        return &ssh_ecdsa_nistp521;
+    else if (match_ssh_id(namelen, name, "ssh-ed25519"))
+        return &ssh_ecdsa_ed25519;
     else
 	return NULL;
 }
 
+const struct ssh_signkey *find_pubkey_alg(const char *name)
+{
+    return find_pubkey_alg_len(strlen(name), name);
+}
+
 struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
-				       char *passphrase, const char **errorstr)
+				       const char *passphrase,
+                                       const char **errorstr)
 {
     FILE *fp;
     char header[40], *b, *encryption, *comment, *mac;
@@ -787,7 +853,7 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
     ret = snew(struct ssh2_userkey);
     ret->alg = alg;
     ret->comment = comment;
-    ret->data = alg->createkey(public_blob, public_blob_len,
+    ret->data = alg->createkey(alg, public_blob, public_blob_len,
 			       private_blob, private_blob_len);
     if (!ret->data) {
 	sfree(ret);
@@ -826,6 +892,214 @@ struct ssh2_userkey *ssh2_load_userkey(const Filename *filename,
     return ret;
 }
 
+unsigned char *rfc4716_loadpub(FILE *fp, char **algorithm,
+                               int *pub_blob_len, char **commentptr,
+                               const char **errorstr)
+{
+    const char *error;
+    char *line, *colon, *value;
+    char *comment = NULL;
+    unsigned char *pubblob = NULL;
+    int pubbloblen, pubblobsize;
+    char base64in[4];
+    unsigned char base64out[3];
+    int base64bytes;
+    int alglen;
+
+    line = chomp(fgetline(fp));
+    if (!line || 0 != strcmp(line, "---- BEGIN SSH2 PUBLIC KEY ----")) {
+        error = "invalid begin line in SSH-2 public key file";
+        goto error;
+    }
+    sfree(line); line = NULL;
+
+    while (1) {
+        line = chomp(fgetline(fp));
+        if (!line) {
+            error = "truncated SSH-2 public key file";
+            goto error;
+        }
+        colon = strstr(line, ": ");
+        if (!colon)
+            break;
+        *colon = '\0';
+        value = colon + 2;
+
+        if (!strcmp(line, "Comment")) {
+            char *p, *q;
+
+            /* Remove containing double quotes, if present */
+            p = value;
+            if (*p == '"' && p[strlen(p)-1] == '"') {
+                p[strlen(p)-1] = '\0';
+                p++;
+            }
+
+            /* Remove \-escaping, not in RFC4716 but seen in the wild
+             * in practice. */
+            for (q = line; *p; p++) {
+                if (*p == '\\' && p[1])
+                    p++;
+                *q++ = *p;
+            }
+
+            *q = '\0';
+            comment = dupstr(line);
+        } else if (!strcmp(line, "Subject") ||
+                   !strncmp(line, "x-", 2)) {
+            /* Headers we recognise and ignore. Do nothing. */
+        } else {
+            error = "unrecognised header in SSH-2 public key file";
+            goto error;
+        }
+
+        sfree(line); line = NULL;
+    }
+
+    /*
+     * Now line contains the initial line of base64 data. Loop round
+     * while it still does contain base64.
+     */
+    pubblobsize = 4096;
+    pubblob = snewn(pubblobsize, unsigned char);
+    pubbloblen = 0;
+    base64bytes = 0;
+    while (line && line[0] != '-') {
+        char *p;
+        for (p = line; *p; p++) {
+            base64in[base64bytes++] = *p;
+            if (base64bytes == 4) {
+                int n = base64_decode_atom(base64in, base64out);
+                if (pubbloblen + n > pubblobsize) {
+                    pubblobsize = (pubbloblen + n) * 5 / 4 + 1024;
+                    pubblob = sresize(pubblob, pubblobsize, unsigned char);
+                }
+                memcpy(pubblob + pubbloblen, base64out, n);
+                pubbloblen += n;
+                base64bytes = 0;
+            }
+        }
+        sfree(line); line = NULL;
+        line = chomp(fgetline(fp));
+    }
+
+    /*
+     * Finally, check the END line makes sense.
+     */
+    if (!line || 0 != strcmp(line, "---- END SSH2 PUBLIC KEY ----")) {
+        error = "invalid end line in SSH-2 public key file";
+        goto error;
+    }
+    sfree(line); line = NULL;
+
+    /*
+     * OK, we now have a public blob and optionally a comment. We must
+     * return the key algorithm string too, so look for that at the
+     * start of the public blob.
+     */
+    if (pubbloblen < 4) {
+        error = "not enough data in SSH-2 public key file";
+        goto error;
+    }
+    alglen = toint(GET_32BIT(pubblob));
+    if (alglen < 0 || alglen > pubbloblen-4) {
+        error = "invalid algorithm prefix in SSH-2 public key file";
+        goto error;
+    }
+    if (algorithm)
+        *algorithm = dupprintf("%.*s", alglen, pubblob+4);
+    if (pub_blob_len)
+        *pub_blob_len = pubbloblen;
+    if (commentptr)
+        *commentptr = comment;
+    else
+        sfree(comment);
+    return pubblob;
+
+  error:
+    sfree(line);
+    sfree(comment);
+    sfree(pubblob);
+    if (errorstr)
+        *errorstr = error;
+    return NULL;
+}
+
+unsigned char *openssh_loadpub(FILE *fp, char **algorithm,
+                               int *pub_blob_len, char **commentptr,
+                               const char **errorstr)
+{
+    const char *error;
+    char *line, *base64;
+    char *comment = NULL;
+    unsigned char *pubblob = NULL;
+    int pubbloblen, pubblobsize;
+    int alglen;
+
+    line = chomp(fgetline(fp));
+
+    base64 = strchr(line, ' ');
+    if (!base64) {
+        error = "no key blob in OpenSSH public key file";
+        goto error;
+    }
+    *base64++ = '\0';
+
+    comment = strchr(base64, ' ');
+    if (comment) {
+        *comment++ = '\0';
+        comment = dupstr(comment);
+    }
+
+    pubblobsize = strlen(base64) / 4 * 3;
+    pubblob = snewn(pubblobsize, unsigned char);
+    pubbloblen = 0;
+
+    while (!memchr(base64, '\0', 4)) {
+        assert(pubbloblen + 3 <= pubblobsize);
+        pubbloblen += base64_decode_atom(base64, pubblob + pubbloblen);
+        base64 += 4;
+    }
+    if (*base64) {
+        error = "invalid length for base64 data in OpenSSH public key file";
+        goto error;
+    }
+
+    /*
+     * Sanity check: the first word on the line should be the key
+     * algorithm, and should match the encoded string at the start of
+     * the public blob.
+     */
+    alglen = strlen(line);
+    if (pubbloblen < alglen + 4 ||
+        GET_32BIT(pubblob) != alglen ||
+        0 != memcmp(pubblob + 4, line, alglen)) {
+        error = "key algorithms do not match in OpenSSH public key file";
+        goto error;
+    }
+
+    /*
+     * Done.
+     */
+    if (algorithm)
+        *algorithm = dupstr(line);
+    if (pub_blob_len)
+        *pub_blob_len = pubbloblen;
+    if (commentptr)
+        *commentptr = comment;
+    else
+        sfree(comment);
+    return pubblob;
+
+  error:
+    sfree(line);
+    sfree(comment);
+    sfree(pubblob);
+    if (errorstr)
+        *errorstr = error;
+    return NULL;
+}
+
 unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
 				    int *pub_blob_len, char **commentptr,
 				    const char **errorstr)
@@ -835,7 +1109,7 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
     const struct ssh_signkey *alg;
     unsigned char *public_blob;
     int public_blob_len;
-    int i;
+    int type, i;
     const char *error = NULL;
     char *comment = NULL;
 
@@ -847,6 +1121,24 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
 	goto error;
     }
 
+    /* Initially, check if this is a public-only key file. Sometimes
+     * we'll be asked to read a public blob from one of those. */
+    type = key_type_fp(fp);
+    if (type == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) {
+        unsigned char *ret = rfc4716_loadpub(fp, algorithm, pub_blob_len,
+                                             commentptr, errorstr);
+        fclose(fp);
+        return ret;
+    } else if (type == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
+        unsigned char *ret = openssh_loadpub(fp, algorithm, pub_blob_len,
+                                             commentptr, errorstr);
+        fclose(fp);
+        return ret;
+    } else if (type != SSH_KEYTYPE_SSH2) {
+        error = "not a PuTTY SSH-2 private key";
+        goto error;
+    }
+
     /* Read the first header line which contains the key type. */
     if (!read_header(fp, header)
 	|| (0 != strcmp(header, "PuTTY-User-Key-File-2") &&
@@ -899,7 +1191,7 @@ unsigned char *ssh2_userkey_loadpub(const Filename *filename, char **algorithm,
     if (pub_blob_len)
 	*pub_blob_len = public_blob_len;
     if (algorithm)
-	*algorithm = alg->name;
+	*algorithm = dupstr(alg->name);
     return public_blob;
 
     /*
@@ -984,7 +1276,7 @@ int base64_lines(int datalen)
     return (datalen + 47) / 48;
 }
 
-void base64_encode(FILE * fp, unsigned char *data, int datalen, int cpl)
+void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl)
 {
     int linelen = 0;
     char out[4];
@@ -1016,7 +1308,7 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
     int passlen;
     int cipherblk;
     int i;
-    char *cipherstr;
+    const char *cipherstr;
     unsigned char priv_mac[20];
 
     /*
@@ -1142,51 +1434,280 @@ int ssh2_save_userkey(const Filename *filename, struct ssh2_userkey *key,
 }
 
 /* ----------------------------------------------------------------------
- * A function to determine the type of a private key file. Returns
- * 0 on failure, 1 or 2 on success.
+ * Output public keys.
  */
-int key_type(const Filename *filename)
+char *ssh1_pubkey_str(struct RSAKey *key)
 {
-    FILE *fp;
-    char buf[32];
+    char *buffer;
+    char *dec1, *dec2;
+
+    dec1 = bignum_decimal(key->exponent);
+    dec2 = bignum_decimal(key->modulus);
+    buffer = dupprintf("%d %s %s%s%s", bignum_bitcount(key->modulus),
+		       dec1, dec2,
+                       key->comment ? " " : "",
+                       key->comment ? key->comment : "");
+    sfree(dec1);
+    sfree(dec2);
+    return buffer;
+}
+
+void ssh1_write_pubkey(FILE *fp, struct RSAKey *key)
+{
+    char *buffer = ssh1_pubkey_str(key);
+    fprintf(fp, "%s\n", buffer);
+    sfree(buffer);
+}
+
+static char *ssh2_pubkey_openssh_str_internal(const char *comment,
+                                              const void *v_pub_blob,
+                                              int pub_len)
+{
+    const unsigned char *ssh2blob = (const unsigned char *)v_pub_blob;
+    const char *alg;
+    int alglen;
+    char *buffer, *p;
+    int i;
+
+    if (pub_len < 4) {
+        alg = NULL;
+    } else {
+        alglen = GET_32BIT(ssh2blob);
+        if (alglen > 0 && alglen < pub_len - 4) {
+            alg = (const char *)ssh2blob + 4;
+        } else {
+            alg = NULL;
+        }
+    }
+
+    if (!alg) {
+        alg = "INVALID-ALGORITHM";
+        alglen = strlen(alg);
+    }
+
+    buffer = snewn(alglen +
+                   4 * ((pub_len+2) / 3) +
+                   (comment ? strlen(comment) : 0) + 3, char);
+    p = buffer + sprintf(buffer, "%.*s ", alglen, alg);
+    i = 0;
+    while (i < pub_len) {
+        int n = (pub_len - i < 3 ? pub_len - i : 3);
+        base64_encode_atom(ssh2blob + i, n, p);
+        i += n;
+        p += 4;
+    }
+    if (*comment) {
+        *p++ = ' ';
+        strcpy(p, comment);
+    } else
+        *p++ = '\0';
+
+    return buffer;
+}
+
+char *ssh2_pubkey_openssh_str(struct ssh2_userkey *key)
+{
+    int bloblen;
+    unsigned char *blob;
+    char *ret;
+
+    blob = key->alg->public_blob(key->data, &bloblen);
+    ret = ssh2_pubkey_openssh_str_internal(key->comment, blob, bloblen);
+    sfree(blob);
+
+    return ret;
+}
+
+void ssh2_write_pubkey(FILE *fp, const char *comment,
+                       const void *v_pub_blob, int pub_len,
+                       int keytype)
+{
+    unsigned char *pub_blob = (unsigned char *)v_pub_blob;
+
+    if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) {
+        const char *p;
+        int i, column;
+
+        fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
+
+        if (comment) {
+            fprintf(fp, "Comment: \"");
+            for (p = comment; *p; p++) {
+                if (*p == '\\' || *p == '\"')
+                    fputc('\\', fp);
+                fputc(*p, fp);
+            }
+            fprintf(fp, "\"\n");
+        }
+
+        i = 0;
+        column = 0;
+        while (i < pub_len) {
+            char buf[5];
+            int n = (pub_len - i < 3 ? pub_len - i : 3);
+            base64_encode_atom(pub_blob + i, n, buf);
+            i += n;
+            buf[4] = '\0';
+            fputs(buf, fp);
+            if (++column >= 16) {
+                fputc('\n', fp);
+                column = 0;
+            }
+        }
+        if (column > 0)
+            fputc('\n', fp);
+
+        fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
+    } else if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
+        char *buffer = ssh2_pubkey_openssh_str_internal(comment,
+                                                        v_pub_blob, pub_len);
+        fprintf(fp, "%s\n", buffer);
+        sfree(buffer);
+    } else {
+        assert(0 && "Bad key type in ssh2_write_pubkey");
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Utility functions to compute SSH-2 fingerprints in a uniform way.
+ */
+char *ssh2_fingerprint_blob(const void *blob, int bloblen)
+{
+    unsigned char digest[16];
+    char fingerprint_str[16*3];
+    const char *algstr;
+    int alglen;
+    const struct ssh_signkey *alg;
+    int i;
+
+    /*
+     * The fingerprint hash itself is always just the MD5 of the blob.
+     */
+    MD5Simple(blob, bloblen, digest);
+    for (i = 0; i < 16; i++)
+        sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":");
+
+    /*
+     * Identify the key algorithm, if possible.
+     */
+    alglen = toint(GET_32BIT((const unsigned char *)blob));
+    if (alglen > 0 && alglen < bloblen-4) {
+        algstr = (const char *)blob + 4;
+
+        /*
+         * If we can actually identify the algorithm as one we know
+         * about, get hold of the key's bit count too.
+         */
+        alg = find_pubkey_alg_len(alglen, algstr);
+        if (alg) {
+            int bits = alg->pubkey_bits(alg, blob, bloblen);
+            return dupprintf("%.*s %d %s", alglen, algstr,
+                             bits, fingerprint_str);
+        } else {
+            return dupprintf("%.*s %s", alglen, algstr, fingerprint_str);
+        }
+    } else {
+        /*
+         * No algorithm available (which means a seriously confused
+         * key blob, but there we go). Return only the hash.
+         */
+        return dupstr(fingerprint_str);
+    }
+}
+
+char *ssh2_fingerprint(const struct ssh_signkey *alg, void *data)
+{
+    int len;
+    unsigned char *blob = alg->public_blob(data, &len);
+    char *ret = ssh2_fingerprint_blob(blob, len);
+    sfree(blob);
+    return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Determine the type of a private key file.
+ */
+static int key_type_fp(FILE *fp)
+{
+    char buf[1024];
+    const char public_std_sig[] = "---- BEGIN SSH2 PUBLIC KEY";
     const char putty2_sig[] = "PuTTY-User-Key-File-";
     const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT";
+    const char openssh_new_sig[] = "-----BEGIN OPENSSH PRIVATE KEY";
     const char openssh_sig[] = "-----BEGIN ";
     int i;
+    char *p;
+
+    i = fread(buf, 1, sizeof(buf)-1, fp);
+    rewind(fp);
 
-    fp = f_open(filename, "r", FALSE);
-    if (!fp)
-	return SSH_KEYTYPE_UNOPENABLE;
-    i = fread(buf, 1, sizeof(buf), fp);
-    fclose(fp);
     if (i < 0)
 	return SSH_KEYTYPE_UNOPENABLE;
     if (i < 32)
 	return SSH_KEYTYPE_UNKNOWN;
+    assert(i > 0 && i < sizeof(buf));
+    buf[i] = '\0';
     if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1))
 	return SSH_KEYTYPE_SSH1;
+    if (!memcmp(buf, public_std_sig, sizeof(public_std_sig)-1))
+	return SSH_KEYTYPE_SSH2_PUBLIC_RFC4716;
     if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1))
 	return SSH_KEYTYPE_SSH2;
+    if (!memcmp(buf, openssh_new_sig, sizeof(openssh_new_sig)-1))
+	return SSH_KEYTYPE_OPENSSH_NEW;
     if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1))
-	return SSH_KEYTYPE_OPENSSH;
+	return SSH_KEYTYPE_OPENSSH_PEM;
     if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1))
 	return SSH_KEYTYPE_SSHCOM;
+    if ((p = buf + strspn(buf, "0123456789"), *p == ' ') &&
+        (p = p+1 + strspn(p+1, "0123456789"), *p == ' ') &&
+        (p = p+1 + strspn(p+1, "0123456789"), *p == ' ' || *p == '\n' || !*p))
+	return SSH_KEYTYPE_SSH1_PUBLIC;
+    if ((p = buf + strcspn(buf, " "), find_pubkey_alg_len(p-buf, buf)) &&
+        (p = p+1 + strspn(p+1, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij"
+                          "klmnopqrstuvwxyz+/="),
+         *p == ' ' || *p == '\n' || !*p))
+	return SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH;
     return SSH_KEYTYPE_UNKNOWN;	       /* unrecognised or EOF */
 }
 
+int key_type(const Filename *filename)
+{
+    FILE *fp;
+    int ret;
+
+    fp = f_open(filename, "r", FALSE);
+    if (!fp)
+	return SSH_KEYTYPE_UNOPENABLE;
+    ret = key_type_fp(fp);
+    fclose(fp);
+    return ret;
+}
+
 /*
  * Convert the type word to a string, for `wrong type' error
  * messages.
  */
-char *key_type_to_str(int type)
+const char *key_type_to_str(int type)
 {
     switch (type) {
       case SSH_KEYTYPE_UNOPENABLE: return "unable to open file"; break;
-      case SSH_KEYTYPE_UNKNOWN: return "not a private key"; break;
+      case SSH_KEYTYPE_UNKNOWN: return "not a recognised key file format"; break;
+      case SSH_KEYTYPE_SSH1_PUBLIC: return "SSH-1 public key"; break;
+      case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: return "SSH-2 public key (RFC 4716 format)"; break;
+      case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: return "SSH-2 public key (OpenSSH format)"; break;
       case SSH_KEYTYPE_SSH1: return "SSH-1 private key"; break;
       case SSH_KEYTYPE_SSH2: return "PuTTY SSH-2 private key"; break;
-      case SSH_KEYTYPE_OPENSSH: return "OpenSSH SSH-2 private key"; break;
+      case SSH_KEYTYPE_OPENSSH_PEM: return "OpenSSH SSH-2 private key (old PEM format)"; break;
+      case SSH_KEYTYPE_OPENSSH_NEW: return "OpenSSH SSH-2 private key (new format)"; break;
       case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH-2 private key"; break;
+        /*
+         * This function is called with a key type derived from
+         * looking at an actual key file, so the output-only type
+         * OPENSSH_AUTO should never get here, and is much an INTERNAL
+         * ERROR as a code we don't even understand.
+         */
+      case SSH_KEYTYPE_OPENSSH_AUTO: return "INTERNAL ERROR (OPENSSH_AUTO)"; break;
       default: return "INTERNAL ERROR"; break;
     }
 }

+ 17 - 1
source/putty/sshrand.c

@@ -45,8 +45,23 @@ struct RandPool {
     int stir_pending;
 };
 
-static struct RandPool pool;
 int random_active = 0;
+
+#ifdef FUZZING
+/*
+ * Special dummy version of the RNG for use when fuzzing.
+ */
+void random_add_noise(void *noise, int length) { }
+void random_add_heavynoise(void *noise, int length) { }
+void random_ref(void) { }
+void random_unref(void) { }
+int random_byte(void)
+{
+    return 0x45; /* Chosen by eight fair coin tosses */
+}
+void random_get_savedata(void **data, int *len) { }
+#else /* !FUZZING */
+static struct RandPool pool;
 long next_noise_collection;
 
 #ifdef RANDOM_DIAGNOSTICS
@@ -364,3 +379,4 @@ void random_get_savedata(void **data, int *len)
     random_stir();
     MPEXT_PUTTY_SECTION_LEAVE;
 }
+#endif

+ 32 - 61
source/putty/sshrsa.c

@@ -10,10 +10,10 @@
 #include "ssh.h"
 #include "misc.h"
 
-int makekey(unsigned char *data, int len, struct RSAKey *result,
-	    unsigned char **keystr, int order)
+int makekey(const unsigned char *data, int len, struct RSAKey *result,
+	    const unsigned char **keystr, int order)
 {
-    unsigned char *p = data;
+    const unsigned char *p = data;
     int i, n;
 
     if (len < 4)
@@ -59,7 +59,7 @@ int makekey(unsigned char *data, int len, struct RSAKey *result,
     return p - data;
 }
 
-int makeprivate(unsigned char *data, int len, struct RSAKey *result)
+int makeprivate(const unsigned char *data, int len, struct RSAKey *result)
 {
     return ssh1_read_bignum(data, len, &result->private_exponent);
 }
@@ -533,7 +533,8 @@ void freersakey(struct RSAKey *key)
  * Implementation of the ssh-rsa signing key type. 
  */
 
-static void getstring(char **data, int *datalen, char **p, int *length)
+static void getstring(const char **data, int *datalen,
+                      const char **p, int *length)
 {
     *p = NULL;
     if (*datalen < 4)
@@ -549,9 +550,9 @@ static void getstring(char **data, int *datalen, char **p, int *length)
     *data += *length;
     *datalen -= *length;
 }
-static Bignum getmp(char **data, int *datalen)
+static Bignum getmp(const char **data, int *datalen)
 {
-    char *p;
+    const char *p;
     int length;
     Bignum b;
 
@@ -564,9 +565,10 @@ static Bignum getmp(char **data, int *datalen)
 
 static void rsa2_freekey(void *key);   /* forward reference */
 
-static void *rsa2_newkey(char *data, int len)
+static void *rsa2_newkey(const struct ssh_signkey *self,
+                         const char *data, int len)
 {
-    char *p;
+    const char *p;
     int slen;
     struct RSAKey *rsa;
 
@@ -684,13 +686,14 @@ static unsigned char *rsa2_private_blob(void *key, int *len)
     return blob;
 }
 
-static void *rsa2_createkey(unsigned char *pub_blob, int pub_len,
-			    unsigned char *priv_blob, int priv_len)
+static void *rsa2_createkey(const struct ssh_signkey *self,
+                            const unsigned char *pub_blob, int pub_len,
+			    const unsigned char *priv_blob, int priv_len)
 {
     struct RSAKey *rsa;
-    char *pb = (char *) priv_blob;
+    const char *pb = (const char *) priv_blob;
 
-    rsa = rsa2_newkey((char *) pub_blob, pub_len);
+    rsa = rsa2_newkey(self, (char *) pub_blob, pub_len);
     rsa->private_exponent = getmp(&pb, &priv_len);
     rsa->p = getmp(&pb, &priv_len);
     rsa->q = getmp(&pb, &priv_len);
@@ -704,9 +707,10 @@ static void *rsa2_createkey(unsigned char *pub_blob, int pub_len,
     return rsa;
 }
 
-static void *rsa2_openssh_createkey(unsigned char **blob, int *len)
+static void *rsa2_openssh_createkey(const struct ssh_signkey *self,
+                                    const unsigned char **blob, int *len)
 {
-    char **b = (char **) blob;
+    const char **b = (const char **) blob;
     struct RSAKey *rsa;
 
     rsa = snew(struct RSAKey);
@@ -762,12 +766,13 @@ static int rsa2_openssh_fmtkey(void *key, unsigned char *blob, int len)
     return bloblen;
 }
 
-static int rsa2_pubkey_bits(void *blob, int len)
+static int rsa2_pubkey_bits(const struct ssh_signkey *self,
+                            const void *blob, int len)
 {
     struct RSAKey *rsa;
     int ret;
 
-    rsa = rsa2_newkey((char *) blob, len);
+    rsa = rsa2_newkey(self, (const char *) blob, len);
     if (!rsa)
 	return -1;
     ret = bignum_bitcount(rsa->modulus);
@@ -776,41 +781,6 @@ static int rsa2_pubkey_bits(void *blob, int len)
     return ret;
 }
 
-static char *rsa2_fingerprint(void *key)
-{
-    struct RSAKey *rsa = (struct RSAKey *) key;
-    struct MD5Context md5c;
-    unsigned char digest[16], lenbuf[4];
-    char buffer[16 * 3 + 40];
-    char *ret;
-    int numlen, i;
-
-    MD5Init(&md5c);
-    MD5Update(&md5c, (unsigned char *)"\0\0\0\7ssh-rsa", 11);
-
-#define ADD_BIGNUM(bignum) \
-    numlen = (bignum_bitcount(bignum)+8)/8; \
-    PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \
-    for (i = numlen; i-- ;) { \
-        unsigned char c = bignum_byte(bignum, i); \
-        MD5Update(&md5c, &c, 1); \
-    }
-    ADD_BIGNUM(rsa->exponent);
-    ADD_BIGNUM(rsa->modulus);
-#undef ADD_BIGNUM
-
-    MD5Final(digest, &md5c);
-
-    sprintf(buffer, "ssh-rsa %d ", bignum_bitcount(rsa->modulus));
-    for (i = 0; i < 16; i++)
-	sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",
-		digest[i]);
-    ret = snewn(strlen(buffer) + 1, char);
-    if (ret)
-	strcpy(ret, buffer);
-    return ret;
-}
-
 /*
  * This is the magic ASN.1/DER prefix that goes in the decoded
  * signature, between the string of FFs and the actual SHA hash
@@ -842,12 +812,12 @@ static const unsigned char asn1_weird_stuff[] = {
 
 #define ASN1_LEN ( (int) sizeof(asn1_weird_stuff) )
 
-static int rsa2_verifysig(void *key, char *sig, int siglen,
-			  char *data, int datalen)
+static int rsa2_verifysig(void *key, const char *sig, int siglen,
+			  const char *data, int datalen)
 {
     struct RSAKey *rsa = (struct RSAKey *) key;
     Bignum in, out;
-    char *p;
+    const char *p;
     int slen;
     int bytes, i, j, ret;
     unsigned char hash[20];
@@ -892,7 +862,7 @@ static int rsa2_verifysig(void *key, char *sig, int siglen,
     return ret;
 }
 
-static unsigned char *rsa2_sign(void *key, char *data, int datalen,
+static unsigned char *rsa2_sign(void *key, const char *data, int datalen,
 				int *siglen)
 {
     struct RSAKey *rsa = (struct RSAKey *) key;
@@ -944,17 +914,18 @@ const struct ssh_signkey ssh_rsa = {
     rsa2_createkey,
     rsa2_openssh_createkey,
     rsa2_openssh_fmtkey,
+    6 /* n,e,d,iqmp,q,p */,
     rsa2_pubkey_bits,
-    rsa2_fingerprint,
     rsa2_verifysig,
     rsa2_sign,
     "ssh-rsa",
-    "rsa2"
+    "rsa2",
+    NULL,
 };
 
 void *ssh_rsakex_newkey(char *data, int len)
 {
-    return rsa2_newkey(data, len);
+    return rsa2_newkey(&ssh_rsa, data, len);
 }
 
 void ssh_rsakex_freekey(void *key)
@@ -1089,11 +1060,11 @@ void ssh_rsakex_encrypt(const struct ssh_hash *h, unsigned char *in, int inlen,
 }
 
 static const struct ssh_kex ssh_rsa_kex_sha1 = {
-    "rsa1024-sha1", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha1
+    "rsa1024-sha1", NULL, KEXTYPE_RSA, &ssh_sha1, NULL,
 };
 
 static const struct ssh_kex ssh_rsa_kex_sha256 = {
-    "rsa2048-sha256", NULL, KEXTYPE_RSA, NULL, NULL, 0, 0, &ssh_sha256
+    "rsa2048-sha256", NULL, KEXTYPE_RSA, &ssh_sha256, NULL,
 };
 
 static const struct ssh_kex *const rsa_kex_list[] = {

+ 25 - 7
source/putty/sshsh256.c

@@ -207,7 +207,25 @@ static void *sha256_init(void)
     return s;
 }
 
-static void sha256_bytes(void *handle, void *p, int len)
+static void *sha256_copy(const void *vold)
+{
+    const SHA256_State *old = (const SHA256_State *)vold;
+    SHA256_State *s;
+
+    s = snew(SHA256_State);
+    *s = *old;
+    return s;
+}
+
+static void sha256_free(void *handle)
+{
+    SHA256_State *s = handle;
+
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+static void sha256_bytes(void *handle, const void *p, int len)
 {
     SHA256_State *s = handle;
 
@@ -219,12 +237,12 @@ static void sha256_final(void *handle, unsigned char *output)
     SHA256_State *s = handle;
 
     SHA256_Final(s, output);
-    smemclr(s, sizeof(*s));
-    sfree(s);
+    sha256_free(s);
 }
 
 const struct ssh_hash ssh_sha256 = {
-    sha256_init, sha256_bytes, sha256_final, 32, "SHA-256"
+    sha256_init, sha256_copy, sha256_bytes, sha256_final, sha256_free,
+    32, "SHA-256"
 };
 
 /* ----------------------------------------------------------------------
@@ -232,7 +250,7 @@ const struct ssh_hash ssh_sha256 = {
  * HMAC wrapper on it.
  */
 
-static void *sha256_make_context(void)
+static void *sha256_make_context(void *cipher_ctx)
 {
     return snewn(3, SHA256_State);
 }
@@ -333,8 +351,8 @@ const struct ssh_mac ssh_hmac_sha256 = {
     sha256_generate, sha256_verify,
     hmacsha256_start, hmacsha256_bytes,
     hmacsha256_genresult, hmacsha256_verresult,
-    "hmac-sha2-256",
-    32,
+    "hmac-sha2-256", "[email protected]",
+    32, 32,
     "HMAC-SHA-256"
 };
 #endif // !WINSCP_VS

+ 115 - 0
source/putty/sshsh512.c

@@ -2,6 +2,8 @@
  * SHA-512 algorithm as described at
  * 
  *   http://csrc.nist.gov/cryptval/shs.html
+ *
+ * Modifications made for SHA-384 also
  */
 
 #include "ssh.h"
@@ -61,6 +63,22 @@ static void SHA512_Core_Init(SHA512_State *s) {
 	s->h[i] = iv[i];
 }
 
+static void SHA384_Core_Init(SHA512_State *s) {
+    static const uint64 iv[] = {
+        INIT(0xcbbb9d5d, 0xc1059ed8),
+        INIT(0x629a292a, 0x367cd507),
+        INIT(0x9159015a, 0x3070dd17),
+        INIT(0x152fecd8, 0xf70e5939),
+        INIT(0x67332667, 0xffc00b31),
+        INIT(0x8eb44a87, 0x68581511),
+        INIT(0xdb0c2e0d, 0x64f98fa7),
+        INIT(0x47b5481d, 0xbefa4fa4),
+    };
+    int i;
+    for (i = 0; i < 8; i++)
+        s->h[i] = iv[i];
+}
+
 static void SHA512_Block(SHA512_State *s, uint64 *block) {
     uint64 w[80];
     uint64 a,b,c,d,e,f,g,h;
@@ -175,6 +193,14 @@ void SHA512_Init(SHA512_State *s) {
 	s->len[i] = 0;
 }
 
+void SHA384_Init(SHA512_State *s) {
+    int i;
+    SHA384_Core_Init(s);
+    s->blkused = 0;
+    for (i = 0; i < 4; i++)
+        s->len[i] = 0;
+}
+
 void SHA512_Bytes(SHA512_State *s, const void *p, int len) {
     unsigned char *q = (unsigned char *)p;
     uint64 wordblock[16];
@@ -268,6 +294,12 @@ void SHA512_Final(SHA512_State *s, unsigned char *digest) {
     }
 }
 
+void SHA384_Final(SHA512_State *s, unsigned char *digest) {
+    unsigned char biggerDigest[512 / 8];
+    SHA512_Final(s, biggerDigest);
+    memcpy(digest, biggerDigest, 384 / 8);
+}
+
 void SHA512_Simple(const void *p, int len, unsigned char *output) {
     SHA512_State s;
 
@@ -277,6 +309,89 @@ void SHA512_Simple(const void *p, int len, unsigned char *output) {
     smemclr(&s, sizeof(s));
 }
 
+void SHA384_Simple(const void *p, int len, unsigned char *output) {
+    SHA512_State s;
+
+    SHA384_Init(&s);
+    SHA512_Bytes(&s, p, len);
+    SHA384_Final(&s, output);
+    smemclr(&s, sizeof(s));
+}
+
+/*
+ * Thin abstraction for things where hashes are pluggable.
+ */
+
+static void *sha512_init(void)
+{
+    SHA512_State *s;
+
+    s = snew(SHA512_State);
+    SHA512_Init(s);
+    return s;
+}
+
+static void *sha512_copy(const void *vold)
+{
+    const SHA512_State *old = (const SHA512_State *)vold;
+    SHA512_State *s;
+
+    s = snew(SHA512_State);
+    *s = *old;
+    return s;
+}
+
+static void sha512_free(void *handle)
+{
+    SHA512_State *s = handle;
+
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+static void sha512_bytes(void *handle, const void *p, int len)
+{
+    SHA512_State *s = handle;
+
+    SHA512_Bytes(s, p, len);
+}
+
+static void sha512_final(void *handle, unsigned char *output)
+{
+    SHA512_State *s = handle;
+
+    SHA512_Final(s, output);
+    sha512_free(s);
+}
+
+const struct ssh_hash ssh_sha512 = {
+    sha512_init, sha512_copy, sha512_bytes, sha512_final, sha512_free,
+    64, "SHA-512"
+};
+
+static void *sha384_init(void)
+{
+    SHA512_State *s;
+
+    s = snew(SHA512_State);
+    SHA384_Init(s);
+    return s;
+}
+
+static void sha384_final(void *handle, unsigned char *output)
+{
+    SHA512_State *s = handle;
+
+    SHA384_Final(s, output);
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+const struct ssh_hash ssh_sha384 = {
+    sha384_init, sha512_copy, sha512_bytes, sha384_final, sha512_free,
+    48, "SHA-384"
+};
+
 #ifdef TEST
 
 #include <stdio.h>

+ 30 - 13
source/putty/sshsha.c

@@ -233,7 +233,25 @@ static void *sha1_init(void)
     return s;
 }
 
-static void sha1_bytes(void *handle, void *p, int len)
+static void *sha1_copy(const void *vold)
+{
+    const SHA_State *old = (const SHA_State *)vold;
+    SHA_State *s;
+
+    s = snew(SHA_State);
+    *s = *old;
+    return s;
+}
+
+static void sha1_free(void *handle)
+{
+    SHA_State *s = handle;
+
+    smemclr(s, sizeof(*s));
+    sfree(s);
+}
+
+static void sha1_bytes(void *handle, const void *p, int len)
 {
     SHA_State *s = handle;
 
@@ -245,12 +263,11 @@ static void sha1_final(void *handle, unsigned char *output)
     SHA_State *s = handle;
 
     SHA_Final(s, output);
-    smemclr(s, sizeof(*s));
-    sfree(s);
+    sha1_free(s);
 }
 
 const struct ssh_hash ssh_sha1 = {
-    sha1_init, sha1_bytes, sha1_final, 20, "SHA-1"
+    sha1_init, sha1_copy, sha1_bytes, sha1_final, sha1_free, 20, "SHA-1"
 };
 
 /* ----------------------------------------------------------------------
@@ -258,7 +275,7 @@ const struct ssh_hash ssh_sha1 = {
  * HMAC wrapper on it.
  */
 
-static void *sha1_make_context(void)
+static void *sha1_make_context(void *cipher_ctx)
 {
     return snewn(3, SHA_State);
 }
@@ -406,8 +423,8 @@ const struct ssh_mac ssh_hmac_sha1 = {
     sha1_make_context, sha1_free_context, sha1_key,
     sha1_generate, sha1_verify,
     hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
-    "hmac-sha1",
-    20,
+    "hmac-sha1", "[email protected]",
+    20, 20,
     "HMAC-SHA1"
 };
 
@@ -416,8 +433,8 @@ const struct ssh_mac ssh_hmac_sha1_96 = {
     sha1_96_generate, sha1_96_verify,
     hmacsha1_start, hmacsha1_bytes,
     hmacsha1_96_genresult, hmacsha1_96_verresult,
-    "hmac-sha1-96",
-    12,
+    "hmac-sha1-96", "[email protected]",
+    12, 20,
     "HMAC-SHA1-96"
 };
 
@@ -425,8 +442,8 @@ const struct ssh_mac ssh_hmac_sha1_buggy = {
     sha1_make_context, sha1_free_context, sha1_key_buggy,
     sha1_generate, sha1_verify,
     hmacsha1_start, hmacsha1_bytes, hmacsha1_genresult, hmacsha1_verresult,
-    "hmac-sha1",
-    20,
+    "hmac-sha1", NULL,
+    20, 16,
     "bug-compatible HMAC-SHA1"
 };
 
@@ -435,8 +452,8 @@ const struct ssh_mac ssh_hmac_sha1_96_buggy = {
     sha1_96_generate, sha1_96_verify,
     hmacsha1_start, hmacsha1_bytes,
     hmacsha1_96_genresult, hmacsha1_96_verresult,
-    "hmac-sha1-96",
-    12,
+    "hmac-sha1-96", NULL,
+    12, 16,
     "bug-compatible HMAC-SHA1-96"
 };
 

+ 115 - 43
source/putty/sshshare.c

@@ -133,6 +133,7 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <limits.h>
+#include <errno.h>
 
 #include "putty.h"
 #include "tree234.h"
@@ -914,8 +915,25 @@ static int share_closing(Plug plug, const char *error_msg, int error_code,
                          int calling_back)
 {
     struct ssh_sharing_connstate *cs = (struct ssh_sharing_connstate *)plug;
-    if (error_msg)
-        ssh_sharing_logf(cs->parent->ssh, cs->id, "%s", error_msg);
+
+    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
+            ssh_sharing_logf(cs->parent->ssh, cs->id,
+                             "Socket error: %s", error_msg);
+    }
     share_begin_cleanup(cs);
     return 1;
 }
@@ -1810,6 +1828,7 @@ static int share_receive(Plug plug, int urgent, char *data, int len)
     ssh_sharing_logf(cs->parent->ssh, cs->id,
                      "Downstream version string: %.*s",
                      cs->recvlen, cs->recvbuf);
+    cs->got_verstring = TRUE;
 
     /*
      * Loop round reading packets.
@@ -1982,6 +2001,99 @@ static int share_listen_accepting(Plug plug,
 extern const int share_can_be_downstream;
 extern const int share_can_be_upstream;
 
+/*
+ * 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 = get_remote_username(conf);
+    char *sockname;
+
+    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;
+}
+
+static void nullplug_socket_log(Plug plug, int type, SockAddr addr, int port,
+                                const char *error_msg, int error_code) {}
+static int nullplug_closing(Plug plug, const char *error_msg, int error_code,
+                            int calling_back) { return 0; }
+static int nullplug_receive(Plug plug, int urgent, char *data,
+                            int len) { return 0; }
+static void nullplug_sent(Plug plug, int bufsize) {}
+
+int ssh_share_test_for_upstream(const char *host, int port, Conf *conf)
+{
+    static const struct plug_function_table fn_table = {
+	nullplug_socket_log,
+	nullplug_closing,
+	nullplug_receive,
+	nullplug_sent,
+	NULL
+    };
+    struct nullplug {
+        const struct plug_function_table *fn;
+    } np;
+
+    char *sockname, *logtext, *ds_err, *us_err;
+    int result;
+    Socket sock;
+
+    np.fn = &fn_table;
+
+    sockname = ssh_share_sockname(host, port, conf);
+
+    sock = NULL;
+    logtext = ds_err = us_err = NULL;
+    result = platform_ssh_share(sockname, conf, (Plug)&np, (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;
+    }
+}
+
 /*
  * Init function for connection sharing. We either open a listening
  * socket and become an upstream, or connect to an existing one and
@@ -2018,47 +2130,7 @@ Socket ssh_connection_sharing_init(const char *host, int port,
     if (!can_upstream && !can_downstream)
         return NULL;
 
-    /*
-     * 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.
-     */
-    {
-        char *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);
-
-        /*
-         * The platform-specific code may transform this further in
-         * order to conform to local namespace conventions (e.g. not
-         * using slashes in filenames), but that's its job and not
-         * ours.
-         */
-    }
+    sockname = ssh_share_sockname(host, port, conf);
 
     /*
      * Create a data structure for the listening plug if we turn out

+ 1 - 0
source/putty/sshzlib.c

@@ -1366,6 +1366,7 @@ int main(int argc, char **argv)
             sfree(outbuf);
         } else {
             fprintf(stderr, "decoding error\n");
+            fclose(fp);
             return 1;
         }
     }

+ 4 - 4
source/putty/version.h

@@ -1,5 +1,5 @@
 /* Generated by automated build script */
-#define RELEASE 0.66
-#define TEXTVER "Release 0.66"
-#define SSHVER "PuTTY-Release-0.66"
-#define BINARY_VERSION 0,66,0,0
+#define SNAPSHOT
+#define TEXTVER "Development snapshot 2016-01-04.51465fa"
+#define SSHVER "PuTTY-Snapshot-2016-01-04.51465fa"
+#define BINARY_VERSION 0,66,1058,0

+ 1 - 1
source/putty/windows/wingss.c

@@ -392,7 +392,7 @@ static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
 					    Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
 {
     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
-    char *msg;
+    const char *msg;
 
     if (winctx == NULL) return SSH_GSS_FAILURE;
 

+ 4 - 4
source/putty/windows/winhandl.c

@@ -411,9 +411,9 @@ static int handle_cmp_evtomain(void *av, void *bv)
     struct handle *a = (struct handle *)av;
     struct handle *b = (struct handle *)bv;
 
-    if ((unsigned)a->u.g.ev_to_main < (unsigned)b->u.g.ev_to_main)
+    if ((uintptr_t)a->u.g.ev_to_main < (uintptr_t)b->u.g.ev_to_main)
 	return -1;
-    else if ((unsigned)a->u.g.ev_to_main > (unsigned)b->u.g.ev_to_main)
+    else if ((uintptr_t)a->u.g.ev_to_main > (uintptr_t)b->u.g.ev_to_main)
 	return +1;
     else
 	return 0;
@@ -424,9 +424,9 @@ static int handle_find_evtomain(void *av, void *bv)
     HANDLE *a = (HANDLE *)av;
     struct handle *b = (struct handle *)bv;
 
-    if ((unsigned)*a < (unsigned)b->u.g.ev_to_main)
+    if ((uintptr_t)*a < (uintptr_t)b->u.g.ev_to_main)
 	return -1;
-    else if ((unsigned)*a > (unsigned)b->u.g.ev_to_main)
+    else if ((uintptr_t)*a > (uintptr_t)b->u.g.ev_to_main)
 	return +1;
     else
 	return 0;

+ 23 - 4
source/putty/windows/winhsock.c

@@ -22,8 +22,8 @@ struct Socket_handle_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
 
-    HANDLE send_H, recv_H;
-    struct handle *send_h, *recv_h;
+    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,
@@ -43,6 +43,9 @@ struct Socket_handle_tag {
     /* We buffer data here if we receive it from winhandl while frozen. */
     bufchain inputdata;
 
+    /* Data received from stderr_H, if we have one. */
+    bufchain stderrdata;
+
     char *error;
 
     Plug plug;
@@ -79,6 +82,16 @@ static int handle_gotdata(struct handle *h, void *data, int len)
     }
 }
 
+static int handle_stderr(struct handle *h, void *data, int len)
+{
+    Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
+
+    if (len > 0)
+        log_proxy_stderr(ps->plug, &ps->stderrdata, data, len);
+
+    return 0;
+}
+
 static void handle_sentdata(struct handle *h, int new_backlog)
 {
     Handle_Socket ps = (Handle_Socket) handle_get_privdata(h);
@@ -110,6 +123,7 @@ static void sk_handle_close(Socket s)
     if (ps->recv_H != ps->send_H)
         CloseHandle(ps->recv_H);
     bufchain_clear(&ps->inputdata);
+    bufchain_clear(&ps->stderrdata);
 
     sfree(ps);
 }
@@ -268,8 +282,8 @@ static char *sk_handle_peer_info(Socket s)
     return NULL;
 }
 
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
-                          int overlapped)
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+                          Plug plug, int overlapped)
 {
     static const struct socket_function_table socket_fn_table = {
 	sk_handle_plug,
@@ -292,11 +306,16 @@ Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
     ret->error = NULL;
     ret->frozen = UNFROZEN;
     bufchain_init(&ret->inputdata);
+    bufchain_init(&ret->stderrdata);
 
     ret->recv_H = recv_H;
     ret->recv_h = handle_input_new(ret->recv_H, handle_gotdata, ret, flags);
     ret->send_H = send_H;
     ret->send_h = handle_output_new(ret->send_H, handle_sentdata, ret, flags);
+    ret->stderr_H = stderr_H;
+    if (ret->stderr_H)
+        ret->stderr_h = handle_input_new(ret->stderr_H, handle_stderr,
+                                         ret, flags);
 
     #ifdef MPEXT
     // WinSCP core uses do_select as signalization of connection up/down

+ 5 - 3
source/putty/windows/winmisc.c

@@ -5,7 +5,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "putty.h"
+#ifndef SECURITY_WIN32
 #define SECURITY_WIN32
+#endif
 #include <security.h>
 #ifdef MPEXT
 #include <assert.h>
@@ -275,8 +277,8 @@ const char *win_strerror(int error)
                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                            msgtext, lenof(msgtext)-1, NULL)) {
             sprintf(msgtext,
-                    "(unable to format: FormatMessage returned %d)",
-                    GetLastError());
+                    "(unable to format: FormatMessage returned %u)",
+                    (unsigned int)GetLastError());
         } else {
             int len = strlen(msgtext);
             if (len > 0 && msgtext[len-1] == '\n')
@@ -294,7 +296,7 @@ static FILE *debug_fp = NULL;
 static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
 static int debug_got_console = 0;
 
-void dputs(char *buf)
+void dputs(const char *buf)
 {
     DWORD dw;
 

+ 8 - 8
source/putty/windows/winnet.c

@@ -54,7 +54,7 @@ struct SockAddrStep_tag {
 struct Socket_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
-    char *error;
+    const char *error;
     SOCKET s;
     Plug plug;
     bufchain output_data;
@@ -142,7 +142,7 @@ static int cmpfortree(void *av, void *bv)
 static int cmpforsearch(void *av, void *bv)
 {
     Actual_Socket b = (Actual_Socket) bv;
-    unsigned long as = (unsigned long) av, bs = (unsigned long) b->s;
+    uintptr_t as = (uintptr_t) av, bs = (uintptr_t) b->s;
     if (as < bs)
 	return -1;
     if (as > bs)
@@ -375,7 +375,7 @@ static int errstring_compare(void *av, void *bv)
 
 static tree234 *errstrings = NULL;
 
-char *winsock_error_string(int error)
+const char *winsock_error_string(int error)
 {
     const char prefix[] = "Network error: ";
     struct errstring *es;
@@ -491,8 +491,8 @@ char *winsock_error_string(int error)
                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                            es->text + bufused, bufsize - bufused, NULL)) {
             sprintf(es->text + bufused,
-                    "Windows error code %d (and FormatMessage returned %d)", 
-                    error, GetLastError());
+                    "Windows error code %d (and FormatMessage returned %u)",
+                    error, (unsigned int)GetLastError());
         } else {
             int len = strlen(es->text);
             if (len > 0 && es->text[len-1] == '\n')
@@ -1272,8 +1272,8 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     return (Socket) ret;
 }
 
-Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
-		      int orig_address_family)
+Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
+                      int local_host_only, int orig_address_family)
 {
     static const struct socket_function_table fn_table = {
 	sk_tcp_plug,
@@ -1775,7 +1775,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
 	ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB);
 	noise_ultralight(ret);
 	if (ret <= 0) {
-	    char *str = (ret == 0 ? "Internal networking trouble" :
+	    const char *str = (ret == 0 ? "Internal networking trouble" :
 			 winsock_error_string(p_WSAGetLastError()));
 	    /* We're inside the Windows frontend here, so we know
 	     * that the frontend handle is unnecessary. */

+ 30 - 9
source/putty/windows/winproxy.c

@@ -13,15 +13,17 @@
 #include "network.h"
 #include "proxy.h"
 
-Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, Plug plug,
-                          int overlapped);
-Socket platform_new_connection(SockAddr addr, char *hostname,
+Socket make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H,
+                          Plug plug, int overlapped);
+Socket platform_new_connection(SockAddr addr, const char *hostname,
 			       int port, int privport,
 			       int oobinline, int nodelay, int keepalive,
 			       Plug plug, Conf *conf)
 {
     char *cmd;
-    HANDLE us_to_cmd, us_from_cmd, cmd_to_us, cmd_from_us;
+    HANDLE us_to_cmd, cmd_from_us;
+    HANDLE us_from_cmd, cmd_to_us;
+    HANDLE us_from_cmd_err, cmd_err_to_us;
     SECURITY_ATTRIBUTES sa;
     STARTUPINFO si;
     PROCESS_INFORMATION pi;
@@ -36,9 +38,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
 
     {
 	char *msg = dupprintf("Starting local proxy command: %s", cmd);
-	/* We're allowed to pass NULL here, because we're part of the Windows
-	 * front end so we know logevent doesn't expect any data. */
-	logevent(NULL, msg);
+	plug_log(plug, 2, NULL, 0, msg, 0);
 	sfree(msg);
     }
 
@@ -65,8 +65,25 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
 	return ret;
     }
 
+    if (flags & FLAG_STDERR) {
+        /* If we have a sensible stderr, the proxy command can send
+         * its own standard error there, so we won't interfere. */
+        us_from_cmd_err = cmd_err_to_us = NULL;
+    } else {
+        /* If we don't have a sensible stderr, we should catch the
+         * proxy command's standard error to put in our event log. */
+        if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
+            Socket ret = new_error_socket
+                ("Unable to create pipes for proxy command", plug);
+            sfree(cmd);
+            return ret;
+        }
+    }
+
     SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);
     SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0);
+    if (us_from_cmd_err != NULL)
+        SetHandleInformation(us_from_cmd_err, HANDLE_FLAG_INHERIT, 0);
 
     si.cb = sizeof(si);
     si.lpReserved = NULL;
@@ -77,7 +94,7 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
     si.lpReserved2 = NULL;
     si.hStdInput = cmd_from_us;
     si.hStdOutput = cmd_to_us;
-    si.hStdError = NULL;
+    si.hStdError = cmd_err_to_us;
     CreateProcess(NULL, cmd, NULL, NULL, TRUE,
 		  CREATE_NO_WINDOW | NORMAL_PRIORITY_CLASS,
 		  NULL, NULL, &si, &pi);
@@ -89,5 +106,9 @@ Socket platform_new_connection(SockAddr addr, char *hostname,
     CloseHandle(cmd_from_us);
     CloseHandle(cmd_to_us);
 
-    return make_handle_socket(us_to_cmd, us_from_cmd, plug, FALSE);
+    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);
 }

+ 105 - 36
source/putty/windows/winsecur.c

@@ -12,6 +12,10 @@
 #define WINSECUR_GLOBAL
 #include "winsecur.h"
 
+/* Initialised once, then kept around to reuse forever */
+static PSID worldsid, networksid, usersid;
+
+
 int got_advapi(void)
 {
     static int attempted = FALSE;
@@ -23,6 +27,7 @@ int got_advapi(void)
         advapi = load_system32_dll("advapi32.dll");
         successful = advapi &&
             GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
+            GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) &&
             GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
             GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
             GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
@@ -32,21 +37,6 @@ int got_advapi(void)
     return successful;
 }
 
-int got_crypt(void)
-{
-    static int attempted = FALSE;
-    static int successful;
-    static HMODULE crypt;
-
-    if (!attempted) {
-        attempted = TRUE;
-        crypt = load_system32_dll("crypt32.dll");
-        successful = crypt &&
-            GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory);
-    }
-    return successful;
-}
-
 PSID get_user_sid(void)
 {
     HANDLE proc = NULL, tok = NULL;
@@ -99,32 +89,17 @@ PSID get_user_sid(void)
     return ret;
 }
 
-int make_private_security_descriptor(DWORD permissions,
-                                     PSECURITY_DESCRIPTOR *psd,
-                                     PACL *acl,
-                                     char **error)
+int getsids(char *error)
 {
     SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
     SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
-    EXPLICIT_ACCESS ea[3];
-    int acl_err;
-    int ret = FALSE;
+    int ret;
 
-    /* Initialised once, then kept around to reuse forever */
-    static PSID worldsid, networksid, usersid;
-
-    *psd = NULL;
-    *acl = NULL;
-    *error = NULL;
-
-    if (!got_advapi()) {
-        *error = dupprintf("unable to load advapi32.dll");
-        goto cleanup;
-    }
+    error=NULL;
 
     if (!usersid) {
         if ((usersid = get_user_sid()) == NULL) {
-            *error = dupprintf("unable to construct SID for current user: %s",
+            error = dupprintf("unable to construct SID for current user: %s",
                                win_strerror(GetLastError()));
             goto cleanup;
         }
@@ -133,7 +108,7 @@ int make_private_security_descriptor(DWORD permissions,
     if (!worldsid) {
         if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
                                       0, 0, 0, 0, 0, 0, 0, &worldsid)) {
-            *error = dupprintf("unable to construct SID for world: %s",
+            error = dupprintf("unable to construct SID for world: %s",
                                win_strerror(GetLastError()));
             goto cleanup;
         }
@@ -142,13 +117,43 @@ int make_private_security_descriptor(DWORD permissions,
     if (!networksid) {
         if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
                                       0, 0, 0, 0, 0, 0, 0, &networksid)) {
-            *error = dupprintf("unable to construct SID for "
+            error = dupprintf("unable to construct SID for "
                                "local same-user access only: %s",
                                win_strerror(GetLastError()));
             goto cleanup;
         }
     }
 
+    ret=TRUE;
+
+ cleanup:
+    if (ret) {
+      sfree(error);
+      error = NULL;
+    }
+    return ret;
+}
+  
+
+int make_private_security_descriptor(DWORD permissions,
+                                     PSECURITY_DESCRIPTOR *psd,
+                                     PACL *acl,
+                                     char **error)
+{
+    SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
+    SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
+    EXPLICIT_ACCESS ea[3];
+    int acl_err;
+    int ret = FALSE;
+
+
+    *psd = NULL;
+    *acl = NULL;
+    *error = NULL;
+
+    if (!getsids(*error))
+      goto cleanup;
+
     memset(ea, 0, sizeof(ea));
     ea[0].grfAccessPermissions = permissions;
     ea[0].grfAccessMode = REVOKE_ACCESS;
@@ -218,4 +223,68 @@ int make_private_security_descriptor(DWORD permissions,
     return ret;
 }
 
+int setprocessacl(char *error)
+{
+    SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
+    SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
+    EXPLICIT_ACCESS ea[2];
+    int acl_err;
+    int ret=FALSE;
+    PACL acl = NULL;
+
+    static const nastyace=WRITE_DAC | WRITE_OWNER |
+	PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
+	PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION |
+	PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
+	PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
+	PROCESS_SUSPEND_RESUME;
+
+    if (!getsids(error))
+	goto cleanup;
+
+    memset(ea, 0, sizeof(ea));
+
+    /* Everyone: deny */
+    ea[0].grfAccessPermissions = nastyace;
+    ea[0].grfAccessMode = DENY_ACCESS;
+    ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+    ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
+
+    /* User: user ace */
+    ea[1].grfAccessPermissions = ~nastyace & 0x1fff;
+    ea[1].grfAccessMode = GRANT_ACCESS;
+    ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+    ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+    ea[1].Trustee.ptstrName = (LPTSTR)usersid;
+
+    acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl);
+
+    if (acl_err != ERROR_SUCCESS || acl == NULL) {
+	error = dupprintf("unable to construct ACL: %s",
+			  win_strerror(acl_err));
+        goto cleanup;
+    }
+
+    if (ERROR_SUCCESS != p_SetSecurityInfo
+        (GetCurrentProcess(), SE_KERNEL_OBJECT,
+         OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+         usersid, NULL, acl, NULL)) {
+	error=dupprintf("Unable to set process ACL: %s",
+			win_strerror(GetLastError()));
+	goto cleanup;
+    }
+		      
+
+    ret=TRUE;
+    
+  cleanup:
+    if (!ret) {
+        if (acl) {
+            LocalFree(acl);
+            acl = NULL;
+        }
+    }
+    return ret;
+}  
 #endif /* !defined NO_SECURITY */

+ 5 - 7
source/putty/windows/winsecur.h

@@ -28,17 +28,13 @@ DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, GetSecurityInfo,
 		      (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
 		       PSID *, PSID *, PACL *, PACL *,
 		       PSECURITY_DESCRIPTOR *));
+DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetSecurityInfo,
+		      (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION,
+		       PSID, PSID, PACL, PACL));
 DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetEntriesInAclA,
 		      (ULONG, PEXPLICIT_ACCESS, PACL, PACL *));
 int got_advapi(void);
 
-/*
- * Functions loaded from crypt32.dll.
- */
-DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, CryptProtectMemory,
-		      (LPVOID, DWORD, DWORD));
-int got_crypt(void);
-
 /*
  * Find the SID describing the current user. The return value (if not
  * NULL for some error-related reason) is smalloced.
@@ -60,4 +56,6 @@ int make_private_security_descriptor(DWORD permissions,
                                      PACL *acl,
                                      char **error);
 
+int setprocessacl(char *error);
+
 #endif

+ 11 - 1
source/putty/windows/winstore.c

@@ -491,6 +491,16 @@ int verify_host_key(const char *hostname, int port,
 	return 0;		       /* key matched OK in registry */
 }
 
+int have_ssh_host_key(const char *hostname, int port,
+		      const char *keytype)
+{
+    /*
+     * If we have a host key, verify_host_key will return 0 or 2.
+     * If we don't have one, it'll return 1.
+     */
+    return verify_host_key(hostname, port, keytype, "") != 1;
+}
+
 void store_host_key(const char *hostname, int port,
 		    const char *keytype, const char *key)
 {
@@ -684,7 +694,7 @@ static int transform_jumplist_registry
     int ret;
     HKEY pjumplist_key, psettings_tmp;
     DWORD type;
-    int value_length;
+    DWORD value_length;
     char *old_value, *new_value;
     char *piterator_old, *piterator_new, *piterator_tmp;
 

+ 14 - 0
source/putty/windows/winstuff.h

@@ -11,6 +11,18 @@
 #include <windows.h>
 #include <stdio.h>		       /* for FILENAME_MAX */
 
+/* We use uintptr_t for Win32/Win64 portability, so we should in
+ * principle include stdint.h, which defines it according to the C
+ * standard. But older versions of Visual Studio - including the one
+ * used for official PuTTY builds as of 2015-09-28 - don't provide
+ * stdint.h at all, but do (non-standardly) define uintptr_t in
+ * stddef.h. So here we try to make sure _some_ standard header is
+ * included which defines uintptr_t. */
+#include <stddef.h>
+#if !defined _MSC_VER || _MSC_VER >= 1600
+#include <stdint.h>
+#endif
+
 #include "tree234.h"
 
 #ifndef MPEXT
@@ -86,6 +98,8 @@ struct FontSpec *fontspec_new(const char *name,
 #define PLATFORM_HAS_SMEMCLR /* inhibit cross-platform one in misc.c */
 #endif
 
+#define BROKEN_PIPE_ERROR_CODE ERROR_BROKEN_PIPE   /* used in sshshare.c */
+
 /*
  * Dynamically linked functions. These come in two flavours:
  *

+ 9 - 7
source/putty/x11fwd.c

@@ -190,7 +190,7 @@ int x11_authcmp(void *av, void *bv)
     }
 }
 
-struct X11Display *x11_setup_display(char *display, Conf *conf)
+struct X11Display *x11_setup_display(const char *display, Conf *conf)
 {
     struct X11Display *disp = snew(struct X11Display);
     char *localcopy;
@@ -286,7 +286,8 @@ struct X11Display *x11_setup_display(char *display, Conf *conf)
 
 	disp->port = 6000 + disp->displaynum;
 	disp->addr = name_lookup(disp->hostname, disp->port,
-				 &disp->realhost, conf, ADDRTYPE_UNSPEC);
+				 &disp->realhost, conf, ADDRTYPE_UNSPEC,
+                                 NULL, NULL);
     
 	if ((err = sk_addr_error(disp->addr)) != NULL) {
 	    sk_addr_free(disp->addr);
@@ -361,10 +362,10 @@ void x11_free_display(struct X11Display *disp)
 
 #define XDM_MAXSKEW 20*60      /* 20 minute clock skew should be OK */
 
-static char *x11_verify(unsigned long peer_ip, int peer_port,
-			tree234 *authtree, char *proto,
-			unsigned char *data, int dlen,
-                        struct X11FakeAuth **auth_ret)
+static const char *x11_verify(unsigned long peer_ip, int peer_port,
+                              tree234 *authtree, char *proto,
+                              unsigned char *data, int dlen,
+                              struct X11FakeAuth **auth_ret)
 {
     struct X11FakeAuth match_dummy;    /* for passing to find234 */
     struct X11FakeAuth *auth;
@@ -424,7 +425,8 @@ static char *x11_verify(unsigned long peer_ip, int peer_port,
 	    if (data[i] != 0)	       /* zero padding wrong */
 		return "XDM-AUTHORIZATION-1 data failed check";
 	tim = time(NULL);
-	if (abs(t - tim) > XDM_MAXSKEW)
+	if (((unsigned long)t - (unsigned long)tim
+             + XDM_MAXSKEW) > 2*XDM_MAXSKEW)
 	    return "XDM-AUTHORIZATION-1 time stamp was too far out";
 	seen = snew(struct XDMSeen);
 	seen->time = t;

Some files were not shown because too many files changed in this diff