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