wpa: initial support for rsn
This commit is contained in:
parent
f37465fd7f
commit
d6da605605
1 changed files with 199 additions and 48 deletions
|
@ -8,6 +8,9 @@
|
||||||
enum {
|
enum {
|
||||||
PTKlen = 512/8,
|
PTKlen = 512/8,
|
||||||
GTKlen = 256/8,
|
GTKlen = 256/8,
|
||||||
|
|
||||||
|
MIClen = 16,
|
||||||
|
|
||||||
Noncelen = 32,
|
Noncelen = 32,
|
||||||
Eaddrlen = 6,
|
Eaddrlen = 6,
|
||||||
};
|
};
|
||||||
|
@ -74,10 +77,6 @@ uchar wpaie[] = {
|
||||||
0x00, 0x50, 0xf2, 0x02, /* authentication suite PSK */
|
0x00, 0x50, 0xf2, 0x02, /* authentication suite PSK */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* only WPA for now */
|
|
||||||
uchar *rsne = wpaie;
|
|
||||||
int rsnelen = sizeof(wpaie);
|
|
||||||
|
|
||||||
char*
|
char*
|
||||||
getessid(void)
|
getessid(void)
|
||||||
{
|
{
|
||||||
|
@ -181,8 +180,8 @@ dumpkeydescr(Keydescr *kd)
|
||||||
int i, f;
|
int i, f;
|
||||||
|
|
||||||
f = kd->flags[0]<<8 | kd->flags[1];
|
f = kd->flags[0]<<8 | kd->flags[1];
|
||||||
fprint(2, "type=%.*H flags=%.*H ( ",
|
fprint(2, "type=%.*H vers=%d flags=%.*H ( ",
|
||||||
sizeof(kd->type), kd->type,
|
sizeof(kd->type), kd->type, kd->flags[1] & 7,
|
||||||
sizeof(kd->flags), kd->flags);
|
sizeof(kd->flags), kd->flags);
|
||||||
for(i=0; i<nelem(flags); i++)
|
for(i=0; i<nelem(flags); i++)
|
||||||
if(flags[i].flag & f)
|
if(flags[i].flag & f)
|
||||||
|
@ -196,13 +195,96 @@ dumpkeydescr(Keydescr *kd)
|
||||||
sizeof(kd->id), kd->id,
|
sizeof(kd->id), kd->id,
|
||||||
sizeof(kd->mic), kd->mic);
|
sizeof(kd->mic), kd->mic);
|
||||||
i = kd->datalen[0]<<8 | kd->datalen[1];
|
i = kd->datalen[0]<<8 | kd->datalen[1];
|
||||||
fprint(2, "data[%.4x]=%.*H\n\n", i, i, kd->data);
|
fprint(2, "data[%.4x]=%.*H\n", i, i, kd->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
rc4unwrap(uchar key[16], uchar iv[16], uchar *data, int len)
|
||||||
|
{
|
||||||
|
uchar seed[32];
|
||||||
|
RC4state rs;
|
||||||
|
|
||||||
|
memmove(seed, iv, 16);
|
||||||
|
memmove(seed+16, key, 16);
|
||||||
|
setupRC4state(&rs, seed, sizeof(seed));
|
||||||
|
rc4skip(&rs, 256);
|
||||||
|
rc4(&rs, data, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
aesunwrap(uchar *key, int nkey, uchar *data, int len)
|
||||||
|
{
|
||||||
|
static uchar IV[8] = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, };
|
||||||
|
uchar B[16], *R;
|
||||||
|
AESstate s;
|
||||||
|
uint t;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
len -= 8;
|
||||||
|
if(len < 16 || (len % 8) != 0)
|
||||||
|
return -1;
|
||||||
|
n = len/8;
|
||||||
|
t = n*6;
|
||||||
|
setupAESstate(&s, key, nkey, 0);
|
||||||
|
memmove(B, data, 8);
|
||||||
|
memmove(data, data+8, n*8);
|
||||||
|
do {
|
||||||
|
for(R = data + (n - 1)*8; R >= data; t--, R -= 8){
|
||||||
|
memmove(B+8, R, 8);
|
||||||
|
B[7] ^= (t >> 0);
|
||||||
|
B[6] ^= (t >> 8);
|
||||||
|
B[5] ^= (t >> 16);
|
||||||
|
B[4] ^= (t >> 24);
|
||||||
|
aes_decrypt(s.dkey, s.rounds, B, B);
|
||||||
|
memmove(R, B+8, 8);
|
||||||
|
}
|
||||||
|
} while(t > 0);
|
||||||
|
fprint(2, "unwrap: A=%.8H %.*H\n", B, n*8, data);
|
||||||
|
if(memcmp(B, IV, 8) != 0)
|
||||||
|
return -1;
|
||||||
|
return n*8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
calcmic(Keydescr *kd, uchar *msg, int msglen)
|
||||||
|
{
|
||||||
|
int vers;
|
||||||
|
|
||||||
|
vers = kd->flags[1] & 7;
|
||||||
|
memset(kd->mic, 0, MIClen);
|
||||||
|
if(vers == 1){
|
||||||
|
uchar digest[MD5dlen];
|
||||||
|
|
||||||
|
hmac_md5(msg, msglen, ptk, 16, digest, nil);
|
||||||
|
memmove(kd->mic, digest, MIClen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(vers == 2){
|
||||||
|
uchar digest[SHA1dlen];
|
||||||
|
|
||||||
|
hmac_sha1(msg, msglen, ptk, 16, digest, nil);
|
||||||
|
memmove(kd->mic, digest, MIClen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
checkmic(Keydescr *kd, uchar *msg, int msglen)
|
||||||
|
{
|
||||||
|
uchar tmp[MIClen];
|
||||||
|
|
||||||
|
memmove(tmp, kd->mic, MIClen);
|
||||||
|
if(calcmic(kd, msg, msglen) != 0)
|
||||||
|
return -1;
|
||||||
|
return memcmp(tmp, kd->mic, MIClen) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen)
|
reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar *data, int datalen)
|
||||||
{
|
{
|
||||||
uchar buf[4096], mic[MD5dlen], *m, *p = buf;
|
uchar buf[4096], *m, *p = buf;
|
||||||
|
|
||||||
memmove(p, amac, Eaddrlen); p += Eaddrlen;
|
memmove(p, amac, Eaddrlen); p += Eaddrlen;
|
||||||
memmove(p, smac, Eaddrlen); p += Eaddrlen;
|
memmove(p, smac, Eaddrlen); p += Eaddrlen;
|
||||||
|
@ -227,13 +309,11 @@ reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar
|
||||||
memmove(p, data, datalen);
|
memmove(p, data, datalen);
|
||||||
p += datalen;
|
p += datalen;
|
||||||
|
|
||||||
memset(kd->mic, 0, sizeof(kd->mic));
|
memset(kd->mic, 0, MIClen);
|
||||||
if(flags & Fmic){
|
if(flags & Fmic)
|
||||||
hmac_md5(m, p - m, ptk, 16, mic, nil);
|
calcmic(kd, m, p - m);
|
||||||
memmove(kd->mic, mic, sizeof(kd->mic));
|
|
||||||
}
|
|
||||||
if(debug != 0){
|
if(debug != 0){
|
||||||
fprint(2, "reply %E -> %E: ", smac, amac);
|
fprint(2, "\nreply %E -> %E: ", smac, amac);
|
||||||
dumpkeydescr(kd);
|
dumpkeydescr(kd);
|
||||||
}
|
}
|
||||||
datalen = p - buf;
|
datalen = p - buf;
|
||||||
|
@ -253,12 +333,18 @@ main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
uchar mac[Eaddrlen], buf[1024];
|
uchar mac[Eaddrlen], buf[1024];
|
||||||
char addr[128];
|
char addr[128];
|
||||||
|
uchar *rsne;
|
||||||
|
int rsnelen;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
quotefmtinstall();
|
quotefmtinstall();
|
||||||
fmtinstall('H', Hfmt);
|
fmtinstall('H', Hfmt);
|
||||||
fmtinstall('E', eipfmt);
|
fmtinstall('E', eipfmt);
|
||||||
|
|
||||||
|
/* default is WPA */
|
||||||
|
rsne = wpaie;
|
||||||
|
rsnelen = sizeof(wpaie);
|
||||||
|
|
||||||
ARGBEGIN {
|
ARGBEGIN {
|
||||||
case 'd':
|
case 'd':
|
||||||
debug = 1;
|
debug = 1;
|
||||||
|
@ -269,6 +355,14 @@ main(int argc, char *argv[])
|
||||||
case 's':
|
case 's':
|
||||||
strncpy(essid, EARGF(usage()), 32);
|
strncpy(essid, EARGF(usage()), 32);
|
||||||
break;
|
break;
|
||||||
|
case '1':
|
||||||
|
rsne = wpaie;
|
||||||
|
rsnelen = sizeof(wpaie);
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
rsne = rsnie;
|
||||||
|
rsnelen = sizeof(rsnie);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage();
|
usage();
|
||||||
} ARGEND;
|
} ARGEND;
|
||||||
|
@ -325,7 +419,7 @@ main(int argc, char *argv[])
|
||||||
|
|
||||||
for(;;){
|
for(;;){
|
||||||
uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
|
uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
|
||||||
int proto, flags, kid;
|
int proto, flags, vers, datalen;
|
||||||
uvlong repc, rsc;
|
uvlong repc, rsc;
|
||||||
Keydescr *kd;
|
Keydescr *kd;
|
||||||
|
|
||||||
|
@ -349,18 +443,16 @@ main(int argc, char *argv[])
|
||||||
p += 4;
|
p += 4;
|
||||||
if(n < Keydescrlen || p + n > e)
|
if(n < Keydescrlen || p + n > e)
|
||||||
continue;
|
continue;
|
||||||
|
e = p + n;
|
||||||
kd = (Keydescr*)p;
|
kd = (Keydescr*)p;
|
||||||
if(debug){
|
if(debug){
|
||||||
fprint(2, "recv %E <- %E: ", smac, amac);
|
fprint(2, "\nrecv %E <- %E: ", smac, amac);
|
||||||
dumpkeydescr(kd);
|
dumpkeydescr(kd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only WPA, RSN descriptor format not suppoted yet */
|
if(kd->type[0] != 0xFE && kd->type[0] != 0x02)
|
||||||
if(kd->type[0] != 0xFE)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
flags = kd->flags[0]<<8 | kd->flags[1];
|
|
||||||
|
|
||||||
rsc = (uvlong)kd->rsc[0] |
|
rsc = (uvlong)kd->rsc[0] |
|
||||||
(uvlong)kd->rsc[1]<<8 |
|
(uvlong)kd->rsc[1]<<8 |
|
||||||
(uvlong)kd->rsc[2]<<16 |
|
(uvlong)kd->rsc[2]<<16 |
|
||||||
|
@ -368,6 +460,12 @@ main(int argc, char *argv[])
|
||||||
(uvlong)kd->rsc[4]<<32 |
|
(uvlong)kd->rsc[4]<<32 |
|
||||||
(uvlong)kd->rsc[5]<<40;
|
(uvlong)kd->rsc[5]<<40;
|
||||||
|
|
||||||
|
vers = kd->flags[1] & 7;
|
||||||
|
flags = kd->flags[0]<<8 | kd->flags[1];
|
||||||
|
datalen = kd->datalen[0]<<8 | kd->datalen[1];
|
||||||
|
if(kd->data + datalen > e)
|
||||||
|
continue;
|
||||||
|
|
||||||
if((flags & Fmic) == 0){
|
if((flags & Fmic) == 0){
|
||||||
if((flags & (Fptk|Fack)) != (Fptk|Fack))
|
if((flags & (Fptk|Fack)) != (Fptk|Fack))
|
||||||
continue;
|
continue;
|
||||||
|
@ -383,14 +481,14 @@ main(int argc, char *argv[])
|
||||||
memmove(kd->nonce, snonce, sizeof(kd->nonce));
|
memmove(kd->nonce, snonce, sizeof(kd->nonce));
|
||||||
reply(smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
|
reply(smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
|
||||||
} else {
|
} else {
|
||||||
uchar tmp[MD5dlen], mic[MD5dlen];
|
uchar gtk[GTKlen];
|
||||||
|
int gtklen, gtkkid;
|
||||||
|
|
||||||
/* check mic */
|
if(checkmic(kd, m, e - m) != 0){
|
||||||
memmove(tmp, kd->mic, sizeof(mic));
|
if(debug != 0)
|
||||||
memset(kd->mic, 0, sizeof(kd->mic));
|
fprint(2, "bad mic\n");
|
||||||
hmac_md5(m, e - m, ptk, 16, mic, nil);
|
|
||||||
if(memcmp(tmp, mic, sizeof(mic)) != 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
repc = (uvlong)kd->repc[7] |
|
repc = (uvlong)kd->repc[7] |
|
||||||
(uvlong)kd->repc[6]<<8 |
|
(uvlong)kd->repc[6]<<8 |
|
||||||
|
@ -400,11 +498,61 @@ main(int argc, char *argv[])
|
||||||
(uvlong)kd->repc[2]<<40 |
|
(uvlong)kd->repc[2]<<40 |
|
||||||
(uvlong)kd->repc[1]<<48 |
|
(uvlong)kd->repc[1]<<48 |
|
||||||
(uvlong)kd->repc[0]<<56;
|
(uvlong)kd->repc[0]<<56;
|
||||||
if(repc <= lastrepc)
|
if(repc <= lastrepc){
|
||||||
|
if(debug != 0)
|
||||||
|
fprint(2, "bad repc: %llux <= %llux\n", repc, lastrepc);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
lastrepc = repc;
|
lastrepc = repc;
|
||||||
|
|
||||||
if((flags & (Fptk|Fsec|Fack)) == (Fptk|Fack)){
|
if(datalen > 0 && (flags & Fenc) != 0){
|
||||||
|
if(vers == 1)
|
||||||
|
datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen);
|
||||||
|
else
|
||||||
|
datalen = aesunwrap(ptk+16, 16, kd->data, datalen);
|
||||||
|
if(datalen <= 0){
|
||||||
|
if(debug != 0)
|
||||||
|
fprint(2, "bad keywrap\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(debug != 0)
|
||||||
|
fprint(2, "unwraped keydata[%.4x]=%.*H\n", datalen, datalen, kd->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtklen = 0;
|
||||||
|
gtkkid = -1;
|
||||||
|
|
||||||
|
if(kd->type[0] != 0xFE || (flags & (Fptk|Fack)) == (Fptk|Fack)){
|
||||||
|
uchar *p, *x, *e;
|
||||||
|
|
||||||
|
p = kd->data;
|
||||||
|
e = p + datalen;
|
||||||
|
for(; p+2 <= e; p = x){
|
||||||
|
if((x = p+2+p[1]) > e)
|
||||||
|
break;
|
||||||
|
if(debug != 0)
|
||||||
|
fprint(2, "ie=%.2x data[%.2x]=%.*H\n", p[0], p[1], p[1], p+2);
|
||||||
|
if(p[0] == 0x30){ /* RSN */
|
||||||
|
}
|
||||||
|
if(p[0] == 0xDD){ /* WPA */
|
||||||
|
static uchar oui[] = { 0x00, 0x0f, 0xac, 0x01, };
|
||||||
|
|
||||||
|
if(p+2+sizeof(oui) > x || memcmp(p+2, oui, sizeof(oui)) != 0)
|
||||||
|
continue;
|
||||||
|
if((flags & Fenc) == 0)
|
||||||
|
continue; /* ignore gorup key if unencrypted */
|
||||||
|
gtklen = x - (p + 8);
|
||||||
|
if(gtklen <= 0)
|
||||||
|
continue;
|
||||||
|
if(gtklen > sizeof(gtk))
|
||||||
|
gtklen = sizeof(gtk);
|
||||||
|
memmove(gtk, p + 8, gtklen);
|
||||||
|
gtkkid = p[6] & 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if((flags & (Fptk|Fack)) == (Fptk|Fack)){
|
||||||
/* install peerwise receive key */
|
/* install peerwise receive key */
|
||||||
if(fprint(cfd, "rxkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
|
if(fprint(cfd, "rxkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
|
||||||
sysfatal("write rxkey: %r");
|
sysfatal("write rxkey: %r");
|
||||||
|
@ -416,37 +564,40 @@ main(int argc, char *argv[])
|
||||||
kd->rsc[1] = rsc>>8;
|
kd->rsc[1] = rsc>>8;
|
||||||
memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
|
memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
|
||||||
memset(kd->nonce, 0, sizeof(kd->nonce));
|
memset(kd->nonce, 0, sizeof(kd->nonce));
|
||||||
reply(smac, amac, flags & ~Fack, kd, nil, 0);
|
reply(smac, amac, flags & ~(Fack|Fenc|Fsec), kd, nil, 0);
|
||||||
sleep(100);
|
sleep(100);
|
||||||
|
|
||||||
/* install peerwise transmit key */
|
/* install peerwise transmit key */
|
||||||
if(fprint(cfd, "txkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
|
if(fprint(cfd, "txkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
|
||||||
sysfatal("write txkey: %r");
|
sysfatal("write txkey: %r");
|
||||||
|
|
||||||
|
/* reset rsc for group key */
|
||||||
|
rsc = 0;
|
||||||
} else
|
} else
|
||||||
if((flags & (Fptk|Fsec|Fack)) == (Fsec|Fack)){
|
if((flags & (Fptk|Fsec|Fack)) == (Fsec|Fack)){
|
||||||
uchar seed[32], gtk[GTKlen];
|
if(kd->type[0] == 0xFE){
|
||||||
RC4state rs;
|
/* WPA always RC4 encrypts the GTK, even tho the flag isnt set */
|
||||||
int len;
|
if((flags & Fenc) == 0)
|
||||||
|
datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen);
|
||||||
len = kd->datalen[1]<<8 | kd->datalen[0];
|
gtklen = datalen;
|
||||||
if(len > sizeof(gtk))
|
if(gtklen > sizeof(gtk))
|
||||||
len = sizeof(gtk);
|
gtklen = sizeof(gtk);
|
||||||
memmove(gtk, kd->data, len);
|
memmove(gtk, kd->data, gtklen);
|
||||||
memmove(seed, kd->eapoliv, 16);
|
gtkkid = (flags >> 4) & 3;
|
||||||
memmove(seed+16, ptk+16, 16);
|
}
|
||||||
setupRC4state(&rs, seed, sizeof(seed));
|
|
||||||
rc4skip(&rs, 256);
|
|
||||||
rc4(&rs, gtk, len);
|
|
||||||
|
|
||||||
/* install group key */
|
|
||||||
kid = (flags >> 4) & 3;
|
|
||||||
if(fprint(cfd, "rxkey%d %.*H tkip:%.*H@%llux", kid, Eaddrlen, amac, len, gtk, rsc) < 0)
|
|
||||||
sysfatal("write rxkey%d: %r", kid);
|
|
||||||
|
|
||||||
memset(kd->rsc, 0, sizeof(kd->rsc));
|
memset(kd->rsc, 0, sizeof(kd->rsc));
|
||||||
memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
|
memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
|
||||||
memset(kd->nonce, 0, sizeof(kd->nonce));
|
memset(kd->nonce, 0, sizeof(kd->nonce));
|
||||||
reply(smac, amac, flags & ~Fack, kd, nil, 0);
|
reply(smac, amac, flags & ~(Fenc|Fack), kd, nil, 0);
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(gtklen > 0 && gtkkid != -1){
|
||||||
|
/* install group key */
|
||||||
|
if(fprint(cfd, "rxkey%d %.*H tkip:%.*H@%llux",
|
||||||
|
gtkkid, Eaddrlen, amac, gtklen, gtk, rsc) < 0)
|
||||||
|
sysfatal("write rxkey%d: %r", gtkkid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue