]> de.git.xonotic.org Git - xonotic/d0_blind_id.git/blob - d0_blind_id.c
initial import of blind_id
[xonotic/d0_blind_id.git] / d0_blind_id.c
1 #include "d0_blind_id.h"
2
3 #include <stdio.h>
4 #include <string.h>
5 #include "d0_bignum.h"
6 #include "sha1.h"
7
8 // for zero knowledge, we need multiple instances of schnorr ID scheme... should normally be sequential
9 // parallel schnorr ID is not provably zero knowledge :(
10 //   (evil verifier can know all questions in advance, so sequential is disadvantage for him)
11 // we'll just live with a 1:1048576 chance of cheating, and support reauthenticating
12
13 #define SCHNORR_BITS 20
14 // probability of cheat: 2^(-bits+1)
15
16 #define SCHNORR_HASHSIZE 3
17 // cannot be >= SHA_DIGEST_LENGTH
18 // *8 must be >= SCHNORR_BITS
19
20 #define MSGSIZE 640 // ought to be enough for anyone
21
22 struct d0_blind_id_s
23 {
24         // signing (Xonotic pub and priv key)
25         d0_bignum_t *rsa_n, *rsa_e, *rsa_d;
26
27         // public data (Schnorr ID)
28         d0_bignum_t *schnorr_G;
29
30         // private data (player ID private key)
31         d0_bignum_t *schnorr_s;
32
33         // public data (player ID public key, this is what the server gets to know)
34         d0_bignum_t *schnorr_4_to_s;
35         d0_bignum_t *schnorr_4_to_s_signature;
36
37         // temp data
38         d0_bignum_t *rn; // random number blind signature
39         d0_bignum_t *r; // random number for schnorr ID
40         char xnbh[SCHNORR_HASHSIZE]; // init hash
41         d0_bignum_t *e; // challenge
42         char msg[MSGSIZE]; // message
43         size_t msglen; // message length
44 };
45
46 #define CHECK(x) do { if(!(x)) goto fail; } while(0)
47 #define CHECK_ASSIGN(var, value) do { d0_bignum_t *val; val = value; if(!val) goto fail; var = val; } while(0)
48
49 static d0_bignum_t *zero, *one, *four, *temp0, *temp1, *temp2, *temp3, *temp4;
50
51 void d0_blind_id_INITIALIZE()
52 {
53         d0_bignum_INITIALIZE();
54         CHECK_ASSIGN(zero, d0_bignum_int(zero, 0));
55         CHECK_ASSIGN(one, d0_bignum_int(one, 1));
56         CHECK_ASSIGN(four, d0_bignum_int(four, 4));
57         CHECK_ASSIGN(temp0, d0_bignum_int(temp0, 0));
58         CHECK_ASSIGN(temp1, d0_bignum_int(temp1, 0));
59         CHECK_ASSIGN(temp2, d0_bignum_int(temp2, 0));
60         CHECK_ASSIGN(temp3, d0_bignum_int(temp3, 0));
61         CHECK_ASSIGN(temp4, d0_bignum_int(temp4, 0));
62 fail:
63         ;
64 }
65
66 void d0_blind_id_SHUTDOWN()
67 {
68         d0_bignum_free(zero);
69         d0_bignum_free(one);
70         d0_bignum_free(four);
71         d0_bignum_free(temp0);
72         d0_bignum_free(temp1);
73         d0_bignum_free(temp2);
74         d0_bignum_free(temp3);
75         d0_bignum_free(temp4);
76         d0_bignum_SHUTDOWN();
77 }
78
79 // (G-1)/2
80 d0_bignum_t *d0_dl_get_order(d0_bignum_t *o, const d0_bignum_t *G)
81 {
82         CHECK_ASSIGN(o, d0_bignum_sub(o, G, one));
83         CHECK(d0_bignum_shl(o, o, -1)); // order o = (G-1)/2
84         return o;
85 fail:
86         return NULL;
87 }
88 // 2o+1
89 d0_bignum_t *d0_dl_get_from_order(d0_bignum_t *G, const d0_bignum_t *o)
90 {
91         CHECK_ASSIGN(G, d0_bignum_shl(G, o, 1));
92         CHECK(d0_bignum_add(G, G, one));
93         return G;
94 fail:
95         return NULL;
96 }
97
98 BOOL d0_dl_generate_key(size_t size, d0_bignum_t *G)
99 {
100         // using: temp0
101         if(size < 16)
102                 size = 16;
103         for(;;)
104         {
105                 CHECK(d0_bignum_rand_bit_exact(temp0, size-1));
106                 if(d0_bignum_isprime(temp0, 0) == 0)
107                         continue;
108                 CHECK(d0_dl_get_from_order(G, temp0));
109                 if(d0_bignum_isprime(G, 10) == 0)
110                         continue;
111                 if(d0_bignum_isprime(temp0, 10) == 0) // finish the previous test
112                         continue;
113                 break;
114         }
115         return 1;
116 fail:
117         return 0;
118 }
119
120 BOOL d0_rsa_generate_key(size_t size, const d0_bignum_t *e, d0_bignum_t *d, d0_bignum_t *n)
121 {
122         // uses temp0 to temp4
123         int fail = 0;
124         int gcdfail = 0;
125         if(size < 16)
126                 size = 16;
127         int pb = (size + 1)/2;
128         int qb = size - pb;
129         for (;;)
130         {
131                 CHECK(d0_bignum_rand_bit_exact(temp0, pb));
132                 if(d0_bignum_isprime(temp0, 10) == 0)
133                         continue;
134                 CHECK(d0_bignum_sub(temp2, temp0, one));
135                 CHECK(d0_bignum_gcd(temp4, NULL, NULL, temp2, e));
136                 if(!d0_bignum_cmp(temp4, one))
137                         break;
138                 if(++gcdfail == 3)
139                         return 0;
140                 ++gcdfail;
141         }
142         gcdfail = 0;
143         for (;;)
144         {
145                 CHECK(d0_bignum_rand_bit_exact(temp1, qb));
146                 if(!d0_bignum_cmp(temp1, temp0))
147                 {
148                         if(++fail == 3)
149                                 return 0;
150                 }
151                 fail = 0;
152                 if(d0_bignum_isprime(temp1, 10) == 0)
153                         continue;
154                 CHECK(d0_bignum_sub(temp3, temp1, one));
155                 CHECK(d0_bignum_gcd(temp4, NULL, NULL, temp3, e));
156                 if(!d0_bignum_cmp(temp4, one))
157                         break;
158                 if(++gcdfail == 3)
159                         return 0;
160                 ++gcdfail;
161         }
162
163         // n = temp0*temp1
164         CHECK(d0_bignum_mul(n, temp0, temp1));
165                 
166         // d = e^-1 mod (temp0-1)(temp1-1)
167         CHECK(d0_bignum_mul(temp0, temp2, temp3));
168         CHECK(d0_bignum_mod_inv(d, e, temp0));
169         return 1;
170 fail:
171         return 0;
172 }
173
174 void d0_blind_id_clear(d0_blind_id_t *ctx)
175 {
176         if(ctx->rsa_n) d0_bignum_free(ctx->rsa_n);
177         if(ctx->rsa_e) d0_bignum_free(ctx->rsa_e);
178         if(ctx->rsa_d) d0_bignum_free(ctx->rsa_d);
179         if(ctx->schnorr_G) d0_bignum_free(ctx->schnorr_G);
180         if(ctx->schnorr_s) d0_bignum_free(ctx->schnorr_s);
181         if(ctx->schnorr_4_to_s) d0_bignum_free(ctx->schnorr_4_to_s);
182         if(ctx->schnorr_4_to_s_signature) d0_bignum_free(ctx->schnorr_4_to_s_signature);
183         if(ctx->rn) d0_bignum_free(ctx->rn);
184         if(ctx->r) d0_bignum_free(ctx->r);
185         if(ctx->e) d0_bignum_free(ctx->e);
186         memset(ctx, 0, sizeof(*ctx));
187 }
188
189 void d0_blind_id_copy(d0_blind_id_t *ctx, const d0_blind_id_t *src)
190 {
191         d0_blind_id_clear(ctx);
192         if(src->rsa_n) ctx->rsa_n = d0_bignum_mov(NULL, src->rsa_n);
193         if(src->rsa_e) ctx->rsa_e = d0_bignum_mov(NULL, src->rsa_e);
194         if(src->rsa_d) ctx->rsa_d = d0_bignum_mov(NULL, src->rsa_d);
195         if(src->schnorr_G) ctx->schnorr_G = d0_bignum_mov(NULL, src->schnorr_G);
196         if(src->schnorr_s) ctx->schnorr_s = d0_bignum_mov(NULL, src->schnorr_s);
197         if(src->schnorr_4_to_s) ctx->schnorr_4_to_s = d0_bignum_mov(NULL, ctx->schnorr_G);
198         if(src->schnorr_4_to_s_signature) ctx->schnorr_4_to_s_signature = d0_bignum_mov(NULL, src->schnorr_4_to_s_signature);
199         if(src->rn) ctx->rn = d0_bignum_mov(NULL, src->rn);
200         if(src->r) ctx->r = d0_bignum_mov(NULL, src->r);
201         if(src->e) ctx->e = d0_bignum_mov(NULL, src->e);
202         // TODO xnbh, msg, msglen?
203 }
204
205 WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_keys(d0_blind_id_t *ctx, int k)
206 {
207         d0_blind_id_clear(ctx);
208         CHECK_ASSIGN(ctx->schnorr_G, d0_bignum_new());
209         CHECK(d0_dl_generate_key(k, ctx->schnorr_G));
210         CHECK_ASSIGN(ctx->rsa_e, d0_bignum_int(NULL, 65537));
211         CHECK_ASSIGN(ctx->rsa_d, d0_bignum_new());
212         CHECK_ASSIGN(ctx->rsa_n, d0_bignum_new());
213         CHECK(d0_rsa_generate_key(k+1, ctx->rsa_e, ctx->rsa_d, ctx->rsa_n)); // must fit G for sure
214         return 1;
215 fail:
216         return 0;
217 }
218
219 WARN_UNUSED_RESULT BOOL d0_blind_id_read_private_keys(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
220 {
221         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
222         d0_blind_id_clear(ctx);
223         CHECK_ASSIGN(ctx->schnorr_G, d0_iobuf_read_bignum(in, NULL));
224         CHECK_ASSIGN(ctx->rsa_n, d0_iobuf_read_bignum(in, NULL));
225         CHECK_ASSIGN(ctx->rsa_e, d0_iobuf_read_bignum(in, NULL));
226         CHECK_ASSIGN(ctx->rsa_d, d0_iobuf_read_bignum(in, NULL));
227         return d0_iobuf_close(in, NULL);
228
229 fail:
230         d0_iobuf_close(in, NULL);
231         return 0;
232 }
233
234 WARN_UNUSED_RESULT BOOL d0_blind_id_read_public_keys(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
235 {
236         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
237         d0_blind_id_clear(ctx);
238         CHECK_ASSIGN(ctx->schnorr_G, d0_iobuf_read_bignum(in, NULL));
239         CHECK_ASSIGN(ctx->rsa_n, d0_iobuf_read_bignum(in, NULL));
240         CHECK_ASSIGN(ctx->rsa_e, d0_iobuf_read_bignum(in, NULL));
241         return d0_iobuf_close(in, NULL);
242
243 fail:
244         d0_iobuf_close(in, NULL);
245         return 0;
246 }
247
248 #define USING(x) if(!(ctx->x)) return 0
249 #define WRITING(x,f) if(ctx->x) { f(ctx->x); ctx->x = NULL; }
250 #define REPLACING(x)
251
252 WARN_UNUSED_RESULT BOOL d0_blind_id_write_private_keys(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
253 {
254         USING(rsa_n); USING(rsa_e); USING(rsa_d);
255
256         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
257         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_G));
258         CHECK(d0_iobuf_write_bignum(out, ctx->rsa_n));
259         CHECK(d0_iobuf_write_bignum(out, ctx->rsa_e));
260         CHECK(d0_iobuf_write_bignum(out, ctx->rsa_d));
261         return d0_iobuf_close(out, outbuflen);
262
263 fail:
264         d0_iobuf_close(out, outbuflen);
265         return 0;
266 }
267
268 WARN_UNUSED_RESULT BOOL d0_blind_id_write_public_keys(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
269 {
270         USING(rsa_n); USING(rsa_e); USING(rsa_d);
271
272         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
273         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_G));
274         CHECK(d0_iobuf_write_bignum(out, ctx->rsa_n));
275         CHECK(d0_iobuf_write_bignum(out, ctx->rsa_e));
276         return d0_iobuf_close(out, outbuflen);
277
278 fail:
279         if(!d0_iobuf_close(out, outbuflen))
280                 return 0;
281         return 0;
282 }
283
284 WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_id_start(d0_blind_id_t *ctx)
285 {
286         // temps: temp0 order
287         USING(schnorr_G);
288         REPLACING(schnorr_s); REPLACING(schnorr_4_to_s);
289
290         CHECK(d0_dl_get_order(temp0, ctx->schnorr_G));
291         CHECK_ASSIGN(ctx->schnorr_s, d0_bignum_rand_range(ctx->schnorr_s, zero, temp0));
292         CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_bignum_mod_pow(ctx->schnorr_4_to_s, four, ctx->schnorr_s, ctx->schnorr_G));
293         return 1;
294
295 fail:
296         return 0;
297 }
298
299 WARN_UNUSED_RESULT BOOL d0_blind_id_generate_private_id_request(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
300 {
301         // temps: temp0 temp1
302         USING(rsa_n); USING(rsa_e); USING(schnorr_4_to_s);
303         REPLACING(rn);
304
305         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
306
307         CHECK_ASSIGN(ctx->rn, d0_bignum_rand_bit_atmost(ctx->rn, d0_bignum_size(ctx->rsa_n)));
308         CHECK(d0_bignum_mod_pow(temp0, ctx->rn, ctx->rsa_e, ctx->rsa_n));
309         CHECK(d0_bignum_mod_mul(temp1, ctx->schnorr_4_to_s, temp0, ctx->rsa_n));
310         CHECK(d0_iobuf_write_bignum(out, temp1));
311         return d0_iobuf_close(out, outbuflen);
312
313 fail:
314         d0_iobuf_close(out, outbuflen);
315         return 0;
316 }
317
318 WARN_UNUSED_RESULT BOOL d0_blind_id_answer_private_id_request(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen)
319 {
320         // temps: temp0 temp1
321         USING(rsa_d); USING(rsa_n);
322
323         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
324         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
325
326         CHECK(d0_iobuf_read_bignum(in, temp0));
327         CHECK(d0_bignum_mod_pow(temp1, temp0, ctx->rsa_d, ctx->rsa_n));
328         CHECK(d0_iobuf_write_bignum(out, temp1));
329
330         d0_iobuf_close(in, NULL);
331         return d0_iobuf_close(out, outbuflen);
332
333 fail:
334         d0_iobuf_close(in, NULL);
335         d0_iobuf_close(out, outbuflen);
336         return 0;
337 }
338
339 WARN_UNUSED_RESULT BOOL d0_blind_id_finish_private_id_request(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
340 {
341         // temps: temp0 temp1
342         USING(rn); USING(rsa_n);
343         REPLACING(schnorr_4_to_s_signature);
344
345         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
346
347         CHECK(d0_iobuf_read_bignum(in, temp0));
348         CHECK(d0_bignum_mod_inv(temp1, ctx->rn, ctx->rsa_n));
349         CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_bignum_mod_mul(ctx->schnorr_4_to_s_signature, temp0, temp1, ctx->rsa_n));
350
351         return d0_iobuf_close(in, NULL);
352
353 fail:
354         d0_iobuf_close(in, NULL);
355         return 0;
356 }
357
358 WARN_UNUSED_RESULT BOOL d0_blind_id_read_private_id(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
359 {
360         REPLACING(schnorr_s); REPLACING(schnorr_4_to_s); REPLACING(schnorr_4_to_s_signature);
361
362         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
363
364         CHECK_ASSIGN(ctx->schnorr_s, d0_iobuf_read_bignum(in, ctx->schnorr_s));
365         CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s));
366         CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s_signature));
367
368         return d0_iobuf_close(in, NULL);
369
370 fail:
371         d0_iobuf_close(in, NULL);
372         return 0;
373 }
374
375 WARN_UNUSED_RESULT BOOL d0_blind_id_read_public_id(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen)
376 {
377         REPLACING(schnorr_4_to_s); REPLACING(schnorr_4_to_s_signature);
378
379         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
380
381         CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s));
382         CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s_signature));
383
384         return d0_iobuf_close(in, NULL);
385
386 fail:
387         d0_iobuf_close(in, NULL);
388         return 0;
389 }
390
391 WARN_UNUSED_RESULT BOOL d0_blind_id_write_private_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
392 {
393         USING(schnorr_s); USING(schnorr_4_to_s); USING(schnorr_4_to_s_signature);
394
395         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
396
397         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_s));
398         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s));
399         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s_signature));
400
401         return d0_iobuf_close(out, outbuflen);
402
403 fail:
404         d0_iobuf_close(out, outbuflen);
405         return 0;
406 }
407
408 WARN_UNUSED_RESULT BOOL d0_blind_id_write_public_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
409 {
410         USING(schnorr_4_to_s); USING(schnorr_4_to_s_signature);
411
412         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
413
414         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s));
415         CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s_signature));
416
417         return d0_iobuf_close(out, outbuflen);
418
419 fail:
420         d0_iobuf_close(out, outbuflen);
421         return 0;
422 }
423
424 WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_start(d0_blind_id_t *ctx, int is_first, char *msg, size_t msglen, char *outbuf, size_t *outbuflen)
425 // start =
426 //   first run: send 4^s, 4^s signature
427 //   1. get random r, send HASH(4^r)
428 {
429         if(is_first)
430         {
431                 USING(schnorr_4_to_s); USING(schnorr_4_to_s_signature);
432         }
433         USING(schnorr_G);
434         REPLACING(r);
435
436         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
437
438         if(is_first)
439         {
440                 // send ID
441                 CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s));
442                 CHECK(d0_iobuf_write_bignum(out, ctx->schnorr_4_to_s_signature));
443         }
444
445         // start schnorr ID scheme
446         // generate random number r; x = g^r; send hash of x, remember r, forget x
447         CHECK(d0_dl_get_order(temp0, ctx->schnorr_G));
448         CHECK_ASSIGN(ctx->r, d0_bignum_rand_range(ctx->r, zero, temp0));
449         CHECK(d0_bignum_mod_pow(temp0, four, ctx->r, ctx->schnorr_G));
450
451         // hash it, hash it, everybody hash it
452         unsigned char convbuf[1024];
453         d0_iobuf_t *conv = d0_iobuf_open_write(convbuf, sizeof(convbuf));
454         size_t sz;
455         CHECK(d0_iobuf_write_bignum(conv, temp0));
456         CHECK(d0_iobuf_write_packet(conv, msg, msglen));
457         CHECK(d0_iobuf_write_bignum(conv, temp0));
458         d0_iobuf_close(conv, &sz);
459         conv = NULL;
460         CHECK(d0_iobuf_write_raw(out, sha(convbuf, sz), SCHNORR_HASHSIZE) == SCHNORR_HASHSIZE);
461         CHECK(d0_iobuf_write_packet(out, msg, msglen));
462
463         return d0_iobuf_close(out, outbuflen);
464
465 fail:
466         d0_iobuf_close(out, outbuflen);
467         return 0;
468 }
469
470 WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_challenge(d0_blind_id_t *ctx, int is_first, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen)
471 //   first run: get 4^s, 4^s signature
472 //   1. check sig
473 //   2. save HASH(4^r)
474 //   3. send challenge e of SCHNORR_BITS
475 {
476         if(is_first)
477         {
478                 REPLACING(schnorr_4_to_s); REPLACING(k); REPLACING(schnorr_4_to_s_signature);
479                 USING(schnorr_G); USING(rsa_n);
480         }
481         else
482         {
483                 USING(schnorr_4_to_s_signature); USING(schnorr_4_to_s);
484         }
485         USING(rsa_e); USING(rsa_n);
486         REPLACING(e); REPLACING(msg); REPLACING(msglen);
487
488         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
489         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
490
491         if(is_first)
492         {
493                 CHECK_ASSIGN(ctx->schnorr_4_to_s, d0_iobuf_read_bignum(in, NULL));
494                 CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s, zero) >= 0);
495                 CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s, ctx->schnorr_G) < 0);
496                 CHECK_ASSIGN(ctx->schnorr_4_to_s_signature, d0_iobuf_read_bignum(in, ctx->schnorr_4_to_s_signature));
497                 CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s_signature, zero) >= 0);
498                 CHECK(d0_bignum_cmp(ctx->schnorr_4_to_s_signature, ctx->rsa_n) < 0);
499         }
500
501         // check signature of key (t = k^d, so, t^e = k)
502         CHECK(d0_bignum_mod_pow(temp0, ctx->schnorr_4_to_s_signature, ctx->rsa_e, ctx->rsa_n));
503         if(d0_bignum_cmp(temp0, ctx->schnorr_4_to_s))
504         {
505                 // FAIL (not signed by Xonotic)
506                 goto fail;
507                 // TODO: accept the key anyway, but mark as failed signature!
508         }
509
510         CHECK(d0_iobuf_read_raw(in, ctx->xnbh, SCHNORR_HASHSIZE));
511         ctx->msglen = MSGSIZE;
512         CHECK(d0_iobuf_read_packet(in, ctx->msg, &ctx->msglen));
513
514         // send challenge
515         CHECK_ASSIGN(ctx->e, d0_bignum_rand_bit_atmost(ctx->e, SCHNORR_BITS));
516
517         CHECK(d0_iobuf_write_bignum(out, ctx->e));
518
519         d0_iobuf_close(in, NULL);
520         return d0_iobuf_close(out, outbuflen);
521
522 fail:
523         d0_iobuf_close(in, NULL);
524         d0_iobuf_close(out, outbuflen);
525         return 0;
526 }
527
528 WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_response(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *outbuf, size_t *outbuflen)
529 //   1. read challenge e of SCHNORR_BITS
530 //   2. reply with r + s * e mod order
531 {
532         // temps: 0 order, 1 prod, 2 y, 3 e
533         USING(schnorr_G); USING(schnorr_s); USING(r);
534
535         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
536         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
537
538         CHECK(d0_iobuf_read_bignum(in, temp3));
539         // TODO check if >= 2^SCHNORR_BITS or < 0, if yes, then fail (needed for zero knowledge)
540         CHECK(d0_bignum_cmp(temp3, zero) >= 0);
541         CHECK(d0_bignum_size(temp3) <= SCHNORR_BITS);
542
543         // send response for schnorr ID scheme
544         // i.e. r + ctx->schnorr_s * temp3
545         CHECK(d0_dl_get_order(temp0, ctx->schnorr_G));
546         CHECK(d0_bignum_mod_mul(temp1, ctx->schnorr_s, temp3, temp0));
547         CHECK(d0_bignum_mod_add(temp2, temp1, ctx->r, temp0));
548         CHECK(d0_iobuf_write_bignum(out, temp2));
549
550         d0_iobuf_close(in, NULL);
551         return d0_iobuf_close(out, outbuflen);
552
553 fail:
554         d0_iobuf_close(in, NULL);
555         d0_iobuf_close(out, outbuflen);
556         return 0;
557 }
558
559 WARN_UNUSED_RESULT BOOL d0_blind_id_authenticate_with_private_id_verify(d0_blind_id_t *ctx, const char *inbuf, size_t inbuflen, char *msg, ssize_t *msglen)
560 //   1. read y = r + s * e mod order
561 //   2. verify: g^y (g^s)^-e = g^(r+s*e-s*e) = g^r
562 //      (check using H(g^r) which we know)
563 {
564         // temps: 0 y 1 order
565         USING(e); USING(schnorr_G);
566
567         d0_iobuf_t *in = d0_iobuf_open_read(inbuf, inbuflen);
568
569         *msglen = -1;
570         CHECK(d0_dl_get_order(temp1, ctx->schnorr_G));
571         CHECK(d0_iobuf_read_bignum(in, temp0));
572         CHECK(d0_bignum_cmp(temp0, zero) >= 0);
573         CHECK(d0_bignum_cmp(temp0, temp1) < 0);
574
575         // verify schnorr ID scheme
576         // we need 4^temp0 (g^s)^-e
577         CHECK(d0_bignum_neg(temp1, ctx->e));
578         CHECK(d0_bignum_mod_pow(temp2, ctx->schnorr_4_to_s, temp1, ctx->schnorr_G));
579         CHECK(d0_bignum_mod_pow(temp1, four, temp0, ctx->schnorr_G));
580         CHECK(d0_bignum_mod_mul(temp3, temp1, temp2, ctx->schnorr_G));
581         // hash must be equal to xnbh
582
583         // hash it, hash it, everybody hash it
584         unsigned char convbuf[1024];
585         d0_iobuf_t *conv = d0_iobuf_open_write(convbuf, sizeof(convbuf));
586         size_t sz;
587         CHECK(d0_iobuf_write_bignum(conv, temp3));
588         CHECK(d0_iobuf_write_packet(conv, ctx->msg, ctx->msglen));
589         CHECK(d0_iobuf_write_bignum(conv, temp3));
590         d0_iobuf_close(conv, &sz);
591         conv = NULL;
592         if(memcmp(sha(convbuf, sz), ctx->xnbh, SCHNORR_HASHSIZE))
593         {
594                 // FAIL (not owned by player)
595                 goto fail;
596         }
597
598         if(ctx->msglen <= (size_t) *msglen)
599                 memcpy(msg, ctx->msg, ctx->msglen);
600         else
601                 memcpy(msg, ctx->msg, *msglen);
602         *msglen = ctx->msglen;
603
604         d0_iobuf_close(in, NULL);
605         return 1;
606
607 fail:
608         d0_iobuf_close(in, NULL);
609         return 0;
610 }
611
612 WARN_UNUSED_RESULT BOOL d0_blind_id_fingerprint64_public_id(d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen)
613 {
614         USING(schnorr_4_to_s);
615
616         static unsigned char convbuf[1024];
617         d0_iobuf_t *out = d0_iobuf_open_write(outbuf, *outbuflen);
618         d0_iobuf_t *conv = d0_iobuf_open_write(convbuf, sizeof(convbuf));
619
620         size_t n, sz;
621
622         CHECK(d0_iobuf_write_bignum(conv, ctx->schnorr_4_to_s));
623         CHECK(d0_iobuf_close(conv, &sz));
624         conv = NULL;
625
626         n = (*outbuflen / 4) * 3;
627         if(n > SHA_DIGESTSIZE)
628                 n = SHA_DIGESTSIZE;
629         if(d0_iobuf_write_raw(out, sha(convbuf, sz), n) != n)
630                 goto fail;
631         if(!d0_iobuf_conv_base64_out(out))
632                 goto fail;
633
634         return d0_iobuf_close(out, outbuflen);
635
636 fail:
637         if(conv)
638                 if(!d0_iobuf_close(conv, &sz)) { }
639         if(!d0_iobuf_close(out, outbuflen))
640                 return 0;
641         return 0;
642 }
643
644 d0_blind_id_t *d0_blind_id_new()
645 {
646         d0_blind_id_t *b = d0_malloc(sizeof(d0_blind_id_t));
647         memset(b, 0, sizeof(*b));
648         return b;
649 }
650
651 void d0_blind_id_free(d0_blind_id_t *a)
652 {
653         d0_blind_id_clear(a);
654         d0_free(a);
655 }