xref: /plan9-contrib/sys/src/cmd/auth/factotum/wpapsk.c (revision 13e26254f5367fe814be8d0c2fe81053db37f162)
1*13e26254SDavid du Colombier /*
2*13e26254SDavid du Colombier  * WPA-PSK
3*13e26254SDavid du Colombier  *
4*13e26254SDavid du Colombier  * Client protocol:
5*13e26254SDavid du Colombier  *	write challenge: smac[6] + amac[6] + snonce[32] + anonce[32]
6*13e26254SDavid du Colombier  *	read response: ptk[64]
7*13e26254SDavid du Colombier  *
8*13e26254SDavid du Colombier  * Server protocol:
9*13e26254SDavid du Colombier  *	unimplemented
10*13e26254SDavid du Colombier  */
11*13e26254SDavid du Colombier #include "dat.h"
12*13e26254SDavid du Colombier 
13*13e26254SDavid du Colombier enum {
14*13e26254SDavid du Colombier 	PMKlen = 256/8,
15*13e26254SDavid du Colombier 	PTKlen = 512/8,
16*13e26254SDavid du Colombier 
17*13e26254SDavid du Colombier 	Eaddrlen = 6,
18*13e26254SDavid du Colombier 	Noncelen = 32,
19*13e26254SDavid du Colombier };
20*13e26254SDavid du Colombier 
21*13e26254SDavid du Colombier enum
22*13e26254SDavid du Colombier {
23*13e26254SDavid du Colombier 	CNeedChal,
24*13e26254SDavid du Colombier 	CHaveResp,
25*13e26254SDavid du Colombier 	Maxphase,
26*13e26254SDavid du Colombier };
27*13e26254SDavid du Colombier 
28*13e26254SDavid du Colombier static char *phasenames[Maxphase] = {
29*13e26254SDavid du Colombier [CNeedChal]	"CNeedChal",
30*13e26254SDavid du Colombier [CHaveResp]	"CHaveResp",
31*13e26254SDavid du Colombier };
32*13e26254SDavid du Colombier 
33*13e26254SDavid du Colombier struct State
34*13e26254SDavid du Colombier {
35*13e26254SDavid du Colombier 	uchar	resp[PTKlen];
36*13e26254SDavid du Colombier };
37*13e26254SDavid du Colombier 
38*13e26254SDavid du Colombier static void
pass2pmk(char * pass,char * ssid,uchar pmk[PMKlen])39*13e26254SDavid du Colombier pass2pmk(char *pass, char *ssid, uchar pmk[PMKlen])
40*13e26254SDavid du Colombier {
41*13e26254SDavid du Colombier 	int npass = strlen(pass);
42*13e26254SDavid du Colombier 	if(npass == 2*PMKlen && dec16(pmk, PMKlen, pass, npass) == PMKlen)
43*13e26254SDavid du Colombier 		return;
44*13e26254SDavid du Colombier 	pbkdf2_x((uchar*)pass, npass, (uchar*)ssid, strlen(ssid), 4096, pmk, PMKlen, hmac_sha1, SHA1dlen);
45*13e26254SDavid du Colombier }
46*13e26254SDavid du Colombier 
47*13e26254SDavid du Colombier static void
prfn(uchar * k,int klen,char * a,uchar * b,int blen,uchar * d,int dlen)48*13e26254SDavid du Colombier prfn(uchar *k, int klen, char *a, uchar *b, int blen, uchar *d, int dlen)
49*13e26254SDavid du Colombier {
50*13e26254SDavid du Colombier 	uchar r[SHA1dlen], i;
51*13e26254SDavid du Colombier 	DigestState *ds;
52*13e26254SDavid du Colombier 	int n;
53*13e26254SDavid du Colombier 
54*13e26254SDavid du Colombier 	i = 0;
55*13e26254SDavid du Colombier 	while(dlen > 0){
56*13e26254SDavid du Colombier 		ds = hmac_sha1((uchar*)a, strlen(a)+1, k, klen, nil, nil);
57*13e26254SDavid du Colombier 		hmac_sha1(b, blen, k, klen, nil, ds);
58*13e26254SDavid du Colombier 		hmac_sha1(&i, 1, k, klen, r, ds);
59*13e26254SDavid du Colombier 		i++;
60*13e26254SDavid du Colombier 		n = dlen;
61*13e26254SDavid du Colombier 		if(n > sizeof(r))
62*13e26254SDavid du Colombier 			n = sizeof(r);
63*13e26254SDavid du Colombier 		memmove(d, r, n); d += n;
64*13e26254SDavid du Colombier 		dlen -= n;
65*13e26254SDavid du Colombier 	}
66*13e26254SDavid du Colombier }
67*13e26254SDavid du Colombier 
68*13e26254SDavid du Colombier static void
calcptk(uchar pmk[PMKlen],uchar smac[Eaddrlen],uchar amac[Eaddrlen],uchar snonce[Noncelen],uchar anonce[Noncelen],uchar ptk[PTKlen])69*13e26254SDavid du Colombier calcptk(uchar pmk[PMKlen], uchar smac[Eaddrlen], uchar amac[Eaddrlen],
70*13e26254SDavid du Colombier 	uchar snonce[Noncelen],  uchar anonce[Noncelen],
71*13e26254SDavid du Colombier 	uchar ptk[PTKlen])
72*13e26254SDavid du Colombier {
73*13e26254SDavid du Colombier 	uchar b[2*Eaddrlen + 2*Noncelen];
74*13e26254SDavid du Colombier 
75*13e26254SDavid du Colombier 	if(memcmp(smac, amac, Eaddrlen) > 0){
76*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*0, amac, Eaddrlen);
77*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*1, smac, Eaddrlen);
78*13e26254SDavid du Colombier 	} else {
79*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*0, smac, Eaddrlen);
80*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*1, amac, Eaddrlen);
81*13e26254SDavid du Colombier 	}
82*13e26254SDavid du Colombier 	if(memcmp(snonce, anonce, Eaddrlen) > 0){
83*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*2 + Noncelen*0, anonce, Noncelen);
84*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*2 + Noncelen*1, snonce, Noncelen);
85*13e26254SDavid du Colombier 	} else {
86*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*2 + Noncelen*0, snonce, Noncelen);
87*13e26254SDavid du Colombier 		memmove(b + Eaddrlen*2 + Noncelen*1, anonce, Noncelen);
88*13e26254SDavid du Colombier 	}
89*13e26254SDavid du Colombier 	prfn(pmk, PMKlen, "Pairwise key expansion", b, sizeof(b), ptk, PTKlen);
90*13e26254SDavid du Colombier }
91*13e26254SDavid du Colombier 
92*13e26254SDavid du Colombier static int
wpapskinit(Proto * p,Fsstate * fss)93*13e26254SDavid du Colombier wpapskinit(Proto *p, Fsstate *fss)
94*13e26254SDavid du Colombier {
95*13e26254SDavid du Colombier 	int iscli;
96*13e26254SDavid du Colombier 	State *s;
97*13e26254SDavid du Colombier 
98*13e26254SDavid du Colombier 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
99*13e26254SDavid du Colombier 		return failure(fss, nil);
100*13e26254SDavid du Colombier 	if(!iscli)
101*13e26254SDavid du Colombier 		return failure(fss, "%s server not supported", p->name);
102*13e26254SDavid du Colombier 
103*13e26254SDavid du Colombier 	s = emalloc(sizeof *s);
104*13e26254SDavid du Colombier 	fss->phasename = phasenames;
105*13e26254SDavid du Colombier 	fss->maxphase = Maxphase;
106*13e26254SDavid du Colombier 	fss->phase = CNeedChal;
107*13e26254SDavid du Colombier 	fss->ps = s;
108*13e26254SDavid du Colombier 	return RpcOk;
109*13e26254SDavid du Colombier }
110*13e26254SDavid du Colombier 
111*13e26254SDavid du Colombier static int
wpapskwrite(Fsstate * fss,void * va,uint n)112*13e26254SDavid du Colombier wpapskwrite(Fsstate *fss, void *va, uint n)
113*13e26254SDavid du Colombier {
114*13e26254SDavid du Colombier 	uchar pmk[PMKlen], *smac, *amac, *snonce, *anonce;
115*13e26254SDavid du Colombier 	char *pass, *essid;
116*13e26254SDavid du Colombier 	State *s;
117*13e26254SDavid du Colombier 	int ret;
118*13e26254SDavid du Colombier 	Key *k;
119*13e26254SDavid du Colombier 	Keyinfo ki;
120*13e26254SDavid du Colombier 	Attr *attr;
121*13e26254SDavid du Colombier 
122*13e26254SDavid du Colombier 	s = fss->ps;
123*13e26254SDavid du Colombier 
124*13e26254SDavid du Colombier 	if(fss->phase != CNeedChal)
125*13e26254SDavid du Colombier 		return phaseerror(fss, "write");
126*13e26254SDavid du Colombier 	if(n != (2*Eaddrlen + 2*Noncelen))
127*13e26254SDavid du Colombier 		return phaseerror(fss, "bad write size");
128*13e26254SDavid du Colombier 
129*13e26254SDavid du Colombier 	attr = _delattr(_copyattr(fss->attr), "role");
130*13e26254SDavid du Colombier 	mkkeyinfo(&ki, fss, attr);
131*13e26254SDavid du Colombier 	ret = findkey(&k, &ki, "%s", fss->proto->keyprompt);
132*13e26254SDavid du Colombier 	_freeattr(attr);
133*13e26254SDavid du Colombier 	if(ret != RpcOk)
134*13e26254SDavid du Colombier 		return ret;
135*13e26254SDavid du Colombier 
136*13e26254SDavid du Colombier 	pass = _strfindattr(k->privattr, "!password");
137*13e26254SDavid du Colombier 	if(pass == nil)
138*13e26254SDavid du Colombier 		return failure(fss, "key has no password");
139*13e26254SDavid du Colombier 	essid = _strfindattr(k->attr, "essid");
140*13e26254SDavid du Colombier 	if(essid == nil)
141*13e26254SDavid du Colombier 		return failure(fss, "key has no essid");
142*13e26254SDavid du Colombier 	setattrs(fss->attr, k->attr);
143*13e26254SDavid du Colombier 	closekey(k);
144*13e26254SDavid du Colombier 
145*13e26254SDavid du Colombier 	pass2pmk(pass, essid, pmk);
146*13e26254SDavid du Colombier 
147*13e26254SDavid du Colombier 	smac = va;
148*13e26254SDavid du Colombier 	amac = smac + Eaddrlen;
149*13e26254SDavid du Colombier 	snonce = amac + Eaddrlen;
150*13e26254SDavid du Colombier 	anonce = snonce + Noncelen;
151*13e26254SDavid du Colombier 	calcptk(pmk, smac, amac, snonce, anonce, s->resp);
152*13e26254SDavid du Colombier 
153*13e26254SDavid du Colombier 	fss->phase = CHaveResp;
154*13e26254SDavid du Colombier 	return RpcOk;
155*13e26254SDavid du Colombier }
156*13e26254SDavid du Colombier 
157*13e26254SDavid du Colombier static int
wpapskread(Fsstate * fss,void * va,uint * n)158*13e26254SDavid du Colombier wpapskread(Fsstate *fss, void *va, uint *n)
159*13e26254SDavid du Colombier {
160*13e26254SDavid du Colombier 	State *s;
161*13e26254SDavid du Colombier 
162*13e26254SDavid du Colombier 	s = fss->ps;
163*13e26254SDavid du Colombier 	if(fss->phase != CHaveResp)
164*13e26254SDavid du Colombier 		return phaseerror(fss, "read");
165*13e26254SDavid du Colombier 	if(*n > sizeof(s->resp))
166*13e26254SDavid du Colombier 		*n = sizeof(s->resp);
167*13e26254SDavid du Colombier 	memmove(va, s->resp, *n);
168*13e26254SDavid du Colombier 	fss->phase = Established;
169*13e26254SDavid du Colombier 	fss->haveai = 0;
170*13e26254SDavid du Colombier 	return RpcOk;
171*13e26254SDavid du Colombier }
172*13e26254SDavid du Colombier 
173*13e26254SDavid du Colombier static void
wpapskclose(Fsstate * fss)174*13e26254SDavid du Colombier wpapskclose(Fsstate *fss)
175*13e26254SDavid du Colombier {
176*13e26254SDavid du Colombier 	State *s;
177*13e26254SDavid du Colombier 	s = fss->ps;
178*13e26254SDavid du Colombier 	free(s);
179*13e26254SDavid du Colombier }
180*13e26254SDavid du Colombier 
181*13e26254SDavid du Colombier Proto wpapsk = {
182*13e26254SDavid du Colombier .name=		"wpapsk",
183*13e26254SDavid du Colombier .init=		wpapskinit,
184*13e26254SDavid du Colombier .write=		wpapskwrite,
185*13e26254SDavid du Colombier .read=		wpapskread,
186*13e26254SDavid du Colombier .close=		wpapskclose,
187*13e26254SDavid du Colombier .addkey=	replacekey,
188*13e26254SDavid du Colombier .keyprompt=	"!password? essid?"
189*13e26254SDavid du Colombier };
190