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