From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=DKIM_SIGNED,DKIM_VALID autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 32371 invoked from network); 29 Sep 2022 06:19:51 -0000 Received: from 9front.inri.net (168.235.81.73) by inbox.vuxu.org with ESMTPUTF8; 29 Sep 2022 06:19:51 -0000 Received: from pb-smtp2.pobox.com ([64.147.108.71]) by 9front; Thu Sep 29 02:17:20 -0400 2022 Received: from pb-smtp2.pobox.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id 1B8B814B590 for <9front@9front.org>; Thu, 29 Sep 2022 02:17:16 -0400 (EDT) (envelope-from unobe@cpan.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed; d=pobox.com; h=message-id :to:subject:date:from:in-reply-to:mime-version:content-type; s= sasl; bh=3QdvbdOtyjvEj0gvPWJh8phC4A777tyw0bpi34jOk2U=; b=tNfbHMl aLnB2oQkdlTBlYX4dnCfssiZNN0gU3XHfbxHhXRTZULlq8f0BDOYB1i35dbDTNx7 Mh/sn9vaINwenM7xmxSKOVdHRkNSJuhDgmdU7herfGjfQD4JEDRjZe2CeiDmDBe3 0gMTCLlqBCOqbqyI+Gb9ZiEJ6ZWrpIz7ESH8= Received: from pb-smtp2.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id 1411B14B58F for <9front@9front.org>; Thu, 29 Sep 2022 02:17:16 -0400 (EDT) (envelope-from unobe@cpan.org) Received: from strider.localdomain (unknown [75.237.206.13]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pb-smtp2.pobox.com (Postfix) with ESMTPSA id D4C9E14B58E for <9front@9front.org>; Thu, 29 Sep 2022 02:17:14 -0400 (EDT) (envelope-from unobe@cpan.org) Message-ID: <0E45F4D8CC65A8FD8590EF9070580D16@smtp.pobox.com> To: 9front@9front.org Date: Wed, 28 Sep 2022 23:17:12 -0700 From: unobe@cpan.org In-Reply-To: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="upas-ljjgztuokrveizcyhafgtydmvd" X-Pobox-Relay-ID: 5CB3DD2A-3FBE-11ED-B07A-307A8E0A682E-09620299!pb-smtp2.pobox.com List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: anonymous rich-client-based deep-learning persistence control Subject: Re: [9front] obsolete cryptographic algorithms Reply-To: 9front@9front.org Precedence: bulk This is a multi-part message in MIME format. --upas-ljjgztuokrveizcyhafgtydmvd Content-Disposition: inline Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Quoth ori@eigenstate.org: > Quoth hiro <23hiro@gmail.com>: > > git history is a shitty way to organise rarely used files. > > > > the belief here is that they're never used. > Please don't remove it. I have a patch to ssh.c to access rsync.net. It was never cleaned up, but it serves my purposes well, until ssh-ed25519 is supported in 9front for key exchange. The thread where I discussed this was back in June 2020. The patch has changed since then (due to changes to ssh.c itself), but it allows me to use 9front in just one more area of my life without trampolining through a linux or mac host. Attached is my current patch that shows using the DSA code in case anyone else wants to drive fast and take chances. --upas-ljjgztuokrveizcyhafgtydmvd Content-Disposition: attachment; filename=ssh-dss.c.patch Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit diff 3a47c8bfbe9298fed2b6dcc5ac19a7af4c96c52f uncommitted --- a/sys/src/cmd/ssh.c +++ b/sys/src/cmd/ssh.c @@ -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 #include #include @@ -5,6 +16,7 @@ #include #include +/* 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,52 +691,101 @@ 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; } static char rsasha256[] = "rsa-sha2-256"; 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+12+4+l > len) - return -1; - mptober(S, data+4+12+4, l); - return pack(data, len, "ss", rsasha256, sizeof(rsasha256)-1, data+4+12+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; @@ -419,7 +793,7 @@ m = mpnew(0); if(unpack(data, len, "sm", &s, &n, m) < 0 - || n != sizeof(rsasha256)-1 || memcmp(s, rsasha256, n) != 0){ + || n != sizeof(agreep->kh->host)-1 || memcmp(s, agreep->kh->host, n) != 0){ mpfree(m); return nil; } @@ -426,25 +800,58 @@ 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[SHA2_256dlen], buf[256]; sha2_256(data, len, digest, nil); - return pkcs1padbuf(buf, asn1encodedigest(sha2_256, 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); } @@ -488,37 +895,28 @@ 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[] = ""; - 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, - rsasha256, sizeof(rsasha256)-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){ @@ -532,26 +930,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()){ @@ -561,6 +945,8 @@ 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; @@ -567,7 +953,7 @@ } 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); @@ -589,18 +975,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"); + } if(!curve25519_dh_finish(x, ys, z)) sysfatal("unlucky shared key"); @@ -612,10 +1009,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()){ @@ -708,7 +1112,6 @@ char *s; mpint *S; AuthRpc *rpc; - RSApub *pub; if(!authok(authmeth)) return -1; @@ -720,7 +1123,7 @@ 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); @@ -727,19 +1130,50 @@ 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), @@ -746,7 +1180,7 @@ service, strlen(service), authmeth, sizeof(authmeth)-1, 0, - rsasha256, sizeof(rsasha256)-1, + agreep->kh->host, sizeof(agreep->kh->host)-1, pk, npk); Next1: switch(recvpkt()){ default: @@ -769,9 +1203,16 @@ service, strlen(service), authmeth, sizeof(authmeth)-1, 1, - rsasha256, sizeof(rsasha256)-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); @@ -781,7 +1222,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 */ @@ -790,7 +1237,7 @@ service, strlen(service), authmeth, sizeof(authmeth)-1, 1, - rsasha256, sizeof(rsasha256)-1, + agreep->kh->host, sizeof(agreep->kh->host)-1, pk, npk, sig, nsig); Next2: switch(recvpkt()){ @@ -804,13 +1251,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; @@ -1171,7 +1630,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"); } --upas-ljjgztuokrveizcyhafgtydmvd--