diff options
Diffstat (limited to 'src/cmd/factotum/sshrsa.c')
-rw-r--r-- | src/cmd/factotum/sshrsa.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/cmd/factotum/sshrsa.c b/src/cmd/factotum/sshrsa.c new file mode 100644 index 00000000..7227c855 --- /dev/null +++ b/src/cmd/factotum/sshrsa.c @@ -0,0 +1,172 @@ +/* + * 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 +sshrsainit(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, "sshrsa server unimplemented"); + + s = emalloc(sizeof *s); + fss->phasename = phasenames; + fss->maxphase = Maxphase; + fss->phase = CHavePub; + fss->ps = s; + return RpcOk; +} + +static int +sshrsaread(Fsstate *fss, void *va, uint *n) +{ + RSApriv *priv; + State *s; + + s = fss->ps; + switch(fss->phase){ + default: + return phaseerror(fss, "read"); + case CHavePub: + if(s->key){ + closekey(s->key); + s->key = nil; + } + if((s->key = findkey(fss, Kuser, nil, s->off, fss->attr, nil)) == nil) + 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 +sshrsawrite(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"); + m = strtomp(va, nil, 16, nil); + m = rsadecrypt(s->key->priv, m, m); + s->resp = m; + fss->phase = CHaveResp; + return RpcOk; + } +} + +static void +sshrsaclose(Fsstate *fss) +{ + State *s; + + s = fss->ps; + if(s->key) + closekey(s->key); + if(s->resp) + mpfree(s->resp); + free(s); +} + +static int +sshrsaaddkey(Key *k) +{ + fmtinstall('B', mpconv); + + if((k->priv = readrsapriv(k)) == nil){ + werrstr("malformed key data"); + return -1; + } + return replacekey(k); +} + +static void +sshrsaclosekey(Key *k) +{ + rsaprivfree(k->priv); +} + +Proto sshrsa = { +.name= "sshrsa", +.init= sshrsainit, +.write= sshrsawrite, +.read= sshrsaread, +.close= sshrsaclose, +.addkey= sshrsaaddkey, +.closekey= sshrsaclosekey, +}; |