225 lines
4.7 KiB
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?"
|
|
};
|