OLD | NEW |
| (Empty) |
1 #include "openssl/bn.h" | |
2 #include "openssl/sha.h" | |
3 #include <assert.h> | |
4 #include <string.h> | |
5 #include <stdlib.h> | |
6 | |
7 /* Copyright (C) 2008 Ben Laurie (ben@links.org) */ | |
8 | |
9 /* | |
10 * Implement J-PAKE, as described in | |
11 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf | |
12 * | |
13 * With hints from http://www.cl.cam.ac.uk/~fh240/software/JPAKE2.java. | |
14 */ | |
15 | |
16 static void showbn(const char *name, const BIGNUM *bn) | |
17 { | |
18 fputs(name, stdout); | |
19 fputs(" = ", stdout); | |
20 BN_print_fp(stdout, bn); | |
21 putc('\n', stdout); | |
22 } | |
23 | |
24 typedef struct | |
25 { | |
26 BN_CTX *ctx; // Perhaps not the best place for this? | |
27 BIGNUM *p; | |
28 BIGNUM *q; | |
29 BIGNUM *g; | |
30 } JPakeParameters; | |
31 | |
32 static void JPakeParametersInit(JPakeParameters *params) | |
33 { | |
34 params->ctx = BN_CTX_new(); | |
35 | |
36 // For now use p, q, g from Java sample code. Later, generate them. | |
37 params->p = NULL; | |
38 BN_hex2bn(¶ms->p, "fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3
f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b995
0a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135
a169132f675f3ae2b61d72aeff22203199dd14801c7"); | |
39 params->q = NULL; | |
40 BN_hex2bn(¶ms->q, "9760508f15230bccb292b982a2eb840bf0581cf5"); | |
41 params->g = NULL; | |
42 BN_hex2bn(¶ms->g, "f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574
c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167
a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883d
fe15ae59f06928b665e807b552564014c3bfecf492a"); | |
43 | |
44 showbn("p", params->p); | |
45 showbn("q", params->q); | |
46 showbn("g", params->g); | |
47 } | |
48 | |
49 typedef struct | |
50 { | |
51 BIGNUM *gr; // g^r (r random) | |
52 BIGNUM *b; // b = r - x*h, h=hash(g, g^r, g^x, name) | |
53 } JPakeZKP; | |
54 | |
55 typedef struct | |
56 { | |
57 BIGNUM *gx; // g^x | |
58 JPakeZKP zkpx; // ZKP(x) | |
59 } JPakeStep1; | |
60 | |
61 typedef struct | |
62 { | |
63 BIGNUM *X; // g^(xa + xc + xd) * xb * s | |
64 JPakeZKP zkpxbs; // ZKP(xb * s) | |
65 } JPakeStep2; | |
66 | |
67 typedef struct | |
68 { | |
69 const char *name; // Must be unique | |
70 int base; // 1 for Alice, 3 for Bob. Only used for printing stuff. | |
71 JPakeStep1 s1c; // Alice's g^x3, ZKP(x3) or Bob's g^x1, ZKP(x1) | |
72 JPakeStep1 s1d; // Alice's g^x4, ZKP(x4) or Bob's g^x2, ZKP(x2) | |
73 JPakeStep2 s2; // Alice's A, ZKP(x2 * s) or Bob's B, ZKP(x4 * s) | |
74 } JPakeUserPublic; | |
75 | |
76 /* | |
77 * The user structure. In the definition, (xa, xb, xc, xd) are Alice's | |
78 * (x1, x2, x3, x4) or Bob's (x3, x4, x1, x2). If you see what I mean. | |
79 */ | |
80 typedef struct | |
81 { | |
82 JPakeUserPublic p; | |
83 BIGNUM *secret; // The shared secret | |
84 BIGNUM *key; // The calculated (shared) key | |
85 BIGNUM *xa; // Alice's x1 or Bob's x3 | |
86 BIGNUM *xb; // Alice's x2 or Bob's x4 | |
87 } JPakeUser; | |
88 | |
89 // Generate each party's random numbers. xa is in [0, q), xb is in [1, q). | |
90 static void genrand(JPakeUser *user, const JPakeParameters *params) | |
91 { | |
92 BIGNUM *qm1; | |
93 | |
94 // xa in [0, q) | |
95 user->xa = BN_new(); | |
96 BN_rand_range(user->xa, params->q); | |
97 | |
98 // q-1 | |
99 qm1 = BN_new(); | |
100 BN_copy(qm1, params->q); | |
101 BN_sub_word(qm1, 1); | |
102 | |
103 // ... and xb in [0, q-1) | |
104 user->xb = BN_new(); | |
105 BN_rand_range(user->xb, qm1); | |
106 // [1, q) | |
107 BN_add_word(user->xb, 1); | |
108 | |
109 // cleanup | |
110 BN_free(qm1); | |
111 | |
112 // Show | |
113 printf("x%d", user->p.base); | |
114 showbn("", user->xa); | |
115 printf("x%d", user->p.base+1); | |
116 showbn("", user->xb); | |
117 } | |
118 | |
119 static void hashlength(SHA_CTX *sha, size_t l) | |
120 { | |
121 unsigned char b[2]; | |
122 | |
123 assert(l <= 0xffff); | |
124 b[0] = l >> 8; | |
125 b[1] = l&0xff; | |
126 SHA1_Update(sha, b, 2); | |
127 } | |
128 | |
129 static void hashstring(SHA_CTX *sha, const char *string) | |
130 { | |
131 size_t l = strlen(string); | |
132 | |
133 hashlength(sha, l); | |
134 SHA1_Update(sha, string, l); | |
135 } | |
136 | |
137 static void hashbn(SHA_CTX *sha, const BIGNUM *bn) | |
138 { | |
139 size_t l = BN_num_bytes(bn); | |
140 unsigned char *bin = alloca(l); | |
141 | |
142 hashlength(sha, l); | |
143 BN_bn2bin(bn, bin); | |
144 SHA1_Update(sha, bin, l); | |
145 } | |
146 | |
147 // h=hash(g, g^r, g^x, name) | |
148 static void zkpHash(BIGNUM *h, const JPakeZKP *zkp, const BIGNUM *gx, | |
149 const JPakeUserPublic *from, const JPakeParameters *params) | |
150 { | |
151 unsigned char md[SHA_DIGEST_LENGTH]; | |
152 SHA_CTX sha; | |
153 | |
154 // XXX: hash should not allow moving of the boundaries - Java code | |
155 // is flawed in this respect. Length encoding seems simplest. | |
156 SHA1_Init(&sha); | |
157 hashbn(&sha, params->g); | |
158 hashbn(&sha, zkp->gr); | |
159 hashbn(&sha, gx); | |
160 hashstring(&sha, from->name); | |
161 SHA1_Final(md, &sha); | |
162 BN_bin2bn(md, SHA_DIGEST_LENGTH, h); | |
163 } | |
164 | |
165 // Prove knowledge of x | |
166 // Note that we don't send g^x because, as it happens, we've always | |
167 // sent it elsewhere. Also note that because of that, we could avoid | |
168 // calculating it here, but we don't, for clarity... | |
169 static void CreateZKP(JPakeZKP *zkp, const BIGNUM *x, const JPakeUser *us, | |
170 const BIGNUM *zkpg, const JPakeParameters *params, | |
171 int n, const char *suffix) | |
172 { | |
173 BIGNUM *r = BN_new(); | |
174 BIGNUM *gx = BN_new(); | |
175 BIGNUM *h = BN_new(); | |
176 BIGNUM *t = BN_new(); | |
177 | |
178 // r in [0,q) | |
179 // XXX: Java chooses r in [0, 2^160) - i.e. distribution not uniform | |
180 BN_rand_range(r, params->q); | |
181 // g^r | |
182 zkp->gr = BN_new(); | |
183 BN_mod_exp(zkp->gr, zkpg, r, params->p, params->ctx); | |
184 // g^x | |
185 BN_mod_exp(gx, zkpg, x, params->p, params->ctx); | |
186 | |
187 // h=hash... | |
188 zkpHash(h, zkp, gx, &us->p, params); | |
189 | |
190 // b = r - x*h | |
191 BN_mod_mul(t, x, h, params->q, params->ctx); | |
192 zkp->b = BN_new(); | |
193 BN_mod_sub(zkp->b, r, t, params->q, params->ctx); | |
194 | |
195 // show | |
196 printf(" ZKP(x%d%s)\n", n, suffix); | |
197 showbn(" zkpg", zkpg); | |
198 showbn(" g^x", gx); | |
199 showbn(" g^r", zkp->gr); | |
200 showbn(" b", zkp->b); | |
201 | |
202 // cleanup | |
203 BN_free(t); | |
204 BN_free(h); | |
205 BN_free(gx); | |
206 BN_free(r); | |
207 } | |
208 | |
209 static int VerifyZKP(const JPakeZKP *zkp, BIGNUM *gx, | |
210 const JPakeUserPublic *them, const BIGNUM *zkpg, | |
211 const JPakeParameters *params, int n, const char *suffix) | |
212 { | |
213 BIGNUM *h = BN_new(); | |
214 BIGNUM *t1 = BN_new(); | |
215 BIGNUM *t2 = BN_new(); | |
216 BIGNUM *t3 = BN_new(); | |
217 int ret = 0; | |
218 | |
219 zkpHash(h, zkp, gx, them, params); | |
220 | |
221 // t1 = g^b | |
222 BN_mod_exp(t1, zkpg, zkp->b, params->p, params->ctx); | |
223 // t2 = (g^x)^h = g^{hx} | |
224 BN_mod_exp(t2, gx, h, params->p, params->ctx); | |
225 // t3 = t1 * t2 = g^{hx} * g^b = g^{hx+b} = g^r (allegedly) | |
226 BN_mod_mul(t3, t1, t2, params->p, params->ctx); | |
227 | |
228 printf(" ZKP(x%d%s)\n", n, suffix); | |
229 showbn(" zkpg", zkpg); | |
230 showbn(" g^r'", t3); | |
231 | |
232 // verify t3 == g^r | |
233 if(BN_cmp(t3, zkp->gr) == 0) | |
234 ret = 1; | |
235 | |
236 // cleanup | |
237 BN_free(t3); | |
238 BN_free(t2); | |
239 BN_free(t1); | |
240 BN_free(h); | |
241 | |
242 if(ret) | |
243 puts(" OK"); | |
244 else | |
245 puts(" FAIL"); | |
246 | |
247 return ret; | |
248 } | |
249 | |
250 static void sendstep1_substep(JPakeStep1 *s1, const BIGNUM *x, | |
251 const JPakeUser *us, | |
252 const JPakeParameters *params, int n) | |
253 { | |
254 s1->gx = BN_new(); | |
255 BN_mod_exp(s1->gx, params->g, x, params->p, params->ctx); | |
256 printf(" g^{x%d}", n); | |
257 showbn("", s1->gx); | |
258 | |
259 CreateZKP(&s1->zkpx, x, us, params->g, params, n, ""); | |
260 } | |
261 | |
262 static void sendstep1(const JPakeUser *us, JPakeUserPublic *them, | |
263 const JPakeParameters *params) | |
264 { | |
265 printf("\n%s sends %s:\n\n", us->p.name, them->name); | |
266 | |
267 // from's g^xa (which becomes to's g^xc) and ZKP(xa) | |
268 sendstep1_substep(&them->s1c, us->xa, us, params, us->p.base); | |
269 // from's g^xb (which becomes to's g^xd) and ZKP(xb) | |
270 sendstep1_substep(&them->s1d, us->xb, us, params, us->p.base+1); | |
271 } | |
272 | |
273 static int verifystep1(const JPakeUser *us, const JPakeUserPublic *them, | |
274 const JPakeParameters *params) | |
275 { | |
276 printf("\n%s verifies %s:\n\n", us->p.name, them->name); | |
277 | |
278 // verify their ZKP(xc) | |
279 if(!VerifyZKP(&us->p.s1c.zkpx, us->p.s1c.gx, them, params->g, params, | |
280 them->base, "")) | |
281 return 0; | |
282 | |
283 // verify their ZKP(xd) | |
284 if(!VerifyZKP(&us->p.s1d.zkpx, us->p.s1d.gx, them, params->g, params, | |
285 them->base+1, "")) | |
286 return 0; | |
287 | |
288 // g^xd != 1 | |
289 printf(" g^{x%d} != 1: ", them->base+1); | |
290 if(BN_is_one(us->p.s1d.gx)) | |
291 { | |
292 puts("FAIL"); | |
293 return 0; | |
294 } | |
295 puts("OK"); | |
296 | |
297 return 1; | |
298 } | |
299 | |
300 static void sendstep2(const JPakeUser *us, JPakeUserPublic *them, | |
301 const JPakeParameters *params) | |
302 { | |
303 BIGNUM *t1 = BN_new(); | |
304 BIGNUM *t2 = BN_new(); | |
305 | |
306 printf("\n%s sends %s:\n\n", us->p.name, them->name); | |
307 | |
308 // X = g^{(xa + xc + xd) * xb * s} | |
309 // t1 = g^xa | |
310 BN_mod_exp(t1, params->g, us->xa, params->p, params->ctx); | |
311 // t2 = t1 * g^{xc} = g^{xa} * g^{xc} = g^{xa + xc} | |
312 BN_mod_mul(t2, t1, us->p.s1c.gx, params->p, params->ctx); | |
313 // t1 = t2 * g^{xd} = g^{xa + xc + xd} | |
314 BN_mod_mul(t1, t2, us->p.s1d.gx, params->p, params->ctx); | |
315 // t2 = xb * s | |
316 BN_mod_mul(t2, us->xb, us->secret, params->q, params->ctx); | |
317 // X = t1^{t2} = t1^{xb * s} = g^{(xa + xc + xd) * xb * s} | |
318 them->s2.X = BN_new(); | |
319 BN_mod_exp(them->s2.X, t1, t2, params->p, params->ctx); | |
320 | |
321 // Show | |
322 printf(" g^{(x%d + x%d + x%d) * x%d * s)", us->p.base, them->base, | |
323 them->base+1, us->p.base+1); | |
324 showbn("", them->s2.X); | |
325 | |
326 // ZKP(xb * s) | |
327 // XXX: this is kinda funky, because we're using | |
328 // | |
329 // g' = g^{xa + xc + xd} | |
330 // | |
331 // as the generator, which means X is g'^{xb * s} | |
332 CreateZKP(&them->s2.zkpxbs, t2, us, t1, params, us->p.base+1, " * s"); | |
333 | |
334 // cleanup | |
335 BN_free(t1); | |
336 BN_free(t2); | |
337 } | |
338 | |
339 static int verifystep2(const JPakeUser *us, const JPakeUserPublic *them, | |
340 const JPakeParameters *params) | |
341 { | |
342 BIGNUM *t1 = BN_new(); | |
343 BIGNUM *t2 = BN_new(); | |
344 int ret = 0; | |
345 | |
346 printf("\n%s verifies %s:\n\n", us->p.name, them->name); | |
347 | |
348 // g' = g^{xc + xa + xb} [from our POV] | |
349 // t1 = xa + xb | |
350 BN_mod_add(t1, us->xa, us->xb, params->q, params->ctx); | |
351 // t2 = g^{t1} = g^{xa+xb} | |
352 BN_mod_exp(t2, params->g, t1, params->p, params->ctx); | |
353 // t1 = g^{xc} * t2 = g^{xc + xa + xb} | |
354 BN_mod_mul(t1, us->p.s1c.gx, t2, params->p, params->ctx); | |
355 | |
356 if(VerifyZKP(&us->p.s2.zkpxbs, us->p.s2.X, them, t1, params, them->base+1, | |
357 " * s")) | |
358 ret = 1; | |
359 | |
360 // cleanup | |
361 BN_free(t2); | |
362 BN_free(t1); | |
363 | |
364 return ret; | |
365 } | |
366 | |
367 static void computekey(JPakeUser *us, const JPakeParameters *params) | |
368 { | |
369 BIGNUM *t1 = BN_new(); | |
370 BIGNUM *t2 = BN_new(); | |
371 BIGNUM *t3 = BN_new(); | |
372 | |
373 printf("\n%s calculates the shared key:\n\n", us->p.name); | |
374 | |
375 // K = (X/g^{xb * xd * s})^{xb} | |
376 // = (g^{(xc + xa + xb) * xd * s - xb * xd *s})^{xb} | |
377 // = (g^{(xa + xc) * xd * s})^{xb} | |
378 // = g^{(xa + xc) * xb * xd * s} | |
379 // [which is the same regardless of who calculates it] | |
380 | |
381 // t1 = (g^{xd})^{xb} = g^{xb * xd} | |
382 BN_mod_exp(t1, us->p.s1d.gx, us->xb, params->p, params->ctx); | |
383 // t2 = -s = q-s | |
384 BN_sub(t2, params->q, us->secret); | |
385 // t3 = t1^t2 = g^{-xb * xd * s} | |
386 BN_mod_exp(t3, t1, t2, params->p, params->ctx); | |
387 // t1 = X * t3 = X/g^{xb * xd * s} | |
388 BN_mod_mul(t1, us->p.s2.X, t3, params->p, params->ctx); | |
389 // K = t1^{xb} | |
390 us->key = BN_new(); | |
391 BN_mod_exp(us->key, t1, us->xb, params->p, params->ctx); | |
392 | |
393 // show | |
394 showbn(" K", us->key); | |
395 | |
396 // cleanup | |
397 BN_free(t3); | |
398 BN_free(t2); | |
399 BN_free(t1); | |
400 } | |
401 | |
402 int main(int argc, char **argv) | |
403 { | |
404 JPakeParameters params; | |
405 JPakeUser alice, bob; | |
406 | |
407 alice.p.name = "Alice"; | |
408 alice.p.base = 1; | |
409 bob.p.name = "Bob"; | |
410 bob.p.base = 3; | |
411 | |
412 JPakeParametersInit(¶ms); | |
413 | |
414 // Shared secret | |
415 alice.secret = BN_new(); | |
416 BN_rand(alice.secret, 32, -1, 0); | |
417 bob.secret = alice.secret; | |
418 showbn("secret", alice.secret); | |
419 | |
420 assert(BN_cmp(alice.secret, params.q) < 0); | |
421 | |
422 // Alice's x1, x2 | |
423 genrand(&alice, ¶ms); | |
424 | |
425 // Bob's x3, x4 | |
426 genrand(&bob, ¶ms); | |
427 | |
428 // Now send stuff to each other... | |
429 sendstep1(&alice, &bob.p, ¶ms); | |
430 sendstep1(&bob, &alice.p, ¶ms); | |
431 | |
432 // And verify what each other sent | |
433 if(!verifystep1(&alice, &bob.p, ¶ms)) | |
434 return 1; | |
435 if(!verifystep1(&bob, &alice.p, ¶ms)) | |
436 return 2; | |
437 | |
438 // Second send | |
439 sendstep2(&alice, &bob.p, ¶ms); | |
440 sendstep2(&bob, &alice.p, ¶ms); | |
441 | |
442 // And second verify | |
443 if(!verifystep2(&alice, &bob.p, ¶ms)) | |
444 return 3; | |
445 if(!verifystep2(&bob, &alice.p, ¶ms)) | |
446 return 4; | |
447 | |
448 // Compute common key | |
449 computekey(&alice, ¶ms); | |
450 computekey(&bob, ¶ms); | |
451 | |
452 // Confirm the common key is identical | |
453 // XXX: if the two secrets are not the same, everything works up | |
454 // to this point, so the only way to detect a failure is by the | |
455 // difference in the calculated keys. | |
456 // Since we're all the same code, just compare them directly. In a | |
457 // real system, Alice sends Bob H(H(K)), Bob checks it, then sends | |
458 // back H(K), which Alice checks, or something equivalent. | |
459 puts("\nAlice and Bob check keys are the same:"); | |
460 if(BN_cmp(alice.key, bob.key) == 0) | |
461 puts(" OK"); | |
462 else | |
463 { | |
464 puts(" FAIL"); | |
465 return 5; | |
466 } | |
467 | |
468 return 0; | |
469 } | |
OLD | NEW |