changeset 1683:41bf8f216644

merge rsa-sha256
author Matt Johnston <matt@ucc.asn.au>
date Tue, 26 May 2020 00:24:02 +0800
parents e0871128e61f (current diff) a53e7d2d60be (diff)
children d5d25ce2a2ed
files Makefile.in algo.h common-algo.c common-kex.c process-packet.c session.h sysoptions.h
diffstat 36 files changed, 699 insertions(+), 269 deletions(-) [+]
line wrap: on
line diff
--- a/.travis.yml	Mon May 25 20:55:13 2020 +0500
+++ b/.travis.yml	Tue May 26 00:24:02 2020 +0800
@@ -46,6 +46,7 @@
   - autoheader 
   - ./configure $CONFIGURE_FLAGS CFLAGS="-O2 -Wall -Wno-pointer-sign $WEXTRAFLAGS $EXTRACFLAGS" --prefix="$HOME/inst" || (cat config.log; exit 1)
   - if [ "$NOWRITEV" = "1" ]; then sed -i -e s/HAVE_WRITEV/DONT_HAVE_WRITEV/ config.h ; fi
+  - make lint
   - make -j3 
   - test -z $DO_FUZZ || make fuzzstandalone
   # avoid concurrent install, osx/freebsd is racey (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208093)
--- a/Makefile.in	Mon May 25 20:55:13 2020 +0500
+++ b/Makefile.in	Tue May 26 00:24:02 2020 +0800
@@ -236,7 +236,7 @@
 $(STATIC_LTM): $(OPTION_HEADERS)
 	$(MAKE) -C libtommath
 
-.PHONY : clean sizes thisclean distclean tidy ltc-clean ltm-clean
+.PHONY : clean sizes thisclean distclean tidy ltc-clean ltm-clean lint
 
 ltc-clean:
 	$(MAKE) -C libtomcrypt clean
@@ -262,6 +262,9 @@
 tidy:
 	-rm -f *~ *.gcov */*~
 
+lint:
+	cd $(srcdir); ./dropbear_lint.sh
+
 ## Fuzzing targets
 
 # list of fuzz targets
--- a/agentfwd.h	Mon May 25 20:55:13 2020 +0500
+++ b/agentfwd.h	Tue May 26 00:24:02 2020 +0800
@@ -32,6 +32,9 @@
 
 #if DROPBEAR_CLI_AGENTFWD
 
+/* From OpenSSH authfd.h */
+#define SSH_AGENT_RSA_SHA2_256          0x02
+
 /* An agent reply can be reasonably large, as it can
  * contain a list of all public keys held by the agent.
  * 10000 is arbitrary */
@@ -40,7 +43,7 @@
 /* client functions */
 void cli_load_agent_keys(m_list * ret_list);
 void agent_buf_sign(buffer *sigblob, sign_key *key, 
-	const buffer *data_buf);
+		const buffer *data_buf, enum signature_type type);
 void cli_setup_agent(const struct Channel *channel);
 
 #ifdef __hpux
--- a/algo.h	Mon May 25 20:55:13 2020 +0500
+++ b/algo.h	Tue May 26 00:24:02 2020 +0800
@@ -47,7 +47,7 @@
 
 /* lists mapping ssh types of algorithms to internal values */
 extern algo_type sshkex[];
-extern algo_type sshhostkey[];
+extern algo_type sigalgs[];
 extern algo_type sshciphers[];
 extern algo_type sshhashes[];
 extern algo_type ssh_compress[];
@@ -120,21 +120,17 @@
 	const struct ltc_hash_descriptor *hash_desc;
 };
 
-int have_algo(const char* algo, size_t algolen, const algo_type algos[]);
+/* Includes all algorithms is useall is set */
+void buf_put_algolist_all(buffer * buf, const algo_type localalgos[], int useall);
+/* Includes "usable" algorithms */
 void buf_put_algolist(buffer * buf, const algo_type localalgos[]);
 
-enum kexguess2_used {
-	KEXGUESS2_LOOK,
-	KEXGUESS2_NO,
-	KEXGUESS2_YES,
-};
+#define KEXGUESS2_ALGO_NAME "[email protected]"
 
-#define KEXGUESS2_ALGO_NAME "[email protected]"
-#define KEXGUESS2_ALGO_ID 99
-
-
+int buf_has_algo(buffer *buf, const char *algo);
+algo_type * first_usable_algo(algo_type algos[]);
 algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
-		enum kexguess2_used *kexguess2, int *goodguess);
+		int kexguess2, int *goodguess);
 
 #if DROPBEAR_USER_ALGO_LIST
 int check_user_algos(const char* user_algo_list, algo_type * algos, 
--- a/buffer.c	Mon May 25 20:55:13 2020 +0500
+++ b/buffer.c	Tue May 26 00:24:02 2020 +0800
@@ -228,19 +228,37 @@
 }
 
 /* Return a string as a newly allocated buffer */
-buffer * buf_getstringbuf(buffer *buf) {
+static buffer * buf_getstringbuf_int(buffer *buf, int incllen) {
 	buffer *ret = NULL;
 	unsigned int len = buf_getint(buf);
+	int extra = 0;
 	if (len > MAX_STRING_LEN) {
 		dropbear_exit("String too long");
 	}
-	ret = buf_new(len);
+	if (incllen) {
+		extra = 4;
+	}
+	ret = buf_new(len+extra);
+	if (incllen) {
+		buf_putint(ret, len);
+	}
 	memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len);
 	buf_incrpos(buf, len);
 	buf_incrlen(ret, len);
+	buf_setpos(ret, 0);
 	return ret;
 }
 
+/* Return a string as a newly allocated buffer */
+buffer * buf_getstringbuf(buffer *buf) {
+	return buf_getstringbuf_int(buf, 0);
+}
+
+/* Returns a string in a new buffer, including the length */
+buffer * buf_getbuf(buffer *buf) {
+	return buf_getstringbuf_int(buf, 1);
+}
+
 /* Just increment the buffer position the same as if we'd used buf_getstring,
  * but don't bother copying/malloc()ing for it */
 void buf_eatstring(buffer *buf) {
--- a/buffer.h	Mon May 25 20:55:13 2020 +0500
+++ b/buffer.h	Tue May 26 00:24:02 2020 +0800
@@ -58,6 +58,7 @@
 unsigned char* buf_getwriteptr(const buffer* buf, unsigned int len);
 char* buf_getstring(buffer* buf, unsigned int *retlen);
 buffer * buf_getstringbuf(buffer *buf);
+buffer * buf_getbuf(buffer *buf);
 void buf_eatstring(buffer *buf);
 void buf_putint(buffer* buf, unsigned int val);
 void buf_putstring(buffer* buf, const char* str, unsigned int len);
--- a/cli-agentfwd.c	Mon May 25 20:55:13 2020 +0500
+++ b/cli-agentfwd.c	Tue May 26 00:24:02 2020 +0800
@@ -255,11 +255,12 @@
 }
 
 void agent_buf_sign(buffer *sigblob, sign_key *key, 
-		const buffer *data_buf) {
+		const buffer *data_buf, enum signature_type sigtype) {
 	buffer *request_data = NULL;
 	buffer *response = NULL;
 	unsigned int siglen;
 	int packet_type;
+	int flags = 0;
 	
 	/* Request format
 	byte			SSH2_AGENTC_SIGN_REQUEST
@@ -271,7 +272,12 @@
 	buf_put_pub_key(request_data, key, key->type);
 	
 	buf_putbufstring(request_data, data_buf);
-	buf_putint(request_data, 0);
+#if DROPBEAR_RSA_SHA256
+	if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA256) {
+		flags |= SSH_AGENT_RSA_SHA2_256;
+	}
+#endif
+	buf_putint(request_data, flags);
 	
 	response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);
 	
--- a/cli-authpubkey.c	Mon May 25 20:55:13 2020 +0500
+++ b/cli-authpubkey.c	Tue May 26 00:24:02 2020 +0800
@@ -33,7 +33,7 @@
 #include "agentfwd.h"
 
 #if DROPBEAR_CLI_PUBKEY_AUTH
-static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
+static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign);
 
 /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
  * We use it to remove the key we tried from the list */
@@ -59,13 +59,15 @@
 	char* algotype = NULL;
 	unsigned int algolen;
 	enum signkey_type keytype;
+	enum signature_type sigtype;
 	unsigned int remotelen;
 
 	TRACE(("enter recv_msg_userauth_pk_ok"))
 
 	algotype = buf_getstring(ses.payload, &algolen);
-	keytype = signkey_type_from_name(algotype, algolen);
-	TRACE(("recv_msg_userauth_pk_ok: type %d", keytype))
+	sigtype = signature_type_from_name(algotype, algolen);
+	keytype = signkey_type_from_signature(sigtype);
+	TRACE(("recv_msg_userauth_pk_ok: type %d", sigtype))
 	m_free(algotype);
 
 	keybuf = buf_new(MAX_PUBKEY_SIZE);
@@ -112,7 +114,7 @@
 		TRACE(("matching key"))
 		/* XXX TODO: if it's an encrypted key, here we ask for their
 		 * password */
-		send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1);
+		send_msg_userauth_pubkey((sign_key*)iter->item, sigtype, 1);
 	} else {
 		TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
 	}
@@ -120,31 +122,32 @@
 	TRACE(("leave recv_msg_userauth_pk_ok"))
 }
 
-void cli_buf_put_sign(buffer* buf, sign_key *key, int type, 
+static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype,
 			const buffer *data_buf) {
 #if DROPBEAR_CLI_AGENTFWD
+	// TODO: rsa-sha256 agent
 	if (key->source == SIGNKEY_SOURCE_AGENT) {
 		/* Format the agent signature ourselves, as buf_put_sign would. */
 		buffer *sigblob;
 		sigblob = buf_new(MAX_PUBKEY_SIZE);
-		agent_buf_sign(sigblob, key, data_buf);
+		agent_buf_sign(sigblob, key, data_buf, sigtype);
 		buf_putbufstring(buf, sigblob);
 		buf_free(sigblob);
 	} else 
 #endif /* DROPBEAR_CLI_AGENTFWD */
 	{
-		buf_put_sign(buf, key, type, data_buf);
+		buf_put_sign(buf, key, sigtype, data_buf);
 	}
 }
 
-/* TODO: make it take an agent reference to use as well */
-static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
+static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign) {
 
 	const char *algoname = NULL;
 	unsigned int algolen;
 	buffer* sigbuf = NULL;
+	enum signkey_type keytype = signkey_type_from_signature(sigtype);
 
-	TRACE(("enter send_msg_userauth_pubkey"))
+	TRACE(("enter send_msg_userauth_pubkey sigtype %d", sigtype))
 	CHECKCLEARTOWRITE();
 
 	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
@@ -160,10 +163,9 @@
 
 	buf_putbyte(ses.writepayload, realsign);
 
-	algoname = signkey_name_from_type(type, &algolen);
-
+	algoname = signature_name_from_type(sigtype, &algolen);
 	buf_putstring(ses.writepayload, algoname, algolen);
-	buf_put_pub_key(ses.writepayload, key, type);
+	buf_put_pub_key(ses.writepayload, key, keytype);
 
 	if (realsign) {
 		TRACE(("realsign"))
@@ -172,7 +174,7 @@
 		sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len);
 		buf_putbufstring(sigbuf, ses.session_id);
 		buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
-		cli_buf_put_sign(ses.writepayload, key, type, sigbuf);
+		cli_buf_put_sign(ses.writepayload, key, sigtype, sigbuf);
 		buf_free(sigbuf); /* Nothing confidential in the buffer */
 	}
 
@@ -182,7 +184,7 @@
 
 /* Returns 1 if a key was tried */
 int cli_auth_pubkey() {
-
+	enum signature_type sigtype;
 	TRACE(("enter cli_auth_pubkey"))
 
 #if DROPBEAR_CLI_AGENTFWD
@@ -190,13 +192,79 @@
 		/* get the list of available keys from the agent */
 		cli_load_agent_keys(cli_opts.privkeys);
 		cli_opts.agent_keys_loaded = 1;
+		TRACE(("cli_auth_pubkey: agent keys loaded"))
 	}
 #endif
 
+	/* iterate through privkeys to remove ones not allowed in server-sig-algs */
+ 	while (cli_opts.privkeys->first) {
+		sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
+		if (cli_ses.server_sig_algs) {
+#if DROPBEAR_RSA
+			if (key->type == DROPBEAR_SIGNKEY_RSA) {
+#if DROPBEAR_RSA_SHA256
+				if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNATURE_RSA_SHA256) 
+						== DROPBEAR_SUCCESS) {
+					sigtype = DROPBEAR_SIGNATURE_RSA_SHA256;
+					TRACE(("server-sig-algs allows rsa sha256"))
+					break;
+				}
+#endif /* DROPBEAR_RSA_SHA256 */
+#if DROPBEAR_RSA_SHA1
+				if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNKEY_RSA)
+						== DROPBEAR_SUCCESS) {
+					sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
+					TRACE(("server-sig-algs allows rsa sha1"))
+					break;
+				}
+#endif /* DROPBEAR_RSA_SHA256 */
+			} else
+#endif /* DROPBEAR_RSA */
+			{
+				/* Not RSA */
+				const char *name = NULL;
+				sigtype = signature_type_from_signkey(key->type);
+				name = signature_name_from_type(sigtype, NULL);
+				if (buf_has_algo(cli_ses.server_sig_algs, name)
+						== DROPBEAR_SUCCESS) {
+					TRACE(("server-sig-algs allows %s", name))
+					break;
+				}
+			}
+
+			/* No match, skip this key */
+			TRACE(("server-sig-algs no match keytype %d, skipping", key->type))
+			key = list_remove(cli_opts.privkeys->first);
+			sign_key_free(key); 
+			continue;
+		} else {
+			/* Server didn't provide a server-sig-algs list, we'll 
+			   assume all except rsa-sha256 are OK. */
+#if DROPBEAR_RSA
+			if (key->type == DROPBEAR_SIGNKEY_RSA) {
+#if DROPBEAR_RSA_SHA1
+				sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
+				TRACE(("no server-sig-algs, using rsa sha1"))
+				break;
+#else
+				/* only support rsa-sha256, skip this key */
+				TRACE(("no server-sig-algs, skipping rsa sha256"))
+				key = list_remove(cli_opts.privkeys->first);
+				sign_key_free(key); 
+				continue;
+#endif
+			} /* key->type == DROPBEAR_SIGNKEY_RSA */
+#endif /* DROPBEAR_RSA */
+			sigtype = signature_type_from_signkey(key->type);
+			TRACE(("no server-sig-algs, using key"))
+			break;
+		}
+	}
+
 	if (cli_opts.privkeys->first) {
 		sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
 		/* Send a trial request */
-		send_msg_userauth_pubkey(key, key->type, 0);
+		send_msg_userauth_pubkey(key, sigtype, 0);
 		cli_ses.lastprivkey = key;
 		TRACE(("leave cli_auth_pubkey-success"))
 		return 1;
--- a/cli-kex.c	Mon May 25 20:55:13 2020 +0500
+++ b/cli-kex.c	Tue May 26 00:24:02 2020 +0800
@@ -94,7 +94,7 @@
 void recv_msg_kexdh_reply() {
 
 	sign_key *hostkey = NULL;
-	unsigned int type, keybloblen;
+	unsigned int keytype, keybloblen;
 	unsigned char* keyblob = NULL;
 
 	TRACE(("enter recv_msg_kexdh_reply"))
@@ -102,8 +102,8 @@
 	if (cli_ses.kex_state != KEXDH_INIT_SENT) {
 		dropbear_exit("Received out-of-order kexdhreply");
 	}
-	type = ses.newkeys->algo_hostkey;
-	TRACE(("type is %d", type))
+	keytype = ses.newkeys->algo_hostkey;
+	TRACE(("keytype is %d", keytype))
 
 	hostkey = new_sign_key();
 	keybloblen = buf_getint(ses.payload);
@@ -114,7 +114,7 @@
 		checkhostkey(keyblob, keybloblen);
 	}
 
-	if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
+	if (buf_get_pub_key(ses.payload, hostkey, &keytype) != DROPBEAR_SUCCESS) {
 		TRACE(("failed getting pubkey"))
 		dropbear_exit("Bad KEX packet");
 	}
@@ -173,7 +173,8 @@
 #endif
 
 	cli_ses.param_kex_algo = NULL;
-	if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) {
+	if (buf_verify(ses.payload, hostkey, ses.newkeys->algo_signature, 
+			ses.hash) != DROPBEAR_SUCCESS) {
 		dropbear_exit("Bad hostkey signature");
 	}
 
@@ -410,3 +411,38 @@
 	}
 	m_free(fingerprint);
 }
+
+void recv_msg_ext_info(void) {
+	/* This message is not client-specific in the protocol but Dropbear only handles
+	a server-sent message at present. */
+	unsigned int num_ext;
+	unsigned int i;
+
+	TRACE(("enter recv_msg_ext_info"))
+
+	/* Must be after the first SSH_MSG_NEWKEYS */
+	TRACE(("last %d, donefirst %d, donescond %d", ses.lastpacket, ses.kexstate.donefirstkex, ses.kexstate.donesecondkex))
+	if (!(ses.lastpacket == SSH_MSG_NEWKEYS && !ses.kexstate.donesecondkex)) {
+		TRACE(("leave recv_msg_ext_info: ignoring packet received at the wrong time"))
+		return;
+	}
+
+	num_ext = buf_getint(ses.payload);
+	TRACE(("received SSH_MSG_EXT_INFO with %d items", num_ext))
+
+	for (i = 0; i < num_ext; i++) {
+		unsigned int name_len;
+		char *ext_name = buf_getstring(ses.payload, &name_len);
+		TRACE(("extension %d name '%s'", i, ext_name))
+		if (cli_ses.server_sig_algs == NULL
+				&& name_len == strlen(SSH_SERVER_SIG_ALGS)
+				&& strcmp(ext_name, SSH_SERVER_SIG_ALGS) == 0) {
+			cli_ses.server_sig_algs = buf_getbuf(ses.payload);
+		} else {
+			/* valid extension values could be >MAX_STRING_LEN */
+			buf_eatstring(ses.payload);
+		}
+		m_free(ext_name);
+	}
+	TRACE(("leave recv_msg_ext_info"))
+}
--- a/cli-main.c	Mon May 25 20:55:13 2020 +0500
+++ b/cli-main.c	Tue May 26 00:24:02 2020 +0800
@@ -106,6 +106,7 @@
 
 	/* Render the formatted exit message */
 	vsnprintf(exitmsg, sizeof(exitmsg), format, param);
+	TRACE(("Exited, cleaning up: %s", exitmsg))
 
 	/* Add the prefix depending on session/auth state */
 	if (!ses.init_done) {
--- a/cli-session.c	Mon May 25 20:55:13 2020 +0500
+++ b/cli-session.c	Tue May 26 00:24:02 2020 +0800
@@ -81,6 +81,7 @@
 	{SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
 	{SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
 #endif
+	{SSH_MSG_EXT_INFO, recv_msg_ext_info},
 	{0, NULL} /* End */
 };
 
@@ -352,7 +353,9 @@
 	(void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
 
 	cli_tty_cleanup();
-
+	if (cli_ses.server_sig_algs) {
+		buf_free(cli_ses.server_sig_algs);
+	}
 }
 
 static void cli_finished() {
--- a/common-algo.c	Mon May 25 20:55:13 2020 +0500
+++ b/common-algo.c	Tue May 26 00:24:02 2020 +0800
@@ -32,6 +32,7 @@
 #include "ecc.h"
 #include "gcm.h"
 #include "chachapoly.h"
+#include "ssh.h"
 
 /* This file (algo.c) organises the ciphers which can be used, and is used to
  * decide which ciphers/hashes/compression/signing to use during key exchange*/
@@ -242,26 +243,31 @@
 	{NULL, 0, NULL, 0, NULL}
 };
 
-algo_type sshhostkey[] = {
+algo_type sigalgs[] = {
 #if DROPBEAR_ED25519
-	{"ssh-ed25519", DROPBEAR_SIGNKEY_ED25519, NULL, 1, NULL},
+	{"ssh-ed25519", DROPBEAR_SIGNATURE_ED25519, NULL, 1, NULL},
 #endif
 #if DROPBEAR_ECDSA
 #if DROPBEAR_ECC_256
-	{"ecdsa-sha2-nistp256", DROPBEAR_SIGNKEY_ECDSA_NISTP256, NULL, 1, NULL},
+	{"ecdsa-sha2-nistp256", DROPBEAR_SIGNATURE_ECDSA_NISTP256, NULL, 1, NULL},
 #endif
 #if DROPBEAR_ECC_384
-	{"ecdsa-sha2-nistp384", DROPBEAR_SIGNKEY_ECDSA_NISTP384, NULL, 1, NULL},
+	{"ecdsa-sha2-nistp384", DROPBEAR_SIGNATURE_ECDSA_NISTP384, NULL, 1, NULL},
 #endif
 #if DROPBEAR_ECC_521
-	{"ecdsa-sha2-nistp521", DROPBEAR_SIGNKEY_ECDSA_NISTP521, NULL, 1, NULL},
+	{"ecdsa-sha2-nistp521", DROPBEAR_SIGNATURE_ECDSA_NISTP521, NULL, 1, NULL},
 #endif
 #endif
 #if DROPBEAR_RSA
-	{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL},
+#if DROPBEAR_RSA_SHA256
+	{"rsa-sha2-256", DROPBEAR_SIGNATURE_RSA_SHA256, NULL, 1, NULL},
+#endif
+#if DROPBEAR_RSA_SHA1
+	{"ssh-rsa", DROPBEAR_SIGNATURE_RSA_SHA1, NULL, 1, NULL},
+#endif
 #endif
 #if DROPBEAR_DSS
-	{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL},
+	{"ssh-dss", DROPBEAR_SIGNATURE_DSS, NULL, 1, NULL},
 #endif
 	{NULL, 0, NULL, 0, NULL}
 };
@@ -279,8 +285,6 @@
 static const struct dropbear_kex kex_dh_group16_sha512 = {DROPBEAR_KEX_NORMAL_DH, dh_p_16, DH_P_16_LEN, NULL, &sha512_desc };
 #endif
 
-/* These can't be const since dropbear_ecc_fill_dp() fills out
- ecc_curve at runtime */
 #if DROPBEAR_ECDH
 #if DROPBEAR_ECC_256
 static const struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc };
@@ -298,6 +302,7 @@
 static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc };
 #endif
 
+/* data == NULL for non-kex algorithm identifiers */
 algo_type sshkex[] = {
 #if DROPBEAR_CURVE25519
 	{"curve25519-sha256", 0, &kex_curve25519, 1, NULL},
@@ -327,49 +332,122 @@
 	{"diffie-hellman-group16-sha512", 0, &kex_dh_group16_sha512, 1, NULL},
 #endif
 #if DROPBEAR_KEXGUESS2
-	{KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL},
+	{KEXGUESS2_ALGO_NAME, 0, NULL, 1, NULL},
+#endif
+#if DROPBEAR_EXT_INFO
+#if DROPBEAR_CLIENT
+	/* Set unusable by svr_algos_initialise() */
+	{SSH_EXT_INFO_C, 0, NULL, 1, NULL},
+#endif
 #endif
 	{NULL, 0, NULL, 0, NULL}
 };
 
-/* algolen specifies the length of algo, algos is our local list to match
- * against.
- * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE
- * otherwise */
-int have_algo(const char* algo, size_t algolen, const algo_type algos[]) {
+/* Output a comma separated list of algorithms to a buffer */
+void buf_put_algolist_all(buffer * buf, const algo_type localalgos[], int useall) {
+	unsigned int i, len;
+	unsigned int donefirst = 0;
+	unsigned int startpos;
 
-	int i;
-
-	for (i = 0; algos[i].name != NULL; i++) {
-		if (strlen(algos[i].name) == algolen
-				&& (strncmp(algos[i].name, algo, algolen) == 0)) {
-			return DROPBEAR_SUCCESS;
+	startpos = buf->pos;
+	/* Placeholder for length */
+	buf_putint(buf, 0); 
+	for (i = 0; localalgos[i].name != NULL; i++) {
+		if (localalgos[i].usable || useall) {
+			if (donefirst) {
+				buf_putbyte(buf, ',');
+			}
+			donefirst = 1;
+			len = strlen(localalgos[i].name);
+			buf_putbytes(buf, (const unsigned char *) localalgos[i].name, len);
 		}
 	}
+	/* Fill out the length */
+	len = buf->pos - startpos - 4;
+	buf_setpos(buf, startpos);
+	buf_putint(buf, len);
+	TRACE(("algolist add %d '%*s'", len, len, buf_getptr(buf, len)))
+	buf_incrwritepos(buf, len);
+}
 
-	return DROPBEAR_FAILURE;
+void buf_put_algolist(buffer * buf, const algo_type localalgos[]) {
+	buf_put_algolist_all(buf, localalgos, 0);
 }
 
-/* Output a comma separated list of algorithms to a buffer */
-void buf_put_algolist(buffer * buf, const algo_type localalgos[]) {
+/* returns a list of pointers into algolist, of null-terminated names.
+   ret_list should be passed in with space for *ret_count elements,
+   on return *ret_count has the number of names filled.
+   algolist is modified. */
+static void get_algolist(char* algolist, unsigned int algolist_len,
+				const char* *ret_list, unsigned int *ret_count) {
+	unsigned int max_count = *ret_count;
+	unsigned int i;
 
-	unsigned int i, len;
-	unsigned int donefirst = 0;
-	buffer *algolist = NULL;
+	if (*ret_count == 0) {
+		return;
+	}
+	if (algolist_len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
+		*ret_count = 0;
+		}
 
-	algolist = buf_new(300);
-	for (i = 0; localalgos[i].name != NULL; i++) {
-		if (localalgos[i].usable) {
-			if (donefirst)
-				buf_putbyte(algolist, ',');
-			donefirst = 1;
-			len = strlen(localalgos[i].name);
-			buf_putbytes(algolist, (const unsigned char *) localalgos[i].name, len);
+	/* ret_list will contain a list of the strings parsed out.
+	   We will have at least one string (even if it's just "") */
+	ret_list[0] = algolist;
+	*ret_count = 1;
+	for (i = 0; i < algolist_len; i++) {
+		if (algolist[i] == '\0') {
+			/* someone is trying something strange */
+			*ret_count = 0;
+			return;
+	}
+
+		if (algolist[i] == ',') {
+			if (*ret_count >= max_count) {
+				/* Too many */
+				*ret_count = 0;
+				return;
+			}
+			algolist[i] = '\0';
+			ret_list[*ret_count] = &algolist[i+1];
+			(*ret_count)++;
 		}
 	}
-	buf_putstring(buf, (const char*)algolist->data, algolist->len);
-	TRACE(("algolist add '%*s'", algolist->len, algolist->data))
-	buf_free(algolist);
+}
+
+/* Return DROPBEAR_SUCCESS if the namelist contains algo,
+DROPBEAR_FAILURE otherwise. buf position is not incremented. */
+int buf_has_algo(buffer *buf, const char *algo) {
+	unsigned char* algolist = NULL;
+	unsigned int orig_pos = buf->pos;
+	unsigned int len, remotecount, i;
+	const char *remotenames[MAX_PROPOSED_ALGO];
+	int ret = DROPBEAR_FAILURE;
+
+	algolist = buf_getstring(buf, &len);
+	remotecount = MAX_PROPOSED_ALGO;
+	get_algolist(algolist, len, remotenames, &remotecount);
+	for (i = 0; i < remotecount; i++)
+	{
+		if (strcmp(remotenames[i], algo) == 0) {
+			ret = DROPBEAR_SUCCESS;
+			break;
+		}
+	}
+	if (algolist) {
+		m_free(algolist);
+	}
+	buf_setpos(buf, orig_pos);
+	return ret;
+}
+
+algo_type * first_usable_algo(algo_type algos[]) {
+	int i;
+	for (i = 0; algos[i].name != NULL; i++) {
+		if (algos[i].usable) {
+			return &algos[i];
+		}
+	}
+	return NULL;
 }
 
 /* match the first algorithm in the comma-separated list in buf which is
@@ -378,9 +456,7 @@
  * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
  * guessed correctly */
 algo_type * buf_match_algo(buffer* buf, algo_type localalgos[],
-		enum kexguess2_used *kexguess2, int *goodguess)
-{
-
+		int kexguess2, int *goodguess) {
 	char * algolist = NULL;
 	const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO];
 	unsigned int len;
@@ -395,40 +471,8 @@
 	/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
 	algolist = buf_getstring(buf, &len);
 	TRACE(("buf_match_algo: %s", algolist))
-	if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
-		goto out;
-	}
-
-	/* remotenames will contain a list of the strings parsed out */
-	/* We will have at least one string (even if it's just "") */
-	remotenames[0] = algolist;
-	remotecount = 1;
-	for (i = 0; i < len; i++) {
-		if (algolist[i] == '\0') {
-			/* someone is trying something strange */
-			goto out;
-		}
-		if (algolist[i] == ',') {
-			algolist[i] = '\0';
-			remotenames[remotecount] = &algolist[i+1];
-			remotecount++;
-		}
-		if (remotecount >= MAX_PROPOSED_ALGO) {
-			break;
-		}
-	}
-	if (kexguess2 && *kexguess2 == KEXGUESS2_LOOK) {
-		for (i = 0; i < remotecount; i++)
-		{
-			if (strcmp(remotenames[i], KEXGUESS2_ALGO_NAME) == 0) {
-				*kexguess2 = KEXGUESS2_YES;
-				break;
-			}
-		}
-		if (*kexguess2 == KEXGUESS2_LOOK) {
-			*kexguess2 = KEXGUESS2_NO;
-		}
-	}
+	remotecount = MAX_PROPOSED_ALGO;
+	get_algolist(algolist, len, remotenames, &remotecount);
 
 	for (i = 0; localalgos[i].name != NULL; i++) {
 		if (localalgos[i].usable) {
@@ -460,12 +504,11 @@
 			}
 			if (strcmp(servnames[j], clinames[i]) == 0) {
 				/* set if it was a good guess */
-				if (goodguess && kexguess2) {
-					if (*kexguess2 == KEXGUESS2_YES) {
+				if (goodguess != NULL) {
+					if (kexguess2) {
 						if (i == 0) {
 							*goodguess = 1;
 						}
-
 					} else {
 						if (i == 0 && j == 0) {
 							*goodguess = 1;
--- a/common-kex.c	Mon May 25 20:55:13 2020 +0500
+++ b/common-kex.c	Tue May 26 00:24:02 2020 +0800
@@ -65,7 +65,7 @@
 	buf_put_algolist(ses.writepayload, sshkex);
 
 	/* server_host_key_algorithms */
-	buf_put_algolist(ses.writepayload, sshhostkey);
+	buf_put_algolist(ses.writepayload, sigalgs);
 
 	/* encryption_algorithms_client_to_server */
 	buf_put_algolist(ses.writepayload, sshciphers);
@@ -110,8 +110,9 @@
 	ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
 
 	if (ses.send_kex_first_guess) {
-		ses.newkeys->algo_kex = sshkex[0].data;
-		ses.newkeys->algo_hostkey = sshhostkey[0].val;
+		ses.newkeys->algo_kex = first_usable_algo(sshkex)->data;
+		ses.newkeys->algo_signature = first_usable_algo(sigalgs)->val;
+		ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature);
 		ses.send_kex_first_guess();
 	}
 
@@ -152,6 +153,7 @@
 		TRACE(("switch_keys done"))
 		ses.keys->algo_kex = ses.newkeys->algo_kex;
 		ses.keys->algo_hostkey = ses.newkeys->algo_hostkey;
+		ses.keys->algo_signature = ses.newkeys->algo_signature;
 		ses.keys->allow_compress = 0;
 		m_free(ses.newkeys);
 		ses.newkeys = NULL;
@@ -173,6 +175,9 @@
 	
 	/* set up our state */
 	ses.kexstate.sentnewkeys = 1;
+	if (ses.kexstate.donefirstkex) {
+		ses.kexstate.donesecondkex = 1;
+	}
 	ses.kexstate.donefirstkex = 1;
 	ses.dataallowed = 1; /* we can send other packets again now */
 	gen_new_keys();
@@ -195,8 +200,6 @@
 
 /* Set up the kex for the first time */
 void kexfirstinitialise() {
-	ses.kexstate.donefirstkex = 0;
-
 #ifdef DISABLE_ZLIB
 	ses.compress_algos = ssh_nocompress;
 #else
@@ -332,8 +335,8 @@
 		int recv_cipher = -1;
 		if (ses.newkeys->recv.algo_crypt->cipherdesc->name != NULL) {
 			recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
-			if (recv_cipher < 0)
-				dropbear_exit("Crypto error");
+		if (recv_cipher < 0)
+			dropbear_exit("Crypto error");
 		}
 		if (ses.newkeys->recv.crypt_mode->start(recv_cipher, 
 				recv_IV, recv_key, 
@@ -347,8 +350,8 @@
 		int trans_cipher = -1;
 		if (ses.newkeys->trans.algo_crypt->cipherdesc->name != NULL) {
 			trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
-			if (trans_cipher < 0)
-				dropbear_exit("Crypto error");
+		if (trans_cipher < 0)
+			dropbear_exit("Crypto error");
 		}
 		if (ses.newkeys->trans.crypt_mode->start(trans_cipher, 
 				trans_IV, trans_key, 
@@ -824,21 +827,36 @@
 	int goodguess = 0;
 	int allgood = 1; /* we AND this with each goodguess and see if its still
 						true after */
-
-#if DROPBEAR_KEXGUESS2
-	enum kexguess2_used kexguess2 = KEXGUESS2_LOOK;
-#else
-	enum kexguess2_used kexguess2 = KEXGUESS2_NO;
-#endif
+	int kexguess2 = 0;
 
 	buf_incrpos(ses.payload, 16); /* start after the cookie */
 
 	memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
 
 	/* kex_algorithms */
-	algo = buf_match_algo(ses.payload, sshkex, &kexguess2, &goodguess);
+#if DROPBEAR_KEXGUESS2
+	if (buf_has_algo(ses.payload, KEXGUESS2_ALGO_NAME) == DROPBEAR_SUCCESS) {
+		kexguess2 = 1;
+	}
+#endif
+
+#if DROPBEAR_EXT_INFO
+	/* Determine if SSH_MSG_EXT_INFO messages should be sent.
+	Should be done for the first key exchange. Only required on server side
+    for server-sig-algs */
+	if (IS_DROPBEAR_SERVER) {
+		if (!ses.kexstate.donefirstkex) {
+			if (buf_has_algo(ses.payload, SSH_EXT_INFO_C) == DROPBEAR_SUCCESS) {
+				ses.allow_ext_info = 1;
+			}
+		}
+	}
+#endif
+
+	algo = buf_match_algo(ses.payload, sshkex, kexguess2, &goodguess);
 	allgood &= goodguess;
-	if (algo == NULL || algo->val == KEXGUESS2_ALGO_ID) {
+	if (algo == NULL || algo->data == NULL) {
+		/* kexguess2, ext-info-c, ext-info-s should not match negotiation */
 		erralgo = "kex";
 		goto error;
 	}
@@ -847,17 +865,18 @@
 	ses.newkeys->algo_kex = algo->data;
 
 	/* server_host_key_algorithms */
-	algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess);
+	algo = buf_match_algo(ses.payload, sigalgs, kexguess2, &goodguess);
 	allgood &= goodguess;
 	if (algo == NULL) {
 		erralgo = "hostkey";
 		goto error;
 	}
-	TRACE(("hostkey algo %s", algo->name))
-	ses.newkeys->algo_hostkey = algo->val;
+	TRACE(("signature algo %s", algo->name))
+	ses.newkeys->algo_signature = algo->val;
+	ses.newkeys->algo_hostkey = signkey_type_from_signature(ses.newkeys->algo_signature);
 
 	/* encryption_algorithms_client_to_server */
-	c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL);
+	c2s_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL);
 	if (c2s_cipher_algo == NULL) {
 		erralgo = "enc c->s";
 		goto error;
@@ -865,7 +884,7 @@
 	TRACE(("enc c2s is  %s", c2s_cipher_algo->name))
 
 	/* encryption_algorithms_server_to_client */
-	s2c_cipher_algo = buf_match_algo(ses.payload, sshciphers, NULL, NULL);
+	s2c_cipher_algo = buf_match_algo(ses.payload, sshciphers, 0, NULL);
 	if (s2c_cipher_algo == NULL) {
 		erralgo = "enc s->c";
 		goto error;
@@ -873,7 +892,7 @@
 	TRACE(("enc s2c is  %s", s2c_cipher_algo->name))
 
 	/* mac_algorithms_client_to_server */
-	c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL);
+	c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, NULL);
 #if DROPBEAR_AEAD_MODE
 	if (((struct dropbear_cipher_mode*)c2s_cipher_algo->mode)->aead_crypt != NULL) {
 		c2s_hash_algo = NULL;
@@ -886,7 +905,7 @@
 	TRACE(("hash c2s is  %s", c2s_hash_algo ? c2s_hash_algo->name : "<implicit>"))
 
 	/* mac_algorithms_server_to_client */
-	s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL);
+	s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, NULL);
 #if DROPBEAR_AEAD_MODE
 	if (((struct dropbear_cipher_mode*)s2c_cipher_algo->mode)->aead_crypt != NULL) {
 		s2c_hash_algo = NULL;
@@ -899,7 +918,7 @@
 	TRACE(("hash s2c is  %s", s2c_hash_algo ? s2c_hash_algo->name : "<implicit>"))
 
 	/* compression_algorithms_client_to_server */
-	c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL);
+	c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, 0, NULL);
 	if (c2s_comp_algo == NULL) {
 		erralgo = "comp c->s";
 		goto error;
@@ -907,7 +926,7 @@
 	TRACE(("hash c2s is  %s", c2s_comp_algo->name))
 
 	/* compression_algorithms_server_to_client */
-	s2c_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL);
+	s2c_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, 0, NULL);
 	if (s2c_comp_algo == NULL) {
 		erralgo = "comp s->c";
 		goto error;
--- a/debug.h	Mon May 25 20:55:13 2020 +0500
+++ b/debug.h	Tue May 26 00:24:02 2020 +0800
@@ -66,7 +66,9 @@
 
 /* To debug with GDB it is easier to run with no forking of child processes.
    You will need to pass "-F" as well. */
-/* #define DEBUG_NOFORK */
+#ifndef DEBUG_NOFORK
+#define DEBUG_NOFORK 0
+#endif
 
 
 /* For testing as non-root on shadowed systems, include the crypt of a password
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dropbear_lint.sh	Tue May 26 00:24:02 2020 +0800
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+EXITCODE=0
+
+# #ifdef instead of #if
+grep '#ifdef DROPBEAR' -I *.c *.h && EXITCODE=1
+
+exit $EXITCODE
--- a/dropbearkey.c	Mon May 25 20:55:13 2020 +0500
+++ b/dropbearkey.c	Tue May 26 00:24:02 2020 +0800
@@ -133,7 +133,7 @@
 			}
 			break;
 #endif
-#ifdef DROPEAR_DSS
+#if DROPEAR_DSS
 		case DROPBEAR_SIGNKEY_DSS:
 			if (bits != 1024) {
 				dropbear_exit("DSS keys have a fixed size of 1024 bits\n");
--- a/dss.h	Mon May 25 20:55:13 2020 +0500
+++ b/dss.h	Tue May 26 00:24:02 2020 +0800
@@ -30,7 +30,7 @@
 
 #if DROPBEAR_DSS 
 
-typedef struct {
+typedef struct dropbear_DSS_Key {
 
 	mp_int* p;
 	mp_int* q;
--- a/ed25519.h	Mon May 25 20:55:13 2020 +0500
+++ b/ed25519.h	Tue May 26 00:24:02 2020 +0800
@@ -32,7 +32,7 @@
 
 #define CURVE25519_LEN 32
 
-typedef struct {
+typedef struct dropbear_ED25519_Key {
 
 	unsigned char priv[CURVE25519_LEN];
 	unsigned char pub[CURVE25519_LEN];
--- a/fuzzer-pubkey.c	Mon May 25 20:55:13 2020 +0500
+++ b/fuzzer-pubkey.c	Tue May 26 00:24:02 2020 +0800
@@ -27,7 +27,7 @@
 		unsigned int algolen;
 		char* algoname = buf_getstring(keyblob, &algolen);
 
-		if (have_algo(algoname, algolen, sshhostkey) == DROPBEAR_FAILURE) {
+		if (signature_type_from_name(algoname, algolen) == DROPBEAR_SIGNKEY_NONE) {
 			dropbear_exit("fuzzer imagined a bogus algorithm");
 		}
 
--- a/fuzzer-verify.c	Mon May 25 20:55:13 2020 +0500
+++ b/fuzzer-verify.c	Tue May 26 00:24:02 2020 +0800
@@ -27,15 +27,27 @@
 
 	if (setjmp(fuzz.jmp) == 0) {
 		sign_key *key = new_sign_key();
-		enum signkey_type type = DROPBEAR_SIGNKEY_ANY;
-		if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) {
-			if (buf_verify(fuzz.input, key, verifydata) == DROPBEAR_SUCCESS) {
+		enum signkey_type keytype = DROPBEAR_SIGNKEY_ANY;
+		if (buf_get_pub_key(fuzz.input, key, &keytype) == DROPBEAR_SUCCESS) {
+			enum signature_type sigtype;
+			if (keytype == DROPBEAR_SIGNKEY_RSA) {
+				/* Flip a coin to decide rsa signature type */
+				int flag = buf_getbyte(fuzz_input);
+				if (flag & 0x01) {
+					sigtype = DROPBEAR_SIGNATURE_RSA_SHA256;
+				} else {
+					sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
+				}
+			} else {
+				sigtype = signature_type_from_signkey(keytype);
+			}
+			if (buf_verify(fuzz.input, key, sigtype, verifydata) == DROPBEAR_SUCCESS) {
 				/* The fuzzer is capable of generating keys with a signature to match.
 				We don't want false positives if the key is bogus, since a client/server 
 				wouldn't be trusting a bogus key anyway */
 				int boguskey = 0;
 
-				if (type == DROPBEAR_SIGNKEY_DSS) {
+				if (keytype == DROPBEAR_SIGNKEY_DSS) {
 					/* So far have seen dss keys with bad p/q/g domain parameters */
 					int pprime, qprime, trials;
 					trials = mp_prime_rabin_miller_trials(mp_count_bits(key->dsskey->p));
--- a/kex.h	Mon May 25 20:55:13 2020 +0500
+++ b/kex.h	Tue May 26 00:24:02 2020 +0800
@@ -65,6 +65,8 @@
 void send_msg_kexdh_init(void); /* client */
 void recv_msg_kexdh_reply(void); /* client */
 
+void recv_msg_ext_info(void);
+
 struct KEXState {
 
 	unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
@@ -73,8 +75,9 @@
 	unsigned sentnewkeys : 1; /* set once we've send MSG_NEWKEYS (will be cleared once we have also received */
 	unsigned recvnewkeys : 1; /* set once we've received MSG_NEWKEYS (cleared once we have also sent */
 
-	unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
+	unsigned int donefirstkex; /* Set to 1 after the first kex has completed,
 								  ie the transport layer has been set up */
+	unsigned int donesecondkex; /* Set to 1 after the second kex has completed */
 
 	unsigned our_first_follows_matches : 1;
 
--- a/keyimport.c	Mon May 25 20:55:13 2020 +0500
+++ b/keyimport.c	Tue May 26 00:24:02 2020 +0800
@@ -36,6 +36,9 @@
 #include "dbutil.h"
 #include "ecc.h"
 #include "ssh.h"
+#include "rsa.h"
+#include "dss.h"
+#include "ed25519.h"
 
 static const unsigned char OSSH_PKEY_BLOB[] =
 	"openssh-key-v1\0"			/* AUTH_MAGIC */
--- a/process-packet.c	Mon May 25 20:55:13 2020 +0500
+++ b/process-packet.c	Tue May 26 00:24:02 2020 +0800
@@ -51,8 +51,6 @@
 	type = buf_getbyte(ses.payload);
 	TRACE(("process_packet: packet type = %d,  len %d", type, ses.payload->len))
 
-	ses.lastpacket = type;
-
 	now = monotonic_now();
 	ses.last_packet_time_keepalive_recv = now;
 
@@ -156,6 +154,7 @@
 	recv_unimplemented();
 
 out:
+	ses.lastpacket = type;
 	buf_free(ses.payload);
 	ses.payload = NULL;
 
--- a/release.sh	Mon May 25 20:55:13 2020 +0500
+++ b/release.sh	Tue May 26 00:24:02 2020 +0800
@@ -30,6 +30,7 @@
 hg archive "$RELDIR"  || exit 2
 
 (cd "$RELDIR" && autoconf && autoheader) || exit 2
+(cd "$RELDIR" && make lint) || exit 2
 
 rm -r "$RELDIR/autom4te.cache" || exit 2
 
--- a/rsa.c	Mon May 25 20:55:13 2020 +0500
+++ b/rsa.c	Tue May 26 00:24:02 2020 +0800
@@ -35,11 +35,16 @@
 #include "buffer.h"
 #include "ssh.h"
 #include "dbrandom.h"
+#include "signkey.h"
 
 #if DROPBEAR_RSA 
 
+#if !(DROPBEAR_RSA_SHA1 || DROPBEAR_RSA_SHA256)
+#error Somehow RSA was enabled with neither DROPBEAR_RSA_SHA1 nor DROPBEAR_RSA_SHA256
+#endif
+
 static void rsa_pad_em(const dropbear_rsa_key * key,
-	const buffer *data_buf, mp_int * rsa_em);
+	const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype);
 
 /* Load a public rsa key from a buffer, initialising the values.
  * The key will have the same format as buf_put_rsa_key.
@@ -191,7 +196,8 @@
 #if DROPBEAR_SIGNKEY_VERIFY
 /* Verify a signature in buf, made on data by the key given.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data_buf) {
+int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, 
+		enum signature_type sigtype, const buffer *data_buf) {
 	unsigned int slen;
 	DEF_MP_INT(rsa_s);
 	DEF_MP_INT(rsa_mdash);
@@ -223,7 +229,7 @@
 	}
 
 	/* create the magic PKCS padded value */
-	rsa_pad_em(key, data_buf, &rsa_em);
+	rsa_pad_em(key, data_buf, &rsa_em, sigtype);
 
 	if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) {
 		TRACE(("failed exptmod rsa_s"))
@@ -246,8 +252,10 @@
 
 /* Sign the data presented with key, writing the signature contents
  * to the buffer */
-void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf) {
-	unsigned int nsize, ssize;
+void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, 
+		enum signature_type sigtype, const buffer *data_buf) {
+	const char *name = NULL;
+	unsigned int nsize, ssize, namelen = 0;
 	unsigned int i;
 	DEF_MP_INT(rsa_s);
 	DEF_MP_INT(rsa_tmp1);
@@ -259,7 +267,7 @@
 
 	m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
 
-	rsa_pad_em(key, data_buf, &rsa_tmp1);
+	rsa_pad_em(key, data_buf, &rsa_tmp1, sigtype);
 
 	/* the actual signing of the padded data */
 
@@ -311,7 +319,8 @@
 	mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
 	
 	/* create the signature to return */
-	buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN);
+	name = signature_name_from_type(sigtype, &namelen);
+	buf_putstring(buf, name, namelen);
 
 	nsize = mp_unsigned_bin_size(key->n);
 
@@ -340,51 +349,73 @@
 	TRACE(("leave buf_put_rsa_sign"))
 }
 
-/* Creates the message value as expected by PKCS, see rfc2437 etc */
-/* format to be padded to is:
- * EM = 01 | FF* | 00 | prefix | hash
- *
- * where FF is repeated enough times to make EM one byte
- * shorter than the size of key->n
- *
- * prefix is the ASN1 designator prefix,
- * hex 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14
- *
- * rsa_em must be a pointer to an initialised mp_int.
- */
+/* Creates the message value as expected by PKCS, 
+   see rfc8017 section 9.2 */
 static void rsa_pad_em(const dropbear_rsa_key * key,
-	const buffer *data_buf, mp_int * rsa_em) {
+	const buffer *data_buf, mp_int * rsa_em, enum signature_type sigtype) {
+    /* EM = 0x00 || 0x01 || PS || 0x00 || T 
+	   PS is padding of 0xff to make EM the size of key->n
+
+	   T is the DER encoding of the hash alg (sha1 or sha256)
+	*/
 
-	/* ASN1 designator (including the 0x00 preceding) */
-	const unsigned char rsa_asn1_magic[] = 
-		{0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 
+	/* From rfc8017 page 46 */
+#if DROPBEAR_RSA_SHA1
+	const unsigned char T_sha1[] =
+		{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
 		 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14};
-	const unsigned int RSA_ASN1_MAGIC_LEN = 16;
+#endif
+#if DROPBEAR_RSA_SHA256
+	const unsigned char T_sha256[] =
+		{0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+		 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+#endif
 
+    int Tlen = 0;
+    const unsigned char *T = NULL;
+	const struct ltc_hash_descriptor *hash_desc = NULL;
 	buffer * rsa_EM = NULL;
 	hash_state hs;
 	unsigned int nsize;
+
+	switch (sigtype) {
+#if DROPBEAR_RSA_SHA1
+		case DROPBEAR_SIGNATURE_RSA_SHA1:
+			Tlen = sizeof(T_sha1);
+			T = T_sha1;
+			hash_desc = &sha1_desc;
+			break;
+#endif
+#if DROPBEAR_RSA_SHA256
+		case DROPBEAR_SIGNATURE_RSA_SHA256:
+			Tlen = sizeof(T_sha256);
+			T = T_sha256;
+			hash_desc = &sha256_desc;
+			break;
+#endif
+		default:
+			assert(0);
+	}
 	
-	dropbear_assert(key != NULL);
 	nsize = mp_unsigned_bin_size(key->n);
 
-	rsa_EM = buf_new(nsize-1);
+	rsa_EM = buf_new(nsize);
 	/* type byte */
+	buf_putbyte(rsa_EM, 0x00);
 	buf_putbyte(rsa_EM, 0x01);
-	/* Padding with 0xFF bytes */
-	while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) {
+	/* Padding with PS 0xFF bytes */
+	while(rsa_EM->pos != rsa_EM->size - (1 + Tlen + hash_desc->hashsize)) {
 		buf_putbyte(rsa_EM, 0xff);
 	}
+	buf_putbyte(rsa_EM, 0x00);
 	/* Magic ASN1 stuff */
-	memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN),
-			rsa_asn1_magic, RSA_ASN1_MAGIC_LEN);
-	buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN);
+	buf_putbytes(rsa_EM, T, Tlen);
 
 	/* The hash of the data */
-	sha1_init(&hs);
-	sha1_process(&hs, data_buf->data, data_buf->len);
-	sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE));
-	buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE);
+	hash_desc->init(&hs);
+	hash_desc->process(&hs, data_buf->data, data_buf->len);
+	hash_desc->done(&hs, buf_getwriteptr(rsa_EM, hash_desc->hashsize));
+	buf_incrwritepos(rsa_EM, hash_desc->hashsize);
 
 	dropbear_assert(rsa_EM->pos == rsa_EM->size);
 
--- a/rsa.h	Mon May 25 20:55:13 2020 +0500
+++ b/rsa.h	Tue May 26 00:24:02 2020 +0800
@@ -26,13 +26,12 @@
 #define DROPBEAR_RSA_H_
 
 #include "includes.h"
+#include "signkey.h"
 #include "buffer.h"
 
 #if DROPBEAR_RSA 
 
-#define RSA_SIGNATURE_SIZE (4+7+4+40)
-
-typedef struct {
+typedef struct dropbear_RSA_Key {
 
 	mp_int* n;
 	mp_int* e;
@@ -43,9 +42,11 @@
 
 } dropbear_rsa_key;
 
-void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, const buffer *data_buf);
+void buf_put_rsa_sign(buffer* buf, const dropbear_rsa_key *key, 
+        enum signature_type sigtype, const buffer *data_buf);
 #if DROPBEAR_SIGNKEY_VERIFY
-int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, const buffer *data_buf);
+int buf_rsa_verify(buffer * buf, const dropbear_rsa_key *key, 
+        enum signature_type sigtype, const buffer *data_buf);
 #endif
 int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key);
 int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key);
--- a/session.h	Mon May 25 20:55:13 2020 +0500
+++ b/session.h	Tue May 26 00:24:02 2020 +0800
@@ -102,7 +102,8 @@
 	struct key_context_directional trans;
 
 	const struct dropbear_kex *algo_kex;
-	int algo_hostkey;
+	enum signkey_type algo_hostkey; /* server key type */
+	enum signature_type algo_signature; /* server signature type */
 
 	int allow_compress; /* whether compression has started (useful in 
 							[email protected] delayed compression case) */
@@ -194,6 +195,9 @@
 
 	/* Enables/disables compression */
 	algo_type *compress_algos;
+
+	/* Other side allows SSH_MSG_EXT_INFO. Currently only set for server */
+	int allow_ext_info;
 							
 	/* a list of queued replies that should be sent after a KEX has
 	   concluded (ie, while dataallowed was unset)*/
@@ -259,13 +263,12 @@
 #endif
 
 #if DROPBEAR_PLUGIN
-        /* The shared library handle */
-        void *plugin_handle;
+	/* The shared library handle */
+	void *plugin_handle;
 
-        /* The instance created by the plugin_new function */
-        struct PluginInstance *plugin_instance;
+	/* The instance created by the plugin_new function */
+	struct PluginInstance *plugin_instance;
 #endif
-
 };
 
 typedef enum {
@@ -294,7 +297,6 @@
 
 	cli_kex_state kex_state; /* Used for progressing KEX */
 	cli_state state; /* Used to progress auth/channelsession etc */
-	unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
 
 	int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */
 	struct termios saved_tio;
@@ -322,6 +324,8 @@
 #endif
 	sign_key *lastprivkey;
 
+	buffer *server_sig_algs;
+
 	int retval; /* What the command exit status was - we emulate it */
 #if 0
 	TODO
--- a/signkey.c	Mon May 25 20:55:13 2020 +0500
+++ b/signkey.c	Tue May 26 00:24:02 2020 +0800
@@ -28,6 +28,9 @@
 #include "buffer.h"
 #include "ssh.h"
 #include "ecdsa.h"
+#include "rsa.h"
+#include "dss.h"
+#include "ed25519.h"
 
 static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = {
 #if DROPBEAR_RSA
@@ -44,6 +47,7 @@
 #if DROPBEAR_ED25519
 	"ssh-ed25519",
 #endif /* DROPBEAR_ED25519 */
+	/* "rsa-sha2-256" is special-cased below since it is only a signature name, not key type */
 };
 
 /* malloc a new sign_key and set the dss and rsa keys to NULL */
@@ -105,6 +109,70 @@
 	return DROPBEAR_SIGNKEY_NONE;
 }
 
+/* Special case for rsa-sha2-256. This could be generalised if more 
+   signature names are added that aren't 1-1 with public key names */
+const char* signature_name_from_type(enum signature_type type, unsigned int *namelen) {
+#if DROPBEAR_RSA_SHA256
+	if (type == DROPBEAR_SIGNATURE_RSA_SHA256) {
+		if (namelen) {
+			*namelen = strlen(SSH_SIGNATURE_RSA_SHA256);
+		}
+		return SSH_SIGNATURE_RSA_SHA256;
+	}
+#endif
+#if DROPBEAR_RSA_SHA1
+	if (type == DROPBEAR_SIGNATURE_RSA_SHA1) {
+		if (namelen) {
+			*namelen = strlen(SSH_SIGNKEY_RSA);
+		}
+		return SSH_SIGNKEY_RSA;
+	}
+#endif
+	return signkey_name_from_type((enum signkey_type)type, namelen);
+}
+
+/* Returns DROPBEAR_SIGNATURE_NONE if none match */
+enum signature_type signature_type_from_name(const char* name, unsigned int namelen) {
+#if DROPBEAR_RSA_SHA256
+	if (namelen == strlen(SSH_SIGNATURE_RSA_SHA256) 
+		&& memcmp(name, SSH_SIGNATURE_RSA_SHA256, namelen) == 0) {
+		return DROPBEAR_SIGNATURE_RSA_SHA256;
+	}
+#endif
+#if DROPBEAR_RSA_SHA1
+	if (namelen == strlen(SSH_SIGNKEY_RSA) 
+		&& memcmp(name, SSH_SIGNKEY_RSA, namelen) == 0) {
+		return DROPBEAR_SIGNATURE_RSA_SHA1;
+	}
+#endif
+	return (enum signature_type)signkey_type_from_name(name, namelen);
+}
+
+/* Returns the signature type from a key type. Must not be called 
+   with RSA keytype */
+enum signature_type signature_type_from_signkey(enum signkey_type keytype) {
+#if DROPBEAR_RSA
+	assert(keytype != DROPBEAR_SIGNKEY_RSA);
+#endif
+	assert(keytype < DROPBEAR_SIGNKEY_NUM_NAMED);
+	return (enum signature_type)keytype;
+}
+
+enum signkey_type signkey_type_from_signature(enum signature_type sigtype) {
+#if DROPBEAR_RSA_SHA256
+	if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA256) {
+		return DROPBEAR_SIGNKEY_RSA;
+	}
+#endif
+#if DROPBEAR_RSA_SHA1
+	if (sigtype == DROPBEAR_SIGNATURE_RSA_SHA1) {
+		return DROPBEAR_SIGNKEY_RSA;
+	}
+#endif
+	assert(sigtype < DROPBEAR_SIGNKEY_NUM_NAMED);
+	return (enum signkey_type)sigtype;
+}
+
 /* Returns a pointer to the key part specific to "type".
 Be sure to check both (ret != NULL) and (*ret != NULL) */
 void **
@@ -526,31 +594,39 @@
 #endif
 }
 
-void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, 
+void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, 
 	const buffer *data_buf) {
-	buffer *sigblob;
-	sigblob = buf_new(MAX_PUBKEY_SIZE);
+	buffer *sigblob = buf_new(MAX_PUBKEY_SIZE);
+	enum signkey_type keytype = signkey_type_from_signature(sigtype);
+
+#if DEBUG_TRACE
+	{
+		const char* signame = signature_name_from_type(sigtype, NULL);
+		TRACE(("buf_put_sign type %d %s", sigtype, signame));
+	}
+#endif
+
 
 #if DROPBEAR_DSS
-	if (type == DROPBEAR_SIGNKEY_DSS) {
+	if (keytype == DROPBEAR_SIGNKEY_DSS) {
 		buf_put_dss_sign(sigblob, key->dsskey, data_buf);
 	}
 #endif
 #if DROPBEAR_RSA
-	if (type == DROPBEAR_SIGNKEY_RSA) {
-		buf_put_rsa_sign(sigblob, key->rsakey, data_buf);
+	if (keytype == DROPBEAR_SIGNKEY_RSA) {
+		buf_put_rsa_sign(sigblob, key->rsakey, sigtype, data_buf);
 	}
 #endif
 #if DROPBEAR_ECDSA
-	if (signkey_is_ecdsa(type)) {
-		ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type);
+	if (signkey_is_ecdsa(keytype)) {
+		ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype);
 		if (eck && *eck) {
 			buf_put_ecdsa_sign(sigblob, *eck, data_buf);
 		}
 	}
 #endif
 #if DROPBEAR_ED25519
-	if (type == DROPBEAR_SIGNKEY_ED25519) {
+	if (keytype == DROPBEAR_SIGNKEY_ED25519) {
 		buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf);
 	}
 #endif
@@ -567,21 +643,27 @@
  * If FAILURE is returned, the position of
  * buf is undefined. If SUCCESS is returned, buf will be positioned after the
  * signature blob */
-int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) {
+int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf) {
 	
 	char *type_name = NULL;
 	unsigned int type_name_len = 0;
-	enum signkey_type type;
+	enum signature_type sigtype;
+	enum signkey_type keytype;
 
 	TRACE(("enter buf_verify"))
 
 	buf_getint(buf); /* blob length */
 	type_name = buf_getstring(buf, &type_name_len);
-	type = signkey_type_from_name(type_name, type_name_len);
+	sigtype = signature_type_from_name(type_name, type_name_len);
 	m_free(type_name);
 
+	if (expect_sigtype != sigtype) {
+			dropbear_exit("Non-matching signing type");
+	}
+
+	keytype = signkey_type_from_signature(sigtype);
 #if DROPBEAR_DSS
-	if (type == DROPBEAR_SIGNKEY_DSS) {
+	if (keytype == DROPBEAR_SIGNKEY_DSS) {
 		if (key->dsskey == NULL) {
 			dropbear_exit("No DSS key to verify signature");
 		}
@@ -590,23 +672,23 @@
 #endif
 
 #if DROPBEAR_RSA
-	if (type == DROPBEAR_SIGNKEY_RSA) {
+	if (keytype == DROPBEAR_SIGNKEY_RSA) {
 		if (key->rsakey == NULL) {
 			dropbear_exit("No RSA key to verify signature");
 		}
-		return buf_rsa_verify(buf, key->rsakey, data_buf);
+		return buf_rsa_verify(buf, key->rsakey, sigtype, data_buf);
 	}
 #endif
 #if DROPBEAR_ECDSA
-	if (signkey_is_ecdsa(type)) {
-		ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type);
+	if (signkey_is_ecdsa(keytype)) {
+		ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype);
 		if (eck && *eck) {
 			return buf_ecdsa_verify(buf, *eck, data_buf);
 		}
 	}
 #endif
 #if DROPBEAR_ED25519
-	if (type == DROPBEAR_SIGNKEY_ED25519) {
+	if (keytype == DROPBEAR_SIGNKEY_ED25519) {
 		if (key->ed25519key == NULL) {
 			dropbear_exit("No Ed25519 key to verify signature");
 		}
--- a/signkey.h	Mon May 25 20:55:13 2020 +0500
+++ b/signkey.h	Tue May 26 00:24:02 2020 +0800
@@ -26,10 +26,13 @@
 #define DROPBEAR_SIGNKEY_H_
 
 #include "buffer.h"
-#include "dss.h"
-#include "rsa.h"
-#include "ed25519.h"
 
+/* Forward declarations */
+struct dropbear_DSS_Key;
+struct dropbear_RSA_Key;
+struct dropbear_ED25519_Key;
+
+/* Must match with signature_type below */
 enum signkey_type {
 #if DROPBEAR_RSA
 	DROPBEAR_SIGNKEY_RSA,
@@ -51,6 +54,28 @@
 	DROPBEAR_SIGNKEY_NONE = 90,
 };
 
+/* Must match with signkey_type above, apart from rsa */
+enum signature_type {
+#if DROPBEAR_DSS
+	DROPBEAR_SIGNATURE_DSS = DROPBEAR_SIGNKEY_DSS,
+#endif
+#if DROPBEAR_ECDSA
+	DROPBEAR_SIGNATURE_ECDSA_NISTP256 = DROPBEAR_SIGNKEY_ECDSA_NISTP256,
+	DROPBEAR_SIGNATURE_ECDSA_NISTP384 = DROPBEAR_SIGNKEY_ECDSA_NISTP384,
+	DROPBEAR_SIGNATURE_ECDSA_NISTP521 = DROPBEAR_SIGNKEY_ECDSA_NISTP521,
+#endif /* DROPBEAR_ECDSA */
+#if DROPBEAR_ED25519
+	DROPBEAR_SIGNATURE_ED25519 = DROPBEAR_SIGNKEY_ED25519,
+#endif
+#if DROPBEAR_RSA_SHA1
+	DROPBEAR_SIGNATURE_RSA_SHA1 = 100, /* ssh-rsa signature (sha1) */
+#endif
+#if DROPBEAR_RSA_SHA256
+	DROPBEAR_SIGNATURE_RSA_SHA256 = 101, /* rsa-sha2-256 signature. has a ssh-rsa key */
+#endif
+	DROPBEAR_SIGNATURE_NONE = DROPBEAR_SIGNKEY_NONE,
+};
+
 
 /* Sources for signing keys */
 typedef enum {
@@ -66,10 +91,10 @@
 	char *filename;
 
 #if DROPBEAR_DSS
-	dropbear_dss_key * dsskey;
+	struct dropbear_DSS_Key * dsskey;
 #endif
 #if DROPBEAR_RSA
-	dropbear_rsa_key * rsakey;
+	struct dropbear_RSA_Key * rsakey;
 #endif
 #if DROPBEAR_ECDSA
 #if DROPBEAR_ECC_256
@@ -83,7 +108,7 @@
 #endif
 #endif
 #if DROPBEAR_ED25519
-	dropbear_ed25519_key * ed25519key;
+	struct dropbear_ED25519_Key * ed25519key;
 #endif
 };
 
@@ -92,14 +117,19 @@
 sign_key * new_sign_key(void);
 const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen);
 enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen);
+const char* signature_name_from_type(enum signature_type type, unsigned int *namelen);
+enum signature_type signature_type_from_name(const char* name, unsigned int namelen);
+enum signkey_type signkey_type_from_signature(enum signature_type sigtype);
+enum signature_type signature_type_from_signkey(enum signkey_type keytype);
+
 int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type);
 int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type);
 void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type);
 void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type);
 void sign_key_free(sign_key *key);
-void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, const buffer *data_buf);
+void buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, const buffer *data_buf);
 #if DROPBEAR_SIGNKEY_VERIFY
-int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf);
+int buf_verify(buffer * buf, sign_key *key, enum signature_type expect_sigtype, const buffer *data_buf);
 char * sign_key_fingerprint(const unsigned char* keyblob, unsigned int keybloblen);
 #endif
 int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, 
--- a/ssh.h	Mon May 25 20:55:13 2020 +0500
+++ b/ssh.h	Tue May 26 00:24:02 2020 +0800
@@ -32,6 +32,7 @@
 #define SSH_MSG_DEBUG                  4
 #define SSH_MSG_SERVICE_REQUEST        5
 #define SSH_MSG_SERVICE_ACCEPT         6
+#define SSH_MSG_EXT_INFO               7
 #define SSH_MSG_KEXINIT                20
 #define SSH_MSG_NEWKEYS                21
 #define SSH_MSG_KEXDH_INIT             30
@@ -94,19 +95,26 @@
 #define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE  14
 #define SSH_DISCONNECT_ILLEGAL_USER_NAME               15
 
+/* rfc8308 */
+#define SSH_EXT_INFO_S "ext-info-s"
+#define SSH_EXT_INFO_C "ext-info-c"
+#define SSH_SERVER_SIG_ALGS "server-sig-algs"
+
 /* service types */
 #define SSH_SERVICE_USERAUTH "ssh-userauth"
 #define SSH_SERVICE_USERAUTH_LEN 12
 #define SSH_SERVICE_CONNECTION "ssh-connection"
 #define SSH_SERVICE_CONNECTION_LEN 14
 
-/* public key types */
+/* public/signature key types */
 #define SSH_SIGNKEY_DSS "ssh-dss"
 #define SSH_SIGNKEY_DSS_LEN 7
 #define SSH_SIGNKEY_RSA "ssh-rsa"
 #define SSH_SIGNKEY_RSA_LEN 7
 #define SSH_SIGNKEY_ED25519 "ssh-ed25519"
 #define SSH_SIGNKEY_ED25519_LEN 11
+/* signature type */
+#define SSH_SIGNATURE_RSA_SHA256 "rsa-sha2-256"
 
 /* Agent commands. These aren't part of the spec, and are defined
  * only on the openssh implementation. */
--- a/svr-authpubkey.c	Mon May 25 20:55:13 2020 +0500
+++ b/svr-authpubkey.c	Tue May 26 00:24:02 2020 +0800
@@ -70,10 +70,10 @@
 #define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
 #define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
 
-static int checkpubkey(const char* algo, unsigned int algolen,
+static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
 		const unsigned char* keyblob, unsigned int keybloblen);
 static int checkpubkeyperms(void);
-static void send_msg_userauth_pk_ok(const char* algo, unsigned int algolen,
+static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
 		const unsigned char* keyblob, unsigned int keybloblen);
 static int checkfileperm(char * filename);
 
@@ -82,16 +82,19 @@
 void svr_auth_pubkey(int valid_user) {
 
 	unsigned char testkey; /* whether we're just checking if a key is usable */
-	char* algo = NULL; /* pubkey algo */
-	unsigned int algolen;
+	char* sigalgo = NULL;
+	unsigned int sigalgolen;
+	const char* keyalgo;
+	unsigned int keyalgolen;
 	unsigned char* keyblob = NULL;
 	unsigned int keybloblen;
 	unsigned int sign_payload_length;
 	buffer * signbuf = NULL;
 	sign_key * key = NULL;
 	char* fp = NULL;
-	enum signkey_type type = -1;
-        int auth_failure = 1;
+	enum signature_type sigtype;
+	enum signkey_type keytype;
+    int auth_failure = 1;
 
 	TRACE(("enter pubkeyauth"))
 
@@ -99,7 +102,7 @@
 	 * actual attempt*/
 	testkey = (buf_getbool(ses.payload) == 0);
 
-	algo = buf_getstring(ses.payload, &algolen);
+	sigalgo = buf_getstring(ses.payload, &sigalgolen);
 	keybloblen = buf_getint(ses.payload);
 	keyblob = buf_getptr(ses.payload, keybloblen);
 
@@ -111,14 +114,24 @@
 		send_msg_userauth_failure(0, 0);
 		goto out;
 	}
+
+	sigtype = signature_type_from_name(sigalgo, sigalgolen);
+	if (sigtype == DROPBEAR_SIGNATURE_NONE) {
+		send_msg_userauth_failure(0, 0);
+		goto out;
+	}
+
+	keytype = signkey_type_from_signature(sigtype);
+	keyalgo = signkey_name_from_type(keytype, &keyalgolen);
+
 #if DROPBEAR_PLUGIN
         if (svr_ses.plugin_instance != NULL) {
             char *options_buf;
             if (svr_ses.plugin_instance->checkpubkey(
                         svr_ses.plugin_instance,
                         &ses.plugin_session,
-                        algo, 
-                        algolen, 
+                        keyalgo, 
+                        keyalgolen, 
                         keyblob, 
                         keybloblen,
                         ses.authstate.username) == DROPBEAR_SUCCESS) {
@@ -146,7 +159,7 @@
 #endif
 	/* check if the key is valid */
         if (auth_failure) {
-            auth_failure = checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
+            auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
         }
 
         if (auth_failure) {
@@ -156,7 +169,7 @@
 
 	/* let them know that the key is ok to use */
 	if (testkey) {
-		send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen);
+		send_msg_userauth_pk_ok(sigalgo, sigalgolen, keyblob, keybloblen);
 		goto out;
 	}
 
@@ -164,8 +177,7 @@
 	
 	/* get the key */
 	key = new_sign_key();
-	type = DROPBEAR_SIGNKEY_ANY;
-	if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) {
+	if (buf_get_pub_key(ses.payload, key, &keytype) == DROPBEAR_FAILURE) {
 		send_msg_userauth_failure(0, 1);
 		goto out;
 	}
@@ -188,7 +200,7 @@
 
 	/* ... and finally verify the signature */
 	fp = sign_key_fingerprint(keyblob, keybloblen);
-	if (buf_verify(ses.payload, key, signbuf) == DROPBEAR_SUCCESS) {
+	if (buf_verify(ses.payload, key, sigtype, signbuf) == DROPBEAR_SUCCESS) {
 		dropbear_log(LOG_NOTICE,
 				"Pubkey auth succeeded for '%s' with key %s from %s",
 				ses.authstate.pw_name, fp, svr_ses.addrstring);
@@ -213,8 +225,8 @@
 	if (signbuf) {
 		buf_free(signbuf);
 	}
-	if (algo) {
-		m_free(algo);
+	if (sigalgo) {
+		m_free(sigalgo);
 	}
 	if (key) {
 		sign_key_free(key);
@@ -230,14 +242,14 @@
 /* Reply that the key is valid for auth, this is sent when the user sends
  * a straight copy of their pubkey to test, to avoid having to perform
  * expensive signing operations with a worthless key */
-static void send_msg_userauth_pk_ok(const char* algo, unsigned int algolen,
+static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
 		const unsigned char* keyblob, unsigned int keybloblen) {
 
 	TRACE(("enter send_msg_userauth_pk_ok"))
 	CHECKCLEARTOWRITE();
 
 	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
-	buf_putstring(ses.writepayload, algo, algolen);
+	buf_putstring(ses.writepayload, sigalgo, sigalgolen);
 	buf_putstring(ses.writepayload, (const char*)keyblob, keybloblen);
 
 	encrypt_packet();
@@ -354,7 +366,7 @@
 /* Checks whether a specified publickey (and associated algorithm) is an
  * acceptable key for authentication */
 /* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
-static int checkpubkey(const char* algo, unsigned int algolen,
+static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
 		const unsigned char* keyblob, unsigned int keybloblen) {
 
 	FILE * authfile = NULL;
@@ -368,14 +380,6 @@
 
 	TRACE(("enter checkpubkey"))
 
-	/* check that we can use the algo */
-	if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
-		dropbear_log(LOG_WARNING,
-				"Pubkey auth attempt with unknown algo for '%s' from %s",
-				ses.authstate.pw_name, svr_ses.addrstring);
-		goto out;
-	}
-
 	/* check file permissions, also whether file exists */
 	if (checkpubkeyperms() == DROPBEAR_FAILURE) {
 		TRACE(("bad authorized_keys permissions, or file doesn't exist"))
@@ -427,7 +431,7 @@
 		}
 		line_num++;
 
-		ret = checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen);
+		ret = checkpubkey_line(line, line_num, filename, keyalgo, keyalgolen, keyblob, keybloblen);
 		if (ret == DROPBEAR_SUCCESS) {
 			break;
 		}
--- a/svr-kex.c	Mon May 25 20:55:13 2020 +0500
+++ b/svr-kex.c	Tue May 26 00:24:02 2020 +0800
@@ -38,13 +38,15 @@
 #include "gensignkey.h"
 
 static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs);
+#if DROPBEAR_EXT_INFO
+static void send_msg_ext_info(void);
+#endif
 
 /* Handle a diffie-hellman key exchange initialisation. This involves
  * calculating a session key reply value, and corresponding hash. These
  * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls
  * that function, then brings the new keys into use */
 void recv_msg_kexdh_init() {
-
 	DEF_MP_INT(dh_e);
 	buffer *ecdh_qs = NULL;
 
@@ -86,6 +88,14 @@
 	}
 
 	send_msg_newkeys();
+
+#if DROPBEAR_EXT_INFO
+	/* Only send it following the first newkeys */
+	if (!ses.kexstate.donesecondkex && ses.allow_ext_info) {
+		send_msg_ext_info();
+	}
+#endif
+
 	ses.requirenext = SSH_MSG_NEWKEYS;
 	TRACE(("leave recv_msg_kexdh_init"))
 }
@@ -234,7 +244,7 @@
 
 	/* calc the signature */
 	buf_put_sign(ses.writepayload, svr_opts.hostkey, 
-			ses.newkeys->algo_hostkey, ses.hash);
+			ses.newkeys->algo_signature, ses.hash);
 
 	/* the SSH_MSG_KEXDH_REPLY is done */
 	encrypt_packet();
@@ -242,3 +252,20 @@
 	TRACE(("leave send_msg_kexdh_reply"))
 }
 
+#if DROPBEAR_EXT_INFO
+/* Only used for server-sig-algs on the server side */
+static void send_msg_ext_info(void) {
+	TRACE(("enter send_msg_ext_info"))
+
+	buf_putbyte(ses.writepayload, SSH_MSG_EXT_INFO);
+	/* nr-extensions */
+	buf_putint(ses.writepayload, 1);
+
+	buf_putstring(ses.writepayload, SSH_SERVER_SIG_ALGS, strlen(SSH_SERVER_SIG_ALGS));
+	buf_put_algolist_all(ses.writepayload, sigalgs, 1);
+	
+	encrypt_packet();
+
+	TRACE(("leave send_msg_ext_info"))
+}
+#endif
--- a/svr-main.c	Mon May 25 20:55:13 2020 +0500
+++ b/svr-main.c	Tue May 26 00:24:02 2020 +0800
@@ -273,7 +273,7 @@
 				goto out;
 			}
 
-#ifdef DEBUG_NOFORK
+#if DEBUG_NOFORK
 			fork_ret = 0;
 #else
 			fork_ret = fork();
--- a/svr-runopts.c	Mon May 25 20:55:13 2020 +0500
+++ b/svr-runopts.c	Tue May 26 00:24:02 2020 +0800
@@ -485,9 +485,9 @@
 static void disablekey(int type) {
 	int i;
 	TRACE(("Disabling key type %d", type))
-	for (i = 0; sshhostkey[i].name != NULL; i++) {
-		if (sshhostkey[i].val == type) {
-			sshhostkey[i].usable = 0;
+	for (i = 0; sigalgs[i].name != NULL; i++) {
+		if (sigalgs[i].val == type) {
+			sigalgs[i].usable = 0;
 			break;
 		}
 	}
@@ -567,7 +567,7 @@
 void load_all_hostkeys() {
 	int i;
 	int any_keys = 0;
-#ifdef DROPBEAR_ECDSA
+#if DROPBEAR_ECDSA
 	int loaded_any_ecdsa = 0;
 #endif
 
--- a/svr-session.c	Mon May 25 20:55:13 2020 +0500
+++ b/svr-session.c	Tue May 26 00:24:02 2020 +0800
@@ -330,13 +330,18 @@
 }
 
 static void svr_algos_initialise(void) {
-#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
 	algo_type *algo;
 	for (algo = sshkex; algo->name; algo++) {
+#if DROPBEAR_DH_GROUP1 && DROPBEAR_DH_GROUP1_CLIENTONLY
 		if (strcmp(algo->name, "diffie-hellman-group1-sha1") == 0) {
 			algo->usable = 0;
 		}
+#endif
+#if DROPBEAR_EXT_INFO
+		if (strcmp(algo->name, SSH_EXT_INFO_C) == 0) {
+			algo->usable = 0;
+		}
+#endif
 	}
-#endif
 }
 
--- a/sysoptions.h	Mon May 25 20:55:13 2020 +0500
+++ b/sysoptions.h	Tue May 26 00:24:02 2020 +0800
@@ -143,9 +143,17 @@
  * signing operations slightly slower. */
 #define DROPBEAR_RSA_BLINDING 1
 
+#ifndef DROPBEAR_RSA_SHA1
+#define DROPBEAR_RSA_SHA1 DROPBEAR_RSA
+#endif
+#ifndef DROPBEAR_RSA_SHA256
+#define DROPBEAR_RSA_SHA256 DROPBEAR_RSA
+#endif
+
 /* hashes which will be linked and registered */
-#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256)  \
- 			|| (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256))
+#define DROPBEAR_SHA256 ((DROPBEAR_SHA2_256_HMAC) || (DROPBEAR_ECC_256) \
+ 			|| (DROPBEAR_CURVE25519) || (DROPBEAR_DH_GROUP14_SHA256) \
+			|| (DROPBEAR_RSA_SHA256))
 #define DROPBEAR_SHA384 (DROPBEAR_ECC_384)
 /* LTC SHA384 depends on SHA512 */
 #define DROPBEAR_SHA512 ((DROPBEAR_SHA2_512_HMAC) || (DROPBEAR_ECC_521) \
@@ -157,6 +165,10 @@
 
 #define DROPBEAR_NORMAL_DH ((DROPBEAR_DH_GROUP1) || (DROPBEAR_DH_GROUP14) || (DROPBEAR_DH_GROUP16))
 
+/* Dropbear only uses server-sig-algs, only needed if we have rsa-sha256 pubkey auth */
+#define DROPBEAR_EXT_INFO ((DROPBEAR_RSA_SHA256) \
+		&& ((DROPBEAR_CLI_PUBKEY_AUTH) || (DROPBEAR_SVR_PUBKEY_AUTH)))
+
 /* roughly 2x 521 bits */
 #define MAX_ECC_SIZE 140