fix in entity teleport detection of FlyMove
[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                         me, me, me, me, me, me, me, me, me, me, me, me
156                    );
157 }
158
159 unsigned int seconds;
160 unsigned int generated;
161 unsigned int ntasks = 1;
162 double generated_offset;
163 double guesscount;
164 double guessfactor;
165 void print_generated(int signo)
166 {
167         (void) signo;
168         ++seconds;
169         if(generated >= 1000000000)
170         {
171                 generated_offset += generated;
172                 generated = 0;
173         }
174         fprintf(stderr, "Generated: %.0f (about %.0f, %.1f/s, about %.2f hours for %.0f)\n",
175                 // nasty and dishonest hack:
176                 // we are adjusting the values "back", so the total count is
177                 // divided by guessfactor (as the check function is called
178                 // guessfactor as often as it would be if no fastreject were
179                 // done)
180                 // so the values indicate the relative speed of fastreject vs
181                 // normal!
182                 (generated + generated_offset) / guessfactor,
183                 (generated + generated_offset) * ntasks / guessfactor,
184                 (generated + generated_offset) * ntasks / (guessfactor * seconds),
185                 guesscount * ((guessfactor * seconds) / (generated + generated_offset) / ntasks) / 3600.0,
186                 guesscount);
187         alarm(1);
188 }
189
190 #define CHECK(x) if(!(x)) { fprintf(stderr, "error exit: error returned by %s\n", #x); exit(2); }
191
192 const char *prefix = NULL, *infix = NULL;
193 size_t prefixlen = 0;
194 int ignorecase;
195 typedef D0_BOOL (*fingerprint_func) (const d0_blind_id_t *ctx, char *outbuf, size_t *outbuflen);
196 D0_BOOL fastreject(const d0_blind_id_t *ctx, void *pass)
197 {
198         static char fp64[513]; size_t fp64size = 512;
199         CHECK(((fingerprint_func) pass)(ctx, fp64, &fp64size));
200         ++generated;
201         if(ignorecase)
202         {
203                 if(prefixlen)
204                         if(strncasecmp(fp64, prefix, prefixlen))
205                                 return 1;
206                 if(infix)
207                 {
208                         fp64[fp64size] = 0;
209                         if(!strcasestr(fp64, infix))
210                                 return 1;
211                 }
212         }
213         else
214         {
215                 if(prefixlen)
216                         if(memcmp(fp64, prefix, prefixlen))
217                                 return 1;
218                 if(infix)
219                 {
220                         fp64[fp64size] = 0;
221                         if(!strstr(fp64, infix))
222                                 return 1;
223                 }
224         }
225         return 0;
226 }
227
228 int main(int argc, char **argv)
229 {
230         int opt;
231         size_t lumpsize[2];
232         const char *lumps[2];
233         char lumps_w0[65536];
234         char lumps_w1[65536];
235         const char *pubkeyfile = NULL, *privkeyfile = NULL, *pubidfile = NULL, *prividfile = NULL, *idreqfile = NULL, *idresfile = NULL, *outfile = NULL, *outfile2 = NULL, *camouflagefile = NULL;
236         char fp64[513]; size_t fp64size = 512;
237         int mask = 0;
238         int bits = 1024;
239         int i;
240         D0_BOOL do_fastreject = 1;
241         d0_blind_id_t *ctx;
242         if(!d0_blind_id_INITIALIZE())
243         {
244                 fprintf(stderr, "could not initialize\n");
245                 exit(1);
246         }
247
248         umask_save = umask(0022);
249
250         ctx = d0_blind_id_new();
251         while((opt = getopt(argc, argv, "p:P:i:I:j:J:o:O:c:b:x:X:y:Fn:C")) != -1)
252         {
253                 switch(opt)
254                 {
255                         case 'C':
256                                 ignorecase = 1;
257                                 break;
258                         case 'n':
259                                 ntasks = atoi(optarg);
260                                 break;
261                         case 'b':
262                                 bits = atoi(optarg);
263                                 break;
264                         case 'p': // d0pk = <pubkey> <modulus>
265                                 pubkeyfile = optarg;
266                                 mask |= 1;
267                                 break;
268                         case 'P': // d0sk = <privkey> <modulus>
269                                 privkeyfile = optarg;
270                                 mask |= 2;
271                                 break;
272                         case 'i': // d0pi = <pubid>
273                                 pubidfile = optarg;
274                                 mask |= 4;
275                                 break;
276                         case 'I': // d0si = <privid>
277                                 prividfile = optarg;
278                                 mask |= 8;
279                                 break;
280                         case 'j': // d0iq = <req>
281                                 idreqfile = optarg;
282                                 mask |= 0x10;
283                                 break;
284                         case 'J': // d0ir = <resp>
285                                 idresfile = optarg;
286                                 mask |= 0x20;
287                                 break;
288                         case 'o':
289                                 outfile = optarg;
290                                 mask |= 0x40;
291                                 break;
292                         case 'O':
293                                 outfile2 = optarg;
294                                 mask |= 0x80;
295                                 break;
296                         case 'c':
297                                 camouflagefile = optarg;
298                                 mask |= 0x100;
299                                 break;
300                         case 'x':
301                                 prefix = optarg;
302                                 prefixlen = strlen(prefix);
303                                 break;
304                         case 'X':
305                                 infix = optarg;
306                                 break;
307                         case 'F':
308                                 do_fastreject = 0;
309                                 break;
310                         default:
311                                 USAGE(*argv);
312                                 return 1;
313                 }
314         }
315
316         // fastreject is a slight slowdown when rejecting nothing at all
317         if(!infix && !prefixlen)
318                 do_fastreject = 0;
319
320         guesscount = pow(64.0, prefixlen);
321         if(infix)
322                 guesscount /= (1 - pow(1 - pow(1/64.0, strlen(infix)), 44 - prefixlen - strlen(infix)));
323         // 44 chars; prefix is assumed to not match the infix (although it theoretically could)
324         // 43'th char however is always '=' and does not count
325         if(ignorecase)
326         {
327                 if(infix)
328                         for(i = 0; infix[i]; ++i)
329                                 if(toupper(infix[i]) != tolower(infix[i]))
330                                         guesscount /= 2;
331                 for(i = 0; i < prefixlen; ++i)
332                         if(toupper(prefix[i]) != tolower(prefix[i]))
333                                 guesscount /= 2;
334         }
335
336         if(do_fastreject)
337         {
338                 // fastreject: reject function gets called about log(2^bits) times more often
339                 guessfactor = bits * log(2) / 2;
340                 // so guess function gets called guesscount * guessfactor times, and it tests as many valid keys as guesscount
341         }
342
343         if(mask & 1)
344         {
345                 file2lumps(pubkeyfile, FOURCC_D0PK, lumps, lumpsize, 2);
346                 if(!d0_blind_id_read_public_key(ctx, lumps[0], lumpsize[0]))
347                 {
348                         fprintf(stderr, "could not decode public key\n");
349                         exit(1);
350                 }
351                 if(!d0_blind_id_read_private_id_modulus(ctx, lumps[1], lumpsize[1]))
352                 {
353                         fprintf(stderr, "could not decode modulus\n");
354                         exit(1);
355                 }
356         }
357         else if(mask & 2)
358         {
359                 file2lumps(privkeyfile, FOURCC_D0SK, lumps, lumpsize, 2);
360                 if(!d0_blind_id_read_private_key(ctx, lumps[0], lumpsize[0]))
361                 {
362                         fprintf(stderr, "could not decode private key\n");
363                         exit(1);
364                 }
365                 if(!d0_blind_id_read_private_id_modulus(ctx, lumps[1], lumpsize[1]))
366                 {
367                         fprintf(stderr, "could not decode modulus\n");
368                         exit(1);
369                 }
370         }
371
372         if(mask & 4)
373         {
374                 file2lumps(pubidfile, FOURCC_D0PI, lumps, lumpsize, 1);
375                 if(!d0_blind_id_read_public_id(ctx, lumps[0], lumpsize[0]))
376                 {
377                         fprintf(stderr, "could not decode public ID\n");
378                         exit(1);
379                 }
380         }
381         if(mask & 8)
382         {
383                 file2lumps(prividfile, FOURCC_D0SI, lumps, lumpsize, 1);
384                 if(!d0_blind_id_read_private_id(ctx, lumps[0], lumpsize[0]))
385                 {
386                         fprintf(stderr, "could not decode private ID\n");
387                         exit(1);
388                 }
389         }
390
391         if(mask & 0x10)
392         {
393                 file2lumps(idreqfile, FOURCC_D0IQ, lumps, lumpsize, 1);
394                 lumpsize[1] = sizeof(lumps_w1);
395                 lumps[1] = lumps_w1;
396                 if(!d0_blind_id_answer_private_id_request(ctx, lumps[0], lumpsize[0], lumps_w1, &lumpsize[1]))
397                 {
398                         fprintf(stderr, "could not answer private ID request\n");
399                         exit(1);
400                 }
401         }
402         else if((mask & 0x120) == 0x120)
403         {
404                 file2lumps(camouflagefile, FOURCC_D0IC, lumps, lumpsize, 1);
405                 if(!d0_blind_id_read_private_id_request_camouflage(ctx, lumps[0], lumpsize[0]))
406                 {
407                         fprintf(stderr, "could not decode camouflage\n");
408                         exit(1);
409                 }
410
411                 file2lumps(idresfile, FOURCC_D0IR, lumps, lumpsize, 1);
412                 if(!d0_blind_id_finish_private_id_request(ctx, lumps[0], lumpsize[0]))
413                 {
414                         fprintf(stderr, "could not finish private ID request\n");
415                         exit(1);
416                 }
417         }
418
419         switch(mask)
420         {
421                 // modes of operation:
422                 case 0x40:
423                         //   nothing -> private key file (incl modulus), print fingerprint
424                         generated = 0;
425                         generated_offset = 0;
426                         seconds = 0;
427                         signal(SIGALRM, print_generated);
428                         alarm(1);
429                         if(do_fastreject)
430                         {
431                                 CHECK(d0_blind_id_generate_private_key_fastreject(ctx, bits, fastreject, d0_blind_id_fingerprint64_public_key));
432                         }
433                         else
434                         {
435                                 guessfactor = 1; // no fastreject here
436                                 do
437                                 {
438                                         CHECK(d0_blind_id_generate_private_key(ctx, bits));
439                                 }
440                                 while(fastreject(ctx, d0_blind_id_fingerprint64_public_key));
441                         }
442                         alarm(0);
443                         signal(SIGALRM, NULL);
444                         CHECK(d0_blind_id_generate_private_id_modulus(ctx));
445                         lumps[0] = lumps_w0;
446                         lumpsize[0] = sizeof(lumps_w0);
447                         lumps[1] = lumps_w1;
448                         lumpsize[1] = sizeof(lumps_w1);
449                         CHECK(d0_blind_id_write_private_key(ctx, lumps_w0, &lumpsize[0]));
450                         CHECK(d0_blind_id_write_private_id_modulus(ctx, lumps_w1, &lumpsize[1]));
451                         lumps2file(outfile, FOURCC_D0SK, lumps, lumpsize, 2, 1);
452                         break;
453                 case 0x42:
454                         //   private key file -> public key file (incl modulus)
455                         lumps[0] = lumps_w0;
456                         lumpsize[0] = sizeof(lumps_w0);
457                         lumps[1] = lumps_w1;
458                         lumpsize[1] = sizeof(lumps_w1);
459                         CHECK(d0_blind_id_write_public_key(ctx, lumps_w0, &lumpsize[0]));
460                         CHECK(d0_blind_id_write_private_id_modulus(ctx, lumps_w1, &lumpsize[1]));
461                         lumps2file(outfile, FOURCC_D0PK, lumps, lumpsize, 2, 0);
462                         break;
463                 case 0x41:
464                         //   public key file -> unsigned private ID file
465                         generated = 0;
466                         generated_offset = 0;
467                         seconds = 0;
468                         signal(SIGALRM, print_generated);
469                         alarm(1);
470                         guessfactor = 1; // no fastreject here
471                         do
472                         {
473                                 CHECK(d0_blind_id_generate_private_id_start(ctx));
474                         }
475                         while(fastreject(ctx, d0_blind_id_fingerprint64_public_id));
476                         alarm(0);
477                         signal(SIGALRM, 0);
478                         lumps[0] = lumps_w0;
479                         lumpsize[0] = sizeof(lumps_w0);
480                         CHECK(d0_blind_id_write_private_id(ctx, lumps_w0, &lumpsize[0]));
481                         lumps2file(outfile, FOURCC_D0SI, lumps, lumpsize, 1, 1);
482                         break;
483                 case 0xC9:
484                         //   public key file, unsigned private ID file -> ID request file and camouflage file
485                         lumps[0] = lumps_w0;
486                         lumpsize[0] = sizeof(lumps_w0);
487                         CHECK(d0_blind_id_generate_private_id_request(ctx, lumps_w0, &lumpsize[0]));
488                         lumps2file(outfile, FOURCC_D0IQ, lumps, lumpsize, 1, 0);
489                         lumpsize[0] = sizeof(lumps_w0);
490                         CHECK(d0_blind_id_write_private_id_request_camouflage(ctx, lumps_w0, &lumpsize[0]));
491                         lumps2file(outfile2, FOURCC_D0IC, lumps, lumpsize, 1, 1);
492                         break;
493                 case 0x52:
494                         //   private key file, ID request file -> ID response file
495                         lumps2file(outfile, FOURCC_D0IR, lumps+1, lumpsize+1, 1, 0);
496                         break;
497                 case 0x169:
498                         //   public key file, ID response file, private ID file -> signed private ID file
499                         lumps[0] = lumps_w0;
500                         lumpsize[0] = sizeof(lumps_w0);
501                         CHECK(d0_blind_id_write_private_id(ctx, lumps_w0, &lumpsize[0]));
502                         lumps2file(outfile, FOURCC_D0SI, lumps, lumpsize, 1, 1);
503                         break;
504                 case 0x4A:
505                         //   private key file, private ID file -> signed private ID file
506                         {
507                                 char buf[65536]; size_t bufsize;
508                                 char buf2[65536]; size_t buf2size;
509                                 D0_BOOL status;
510                                 d0_blind_id_t *ctx2 = d0_blind_id_new();
511                                 CHECK(d0_blind_id_copy(ctx2, ctx));
512                                 bufsize = sizeof(buf);
513                                 CHECK(d0_blind_id_authenticate_with_private_id_start(ctx, 1, 1, "hello world", 11, buf, &bufsize));
514                                 buf2size = sizeof(buf2);
515                                 CHECK(d0_blind_id_authenticate_with_private_id_challenge(ctx2, 1, 1, buf, bufsize, buf2, &buf2size, &status));
516                                 bufsize = sizeof(buf);
517                                 CHECK(d0_blind_id_authenticate_with_private_id_response(ctx, buf2, buf2size, buf, &bufsize));
518                                 buf2size = sizeof(buf2);
519                                 CHECK(d0_blind_id_authenticate_with_private_id_verify(ctx2, buf, bufsize, buf2, &buf2size, &status));
520                                 CHECK(status == 0);
521                                 CHECK(d0_blind_id_authenticate_with_private_id_generate_missing_signature(ctx2));
522                                 lumps[0] = lumps_w0;
523                                 lumpsize[0] = sizeof(lumps_w0);
524                                 CHECK(d0_blind_id_write_private_id(ctx2, lumps_w0, &lumpsize[0]));
525                                 lumps2file(outfile, FOURCC_D0SI, lumps, lumpsize, 1, 1);
526                         }
527                         break;
528                 case 0x48:
529                         //   private ID file -> public ID file
530                         lumps[0] = lumps_w0;
531                         lumpsize[0] = sizeof(lumps_w0);
532                         CHECK(d0_blind_id_write_public_id(ctx, lumps_w0, &lumpsize[0]));
533                         lumps2file(outfile, FOURCC_D0PI, lumps, lumpsize, 1, 0);
534                         break;
535                 case 0x01:
536                 case 0x02:
537                         //   public/private key file -> fingerprint
538                         CHECK(d0_blind_id_fingerprint64_public_key(ctx, fp64, &fp64size));
539                         printf("%.*s\n", (int)fp64size, fp64);
540                         break;
541                 case 0x05:
542                 case 0x09:
543                         //   public/private ID file -> fingerprint
544                         CHECK(d0_blind_id_fingerprint64_public_id(ctx, fp64, &fp64size));
545                         printf("%.*s\n", (int)fp64size, fp64);
546                         break;
547 /*
548                 case 0x09:
549                         //   public key, private ID file -> test whether key is properly signed
550                         {
551                                 char buf[65536]; size_t bufsize;
552                                 char buf2[65536]; size_t buf2size;
553                                 D0_BOOL status;
554                                 d0_blind_id_t *ctx2 = d0_blind_id_new();
555                                 CHECK(d0_blind_id_copy(ctx2, ctx));
556                                 bufsize = sizeof(buf);
557                                 CHECK(d0_blind_id_authenticate_with_private_id_start(ctx, 1, 1, "hello world", 11, buf, &bufsize));
558                                 buf2size = sizeof(buf2);
559                                 CHECK(d0_blind_id_authenticate_with_private_id_challenge(ctx2, 1, 1, buf, bufsize, buf2, &buf2size, &status));
560                                 bufsize = sizeof(buf);
561                                 CHECK(d0_blind_id_authenticate_with_private_id_response(ctx, buf2, buf2size, buf, &bufsize));
562                                 buf2size = sizeof(buf2);
563                                 CHECK(d0_blind_id_authenticate_with_private_id_verify(ctx2, buf, bufsize, buf2, &buf2size, &status));
564                                 if(status)
565                                         printf("OK\n");
566                                 else
567                                         printf("EPIC FAIL\n");
568                         }
569                         break;
570 */
571                 default:
572                         USAGE(*argv);
573                         exit(1);
574                         break;
575         }
576         d0_blind_id_SHUTDOWN();
577         return 0;
578 }