]> icculus.org git repositories - divverent/darkplaces.git/blob - crypto-keygen-standalone.c
change default snd_soundradius back to 1000, matching Quake
[divverent/darkplaces.git] / crypto-keygen-standalone.c
1 #define _GNU_SOURCE
2
3 #include <d0_blind_id/d0_blind_id.h>
4
5 #include <ctype.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <getopt.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <signal.h>
12 #include <math.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15
16 // BEGIN stuff shared with crypto.c
17 #define FOURCC_D0PK (('d' << 0) | ('0' << 8) | ('p' << 16) | ('k' << 24))
18 #define FOURCC_D0SK (('d' << 0) | ('0' << 8) | ('s' << 16) | ('k' << 24))
19 #define FOURCC_D0PI (('d' << 0) | ('0' << 8) | ('p' << 16) | ('i' << 24))
20 #define FOURCC_D0SI (('d' << 0) | ('0' << 8) | ('s' << 16) | ('i' << 24))
21 #define FOURCC_D0IQ (('d' << 0) | ('0' << 8) | ('i' << 16) | ('q' << 24))
22 #define FOURCC_D0IR (('d' << 0) | ('0' << 8) | ('i' << 16) | ('r' << 24))
23 #define FOURCC_D0ER (('d' << 0) | ('0' << 8) | ('e' << 16) | ('r' << 24))
24 #define FOURCC_D0IC (('d' << 0) | ('0' << 8) | ('i' << 16) | ('c' << 24))
25
26 static unsigned long Crypto_LittleLong(const char *data)
27 {
28         return
29                 ((unsigned char) data[0]) |
30                 (((unsigned char) data[1]) << 8) |
31                 (((unsigned char) data[2]) << 16) |
32                 (((unsigned char) data[3]) << 24);
33 }
34
35 static void Crypto_UnLittleLong(char *data, unsigned long l)
36 {
37         data[0] = l & 0xFF;
38         data[1] = (l >> 8) & 0xFF;
39         data[2] = (l >> 16) & 0xFF;
40         data[3] = (l >> 24) & 0xFF;
41 }
42
43 static size_t Crypto_ParsePack(const char *buf, size_t len, unsigned long header, const char **lumps, size_t *lumpsize, size_t nlumps)
44 {
45         size_t i;
46         size_t pos;
47         pos = 0;
48         if(header)
49         {
50                 if(len < 4)
51                         return 0;
52                 if(Crypto_LittleLong(buf) != header)
53                         return 0;
54                 pos += 4;
55         }
56         for(i = 0; i < nlumps; ++i)
57         {
58                 if(pos + 4 > len)
59                         return 0;
60                 lumpsize[i] = Crypto_LittleLong(&buf[pos]);
61                 pos += 4;
62                 if(pos + lumpsize[i] > len)
63                         return 0;
64                 lumps[i] = &buf[pos];
65                 pos += lumpsize[i];
66         }
67         return pos;
68 }
69
70 static size_t Crypto_UnParsePack(char *buf, size_t len, unsigned long header, const char *const *lumps, const size_t *lumpsize, size_t nlumps)
71 {
72         size_t i;
73         size_t pos;
74         pos = 0;
75         if(header)
76         {
77                 if(len < 4)
78                         return 0;
79                 Crypto_UnLittleLong(buf, header);
80                 pos += 4;
81         }
82         for(i = 0; i < nlumps; ++i)
83         {
84                 if(pos + 4 + lumpsize[i] > len)
85                         return 0;
86                 Crypto_UnLittleLong(&buf[pos], lumpsize[i]);
87                 pos += 4;
88                 memcpy(&buf[pos], lumps[i], lumpsize[i]);
89                 pos += lumpsize[i];
90         }
91         return pos;
92 }
93
94 void file2lumps(const char *fn, unsigned long header, const char **lumps, size_t *lumpsize, size_t nlumps)
95 {
96         FILE *f;
97         char buf[65536];
98         size_t n;
99         f = fopen(fn, "rb");
100         if(!f)
101         {
102                 fprintf(stderr, "could not open %s\n", fn);
103                 exit(1);
104         }
105         n = fread(buf, 1, sizeof(buf), f);
106         fclose(f);
107         if(!Crypto_ParsePack(buf, n, header, lumps, lumpsize, nlumps))
108         {
109                 fprintf(stderr, "could not parse %s as %c%c%c%c (%d lumps expected)\n", fn, (int) header & 0xFF, (int) (header >> 8) & 0xFF, (int) (header >> 16) & 0xFF, (int) (header >> 24) & 0xFF, (int) nlumps);
110                 exit(1);
111         }
112 }
113
114 mode_t umask_save;
115 void lumps2file(const char *fn, unsigned long header, const char *const *lumps, size_t *lumpsize, size_t nlumps, D0_BOOL private)
116 {
117         FILE *f;
118         char buf[65536];
119         size_t n;
120         if(private)
121                 umask(umask_save | 0077);
122         else
123                 umask(umask_save);
124         f = fopen(fn, "wb");
125         if(!f)
126         {
127                 fprintf(stderr, "could not open %s\n", fn);
128                 exit(1);
129         }
130         if(!(n = Crypto_UnParsePack(buf, sizeof(buf), header, lumps, lumpsize, nlumps)))
131         {
132                 fprintf(stderr, "could not unparse for %s\n", fn);
133                 exit(1);
134         }
135         n = fwrite(buf, n, 1, f);
136         if(fclose(f) || !n)
137         {
138                 fprintf(stderr, "could not write %s\n", fn);
139                 exit(1);
140         }
141 }
142
143 void USAGE(const char *me)
144 {
145         printf("Usage:\n"
146                         "%s [-F] [-b bits] [-n progress-denominator] [-x prefix] [-X infix] [-C] -o private.d0sk\n"
147                         "%s -P private.d0sk -o public.d0pk\n"
148                         "%s [-n progress-denominator] [-x prefix] [-X infix] [-C] -p public.d0pk -o idkey-unsigned.d0si\n"
149                         "%s -p public.d0pk -I idkey-unsigned.d0si -o request.d0iq -O camouflage.d0ic\n"
150                         "%s -P private.d0sk -j request.d0iq -o response.d0ir\n"
151                         "%s -p public.d0pk -I idkey-unsigned.d0si -c camouflage.d0ic -J response.d0ir -o idkey.d0si\n"
152                         "%s -P private.d0sk -I idkey-unsigned.d0si -o idkey.d0si\n"
153                         "%s -I idkey.d0si -o id.d0pi\n"
154                         "%s -p public.d0pk\n"
155                         "%s -P private.d0sk\n"
156                         "%s -p public.d0pk -i id.d0pi\n"
157                         "%s -p public.d0pk -I idkey.d0si\n"
158                         "%s -0 -p public.d0pk -I idkey.d0si\n"
159                         "%s -0 -p public.d0pk\n",
160                         me, me, me, me, me, me, me, me, me, me, me, me, me, me
161                    );
162 }
163
164 unsigned int seconds;
165 unsigned int generated;
166 unsigned int ntasks = 1;
167 double generated_offset;
168 double guesscount;
169 double guessfactor;
170 void print_generated(int signo)
171 {
172         (void) signo;
173         ++seconds;
174         if(generated >= 1000000000)
175         {
176                 generated_offset += generated;
177                 generated = 0;
178         }
179         fprintf(stderr, "Generated: %.0f (about %.0f, %.1f/s, about %.2f hours for %.0f)\n",
180                 // nasty and dishonest hack:
181                 // we are adjusting the values "back", so the total count is
182                 // divided by guessfactor (as the check function is called
183                 // guessfactor as often as it would be if no fastreject were
184                 // done)
185                 // so the values indicate the relative speed of fastreject vs
186                 // normal!
187                 (generated + generated_offset) / guessfactor,
188                 (generated + generated_offset) * ntasks / guessfactor,
189                 (generated + generated_offset) * ntasks / (guessfactor * seconds),
190                 guesscount * ((guessfactor * seconds) / (generated + generated_offset) / ntasks) / 3600.0,
191                 guesscount);
192         alarm(1);
193 }
194
195 #define CHECK(x) if(!(x)) { fprintf(stderr, "error exit: error returned by %s\n", #x); exit(2); }
196
197 const char *prefix = NULL, *infix = NULL;
198 size_t prefixlen = 0;
199 int ignorecase;
200 typedef D0_BOOL (*fingerprint_func) (const d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
201 D0_BOOL fastreject(const d0_blind_id_t *ctx, void *pass)
202 {
203         static char fp64[513]; size_t fp64size = 512;
204         CHECK(((fingerprint_func) pass)(ctx, fp64, &fp64size));
205         ++generated;
206         if(ignorecase)
207         {
208                 if(prefixlen)
209                         if(strncasecmp(fp64, prefix, prefixlen))
210                                 return 1;
211                 if(infix)
212                 {
213                         fp64[fp64size] = 0;
214                         if(!strcasestr(fp64, infix))
215                                 return 1;
216                 }
217         }
218         else
219         {
220                 if(prefixlen)
221                         if(memcmp(fp64, prefix, prefixlen))
222                                 return 1;
223                 if(infix)
224                 {
225                         fp64[fp64size] = 0;
226                         if(!strstr(fp64, infix))
227                                 return 1;
228                 }
229         }
230         return 0;
231 }
232
233 int main(int argc, char **argv)
234 {
235         int opt;
236         size_t lumpsize[2];
237         const char *lumps[2];
238         char lumps_w0[65536];
239         char lumps_w1[65536];
240         const char *pubkeyfile = NULL, *privkeyfile = NULL, *pubidfile = NULL, *prividfile = NULL, *idreqfile = NULL, *idresfile = NULL, *outfile = NULL, *outfile2 = NULL, *camouflagefile = NULL;
241         char fp64[513]; size_t fp64size = 512;
242         int mask = 0;
243         int bits = 1024;
244         int i;
245         D0_BOOL do_fastreject = 1;
246         d0_blind_id_t *ctx;
247         if(!d0_blind_id_INITIALIZE())
248         {
249                 fprintf(stderr, "could not initialize\n");
250                 exit(1);
251         }
252
253         umask_save = umask(0022);
254
255         ctx = d0_blind_id_new();
256         while((opt = getopt(argc, argv, "p:P:i:I:j:J:o:O:c:b:x:X:y:Fn:C0")) != -1)
257         {
258                 switch(opt)
259                 {
260                         case 'C':
261                                 ignorecase = 1;
262                                 break;
263                         case 'n':
264                                 ntasks = atoi(optarg);
265                                 break;
266                         case 'b':
267                                 bits = atoi(optarg);
268                                 break;
269                         case 'p': // d0pk = <pubkey> <modulus>
270                                 pubkeyfile = optarg;
271                                 mask |= 1;
272                                 break;
273                         case 'P': // d0sk = <privkey> <modulus>
274                                 privkeyfile = optarg;
275                                 mask |= 2;
276                                 break;
277                         case 'i': // d0pi = <pubid>
278                                 pubidfile = optarg;
279                                 mask |= 4;
280                                 break;
281                         case 'I': // d0si = <privid>
282                                 prividfile = optarg;
283                                 mask |= 8;
284                                 break;
285                         case 'j': // d0iq = <req>
286                                 idreqfile = optarg;
287                                 mask |= 0x10;
288                                 break;
289                         case 'J': // d0ir = <resp>
290                                 idresfile = optarg;
291                                 mask |= 0x20;
292                                 break;
293                         case 'o':
294                                 outfile = optarg;
295                                 mask |= 0x40;
296                                 break;
297                         case 'O':
298                                 outfile2 = optarg;
299                                 mask |= 0x80;
300                                 break;
301                         case 'c':
302                                 camouflagefile = optarg;
303                                 mask |= 0x100;
304                                 break;
305                         case 'x':
306                                 prefix = optarg;
307                                 prefixlen = strlen(prefix);
308                                 break;
309                         case '0':
310                                 // test mode
311                                 mask |= 0x200;
312                                 break;
313                         case 'X':
314                                 infix = optarg;
315                                 break;
316                         case 'F':
317                                 do_fastreject = 0;
318                                 break;
319                         default:
320                                 USAGE(*argv);
321                                 return 1;
322                 }
323         }
324
325         // fastreject is a slight slowdown when rejecting nothing at all
326         if(!infix && !prefixlen)
327                 do_fastreject = 0;
328
329         guesscount = pow(64.0, prefixlen);
330         if(infix)
331                 guesscount /= (1 - pow(1 - pow(1/64.0, strlen(infix)), 44 - prefixlen - strlen(infix)));
332         // 44 chars; prefix is assumed to not match the infix (although it theoretically could)
333         // 43'th char however is always '=' and does not count
334         if(ignorecase)
335         {
336                 if(infix)
337                         for(i = 0; infix[i]; ++i)
338                                 if(toupper(infix[i]) != tolower(infix[i]))
339                                         guesscount /= 2;
340                 for(i = 0; i < (int)prefixlen; ++i)
341                         if(toupper(prefix[i]) != tolower(prefix[i]))
342                                 guesscount /= 2;
343         }
344
345         if(do_fastreject)
346         {
347                 // fastreject: reject function gets called about log(2^bits) times more often
348                 guessfactor = bits * log(2) / 2;
349                 // so guess function gets called guesscount * guessfactor times, and it tests as many valid keys as guesscount
350         }
351
352         if(mask & 1)
353         {
354                 file2lumps(pubkeyfile, FOURCC_D0PK, lumps, lumpsize, 2);
355                 if(!d0_blind_id_read_public_key(ctx, lumps[0], lumpsize[0]))
356                 {
357                         fprintf(stderr, "could not decode public key\n");
358                         exit(1);
359                 }
360                 if(!d0_blind_id_read_private_id_modulus(ctx, lumps[1], lumpsize[1]))
361                 {
362                         fprintf(stderr, "could not decode modulus\n");
363                         exit(1);
364                 }
365         }
366         else if(mask & 2)
367         {
368                 file2lumps(privkeyfile, FOURCC_D0SK, lumps, lumpsize, 2);
369                 if(!d0_blind_id_read_private_key(ctx, lumps[0], lumpsize[0]))
370                 {
371                         fprintf(stderr, "could not decode private key\n");
372                         exit(1);
373                 }
374                 if(!d0_blind_id_read_private_id_modulus(ctx, lumps[1], lumpsize[1]))
375                 {
376                         fprintf(stderr, "could not decode modulus\n");
377                         exit(1);
378                 }
379         }
380
381         if(mask & 4)
382         {
383                 file2lumps(pubidfile, FOURCC_D0PI, lumps, lumpsize, 1);
384                 if(!d0_blind_id_read_public_id(ctx, lumps[0], lumpsize[0]))
385                 {
386                         fprintf(stderr, "could not decode public ID\n");
387                         exit(1);
388                 }
389         }
390         if(mask & 8)
391         {
392                 file2lumps(prividfile, FOURCC_D0SI, lumps, lumpsize, 1);
393                 if(!d0_blind_id_read_private_id(ctx, lumps[0], lumpsize[0]))
394                 {
395                         fprintf(stderr, "could not decode private ID\n");
396                         exit(1);
397                 }
398         }
399
400         if(mask & 0x10)
401         {
402                 file2lumps(idreqfile, FOURCC_D0IQ, lumps, lumpsize, 1);
403                 lumpsize[1] = sizeof(lumps_w1);
404                 lumps[1] = lumps_w1;
405                 if(!d0_blind_id_answer_private_id_request(ctx, lumps[0], lumpsize[0], lumps_w1, &lumpsize[1]))
406                 {
407                         fprintf(stderr, "could not answer private ID request\n");
408                         exit(1);
409                 }
410         }
411         else if((mask & 0x120) == 0x120)
412         {
413                 file2lumps(camouflagefile, FOURCC_D0IC, lumps, lumpsize, 1);
414                 if(!d0_blind_id_read_private_id_request_camouflage(ctx, lumps[0], lumpsize[0]))
415                 {
416                         fprintf(stderr, "could not decode camouflage\n");
417                         exit(1);
418                 }
419
420                 file2lumps(idresfile, FOURCC_D0IR, lumps, lumpsize, 1);
421                 if(!d0_blind_id_finish_private_id_request(ctx, lumps[0], lumpsize[0]))
422                 {
423                         fprintf(stderr, "could not finish private ID request\n");
424                         exit(1);
425                 }
426         }
427
428         switch(mask)
429         {
430                 // modes of operation:
431                 case 0x40:
432                         //   nothing -> private key file (incl modulus), print fingerprint
433                         generated = 0;
434                         generated_offset = 0;
435                         seconds = 0;
436                         signal(SIGALRM, print_generated);
437                         alarm(1);
438                         if(do_fastreject)
439                         {
440                                 CHECK(d0_blind_id_generate_private_key_fastreject(ctx, bits, fastreject, d0_blind_id_fingerprint64_public_key));
441                         }
442                         else
443                         {
444                                 guessfactor = 1; // no fastreject here
445                                 do
446                                 {
447                                         CHECK(d0_blind_id_generate_private_key(ctx, bits));
448                                 }
449                                 while(fastreject(ctx, d0_blind_id_fingerprint64_public_key));
450                         }
451                         alarm(0);
452                         signal(SIGALRM, NULL);
453                         CHECK(d0_blind_id_generate_private_id_modulus(ctx));
454                         lumps[0] = lumps_w0;
455                         lumpsize[0] = sizeof(lumps_w0);
456                         lumps[1] = lumps_w1;
457                         lumpsize[1] = sizeof(lumps_w1);
458                         CHECK(d0_blind_id_write_private_key(ctx, lumps_w0, &lumpsize[0]));
459                         CHECK(d0_blind_id_write_private_id_modulus(ctx, lumps_w1, &lumpsize[1]));
460                         lumps2file(outfile, FOURCC_D0SK, lumps, lumpsize, 2, 1);
461                         break;
462                 case 0x42:
463                         //   private key file -> public key file (incl modulus)
464                         lumps[0] = lumps_w0;
465                         lumpsize[0] = sizeof(lumps_w0);
466                         lumps[1] = lumps_w1;
467                         lumpsize[1] = sizeof(lumps_w1);
468                         CHECK(d0_blind_id_write_public_key(ctx, lumps_w0, &lumpsize[0]));
469                         CHECK(d0_blind_id_write_private_id_modulus(ctx, lumps_w1, &lumpsize[1]));
470                         lumps2file(outfile, FOURCC_D0PK, lumps, lumpsize, 2, 0);
471                         break;
472                 case 0x41:
473                         //   public key file -> unsigned private ID file
474                         generated = 0;
475                         generated_offset = 0;
476                         seconds = 0;
477                         signal(SIGALRM, print_generated);
478                         alarm(1);
479                         guessfactor = 1; // no fastreject here
480                         do
481                         {
482                                 CHECK(d0_blind_id_generate_private_id_start(ctx));
483                         }
484                         while(fastreject(ctx, d0_blind_id_fingerprint64_public_id));
485                         alarm(0);
486                         signal(SIGALRM, 0);
487                         lumps[0] = lumps_w0;
488                         lumpsize[0] = sizeof(lumps_w0);
489                         CHECK(d0_blind_id_write_private_id(ctx, lumps_w0, &lumpsize[0]));
490                         lumps2file(outfile, FOURCC_D0SI, lumps, lumpsize, 1, 1);
491                         break;
492                 case 0xC9:
493                         //   public key file, unsigned private ID file -> ID request file and camouflage file
494                         lumps[0] = lumps_w0;
495                         lumpsize[0] = sizeof(lumps_w0);
496                         CHECK(d0_blind_id_generate_private_id_request(ctx, lumps_w0, &lumpsize[0]));
497                         lumps2file(outfile, FOURCC_D0IQ, lumps, lumpsize, 1, 0);
498                         lumpsize[0] = sizeof(lumps_w0);
499                         CHECK(d0_blind_id_write_private_id_request_camouflage(ctx, lumps_w0, &lumpsize[0]));
500                         lumps2file(outfile2, FOURCC_D0IC, lumps, lumpsize, 1, 1);
501                         break;
502                 case 0x52:
503                         //   private key file, ID request file -> ID response file
504                         lumps2file(outfile, FOURCC_D0IR, lumps+1, lumpsize+1, 1, 0);
505                         break;
506                 case 0x169:
507                         //   public key file, ID response file, private ID file -> signed private ID file
508                         lumps[0] = lumps_w0;
509                         lumpsize[0] = sizeof(lumps_w0);
510                         CHECK(d0_blind_id_write_private_id(ctx, lumps_w0, &lumpsize[0]));
511                         lumps2file(outfile, FOURCC_D0SI, lumps, lumpsize, 1, 1);
512                         break;
513                 case 0x4A:
514                         //   private key file, private ID file -> signed private ID file
515                         {
516                                 char buf[65536]; size_t bufsize;
517                                 char buf2[65536]; size_t buf2size;
518                                 D0_BOOL status;
519                                 d0_blind_id_t *ctx2 = d0_blind_id_new();
520                                 CHECK(d0_blind_id_copy(ctx2, ctx));
521                                 bufsize = sizeof(buf);
522                                 CHECK(d0_blind_id_authenticate_with_private_id_start(ctx, 1, 1, "hello world", 11, buf, &bufsize));
523                                 buf2size = sizeof(buf2);
524                                 CHECK(d0_blind_id_authenticate_with_private_id_challenge(ctx2, 1, 1, buf, bufsize, buf2, &buf2size, &status));
525                                 bufsize = sizeof(buf);
526                                 CHECK(d0_blind_id_authenticate_with_private_id_response(ctx, buf2, buf2size, buf, &bufsize));
527                                 buf2size = sizeof(buf2);
528                                 CHECK(d0_blind_id_authenticate_with_private_id_verify(ctx2, buf, bufsize, buf2, &buf2size, &status));
529                                 CHECK(status == 0);
530                                 CHECK(d0_blind_id_authenticate_with_private_id_generate_missing_signature(ctx2));
531                                 lumps[0] = lumps_w0;
532                                 lumpsize[0] = sizeof(lumps_w0);
533                                 CHECK(d0_blind_id_write_private_id(ctx2, lumps_w0, &lumpsize[0]));
534                                 lumps2file(outfile, FOURCC_D0SI, lumps, lumpsize, 1, 1);
535                         }
536                         break;
537                 case 0x48:
538                         //   private ID file -> public ID file
539                         lumps[0] = lumps_w0;
540                         lumpsize[0] = sizeof(lumps_w0);
541                         CHECK(d0_blind_id_write_public_id(ctx, lumps_w0, &lumpsize[0]));
542                         lumps2file(outfile, FOURCC_D0PI, lumps, lumpsize, 1, 0);
543                         break;
544                 case 0x01:
545                 case 0x02:
546                         //   public/private key file -> fingerprint
547                         CHECK(d0_blind_id_fingerprint64_public_key(ctx, fp64, &fp64size));
548                         printf("%.*s\n", (int)fp64size, fp64);
549                         break;
550                 case 0x05:
551                 case 0x09:
552                         //   public/private ID file -> fingerprint
553                         CHECK(d0_blind_id_fingerprint64_public_id(ctx, fp64, &fp64size));
554                         printf("%.*s\n", (int)fp64size, fp64);
555                         break;
556 /*
557                 case 0x09:
558                         //   public key, private ID file -> test whether key is properly signed
559                         {
560                                 char buf[65536]; size_t bufsize;
561                                 char buf2[65536]; size_t buf2size;
562                                 D0_BOOL status;
563                                 d0_blind_id_t *ctx2 = d0_blind_id_new();
564                                 CHECK(d0_blind_id_copy(ctx2, ctx));
565                                 bufsize = sizeof(buf);
566                                 CHECK(d0_blind_id_authenticate_with_private_id_start(ctx, 1, 1, "hello world", 11, buf, &bufsize));
567                                 buf2size = sizeof(buf2);
568                                 CHECK(d0_blind_id_authenticate_with_private_id_challenge(ctx2, 1, 1, buf, bufsize, buf2, &buf2size, &status));
569                                 bufsize = sizeof(buf);
570                                 CHECK(d0_blind_id_authenticate_with_private_id_response(ctx, buf2, buf2size, buf, &bufsize));
571                                 buf2size = sizeof(buf2);
572                                 CHECK(d0_blind_id_authenticate_with_private_id_verify(ctx2, buf, bufsize, buf2, &buf2size, &status));
573                                 if(status)
574                                         printf("OK\n");
575                                 else
576                                         printf("EPIC FAIL\n");
577                         }
578                         break;
579 */
580                 case 0x209:
581                         // protocol client
582                         {
583                                 char hexbuf[131073];
584                                 const char hex[] = "0123456789abcdef";
585                                 char buf[65536]; size_t bufsize;
586                                 char buf2[65536]; size_t buf2size;
587                                 bufsize = sizeof(buf);
588                                 CHECK(d0_blind_id_authenticate_with_private_id_start(ctx, 1, 1, "hello world", 11, buf, &bufsize));
589                                 for(i = 0; i < (int)bufsize; ++i)
590                                         sprintf(&hexbuf[2*i], "%02x", (unsigned char)buf[i]);
591                                 printf("%s\n", hexbuf);
592                                 fgets(hexbuf, sizeof(hexbuf), stdin);
593                                 buf2size = strlen(hexbuf) / 2;
594                                 for(i = 0; i < (int)buf2size; ++i)
595                                         buf2[i] = ((strchr(hex, hexbuf[2*i]) - hex) << 4) | (strchr(hex, hexbuf[2*i+1]) - hex);
596                                 bufsize = sizeof(buf);
597                                 CHECK(d0_blind_id_authenticate_with_private_id_response(ctx, buf2, buf2size, buf, &bufsize));
598                                 for(i = 0; i < (int)bufsize; ++i)
599                                         sprintf(&hexbuf[2*i], "%02x", (unsigned char)buf[i]);
600                                 printf("%s\n", hexbuf);
601                         }
602                         break;
603                 case 0x201:
604                         // protocol server
605                         {
606                                 char hexbuf[131073];
607                                 const char hex[] = "0123456789abcdef";
608                                 char buf[65536]; size_t bufsize;
609                                 char buf2[65536]; size_t buf2size;
610                                 D0_BOOL status;
611                                 fgets(hexbuf, sizeof(hexbuf), stdin);
612                                 buf2size = strlen(hexbuf) / 2;
613                                 for(i = 0; i < (int)buf2size; ++i)
614                                         buf2[i] = ((strchr(hex, hexbuf[2*i]) - hex) << 4) | (strchr(hex, hexbuf[2*i+1]) - hex);
615                                 bufsize = sizeof(buf);
616                                 CHECK(d0_blind_id_authenticate_with_private_id_challenge(ctx, 1, 1, buf2, buf2size, buf, &bufsize, &status));
617                                 for(i = 0; i < (int)bufsize; ++i)
618                                         sprintf(&hexbuf[2*i], "%02x", (unsigned char)buf[i]);
619                                 printf("%s\n", hexbuf);
620                                 fgets(hexbuf, sizeof(hexbuf), stdin);
621                                 buf2size = strlen(hexbuf) / 2;
622                                 for(i = 0; i < (int)buf2size; ++i)
623                                         buf2[i] = ((strchr(hex, hexbuf[2*i]) - hex) << 4) | (strchr(hex, hexbuf[2*i+1]) - hex);
624                                 bufsize = sizeof(buf);
625                                 CHECK(d0_blind_id_authenticate_with_private_id_verify(ctx, buf2, buf2size, buf, &bufsize, &status));
626                                 printf("verify status: %d\n", status);
627
628                                 CHECK(d0_blind_id_fingerprint64_public_id(ctx, fp64, &fp64size));
629                                 printf("%.*s\n", (int)fp64size, fp64);
630                         }
631                         break;
632                 default:
633                         USAGE(*argv);
634                         exit(1);
635                         break;
636         }
637         d0_blind_id_SHUTDOWN();
638         return 0;
639 }