Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
// PAK is an encrypted key exchange protocol designed by Philip MacKenzie et al.
2
// It is patented and use outside Plan 9 requires you get a license.
3
// (All other EKE protocols are patented as well, by Lucent or others.)
4
#include <u.h>
5
#include <libc.h>
6
#include <mp.h>
7
#include <libsec.h>
8
#include "SConn.h"
9
#include "secstore.h"
10
 
11
extern int verbose;
12
 
13
char VERSION[] = "secstore";
14
static char *feedback[] = {"alpha","bravo","charlie","delta","echo","foxtrot","golf","hotel"};
15
 
16
typedef struct PAKparams{
17
	mpint *q, *p, *r, *g;
18
} PAKparams;
19
 
20
static PAKparams *pak;
21
 
22
// from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E
23
static void
24
initPAKparams(void)
25
{
26
	if(pak)
27
		return;
28
	pak = (PAKparams*)emalloc(sizeof(*pak));
29
	pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil);
30
	pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB"
31
		"DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86"
32
		"3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9"
33
		"3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D",
34
		nil, 16, nil);
35
	pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241"
36
		"CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E"
37
		"887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D"
38
		"21C4656848614D888A4", nil, 16, nil);
39
	pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734"
40
		"44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD"
41
		"410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734"
42
		"E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1",
43
		nil, 16, nil);
44
}
45
 
46
// H = (sha(ver,C,sha(passphrase)))^r mod p,
47
// a hash function expensive to attack by brute force.
48
static void
49
longhash(char *ver, char *C, uchar *passwd, mpint *H)
50
{
51
	uchar *Cp;
52
	int i, n, nver, nC;
53
	uchar buf[140], key[1];
54
 
55
	nver = strlen(ver);
56
	nC = strlen(C);
57
	n = nver + nC + SHA1dlen;
58
	Cp = (uchar*)emalloc(n);
59
	memmove(Cp, ver, nver);
60
	memmove(Cp+nver, C, nC);
61
	memmove(Cp+nver+nC, passwd, SHA1dlen);
62
	for(i = 0; i < 7; i++){
63
		key[0] = 'A'+i;
64
		hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil);
65
	}
66
	memset(Cp, 0, n);
67
	free(Cp);
68
	betomp(buf, sizeof buf, H);
69
	mpmod(H, pak->p, H);
70
	mpexp(H, pak->r, pak->p, H);
71
}
72
 
73
// Hi = H^-1 mod p
74
char *
75
PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi)
76
{
77
	uchar passhash[SHA1dlen];
78
 
79
	sha1((uchar *)passphrase, strlen(passphrase), passhash, nil);
80
	initPAKparams();
81
	longhash(VERSION, C, passhash, H);
82
	mpinvert(H, pak->p, Hi);
83
	return mptoa(Hi, 64, nil, 0);
84
}
85
 
86
// another, faster, hash function for each party to
87
// confirm that the other has the right secrets.
88
static void
89
shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest)
90
{
91
	SHA1state *state;
92
 
93
	state = sha1((uchar*)mess, strlen(mess), 0, 0);
94
	state = sha1((uchar*)C, strlen(C), 0, state);
95
	state = sha1((uchar*)S, strlen(S), 0, state);
96
	state = sha1((uchar*)m, strlen(m), 0, state);
97
	state = sha1((uchar*)mu, strlen(mu), 0, state);
98
	state = sha1((uchar*)sigma, strlen(sigma), 0, state);
99
	state = sha1((uchar*)Hi, strlen(Hi), 0, state);
100
	state = sha1((uchar*)mess, strlen(mess), 0, state);
101
	state = sha1((uchar*)C, strlen(C), 0, state);
102
	state = sha1((uchar*)S, strlen(S), 0, state);
103
	state = sha1((uchar*)m, strlen(m), 0, state);
104
	state = sha1((uchar*)mu, strlen(mu), 0, state);
105
	state = sha1((uchar*)sigma, strlen(sigma), 0, state);
106
	sha1((uchar*)Hi, strlen(Hi), digest, state);
107
}
108
 
109
// On input, conn provides an open channel to the server;
110
//	C is the name this client calls itself;
111
//	pass is the user's passphrase
112
// On output, session secret has been set in conn
113
//	(unless return code is negative, which means failure).
114
//    If pS is not nil, it is set to the (alloc'd) name the server calls itself.
115
int
116
PAKclient(SConn *conn, char *C, char *pass, char **pS)
117
{
118
	char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi;
119
	char kc[2*SHA1dlen+1];
120
	uchar digest[SHA1dlen];
121
	int rc = -1, n;
122
	mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0);
123
	mpint *H = mpnew(0), *Hi = mpnew(0);
124
 
125
	hexHi = PAK_Hi(C, pass, H, Hi);
126
	if(verbose)
127
		fprint(2, "%s\n", feedback[H->p[0]&0x7]);  // provide a clue to catch typos
128
 
129
	// random 1<=x<=q-1; send C, m=g**x H
130
	x = mprand(240, genrandom, nil);
131
	mpmod(x, pak->q, x);
132
	if(mpcmp(x, mpzero) == 0)
133
		mpassign(mpone, x);
134
	mpexp(pak->g, x, pak->p, m);
135
	mpmul(m, H, m);
136
	mpmod(m, pak->p, m);
137
	hexm = mptoa(m, 64, nil, 0);
138
	mess = (char*)emalloc(2*Maxmsg+2);
139
	mess2 = mess+Maxmsg+1;
140
	snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm);
141
	conn->write(conn, (uchar*)mess, strlen(mess));
142
 
143
	// recv g**y, S, check hash1(g**xy)
144
	if(readstr(conn, mess) < 0){
145
		fprint(2, "%s: error: %s\n", argv0, mess);
146
		writerr(conn, "couldn't read g**y");
147
		goto done;
148
	}
149
	eol = strchr(mess, '\n');
150
	if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){
151
		writerr(conn, "verifier syntax error");
152
		goto done;
153
	}
154
	hexmu = mess+3;
155
	*eol = 0;
156
	ks = eol+3;
157
	eol = strchr(ks, '\n');
158
	if(!eol || strncmp("\nS=", eol, 3) != 0){
159
		writerr(conn, "verifier syntax error for secstore 1.0");
160
		goto done;
161
	}
162
	*eol = 0;
163
	S = eol+3;
164
	eol = strchr(S, '\n');
165
	if(!eol){
166
		writerr(conn, "verifier syntax error for secstore 1.0");
167
		goto done;
168
	}
169
	*eol = 0;
170
	if(pS)
171
		*pS = estrdup(S);
172
	strtomp(hexmu, nil, 64, mu);
173
	mpexp(mu, x, pak->p, sigma);
174
	hexsigma = mptoa(sigma, 64, nil, 0);
175
	shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest);
176
	enc64(kc, sizeof kc, digest, SHA1dlen);
177
	if(strcmp(ks, kc) != 0){
178
		writerr(conn, "verifier didn't match");
179
		goto done;
180
	}
181
 
182
	// send hash2(g**xy)
183
	shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest);
184
	enc64(kc, sizeof kc, digest, SHA1dlen);
185
	snprint(mess2, Maxmsg, "k'=%s\n", kc);
186
	conn->write(conn, (uchar*)mess2, strlen(mess2));
187
 
188
	// set session key
189
	shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest);
190
	memset(hexsigma, 0, strlen(hexsigma));
191
	n = conn->secret(conn, digest, 0);
192
	memset(digest, 0, SHA1dlen);
193
	if(n < 0){
194
		writerr(conn, "can't set secret");
195
		goto done;
196
	}
197
 
198
	rc = 0;
199
done:
200
	mpfree(x);
201
	mpfree(sigma);
202
	mpfree(mu);
203
	mpfree(m);
204
	mpfree(Hi);
205
	mpfree(H);
206
	free(hexsigma);
207
	free(hexHi);
208
	free(hexm);
209
	free(mess);
210
	return rc;
211
}
212
 
213
// On input,
214
//	mess contains first message;
215
//	name is name this server should call itself.
216
// On output, session secret has been set in conn;
217
//	if pw!=nil, then *pw points to PW struct for authenticated user.
218
//	returns -1 if error
219
int
220
PAKserver(SConn *conn, char *S, char *mess, PW **pwp)
221
{
222
	int rc = -1, n;
223
	char mess2[Maxmsg+1], *eol;
224
	char *C, ks[41], *kc, *hexm, *hexmu = nil, *hexsigma = nil, *hexHi = nil;
225
	uchar digest[SHA1dlen];
226
	mpint *H = mpnew(0), *Hi = mpnew(0);
227
	mpint *y = nil, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0);
228
	PW *pw = nil;
229
 
230
	// secstore version and algorithm
231
	snprint(mess2,Maxmsg,"%s\tPAK\n", VERSION);
232
	n = strlen(mess2);
233
	if(strncmp(mess,mess2,n) != 0){
234
		writerr(conn, "protocol should start with ver alg");
235
		return -1;
236
	}
237
	mess += n;
238
	initPAKparams();
239
 
240
	// parse first message into C, m
241
	eol = strchr(mess, '\n');
242
	if(strncmp("C=", mess, 2) != 0 || !eol){
243
		fprint(2, "%s: mess[1]=%s\n", argv0, mess);
244
		writerr(conn, "PAK version mismatch");
245
		goto done;
246
	}
247
	C = mess+2;
248
	*eol = 0;
249
	hexm = eol+3;
250
	eol = strchr(hexm, '\n');
251
	if(strncmp("m=", hexm-2, 2) != 0 || !eol){
252
		writerr(conn, "PAK version mismatch");
253
		goto done;
254
	}
255
	*eol = 0;
256
	strtomp(hexm, nil, 64, m);
257
	mpmod(m, pak->p, m);
258
 
259
	// lookup client
260
	if((pw = getPW(C,0)) == nil) {
261
		snprint(mess2, sizeof mess2, "%r");
262
		writerr(conn, mess2);
263
		goto done;
264
	}
265
	if(mpcmp(m, mpzero) == 0) {
266
		writerr(conn, "account exists");
267
		freePW(pw);
268
		pw = nil;
269
		goto done;
270
	}
271
	hexHi = mptoa(pw->Hi, 64, nil, 0);
272
 
273
	// random y, mu=g**y, sigma=g**xy
274
	y = mprand(240, genrandom, nil);
275
	mpmod(y, pak->q, y);
276
	if(mpcmp(y, mpzero) == 0){
277
		mpassign(mpone, y);
278
	}
279
	mpexp(pak->g, y, pak->p, mu);
280
	mpmul(m, pw->Hi, m);
281
	mpmod(m, pak->p, m);
282
	mpexp(m, y, pak->p, sigma);
283
 
284
	// send g**y, hash1(g**xy)
285
	hexmu = mptoa(mu, 64, nil, 0);
286
	hexsigma = mptoa(sigma, 64, nil, 0);
287
	shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest);
288
	enc64(ks, sizeof ks, digest, SHA1dlen);
289
	snprint(mess2, sizeof mess2, "mu=%s\nk=%s\nS=%s\n", hexmu, ks, S);
290
	conn->write(conn, (uchar*)mess2, strlen(mess2));
291
 
292
	// recv hash2(g**xy)
293
	if(readstr(conn, mess2) < 0){
294
		writerr(conn, "couldn't read verifier");
295
		goto done;
296
	}
297
	eol = strchr(mess2, '\n');
298
	if(strncmp("k'=", mess2, 3) != 0 || !eol){
299
		writerr(conn, "verifier syntax error");
300
		goto done;
301
	}
302
	kc = mess2+3;
303
	*eol = 0;
304
	shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest);
305
	enc64(ks, sizeof ks, digest, SHA1dlen);
306
	if(strcmp(ks, kc) != 0) {
307
		rc = -2;
308
		goto done;
309
	}
310
 
311
	// set session key
312
	shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest);
313
	n = conn->secret(conn, digest, 1);
314
	if(n < 0){
315
		writerr(conn, "can't set secret");
316
		goto done;
317
	}
318
 
319
	rc = 0;
320
done:
321
	if(rc<0 && pw){
322
		pw->failed++;
323
		putPW(pw);
324
	}
325
	if(rc==0 && pw && pw->failed>0){
326
		pw->failed = 0;
327
		putPW(pw);
328
	}
329
	if(pwp)
330
		*pwp = pw;
331
	else
332
		freePW(pw);
333
	free(hexsigma);
334
	free(hexHi);
335
	free(hexmu);
336
	mpfree(y);
337
	mpfree(sigma);
338
	mpfree(mu);
339
	mpfree(m);
340
	mpfree(Hi);
341
	mpfree(H);
342
	return rc;
343
}
344