jehanne/sys/src/cmd/auth/factotum/wpapsk.c

225 lines
4.7 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/* Portions of this file are Copyright (C) 9front's team.
* See /doc/license/9front-mit for details about the licensing.
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
*/
/*
* WPA-PSK
*
* Client protocol:
* write challenge: smac[6] + amac[6] + snonce[32] + anonce[32]
* read response: ptk[64]
*
* Server protocol:
* unimplemented
*/
#include "dat.h"
enum {
PMKlen = 256/8,
PTKlen = 512/8,
Eaddrlen = 6,
Noncelen = 32,
};
enum
{
CNeedChal,
CHaveResp,
Maxphase,
};
static char *phasenames[Maxphase] = {
[CNeedChal] "CNeedChal",
[CHaveResp] "CHaveResp",
};
struct State
{
uint8_t resp[PTKlen];
};
static int
hextob(char *s, char **sp, uint8_t *b, int n)
{
int r;
n <<= 1;
for(r = 0; r < n && *s; s++){
*b <<= 4;
if(*s >= '0' && *s <= '9')
*b |= (*s - '0');
else if(*s >= 'a' && *s <= 'f')
*b |= 10+(*s - 'a');
else if(*s >= 'A' && *s <= 'F')
*b |= 10+(*s - 'A');
else break;
if((++r & 1) == 0)
b++;
}
if(sp != nil)
*sp = s;
return r >> 1;
}
static void
pass2pmk(char *pass, char *ssid, uint8_t pmk[PMKlen])
{
if(hextob(pass, nil, pmk, PMKlen) == PMKlen)
return;
pbkdf2_x((uint8_t*)pass, strlen(pass), (uint8_t*)ssid, strlen(ssid), 4096, pmk, PMKlen, hmac_sha1, SHA1dlen);
}
static void
prfn(uint8_t *k, int klen, char *a, uint8_t *b, int blen, uint8_t *d, int dlen)
{
uint8_t r[SHA1dlen], i;
DigestState *ds;
int n;
i = 0;
while(dlen > 0){
ds = hmac_sha1((uint8_t*)a, strlen(a)+1, k, klen, nil, nil);
hmac_sha1(b, blen, k, klen, nil, ds);
hmac_sha1(&i, 1, k, klen, r, ds);
i++;
n = dlen;
if(n > sizeof(r))
n = sizeof(r);
memmove(d, r, n); d += n;
dlen -= n;
}
}
static void
calcptk(uint8_t pmk[PMKlen], uint8_t smac[Eaddrlen], uint8_t amac[Eaddrlen],
uint8_t snonce[Noncelen], uint8_t anonce[Noncelen],
uint8_t ptk[PTKlen])
{
uint8_t b[2*Eaddrlen + 2*Noncelen];
if(memcmp(smac, amac, Eaddrlen) > 0){
memmove(b + Eaddrlen*0, amac, Eaddrlen);
memmove(b + Eaddrlen*1, smac, Eaddrlen);
} else {
memmove(b + Eaddrlen*0, smac, Eaddrlen);
memmove(b + Eaddrlen*1, amac, Eaddrlen);
}
if(memcmp(snonce, anonce, Eaddrlen) > 0){
memmove(b + Eaddrlen*2 + Noncelen*0, anonce, Noncelen);
memmove(b + Eaddrlen*2 + Noncelen*1, snonce, Noncelen);
} else {
memmove(b + Eaddrlen*2 + Noncelen*0, snonce, Noncelen);
memmove(b + Eaddrlen*2 + Noncelen*1, anonce, Noncelen);
}
prfn(pmk, PMKlen, "Pairwise key expansion", b, sizeof(b), ptk, PTKlen);
}
static int
wpapskinit(Proto *p, Fsstate *fss)
{
int iscli;
State *s;
if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
return failure(fss, nil);
if(!iscli)
return failure(fss, "%s server not supported", p->name);
s = emalloc(sizeof *s);
fss->phasename = phasenames;
fss->maxphase = Maxphase;
fss->phase = CNeedChal;
fss->ps = s;
return RpcOk;
}
static int
wpapskwrite(Fsstate *fss, void *va, uint32_t n)
{
uint8_t pmk[PMKlen], *smac, *amac, *snonce, *anonce;
char *pass, *essid;
State *s;
int ret;
Key *k;
Keyinfo ki;
Attr *attr;
s = fss->ps;
if(fss->phase != CNeedChal)
return phaseerror(fss, "write");
if(n != (2*Eaddrlen + 2*Noncelen))
return phaseerror(fss, "bad write size");
attr = _delattr(_copyattr(fss->attr), "role");
mkkeyinfo(&ki, fss, attr);
ret = findkey(&k, &ki, "%s", fss->proto->keyprompt);
_freeattr(attr);
if(ret != RpcOk)
return ret;
pass = _strfindattr(k->privattr, "!password");
if(pass == nil)
return failure(fss, "key has no password");
essid = _strfindattr(k->attr, "essid");
if(essid == nil)
return failure(fss, "key has no essid");
setattrs(fss->attr, k->attr);
closekey(k);
pass2pmk(pass, essid, pmk);
smac = va;
amac = smac + Eaddrlen;
snonce = amac + Eaddrlen;
anonce = snonce + Noncelen;
calcptk(pmk, smac, amac, snonce, anonce, s->resp);
fss->phase = CHaveResp;
return RpcOk;
}
static int
wpapskread(Fsstate *fss, void *va, uint32_t *n)
{
State *s;
s = fss->ps;
if(fss->phase != CHaveResp)
return phaseerror(fss, "read");
if(*n > sizeof(s->resp))
*n = sizeof(s->resp);
memmove(va, s->resp, *n);
fss->phase = Established;
fss->haveai = 0;
return RpcOk;
}
static void
wpapskclose(Fsstate *fss)
{
State *s;
s = fss->ps;
free(s);
}
Proto wpapsk = {
.name= "wpapsk",
.init= wpapskinit,
.write= wpapskwrite,
.read= wpapskread,
.close= wpapskclose,
.addkey= replacekey,
.keyprompt= "!password? essid?"
};