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