9front - general discussion about 9front
 help / color / mirror / Atom feed
* [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
@ 2020-06-12 20:35 Romano
  2020-06-12 22:58 ` [9front] " cinap_lenrek
  0 siblings, 1 reply; 7+ messages in thread
From: Romano @ 2020-06-12 20:35 UTC (permalink / raw)
  To: 9front

All,

Below is a patch (also at http://okturing.com/src/8696/body ) that handles client and server algorithm negotations so that other key exchange, host, cipher, et c. algorithms and implements ssh-dss key exchange.  The primary impetus for this was to use rsync.net, which right now allows only ssh-dss and ssh-ed25519 for key exchange. ed25519 is related to curve25519 preferable, and there's an implementation that I'm planning to port to 9front (djb's reference implementation[1] which apparently OpenSSH uses ). However, the first step was to make sure the algorithm negotation bits were all in order. I then noticed that dsa(2) provided the basics for implementation ssh-dss. Ssh-dss was deprecated by OpenSSH too hastily[2], so I've implemented it so that I can use rsync.net. Next, I hope to use ssh-ed25519.

This is my first real foray into C besides the little patch I sent in April. I hope the it's not too terrible and useful for others.

[1] https://ed25519.cr.yp.to/software.html
[2] https://security.stackexchange.com/questions/112802

diff -r 307fbb6fd10a sys/src/cmd/ssh.c
--- a/sys/src/cmd/ssh.c	Fri Jun 12 01:36:50 2020 +0200
+++ b/sys/src/cmd/ssh.c	Fri Jun 12 12:37:32 2020 -0700
@@ -1,3 +1,14 @@
+/*
+	2020-June (Romano)
+	I think this would be better if refactored into its own subdir with separate files, e.g.:
+	ssh/ssh.h	<-- structs, enums, et c.
+	ssh/debug.c <-- debugging statement stuff, e.g. printKexinitp()
+	ssh/alloc.c <-- kexinitalloc() et c.
+	ssh/kex.c   <-- key exchange and host server algorithm stuff
+	ssh/algs.c  <-- cipher, mac, zip, and lang stuff
+	ssh/ssh.c   <-- main loop
+*/
+
 #include <u.h>
 #include <libc.h>
 #include <mp.h>
@@ -5,6 +16,7 @@
 #include <auth.h>
 #include <authsrv.h>
 
+/* Cf. RFC2450 & RFC8268 */
 enum {
 	MSG_DISCONNECT = 1,
 	MSG_IGNORE,
@@ -47,7 +59,7 @@
 
 
 enum {
-	Overhead = 256,		// enougth for MSG_CHANNEL_DATA header
+	Overhead = 256,		// enough for MSG_CHANNEL_DATA header
 	MaxPacket = 1<<15,
 	WinPackets = 8,		// (1<<15) * 8 = 256K
 };
@@ -76,6 +88,280 @@
 	Rendez;
 } Oneway;
 
+/* BEGIN RFC 4253 name-list agreement structure and implementation */
+struct nameseq {
+	char *val;
+	struct nameseq *next;
+};
+typedef struct nameseq Nameseq;
+
+Nameseq*
+nameseqalloc(void)
+{
+	Nameseq *nameseq;
+
+	nameseq = mallocz(sizeof(*nameseq), 1);
+	if(nameseq == nil)
+		sysfatal("nameseqalloc");
+	return nameseq;
+}
+
+void
+nameseqfree(Nameseq *nameseq)
+{
+	if(nameseq == nil)
+		return;
+	nameseqfree(nameseq->next);
+	free(nameseq);
+}
+
+Nameseq *
+getnameseq(char *c)
+{
+	Nameseq *orderp = nameseqalloc();
+	Nameseq *pp = orderp;
+	char *str = strdup(c);
+	while (str = strtok(str, ",")) {
+		pp->val = str;
+		pp->next = nameseqalloc();
+		pp = pp->next;
+		str = nil;
+	}
+	return orderp;
+}
+
+char *
+nameseqagree(char *c, char *s)
+{
+	Nameseq *cp, *sp, *startsp;
+	char *val = nil;
+
+	if (strlen(c) == 0 && strlen(s) == 0 )
+		return "";
+
+	cp = getnameseq(c);
+	startsp = sp = getnameseq(s);
+	while (cp) {
+		if (cp->val && sp->val && !strcmp(cp->val, sp->val)) {
+			val = strdup(cp->val);
+			break;
+		}
+		if(!(sp = sp->next)) {
+			if(!(cp = cp->next))
+				break;
+			sp = startsp;
+		}
+	}
+
+	nameseqfree(cp);
+	nameseqfree(sp);
+	return val;
+}
+/* END RFC 4253 name-list agreement implementation */
+
+typedef struct algs {
+	char *cipher;
+	char *mac;
+	char *zip;
+	char *lang;
+} Algs;
+typedef Algs *Algsp;
+
+typedef struct kexhost {
+	char *kex;
+	char *host;
+} Kexhost;
+typedef Kexhost *Kexhostp;
+
+typedef struct kexother {
+	uint firstkexpkt;
+	ulong reserved;
+	uchar msg;
+	uchar cookie[16];
+} Kexother;
+typedef Kexother *Kexotherp;
+
+typedef struct kexinit {
+	Kexhostp kh;
+	Algsp ctos;
+	Algsp stoc;
+	Kexotherp ko;
+} Kexinit;
+typedef Kexinit *Kexinitp;
+
+Algsp
+algsalloc(void)
+{
+	Algsp data;
+
+	data = mallocz(sizeof(data), 1);
+	if(data == nil)
+		sysfatal("algsalloc");
+	return data;
+}
+
+Kexhostp
+kexhostalloc(void)
+{
+	Kexhostp data;
+
+	data = mallocz(sizeof(data), 1);
+	if(data == nil)
+		sysfatal("kexhostalloc");
+	return data;
+}
+
+Kexotherp
+kexotheralloc(void)
+{
+	Kexotherp data;
+
+	data = mallocz(sizeof(data), 1);
+	if(data == nil)
+		sysfatal("kexotheralloc");
+	return data;
+}
+
+void
+printKexhostp(char *prefix, Kexhostp data) {
+	fprint(2, "%s 	->%s: %s\n", prefix, "kex", data->kex );
+	fprint(2, "%s 	->%s: %s\n", prefix, "host", data->host );
+}
+
+Kexinitp
+kexinitalloc(void)
+{
+	Kexinitp data;
+
+	data = mallocz(sizeof(data), 1);
+	if(data == nil)
+		sysfatal("kexinitalloc");
+	data->ko = kexotheralloc();
+	data->kh = kexhostalloc();
+	data->ctos = algsalloc();
+	data->stoc = algsalloc();
+	return data;
+}
+
+void
+defKexotherp(Kexotherp kop) {
+	kop->msg = MSG_KEXINIT;
+	kop->firstkexpkt = 0;
+	kop->reserved = 0L;
+	genrandom(kop->cookie, sizeof(kop->cookie));
+}
+
+void
+defKexhostp(Kexhostp khp) {
+	khp->kex = "curve25519-sha256,curve25519-sha256@libssh.org";
+
+	/*
+	ssh-dss is required per RFC4253. OpenSSH has deprecated its use, but
+	does there's no real basis to support that decision for larger modulo.
+	Support it as a secondary algorithm, most likely until ssh-ed25519 is
+	supported.
+	*/
+	khp->host = "ssh-rsa,ssh-dss";
+}
+
+void
+defAlgsp(Algsp algsp) {
+	/*
+	'At some future time, it is expected that another algorithm, one with better
+	strength, will become so prevalent and ubiquitous that the use of
+	"3des-cbc" will be deprecated by another STANDARDS ACTION.' - RFC4253
+	No standards action has yet deprecated it, but have not seen it supported
+	by default in any server.
+	*/
+	algsp->cipher = "chacha20-poly1305@openssh.com";
+	
+	/*
+	hmac-sha1 is a required algorithm by RFC4253, and some servers still check
+	for it; it's effectively ignored since chacha20-poly1305 cipher is AEAD.
+	No RFC has deprecated it, only provided other algorithms to be placed
+	before it (e.g., SHA-2).
+	*/
+	algsp->mac = "hmac-sha1";
+	
+	algsp->zip = "none";
+	algsp->lang = "";
+}
+
+void
+printKexotherp(char *prefix, Kexotherp data) {
+	fprint(2, "%s 	->%s: %uc\n", prefix, "msg", data->msg );
+	fprint(2, "%s 	->%s: %uhd\n", prefix, "cookie", *data->cookie );
+	fprint(2, "%s 	->%s: %ud\n", prefix, "firstkexpkt", data->firstkexpkt );
+	fprint(2, "%s 	->%s: %uld\n", prefix, "reserved", data->reserved );
+}
+
+void
+printAlgsp(char *prefix, Algsp data) {
+	fprint(2, "%s 	->%s: %s\n", prefix, "cipher", data->cipher );
+	fprint(2, "%s 	->%s: %s\n", prefix, "mac", data->mac );
+	fprint(2, "%s 	->%s: %s\n", prefix, "zip", data->zip );
+	fprint(2, "%s 	->%s: %s\n", prefix, "lang", data->lang );
+}
+
+void
+printKexinitp(char *prefix, Kexinitp data) {
+	fprint(2, "%s	%llX:\n", prefix, (vlong)data);
+	fprint(2, "%s ko->\n", prefix);
+	printKexotherp(prefix, data->ko );
+	fprint(2, "%s kh->\n", prefix);
+	printKexhostp(prefix, data->kh );
+	fprint(2, "%s ctos->\n", prefix);
+	printAlgsp(prefix, data->ctos );
+	fprint(2, "%s stoc->\n", prefix);
+	printAlgsp(prefix, data->stoc );
+}
+
+Kexhostp
+khagree(Kexhostp a, Kexhostp b) {
+	Kexhostp c = kexhostalloc();
+	c->kex = nameseqagree(a->kex, b->kex);
+	if(!c->kex)
+		sysfatal("Cannot agree on key exchange algorithm");
+
+	c->host = nameseqagree(a->host, b->host);
+	if(!c->host)
+		sysfatal("Cannot agree on host server algorithm");
+
+	return c;
+}
+
+Algsp
+algsagree(Algsp a, Algsp b, char * cxt) {
+	Algsp c = algsalloc();
+	c->cipher = nameseqagree(a->cipher, b->cipher);
+	if(!c->cipher)
+		sysfatal(smprint("Cannot agree on cipher algorithm for %s", cxt));
+
+	c->mac = nameseqagree(a->mac, b->mac);
+	if(!c->mac)
+		sysfatal(smprint("Cannot agree on mac algorithm for %s", cxt));
+
+	c->zip = nameseqagree(a->zip, b->zip);
+	if(!c->zip)
+		sysfatal(smprint("Cannot agree on zip algorithm for %s", cxt));
+
+	c->lang = nameseqagree(a->lang, b->lang);
+	if(!c->lang)
+		sysfatal(smprint("Cannot agree on lang algorithm for %s", cxt));
+
+	return c;
+}
+
+Kexinitp agreep;
+
+void
+authagree(Kexinitp client, Kexinitp server) {
+	agreep = kexinitalloc();
+	agreep->kh = khagree(client->kh, server->kh);
+	agreep->ctos = algsagree(client->ctos, server->ctos, "ctos");
+	agreep->stoc = algsagree(client->stoc, server->stoc, "stoc");
+}
+
 int nsid;
 uchar sid[256];
 char thumb[2*SHA2_256dlen+1], *thumbfile;
@@ -84,6 +370,7 @@
 char *user, *service, *status, *host, *remote, *cmd;
 
 Oneway recv, send;
+
 void dispatch(void);
 
 void
@@ -163,6 +450,16 @@
 			memmove(p, s, u);
 			p += u;
 			break;
+		/* Why does 's' exist with passing the string length, when it can just be calculated with strlen()? */
+		case 'S':
+			s = va_arg(a, char*);
+			u = strlen(s);
+			if(p+4 > e) goto err;
+			PUT4(p, u), p += 4;
+			if(u > e-p) goto err;
+			memmove(p, s, u);
+			p += u;
+			break;
 		case 'u':
 			u = va_arg(a, int);
 			if(p+4 > e) goto err;
@@ -211,6 +508,14 @@
 			*va_arg(a, int*) = u;
 			p += u;
 			break;
+		/* Why does 's' exist with passing the string length? casting to (char *) is null-terminated and can simply move that way: */
+		case 'S':
+			if(p+4 > e) goto err;
+			u = GET4(p), p += 4;
+			if(u > e-p) goto err;
+			*va_arg(a, char**)= (char*)p;
+			p += u;
+			break;
 		case '[':
 			s = va_arg(a, void*);
 			u = va_arg(a, int);
@@ -280,7 +585,7 @@
 	send.r = send.b;
 	send.w = send.b+n;
 
-if(debug > 1)
+if(debug > 2)
 	fprint(2, "sendpkt: (%d) %.*H\n", send.r[0], (int)(send.w-send.r), send.r);
 
 	if(nsid){
@@ -309,6 +614,26 @@
 	send.seq++;
 }
 
+void
+sendkexinitpkt(Kexinitp data) {
+	sendpkt(
+		"b[SSSSSSSSSSbu",
+		data->ko->msg,
+		data->ko->cookie, sizeof(data->ko->cookie),
+		data->kh->kex,
+		data->kh->host,
+		data->ctos->cipher,
+		data->stoc->cipher,
+		data->ctos->mac,
+		data->stoc->mac,
+		data->ctos->zip,
+		data->stoc->zip,
+		data->ctos->lang,
+		data->stoc->lang,
+		data->ko->firstkexpkt,
+		data->ko->reserved);
+}
+
 int
 readall(int fd, uchar *data, int len)
 {
@@ -366,50 +691,99 @@
 	recv.w = recv.r + n;
 	recv.seq++;
 
-if(debug > 1)
+if(debug > 2)
 	fprint(2, "recvpkt: (%d) %.*H\n", recv.r[0], (int)(recv.w-recv.r), recv.r);
 
 	return recv.r[0];
 }
 
-static char sshrsa[] = "ssh-rsa";
+Kexinitp
+recvkexinit(void) {
+
+	Kexinitp spropp = kexinitalloc();
+	if(unpack(recv.r, recv.w-recv.r, "b[SSSSSSSSSSbu", &(spropp->ko->msg), spropp->ko->cookie, sizeof(spropp->ko->cookie), &(spropp->kh->kex), &(spropp->kh->host), &(spropp->ctos->cipher), &(spropp->stoc->cipher), &(spropp->ctos->mac), &(spropp->stoc->mac), &(spropp->ctos->zip), &(spropp->stoc->zip), &(spropp->ctos->lang), &(spropp->stoc->lang), &(spropp->ko->firstkexpkt), &(spropp->ko->reserved) ) < 0)
+		sysfatal("bad MSG_KEXINIT reply");
+
+	if(debug > 1)
+		printKexinitp("server kexinit proposal", spropp);
+
+	return spropp;
+}
+
+typedef struct {
+	RSApub *rsa;
+	DSApub *dsa;
+} Pub;
+
+static Pub *pub;
 
 int
-rsapub2ssh(RSApub *rsa, uchar *data, int len)
+dsapub2ssh(uchar *data, int len)
 {
-	return pack(data, len, "smm", sshrsa, sizeof(sshrsa)-1, rsa->ek, rsa->n);
+	return pack(data, len, "smmmm", "ssh-dss", sizeof("ssh-dss")-1, pub->dsa->p, pub->dsa->q, pub->dsa->alpha, pub->dsa->key);
 }
 
-RSApub*
-ssh2rsapub(uchar *data, int len)
+void
+ssh2dsapub(uchar *data, int len)
 {
-	RSApub *pub;
 	char *s;
 	int n;
 
-	pub = rsapuballoc();
-	pub->n = mpnew(0);
-	pub->ek = mpnew(0);
-	if(unpack(data, len, "smm", &s, &n, pub->ek, pub->n) < 0
-	|| n != sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) != 0){
-		rsapubfree(pub);
-		return nil;
+	pub->dsa = dsapuballoc();
+	pub->dsa->p = mpnew(0);
+	pub->dsa->q = mpnew(0);
+	pub->dsa->alpha = mpnew(0);
+	pub->dsa->key = mpnew(0);
+	// RFC4253 Section 6.6: alpha is g, key is y
+	if(unpack(data, len, "smmmm", &s, &n, pub->dsa->p, pub->dsa->q, pub->dsa->alpha, pub->dsa->key ) < 0
+	|| n != sizeof("ssh-dss")-1 || memcmp(s, "ssh-dss", n) != 0){
+		dsapubfree(pub->dsa);
 	}
-	return pub;
 }
 
 int
-rsasig2ssh(RSApub *pub, mpint *S, uchar *data, int len)
+dsasig2ssh(mpint *S, uchar *data, int len)
 {
-	int l = (mpsignif(pub->n)+7)/8;
-	if(4+7+4+l > len)
-		return -1;
-	mptober(S, data+4+7+4, l);
-	return pack(data, len, "ss", sshrsa, sizeof(sshrsa)-1, data+4+7+4, l);
+	DSApriv *dsapriv = dsagen(pub->dsa);
+	DSAsig *dsasig = dsasign(dsapriv, S);
+	mptobe(dsasig->r, data, SHA1dlen, nil);
+	mptobe(dsasig->s, data+SHA1dlen, SHA1dlen, nil);
+	dsasigfree(dsasig);
+	dsaprivfree(dsapriv);
+	return pack(data, len, "ss", "ssh-dss", sizeof("ssh-dss")-1, data, len+2*SHA1dlen);
+}
+
+int
+dsasha1verify(uchar *data, int len, mpint *S)
+{
+	mpint *V;
+	int ret;
+	uchar digest[SHA1dlen];
+
+	sha1(data, len, digest, nil);
+	V = betomp(digest, SHA1dlen, nil);
+	ret = V != nil;
+	if(ret){
+		DSAsig *dsasig = dsasigalloc();
+		dsasig->r = mpnew(0);
+		dsasig->s = mpnew(0);
+		mpright(S, 160, dsasig->r);
+		mptrunc(S, 160, dsasig->s);
+		if(debug > 2) {
+			fmtinstall('B', mpfmt);
+			fprint(2, "V: %B\n", V);
+			fprint(2, "S: %B\n", S);
+			fprint(2, "r:%B; s:%B\n", dsasig->r, dsasig->s);
+		}
+		ret = !dsaverify(pub->dsa, dsasig, V);
+		dsasigfree(dsasig);
+		mpfree(V);
+	}
+	return ret;
 }
 
 mpint*
-ssh2rsasig(uchar *data, int len)
+ssh2sig(uchar *data, int len)
 {
 	mpint *m;
 	char *s;
@@ -417,32 +791,65 @@
 
 	m = mpnew(0);
 	if(unpack(data, len, "sm", &s, &n, m) < 0
-	|| n != sizeof(sshrsa)-1 || memcmp(s, sshrsa, n) != 0){
+	|| n != sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != 0){
 		mpfree(m);
 		return nil;
 	}
 	return m;
 }
 
+static char sshrsa[] = "ssh-rsa";
+
+int
+rsapub2ssh(uchar *data, int len)
+{
+	return pack(data, len, "smm", agreep->kh->host, sizeof(agreep->kh->host)-1, pub->rsa->ek, pub->rsa->n);
+}
+
+void
+ssh2rsapub(uchar *data, int len)
+{
+	char *s;
+	int n;
+
+	pub->rsa = rsapuballoc();
+	pub->rsa->n = mpnew(0);
+	pub->rsa->ek = mpnew(0);
+	if(unpack(data, len, "smm", &s, &n, pub->rsa->ek, pub->rsa->n) < 0
+	|| n != sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != 0){
+		rsapubfree(pub->rsa);
+	}
+}
+
+int
+rsasig2ssh(mpint *S, uchar *data, int len)
+{
+	int l = (mpsignif(pub->rsa->n)+7)/8;
+	if(4+7+4+l > len)
+		return -1;
+	mptober(S, data+4+7+4, l);
+	return pack(data, len, "ss", agreep->kh->host, sizeof(agreep->kh->host)-1, data+4+7+4, l);
+}
+
 mpint*
-pkcs1digest(uchar *data, int len, RSApub *pub)
+pkcs1digest(uchar *data, int len)
 {
 	uchar digest[SHA1dlen], buf[256];
 
 	sha1(data, len, digest, nil);
-	return pkcs1padbuf(buf, asn1encodedigest(sha1, digest, buf, sizeof(buf)), pub->n, 1);
+	return pkcs1padbuf(buf, asn1encodedigest(sha1, digest, buf, sizeof(buf)), pub->rsa->n, 1);
 }
 
 int
-pkcs1verify(uchar *data, int len, RSApub *pub, mpint *S)
+pkcs1verify(uchar *data, int len, mpint *S)
 {
 	mpint *V;
 	int ret;
 
-	V = pkcs1digest(data, len, pub);
+	V = pkcs1digest(data, len);
 	ret = V != nil;
 	if(ret){
-		rsaencrypt(pub, S, S);
+		rsaencrypt(pub->rsa, S, S);
 		ret = mpcmp(V, S) == 0;
 		mpfree(V);
 	}
@@ -486,37 +893,27 @@
 void
 kex(int gotkexinit)
 {
-	static char kexalgs[] = "curve25519-sha256,curve25519-sha256@libssh.org";
-	static char cipheralgs[] = "chacha20-poly1305@openssh.com";
-	static char zipalgs[] = "none";
-	static char macalgs[] = "hmac-sha1";	/* work around for github.com */
-	static char langs[] = "";
+	Kexinitp cpropp = kexinitalloc();
+	defKexotherp(cpropp->ko);
+	defKexhostp(cpropp->kh);
+	defAlgsp(cpropp->ctos);
+	defAlgsp(cpropp->stoc);
 
-	uchar cookie[16], x[32], yc[32], z[32], k[32+1], h[SHA2_256dlen], *ys, *ks, *sig;
+	if(debug > 1)
+		printKexinitp("client kexinit properties", cpropp);
+
+	uchar x[32], yc[32], z[32], k[32+1], h[SHA2_256dlen], *ys, *ks, *sig;
 	uchar k12[2*ChachaKeylen];
 	int i, nk, nys, nks, nsig;
 	DigestState *ds;
 	mpint *S, *K;
-	RSApub *pub;
+	if (pub==nil)
+		pub = mallocz(sizeof(pub), 1);
 
 	ds = hashstr(send.v, strlen(send.v), nil);	
 	ds = hashstr(recv.v, strlen(recv.v), ds);
 
-	genrandom(cookie, sizeof(cookie));
-	sendpkt("b[ssssssssssbu", MSG_KEXINIT,
-		cookie, sizeof(cookie),
-		kexalgs, sizeof(kexalgs)-1,
-		sshrsa, sizeof(sshrsa)-1,
-		cipheralgs, sizeof(cipheralgs)-1,
-		cipheralgs, sizeof(cipheralgs)-1,
-		macalgs, sizeof(macalgs)-1,
-		macalgs, sizeof(macalgs)-1,
-		zipalgs, sizeof(zipalgs)-1,
-		zipalgs, sizeof(zipalgs)-1,
-		langs, sizeof(langs)-1,
-		langs, sizeof(langs)-1,
-		0,
-		0);
+	sendkexinitpkt(cpropp);
 	ds = hashstr(send.r, send.w-send.r, ds);
 
 	if(!gotkexinit){
@@ -530,26 +927,12 @@
 	}
 	ds = hashstr(recv.r, recv.w-recv.r, ds);
 
-	if(debug){
-		char *tab[] = {
-			"kexalgs", "hostalgs",
-			"cipher1", "cipher2",
-			"mac1", "mac2",
-			"zip1", "zip2",
-			"lang1", "lang2",
-			nil,
-		}, **t, *s;
-		uchar *p = recv.r+17;
-		int n;
-		for(t=tab; *t != nil; t++){
-			if(unpack(p, recv.w-p, "s.", &s, &n, &p) < 0)
-				break;
-			fprint(2, "%s: %.*s\n", *t, utfnlen(s, n), s);
-		}
-	}
-
+	Kexinitp spropp = recvkexinit();
+	authagree(cpropp, spropp);
+	if (debug > 1)
+		printKexinitp("agreement", agreep);
 	curve25519_dh_new(x, yc);
-	yc[31] &= ~0x80;
+	yc[31] &= ~0x80; /* curve25519_dh_new does & 0x80, so why reverse? */
 
 	sendpkt("bs", MSG_ECDH_INIT, yc, sizeof(yc));
 Next1:	switch(recvpkt()){
@@ -559,13 +942,15 @@
 	case MSG_KEXINIT:
 		sysfatal("inception");
 	case MSG_ECDH_REPLY:
+
+		/* RFC5656; ys is being set to Q_S */
 		if(unpack(recv.r, recv.w-recv.r, "_sss", &ks, &nks, &ys, &nys, &sig, &nsig) < 0)
 			sysfatal("bad ECDH_REPLY");
 		break;
 	}
 
 	if(nys != 32)
-		sysfatal("bad server ECDH ephermal public key length");
+		sysfatal("bad server ECDH ephemeral public key length");
 
 	ds = hashstr(ks, nks, ds);
 	ds = hashstr(yc, 32, ds);
@@ -587,18 +972,29 @@
 		if(ok == nil || !okThumbprint(h, sizeof(h), ok)){
 			if(ok != nil) werrstr("unknown host");
 			fprint(2, "%s: %r\n", argv0);
-			fprint(2, "verify hostkey: %s %.*[\n", sshrsa, nks, ks);
+			fprint(2, "verify hostkey: %s %.*[\n", agreep->kh->host, nks, ks);
 			fprint(2, "add thumbprint after verification:\n");
-			fprint(2, "\techo 'ssh sha256=%s server=%s' >> %q\n", thumb, host, thumbfile);
+			fprint(2, "\techo 'ssh sha256=%s server=%s type=%s' >> %q\n", thumb, host, agreep->kh->host, thumbfile);
 			sysfatal("checking hostkey failed: %r");
 		}
 		freeThumbprints(ok);
 	}
 
-	if((pub = ssh2rsapub(ks, nks)) == nil)
-		sysfatal("bad server public key");
-	if((S = ssh2rsasig(sig, nsig)) == nil)
-		sysfatal("bad server signature");
+	// TODO: branch for ed25519
+	if(!strcmp(agreep->kh->host,"ssh-dss")) {
+		ssh2dsapub(ks, nks);
+		if(pub->dsa == nil)
+			sysfatal("bad server dss public key");
+		if((S = ssh2sig(sig, nsig)) == nil)
+			sysfatal("no server dss signature");
+	}
+	else {
+		ssh2rsapub(ks, nks);
+		if(pub->rsa == nil)
+			sysfatal("bad server rsa public key");
+		if((S = ssh2sig(sig, nsig)) == nil)
+			sysfatal("no server rsa signature");
+	}
 
 	curve25519_dh_finish(x, ys, z);
 
@@ -609,10 +1005,17 @@
 
 	ds = hashstr(k, nk, ds);
 	sha2_256(nil, 0, h, ds);
-	if(!pkcs1verify(h, sizeof(h), pub, S))
-		sysfatal("server verification failed");
+	// TODO: branch for ssh-ed25519
+	if(!strcmp(agreep->kh->host,"ssh-dss")){
+		if(!dsasha1verify(h, sizeof(h), S))
+			sysfatal("server dss verification failed");
+	}
+	else {
+		if(!pkcs1verify(h, sizeof(h), S))
+			sysfatal("server rsa verification failed");
+		rsapubfree(pub->rsa);
+	}
 	mpfree(S);
-	rsapubfree(pub);
 
 	sendpkt("b", MSG_NEWKEYS);
 Next2:	switch(recvpkt()){
@@ -705,7 +1108,6 @@
 	char *s;
 	mpint *S;
 	AuthRpc *rpc;
-	RSApub *pub;
 
 	if(!authok(authmeth))
 		return -1;
@@ -717,33 +1119,64 @@
 		return -1;
 	}
 
-	s = "proto=rsa service=ssh role=client";
+	s = !strcmp(agreep->kh->host,"ssh-dss") ? "proto=dsa service=ssh role=client" : "proto=rsa service=ssh role=client";
 	if(auth_rpc(rpc, "start", s, strlen(s)) != ARok){
 		auth_freerpc(rpc);
 		close(afd);
 		return -1;
 	}
 
-	pub = rsapuballoc();
-	pub->n = mpnew(0);
-	pub->ek = mpnew(0);
+	// TODO: branch for ssh-ed25519
+	if(!strcmp(agreep->kh->host,"ssh-dss")){
+		pub->dsa = dsapuballoc();
+		pub->dsa->p = mpnew(0);
+		pub->dsa->q = mpnew(0);
+		pub->dsa->alpha = mpnew(0);
+		pub->dsa->key = mpnew(0);
+	}
+	else {
+		pub->rsa = rsapuballoc();
+		pub->rsa->n = mpnew(0);
+		pub->rsa->ek = mpnew(0);
+	}
 
 	while(auth_rpc(rpc, "read", nil, 0) == ARok){
 		s = rpc->arg;
-		if(strtomp(s, &s, 16, pub->n) == nil)
-			break;
-		if(*s++ != ' ')
-			continue;
-		if(strtomp(s, nil, 16, pub->ek) == nil)
-			continue;
-		npk = rsapub2ssh(pub, pk, sizeof(pk));
+		// TODO: branch for ssh-ed25519
+		if(!strcmp(agreep->kh->host,"ssh-dss")){
+			// This seems brittle and dependent upon the order of values set in factotum
+			if(strtomp(s, &s, 16, pub->dsa->p) == nil)
+				break;
+			if(*s++ != ' ')
+				continue;
+			if(strtomp(s, nil, 16, pub->dsa->q) == nil)
+				continue;
+			if(*s++ != ' ')
+				continue;
+			if(strtomp(s, nil, 16, pub->dsa->alpha) == nil)
+				continue;
+			if(*s++ != ' ')
+				continue;
+			if(strtomp(s, nil, 16, pub->dsa->key) == nil)
+				continue;
+			npk = dsapub2ssh(pk, sizeof(pk));
+		}
+		else {
+			if(strtomp(s, &s, 16, pub->rsa->n) == nil)
+				break;
+			if(*s++ != ' ')
+				continue;
+			if(strtomp(s, nil, 16, pub->rsa->ek) == nil)
+				continue;
+			npk = rsapub2ssh(pk, sizeof(pk));
+		}
 
 		sendpkt("bsssbss", MSG_USERAUTH_REQUEST,
 			user, strlen(user),
 			service, strlen(service),
 			authmeth, sizeof(authmeth)-1,
 			0,
-			sshrsa, sizeof(sshrsa)-1,
+			agreep->kh->host, sizeof(agreep->kh->host)-1,
 			pk, npk);
 Next1:		switch(recvpkt()){
 		default:
@@ -766,9 +1199,16 @@
 			service, strlen(service),
 			authmeth, sizeof(authmeth)-1,
 			1,
-			sshrsa, sizeof(sshrsa)-1,
+			agreep->kh->host, sizeof(agreep->kh->host)-1,
 			pk, npk);
-		S = pkcs1digest(send.b, n, pub);
+		// TODO branch for ssh-ed25519
+		if (!strcmp(agreep->kh->host,"ssh-dss")){
+			sha1(send.b, n, sig, nil);
+			S = strtomp((char *)sig, nil, 10, nil);
+		}
+		else {
+			S = pkcs1digest(send.b, n);
+		}
 		n = snprint((char*)send.b, sizeof(send.b), "%B", S);
 		mpfree(S);
 
@@ -778,7 +1218,13 @@
 			break;
 
 		S = strtomp(rpc->arg, nil, 16, nil);
-		nsig = rsasig2ssh(pub, S, sig, sizeof(sig));
+		// TODO branch for ssh-ed25519
+		if (!strcmp(agreep->kh->host,"ssh-dss")){
+			nsig = dsasig2ssh(S, sig, sizeof(sig));
+		}
+		else {
+			nsig = rsasig2ssh(S, sig, sizeof(sig));
+		}
 		mpfree(S);
 
 		/* send final userauth request with the signature */
@@ -787,7 +1233,7 @@
 			service, strlen(service),
 			authmeth, sizeof(authmeth)-1,
 			1,
-			sshrsa, sizeof(sshrsa)-1,
+			agreep->kh->host, sizeof(agreep->kh->host)-1,
 			pk, npk,
 			sig, nsig);
 Next2:		switch(recvpkt()){
@@ -801,13 +1247,25 @@
 		case MSG_USERAUTH_SUCCESS:
 			break;
 		}
-		rsapubfree(pub);
+		// TODO: branch for ssh-ed25519
+		if (!strcmp(agreep->kh->host,"ssh-dss")){
+			dsapubfree(pub->dsa);
+		}
+		else {
+			rsapubfree(pub->rsa);
+		}
 		auth_freerpc(rpc);
 		close(afd);
 		return 0;
 	}
 Failed:
-	rsapubfree(pub);
+	// TODO: branch for ssh-ed25519
+	if (!strcmp(agreep->kh->host,"ssh-dss")){
+		dsapubfree(pub->dsa);
+	}
+	else {
+		rsapubfree(pub->rsa);
+	}
 	auth_freerpc(rpc);
 	close(afd);
 	return -1;	
@@ -1157,7 +1615,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-dR] [-t thumbfile] [-T tries] [-u user] [-h] [user@]host [-W remote!port] [cmd args...]\n", argv0);
+	fprint(2, "usage: %s [-d] [-R] [-r] [-t thumbfile] [-T tries] [-u user] [-h] [user@]host [-W remote!port] [cmd args...]\n", argv0);
 	exits("usage");
 }
 
@@ -1258,7 +1716,7 @@
 	fprint(fd, "%s\r\n", send.v);
 	recv.v = readline();
 	if(debug)
-		fprint(2, "server verison: %s\n", recv.v);
+		fprint(2, "server version: %s\n", recv.v);
 	if(strncmp("SSH-2.0-", recv.v, 8) != 0)
 		sysfatal("bad server version: %s", recv.v);
 	recv.v = strdup(recv.v);


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
  2020-06-12 20:35 [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange Romano
@ 2020-06-12 22:58 ` cinap_lenrek
  2020-06-13  0:18   ` Romano
  0 siblings, 1 reply; 7+ messages in thread
From: cinap_lenrek @ 2020-06-12 22:58 UTC (permalink / raw)
  To: 9front

no. we'r not going to bring back dsa from the grave.

are you sure rsync.net does not support rsa keys?

they give an example on ther website how to generate a keypair
using 4096-bit rsa as an example:

https://rsync.net/resources/howto/ssh_keys.html

introducing edwards-curve support should go into libsec,
and we'd need to add factotum support.

this stuff is fun, but tricky to get right.

we already implemented edwards curves for dp9ik using libmp,
the reason i havnt added edwards curve support for tls is
that the intrgration is quite a bit tricky and the standard
was still in draft at the time.

on the code, it adds quite alot of lines. i hate pointer
typedefs and i dont like the introduction of global "pub"
variable. and all these if(strcmp())'s.

also there are some misleading comments:

+	/*
+	'At some future time, it is expected that another algorithm, one with better
+	strength, will become so prevalent and ubiquitous that the use of
+	"3des-cbc" will be deprecated by another STANDARDS ACTION.' - RFC4253
+	No standards action has yet deprecated it, but have not seen it supported
+	by default in any server.
+	*/
+	algsp->cipher = "chacha20-poly1305@openssh.com";

what is that supposed to mean? what has 3des todo with chacha20-poly1305?

are you trying to indicate that the IETF is going to deprecate it?

--
cinap


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
  2020-06-12 22:58 ` [9front] " cinap_lenrek
@ 2020-06-13  0:18   ` Romano
  2020-06-13  8:38     ` hiro
  0 siblings, 1 reply; 7+ messages in thread
From: Romano @ 2020-06-13  0:18 UTC (permalink / raw)
  To: 9front, cinap_lenrek

Thanks Cinap!

We discussed a bit on irc, but wanted to thank you again for your feedback.  RFC8709 is now published for ssh-ed25519. My comment was indicating that while 3des-cbc is required, I haven't seen it supported. And I meant to imply chacha20-poly1305 as the stronger algorithm implied by the RFC.


On June 12, 2020 10:58:41 PM UTC, cinap_lenrek@felloff.net wrote:
>no. we'r not going to bring back dsa from the grave.
>
>are you sure rsync.net does not support rsa keys?
>
>they give an example on ther website how to generate a keypair
>using 4096-bit rsa as an example:
>
>https://rsync.net/resources/howto/ssh_keys.html
>
>introducing edwards-curve support should go into libsec,
>and we'd need to add factotum support.
>
>this stuff is fun, but tricky to get right.
>
>we already implemented edwards curves for dp9ik using libmp,
>the reason i havnt added edwards curve support for tls is
>that the intrgration is quite a bit tricky and the standard
>was still in draft at the time.
>
>on the code, it adds quite alot of lines. i hate pointer
>typedefs and i dont like the introduction of global "pub"
>variable. and all these if(strcmp())'s.
>
>also there are some misleading comments:
>
>+	/*
>+	'At some future time, it is expected that another algorithm, one with
>better
>+	strength, will become so prevalent and ubiquitous that the use of
>+	"3des-cbc" will be deprecated by another STANDARDS ACTION.' - RFC4253
>+	No standards action has yet deprecated it, but have not seen it
>supported
>+	by default in any server.
>+	*/
>+	algsp->cipher = "chacha20-poly1305@openssh.com";
>
>what is that supposed to mean? what has 3des todo with
>chacha20-poly1305?
>
>are you trying to indicate that the IETF is going to deprecate it?
>
>--
>cinap


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
  2020-06-13  0:18   ` Romano
@ 2020-06-13  8:38     ` hiro
  2020-06-13 16:32       ` Romano
  2020-06-13 16:39       ` ori
  0 siblings, 2 replies; 7+ messages in thread
From: hiro @ 2020-06-13  8:38 UTC (permalink / raw)
  To: 9front

3des-cbc is required by what?


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
  2020-06-13  8:38     ` hiro
@ 2020-06-13 16:32       ` Romano
  2020-06-13 16:39       ` ori
  1 sibling, 0 replies; 7+ messages in thread
From: Romano @ 2020-06-13 16:32 UTC (permalink / raw)
  To: 9front, hiro

It's in the RFC I referenced in the patch.

On June 13, 2020 8:38:48 AM UTC, hiro <23hiro@gmail.com> wrote:
>3des-cbc is required by what?


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
  2020-06-13  8:38     ` hiro
  2020-06-13 16:32       ` Romano
@ 2020-06-13 16:39       ` ori
  2020-06-13 16:46         ` Romano
  1 sibling, 1 reply; 7+ messages in thread
From: ori @ 2020-06-13 16:39 UTC (permalink / raw)
  To: 23hiro, 9front

> 3des-cbc is required by what?

Attackers



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [9front] [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange
  2020-06-13 16:39       ` ori
@ 2020-06-13 16:46         ` Romano
  0 siblings, 0 replies; 7+ messages in thread
From: Romano @ 2020-06-13 16:46 UTC (permalink / raw)
  To: 9front, ori, 23hiro

Which is also spelled NSA.

On June 13, 2020 4:39:14 PM UTC, ori@eigenstate.org wrote:
>> 3des-cbc is required by what?
>
>Attackers


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-06-13 16:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-12 20:35 [PATCH] ssh.c algorithm negotiation + ssh-dss key exchange Romano
2020-06-12 22:58 ` [9front] " cinap_lenrek
2020-06-13  0:18   ` Romano
2020-06-13  8:38     ` hiro
2020-06-13 16:32       ` Romano
2020-06-13 16:39       ` ori
2020-06-13 16:46         ` Romano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).