/*
* 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,
};
|