/* * SSH RSA authentication. * * Client protocol: * read public key * if you don't like it, read another, repeat * write challenge * read response * all numbers are hexadecimal biginits parsable with strtomp. * */ #include "dat.h" enum { CHavePub, CHaveResp, Maxphase, }; static char *phasenames[] = { [CHavePub] "CHavePub", [CHaveResp] "CHaveResp", }; struct State { RSApriv *priv; mpint *resp; int off; Key *key; }; static RSApriv* readrsapriv(Key *k) { char *a; RSApriv *priv; priv = rsaprivalloc(); if((a=_strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil) goto Error; return priv; Error: rsaprivfree(priv); return nil; } static int rsainit(Proto*, Fsstate *fss) { int iscli; State *s; if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) return failure(fss, nil); if(iscli==0) return failure(fss, "rsa server unimplemented"); s = emalloc(sizeof *s); fss->phasename = phasenames; fss->maxphase = Maxphase; fss->phase = CHavePub; fss->ps = s; return RpcOk; } static int rsaread(Fsstate *fss, void *va, uint *n) { RSApriv *priv; State *s; Keyinfo ki; s = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "read"); case CHavePub: if(s->key){ closekey(s->key); s->key = nil; } mkkeyinfo(&ki, fss, nil); ki.skip = s->off; ki.noconf = 1; if(findkey(&s->key, &ki, nil) != RpcOk) return failure(fss, nil); s->off++; priv = s->key->priv; *n = snprint(va, *n, "%B", priv->pub.n); return RpcOk; case CHaveResp: *n = snprint(va, *n, "%B", s->resp); fss->phase = Established; return RpcOk; } } static int rsawrite(Fsstate *fss, void *va, uint) { mpint *m; State *s; s = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "write"); case CHavePub: if(s->key == nil) return failure(fss, "no current key"); switch(canusekey(fss, s->key)){ case -1: return RpcConfirm; case 0: return failure(fss, "confirmation denied"); case 1: break; } m = strtomp(va, nil, 16, nil); if(m == nil) return failure(fss, "invalid challenge value"); m = rsadecrypt(s->key->priv, m, m); s->resp = m; fss->phase = CHaveResp; return RpcOk; } } static void rsaclose(Fsstate *fss) { State *s; s = fss->ps; if(s->key) closekey(s->key); if(s->resp) mpfree(s->resp); free(s); } static int rsaaddkey(Key *k, int before) { fmtinstall('B', mpfmt); if((k->priv = readrsapriv(k)) == nil){ werrstr("malformed key data"); return -1; } return replacekey(k, before); } static void rsaclosekey(Key *k) { rsaprivfree(k->priv); } Proto rsa = { .name= "rsa", .init= rsainit, .write= rsawrite, .read= rsaread, .close= rsaclose, .addkey= rsaaddkey, .closekey= rsaclosekey, };