summaryrefslogtreecommitdiff
path: root/sys/src/cmd/aux/wpa.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2013-03-17 19:39:44 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2013-03-17 19:39:44 +0100
commitd6da605605a1fd5948c46fae8a23a67e8fee3ede (patch)
treeb412c656153aeb713e289cafcde75e924954affa /sys/src/cmd/aux/wpa.c
parentf37465fd7f768a72628506956473ae69112cf774 (diff)
wpa: initial support for rsn
Diffstat (limited to 'sys/src/cmd/aux/wpa.c')
-rw-r--r--sys/src/cmd/aux/wpa.c247
1 files changed, 199 insertions, 48 deletions
diff --git a/sys/src/cmd/aux/wpa.c b/sys/src/cmd/aux/wpa.c
index 2f0442156..05310b6c1 100644
--- a/sys/src/cmd/aux/wpa.c
+++ b/sys/src/cmd/aux/wpa.c
@@ -8,6 +8,9 @@
enum {
PTKlen = 512/8,
GTKlen = 256/8,
+
+ MIClen = 16,
+
Noncelen = 32,
Eaddrlen = 6,
};
@@ -74,10 +77,6 @@ uchar wpaie[] = {
0x00, 0x50, 0xf2, 0x02, /* authentication suite PSK */
};
-/* only WPA for now */
-uchar *rsne = wpaie;
-int rsnelen = sizeof(wpaie);
-
char*
getessid(void)
{
@@ -181,8 +180,8 @@ dumpkeydescr(Keydescr *kd)
int i, f;
f = kd->flags[0]<<8 | kd->flags[1];
- fprint(2, "type=%.*H flags=%.*H ( ",
- sizeof(kd->type), kd->type,
+ fprint(2, "type=%.*H vers=%d flags=%.*H ( ",
+ sizeof(kd->type), kd->type, kd->flags[1] & 7,
sizeof(kd->flags), kd->flags);
for(i=0; i<nelem(flags); i++)
if(flags[i].flag & f)
@@ -196,13 +195,96 @@ dumpkeydescr(Keydescr *kd)
sizeof(kd->id), kd->id,
sizeof(kd->mic), kd->mic);
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
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, smac, Eaddrlen); p += Eaddrlen;
@@ -227,13 +309,11 @@ reply(uchar smac[Eaddrlen], uchar amac[Eaddrlen], int flags, Keydescr *kd, uchar
memmove(p, data, datalen);
p += datalen;
- memset(kd->mic, 0, sizeof(kd->mic));
- if(flags & Fmic){
- hmac_md5(m, p - m, ptk, 16, mic, nil);
- memmove(kd->mic, mic, sizeof(kd->mic));
- }
+ memset(kd->mic, 0, MIClen);
+ if(flags & Fmic)
+ calcmic(kd, m, p - m);
if(debug != 0){
- fprint(2, "reply %E -> %E: ", smac, amac);
+ fprint(2, "\nreply %E -> %E: ", smac, amac);
dumpkeydescr(kd);
}
datalen = p - buf;
@@ -253,12 +333,18 @@ main(int argc, char *argv[])
{
uchar mac[Eaddrlen], buf[1024];
char addr[128];
+ uchar *rsne;
+ int rsnelen;
int n;
quotefmtinstall();
fmtinstall('H', Hfmt);
fmtinstall('E', eipfmt);
+ /* default is WPA */
+ rsne = wpaie;
+ rsnelen = sizeof(wpaie);
+
ARGBEGIN {
case 'd':
debug = 1;
@@ -269,6 +355,14 @@ main(int argc, char *argv[])
case 's':
strncpy(essid, EARGF(usage()), 32);
break;
+ case '1':
+ rsne = wpaie;
+ rsnelen = sizeof(wpaie);
+ break;
+ case '2':
+ rsne = rsnie;
+ rsnelen = sizeof(rsnie);
+ break;
default:
usage();
} ARGEND;
@@ -325,7 +419,7 @@ main(int argc, char *argv[])
for(;;){
uchar smac[Eaddrlen], amac[Eaddrlen], snonce[Noncelen], anonce[Noncelen], *p, *e, *m;
- int proto, flags, kid;
+ int proto, flags, vers, datalen;
uvlong repc, rsc;
Keydescr *kd;
@@ -349,18 +443,16 @@ main(int argc, char *argv[])
p += 4;
if(n < Keydescrlen || p + n > e)
continue;
+ e = p + n;
kd = (Keydescr*)p;
if(debug){
- fprint(2, "recv %E <- %E: ", smac, amac);
+ fprint(2, "\nrecv %E <- %E: ", smac, amac);
dumpkeydescr(kd);
}
- /* only WPA, RSN descriptor format not suppoted yet */
- if(kd->type[0] != 0xFE)
+ if(kd->type[0] != 0xFE && kd->type[0] != 0x02)
continue;
- flags = kd->flags[0]<<8 | kd->flags[1];
-
rsc = (uvlong)kd->rsc[0] |
(uvlong)kd->rsc[1]<<8 |
(uvlong)kd->rsc[2]<<16 |
@@ -368,6 +460,12 @@ main(int argc, char *argv[])
(uvlong)kd->rsc[4]<<32 |
(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 & (Fptk|Fack)) != (Fptk|Fack))
continue;
@@ -383,14 +481,14 @@ main(int argc, char *argv[])
memmove(kd->nonce, snonce, sizeof(kd->nonce));
reply(smac, amac, (flags & ~(Fack|Fins)) | Fmic, kd, rsne, rsnelen);
} else {
- uchar tmp[MD5dlen], mic[MD5dlen];
+ uchar gtk[GTKlen];
+ int gtklen, gtkkid;
- /* check mic */
- memmove(tmp, kd->mic, sizeof(mic));
- memset(kd->mic, 0, sizeof(kd->mic));
- hmac_md5(m, e - m, ptk, 16, mic, nil);
- if(memcmp(tmp, mic, sizeof(mic)) != 0)
+ if(checkmic(kd, m, e - m) != 0){
+ if(debug != 0)
+ fprint(2, "bad mic\n");
continue;
+ }
repc = (uvlong)kd->repc[7] |
(uvlong)kd->repc[6]<<8 |
@@ -400,11 +498,61 @@ main(int argc, char *argv[])
(uvlong)kd->repc[2]<<40 |
(uvlong)kd->repc[1]<<48 |
(uvlong)kd->repc[0]<<56;
- if(repc <= lastrepc)
+ if(repc <= lastrepc){
+ if(debug != 0)
+ fprint(2, "bad repc: %llux <= %llux\n", repc, lastrepc);
continue;
+ }
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 */
if(fprint(cfd, "rxkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
sysfatal("write rxkey: %r");
@@ -416,37 +564,40 @@ main(int argc, char *argv[])
kd->rsc[1] = rsc>>8;
memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
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);
/* install peerwise transmit key */
if(fprint(cfd, "txkey %.*H tkip:%.*H@%llux", Eaddrlen, amac, 32, ptk+32, rsc) < 0)
sysfatal("write txkey: %r");
+
+ /* reset rsc for group key */
+ rsc = 0;
} else
if((flags & (Fptk|Fsec|Fack)) == (Fsec|Fack)){
- uchar seed[32], gtk[GTKlen];
- RC4state rs;
- int len;
-
- len = kd->datalen[1]<<8 | kd->datalen[0];
- if(len > sizeof(gtk))
- len = sizeof(gtk);
- memmove(gtk, kd->data, len);
- memmove(seed, kd->eapoliv, 16);
- 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);
+ if(kd->type[0] == 0xFE){
+ /* WPA always RC4 encrypts the GTK, even tho the flag isnt set */
+ if((flags & Fenc) == 0)
+ datalen = rc4unwrap(ptk+16, kd->eapoliv, kd->data, datalen);
+ gtklen = datalen;
+ if(gtklen > sizeof(gtk))
+ gtklen = sizeof(gtk);
+ memmove(gtk, kd->data, gtklen);
+ gtkkid = (flags >> 4) & 3;
+ }
memset(kd->rsc, 0, sizeof(kd->rsc));
memset(kd->eapoliv, 0, sizeof(kd->eapoliv));
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);
}
}
}