/* * DSA authentication. * * Sign * start n=xxx ek=xxx * write hash(msg) * read signature(hash(msg)) * * Verify: * start n=xxx ek=xxx * write hash(msg) * write signature(hash(msg)) * read ok or fail * */ #include "dat.h" enum { VNeedHash, VNeedSig, VHaveResp, SNeedHash, SHaveResp, Maxphase, DSAdlen = 20, DSAsiglen = DSAdlen*2, }; static char *phasenames[] = { [VNeedHash] "VNeedHash", [VNeedSig] "VNeedSig", [VHaveResp] "VHaveResp", [SNeedHash] "SNeedHash", [SHaveResp] "SHaveResp", }; struct State { DSApriv *priv; mpint *resp; int off; Key *key; mpint *digest; int sigresp; }; static DSApriv* readdsapriv(Key *k) { char *a; DSApriv *priv; priv = dsaprivalloc(); if((a=_strfindattr(k->attr, "p"))==nil || (priv->pub.p=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->attr, "q"))==nil || (priv->pub.q=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->attr, "alpha"))==nil || (priv->pub.alpha=strtomp(a, nil, 16, nil))==nil) goto Error; if((a=_strfindattr(k->attr, "key"))==nil || (priv->pub.key=strtomp(a, nil, 16, nil))==nil) goto Error; if(k->privattr == nil) /* only public half */ return priv; if((a=_strfindattr(k->privattr, "!secret"))==nil || (priv->secret=strtomp(a, nil, 16, nil))==nil) goto Error; return priv; Error: dsaprivfree(priv); return nil; } /* * Mptobe but shift right to fill buffer. */ static void mptoberjust(mpint *b, uchar *buf, uint len) { int n; n = mptobe(b, buf, len, nil); assert(n >= 0); if(n < len){ len -= n; memmove(buf+len, buf, n); memset(buf, 0, len); } } static int dsainit(Proto*, Fsstate *fss) { Keyinfo ki; State *s; char *role; if((role = _strfindattr(fss->attr, "role")) == nil) return failure(fss, "dsa role not specified"); if(strcmp(role, "sign") == 0) fss->phase = SNeedHash; else if(strcmp(role, "verify") == 0) fss->phase = VNeedHash; else return failure(fss, "dsa role %s unimplemented", role); s = emalloc(sizeof *s); fss->phasename = phasenames; fss->maxphase = Maxphase; fss->ps = s; switch(fss->phase){ case SNeedHash: case VNeedHash: mkkeyinfo(&ki, fss, nil); if(findkey(&s->key, &ki, nil) != RpcOk) return failure(fss, nil); /* signing needs private key */ if(fss->phase == SNeedHash && s->key->privattr == nil) return failure(fss, "missing private half of key -- cannot sign"); } return RpcOk; } static int dsaread(Fsstate *fss, void *va, uint *n) { DSApriv *priv; DSAsig *sig; State *s; s = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "read"); case SHaveResp: if(*n < DSAsiglen) failure(fss, "signature buffer too short"); priv = s->key->priv; sig = dsasign(priv, s->digest); assert(sig != nil); if(mpsignif(sig->r) > DSAdlen*8 || mpsignif(sig->s) > DSAdlen*8){ dsasigfree(sig); failure(fss, "signature too long"); } mptoberjust(sig->r, (uchar*)va, DSAdlen); mptoberjust(sig->s, (uchar*)va+DSAdlen, DSAdlen); dsasigfree(sig); *n = DSAsiglen; fss->phase = Established; return RpcOk; case VHaveResp: *n = snprint(va, *n, "%s", s->sigresp == 0? "ok" : "signature does not verify"); fss->phase = Established; return RpcOk; } } static int dsawrite(Fsstate *fss, void *va, uint n) { DSApriv *priv; DSAsig *sig; State *s; s = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "write"); case SNeedHash: case VNeedHash: if(n != SHA1dlen) return failure(fss, "digest length %d should be %d", n, SHA1dlen); s->digest = betomp((uchar*)va, SHA1dlen, nil); assert(s->digest != nil); if(fss->phase == VNeedHash) fss->phase = VNeedSig; else fss->phase = SHaveResp; return RpcOk; case VNeedSig: if(n != DSAsiglen) return failure(fss, "signature length %d should be %d", n, DSAsiglen); sig = dsasigalloc(); sig->r = betomp((uchar*)va, DSAdlen, nil); sig->s = betomp((uchar*)va+DSAdlen, DSAdlen, nil); priv = s->key->priv; s->sigresp = dsaverify(&priv->pub, sig, s->digest); dsasigfree(sig); fss->phase = VHaveResp; return RpcOk; } } static void dsaclose(Fsstate *fss) { State *s; s = fss->ps; if(s->key) closekey(s->key); if(s->resp) mpfree(s->resp); if(s->digest) mpfree(s->digest); free(s); } static int dsaaddkey(Key *k, int before) { fmtinstall('B', mpfmt); if((k->priv = readdsapriv(k)) == nil){ werrstr("malformed key data"); return -1; } return replacekey(k, before); } static void dsaclosekey(Key *k) { dsaprivfree(k->priv); } Proto dsa = { .name= "dsa", .init= dsainit, .write= dsawrite, .read= dsaread, .close= dsaclose, .addkey= dsaaddkey, .closekey= dsaclosekey, };