tests: Add option to time the S2K function.
[libgcrypt.git] / tests / random.c
1 /* random.c - part of the Libgcrypt test suite.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17    USA.  */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <assert.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #ifndef HAVE_W32_SYSTEM
28 # include <signal.h>
29 # include <unistd.h>
30 # include <sys/wait.h>
31 #endif
32
33 #include "../src/gcrypt-int.h"
34
35 #define PGM "random"
36
37
38 static int verbose;
39 static int debug;
40 static int with_progress;
41
42 static void
43 die (const char *format, ...)
44 {
45   va_list arg_ptr;
46
47   va_start (arg_ptr, format);
48   fputs ( PGM ": ", stderr);
49   vfprintf (stderr, format, arg_ptr);
50   va_end (arg_ptr);
51   exit (1);
52 }
53
54
55 static void
56 inf (const char *format, ...)
57 {
58   va_list arg_ptr;
59
60   va_start (arg_ptr, format);
61   fputs ( PGM ": ", stderr);
62   vfprintf (stderr, format, arg_ptr);
63   va_end (arg_ptr);
64 }
65
66
67 static void
68 print_hex (const char *text, const void *buf, size_t n)
69 {
70   const unsigned char *p = buf;
71
72   inf ("%s", text);
73   for (; n; n--, p++)
74     fprintf (stderr, "%02X", *p);
75   putc ('\n', stderr);
76 }
77
78
79 static void
80 progress_cb (void *cb_data, const char *what, int printchar,
81              int current, int total)
82 {
83   (void)cb_data;
84
85   inf ("progress (%s %c %d %d)\n", what, printchar, current, total);
86   fflush (stderr);
87 }
88
89
90
91 static int
92 writen (int fd, const void *buf, size_t nbytes)
93 {
94   size_t nleft = nbytes;
95   int nwritten;
96
97   while (nleft > 0)
98     {
99       nwritten = write (fd, buf, nleft);
100       if (nwritten < 0)
101         {
102           if (errno == EINTR)
103             nwritten = 0;
104           else
105             return -1;
106         }
107       nleft -= nwritten;
108       buf = (const char*)buf + nwritten;
109     }
110
111   return 0;
112 }
113
114 static int
115 readn (int fd, void *buf, size_t buflen, size_t *ret_nread)
116 {
117   size_t nleft = buflen;
118   int nread;
119
120   while ( nleft > 0 )
121     {
122       nread = read ( fd, buf, nleft );
123       if (nread < 0)
124         {
125           if (nread == EINTR)
126             nread = 0;
127           else
128             return -1;
129         }
130       else if (!nread)
131         break; /* EOF */
132       nleft -= nread;
133       buf = (char*)buf + nread;
134     }
135   if (ret_nread)
136     *ret_nread = buflen - nleft;
137   return 0;
138 }
139
140
141
142 /* Check that forking won't return the same random. */
143 static void
144 check_forking (void)
145 {
146 #ifdef HAVE_W32_SYSTEM
147   if (verbose)
148     inf ("check_forking skipped: not applicable on Windows\n");
149 #else /*!HAVE_W32_SYSTEM*/
150   pid_t pid;
151   int rp[2];
152   int i, status;
153   size_t nread;
154   char tmp1[16], tmp1c[16], tmp1p[16];
155
156   if (verbose)
157     inf ("checking that a fork won't cause the same random output\n");
158
159   /* We better make sure that the RNG has been initialzied. */
160   gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
161   if (verbose)
162     print_hex ("initial random: ", tmp1, sizeof tmp1);
163
164   if (pipe (rp) == -1)
165     die ("pipe failed: %s\n", strerror (errno));
166
167   pid = fork ();
168   if (pid == (pid_t)(-1))
169     die ("fork failed: %s\n", strerror (errno));
170   if (!pid)
171     {
172       gcry_randomize (tmp1c, sizeof tmp1c, GCRY_STRONG_RANDOM);
173       if (writen (rp[1], tmp1c, sizeof tmp1c))
174         die ("write failed: %s\n", strerror (errno));
175       if (verbose)
176         {
177           print_hex ("  child random: ", tmp1c, sizeof tmp1c);
178           fflush (stdout);
179         }
180       _exit (0);
181     }
182   gcry_randomize (tmp1p, sizeof tmp1p, GCRY_STRONG_RANDOM);
183   if (verbose)
184     print_hex (" parent random: ", tmp1p, sizeof tmp1p);
185
186   close (rp[1]);
187   if (readn (rp[0], tmp1c, sizeof tmp1c, &nread))
188     die ("read failed: %s\n", strerror (errno));
189   if (nread != sizeof tmp1c)
190     die ("read too short\n");
191
192   while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
193     ;
194   if (i != (pid_t)(-1)
195       && WIFEXITED (status) && !WEXITSTATUS (status))
196     ;
197   else
198     die ("child failed\n");
199
200   if (!memcmp (tmp1p, tmp1c, sizeof tmp1c))
201     die ("parent and child got the same random number\n");
202 #endif  /*!HAVE_W32_SYSTEM*/
203 }
204
205
206
207 /* Check that forking won't return the same nonce. */
208 static void
209 check_nonce_forking (void)
210 {
211 #ifdef HAVE_W32_SYSTEM
212   if (verbose)
213     inf ("check_nonce_forking skipped: not applicable on Windows\n");
214 #else /*!HAVE_W32_SYSTEM*/
215   pid_t pid;
216   int rp[2];
217   int i, status;
218   size_t nread;
219   char nonce1[10], nonce1c[10], nonce1p[10];
220
221   if (verbose)
222     inf ("checking that a fork won't cause the same nonce output\n");
223
224   /* We won't get the same nonce back if we never initialized the
225      nonce subsystem, thus we get one nonce here and forget about
226      it. */
227   gcry_create_nonce (nonce1, sizeof nonce1);
228   if (verbose)
229     print_hex ("initial nonce: ", nonce1, sizeof nonce1);
230
231   if (pipe (rp) == -1)
232     die ("pipe failed: %s\n", strerror (errno));
233
234   pid = fork ();
235   if (pid == (pid_t)(-1))
236     die ("fork failed: %s\n", strerror (errno));
237   if (!pid)
238     {
239       gcry_create_nonce (nonce1c, sizeof nonce1c);
240       if (writen (rp[1], nonce1c, sizeof nonce1c))
241         die ("write failed: %s\n", strerror (errno));
242       if (verbose)
243         {
244           print_hex ("  child nonce: ", nonce1c, sizeof nonce1c);
245           fflush (stdout);
246         }
247       _exit (0);
248     }
249   gcry_create_nonce (nonce1p, sizeof nonce1p);
250   if (verbose)
251     print_hex (" parent nonce: ", nonce1p, sizeof nonce1p);
252
253   close (rp[1]);
254   if (readn (rp[0], nonce1c, sizeof nonce1c, &nread))
255     die ("read failed: %s\n", strerror (errno));
256   if (nread != sizeof nonce1c)
257     die ("read too short\n");
258
259   while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
260     ;
261   if (i != (pid_t)(-1)
262       && WIFEXITED (status) && !WEXITSTATUS (status))
263     ;
264   else
265     die ("child failed\n");
266
267   if (!memcmp (nonce1p, nonce1c, sizeof nonce1c))
268     die ("parent and child got the same nonce\n");
269 #endif  /*!HAVE_W32_SYSTEM*/
270 }
271
272
273 /* Check that a closed random device os re-opened if needed. */
274 static void
275 check_close_random_device (void)
276 {
277 #ifdef HAVE_W32_SYSTEM
278   if (verbose)
279     inf ("check_close_random_device skipped: not applicable on Windows\n");
280 #else /*!HAVE_W32_SYSTEM*/
281   pid_t pid;
282   int i, status;
283   char buf[4];
284
285   if (verbose)
286     inf ("checking that close_random_device works\n");
287
288   gcry_randomize (buf, sizeof buf, GCRY_STRONG_RANDOM);
289   if (verbose)
290     print_hex ("parent random: ", buf, sizeof buf);
291
292   pid = fork ();
293   if (pid == (pid_t)(-1))
294     die ("fork failed: %s\n", strerror (errno));
295   if (!pid)
296     {
297       gcry_control (GCRYCTL_CLOSE_RANDOM_DEVICE, 0);
298
299       /* The next call will re-open the device.  */
300       gcry_randomize (buf, sizeof buf, GCRY_STRONG_RANDOM);
301       if (verbose)
302         {
303           print_hex ("child random : ", buf, sizeof buf);
304           fflush (stdout);
305         }
306       _exit (0);
307     }
308
309   while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
310     ;
311   if (i != (pid_t)(-1)
312       && WIFEXITED (status) && !WEXITSTATUS (status))
313     ;
314   else
315     die ("child failed\n");
316
317 #endif  /*!HAVE_W32_SYSTEM*/
318 }
319
320
321 static int
322 rng_type (void)
323 {
324   int rngtype;
325   if (gcry_control (GCRYCTL_GET_CURRENT_RNG_TYPE, &rngtype))
326     die ("retrieving RNG type failed\n");
327   return rngtype;
328 }
329
330
331 static void
332 check_rng_type_switching (void)
333 {
334   int rngtype, initial;
335   char tmp1[4];
336
337   if (verbose)
338     inf ("checking whether RNG type switching works\n");
339
340   rngtype = rng_type ();
341   if (debug)
342     inf ("rng type: %d\n", rngtype);
343   initial = rngtype;
344   gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
345   if (debug)
346     print_hex ("  sample: ", tmp1, sizeof tmp1);
347   if (rngtype != rng_type ())
348     die ("RNG type unexpectedly changed\n");
349
350   gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
351
352   rngtype = rng_type ();
353   if (debug)
354     inf ("rng type: %d\n", rngtype);
355   if (rngtype != initial)
356     die ("switching to System RNG unexpectedly succeeded\n");
357   gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
358   if (debug)
359     print_hex ("  sample: ", tmp1, sizeof tmp1);
360   if (rngtype != rng_type ())
361     die ("RNG type unexpectedly changed\n");
362
363   gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_FIPS);
364
365   rngtype = rng_type ();
366   if (debug)
367     inf ("rng type: %d\n", rngtype);
368   if (rngtype != initial)
369     die ("switching to FIPS RNG unexpectedly succeeded\n");
370   gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
371   if (debug)
372     print_hex ("  sample: ", tmp1, sizeof tmp1);
373   if (rngtype != rng_type ())
374     die ("RNG type unexpectedly changed\n");
375
376   gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_STANDARD);
377
378   rngtype = rng_type ();
379   if (debug)
380     inf ("rng type: %d\n", rngtype);
381   if (rngtype != GCRY_RNG_TYPE_STANDARD)
382     die ("switching to standard RNG failed\n");
383   gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
384   if (debug)
385     print_hex ("  sample: ", tmp1, sizeof tmp1);
386   if (rngtype != rng_type ())
387     die ("RNG type unexpectedly changed\n");
388 }
389
390
391 static void
392 check_early_rng_type_switching (void)
393 {
394   int rngtype, initial;
395
396   if (verbose)
397     inf ("checking whether RNG type switching works in the early stage\n");
398
399   rngtype = rng_type ();
400   if (debug)
401     inf ("rng type: %d\n", rngtype);
402   initial = rngtype;
403
404   gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
405
406   rngtype = rng_type ();
407   if (debug)
408     inf ("rng type: %d\n", rngtype);
409   if (initial >= GCRY_RNG_TYPE_SYSTEM && rngtype != GCRY_RNG_TYPE_SYSTEM)
410     die ("switching to System RNG failed\n");
411
412   gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_FIPS);
413
414   rngtype = rng_type ();
415   if (debug)
416     inf ("rng type: %d\n", rngtype);
417   if (initial >= GCRY_RNG_TYPE_FIPS && rngtype != GCRY_RNG_TYPE_FIPS)
418     die ("switching to FIPS RNG failed\n");
419
420   gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_STANDARD);
421
422   rngtype = rng_type ();
423   if (debug)
424     inf ("rng type: %d\n", rngtype);
425   if (rngtype != GCRY_RNG_TYPE_STANDARD)
426     die ("switching to standard RNG failed\n");
427 }
428
429
430 /* Because we want to check initialization behaviour, we need to
431    fork/exec this program with several command line arguments.  We use
432    system, so that these tests work also on Windows.  */
433 static void
434 run_all_rng_tests (const char *program)
435 {
436   static const char *options[] = {
437     "--early-rng-check",
438     "--early-rng-check --prefer-standard-rng",
439     "--early-rng-check --prefer-fips-rng",
440     "--early-rng-check --prefer-system-rng",
441     "--prefer-standard-rng",
442     "--prefer-fips-rng",
443     "--prefer-system-rng",
444     NULL
445   };
446   int idx;
447   size_t len, maxlen;
448   char *cmdline;
449
450   maxlen = 0;
451   for (idx=0; options[idx]; idx++)
452     {
453       len = strlen (options[idx]);
454       if (len > maxlen)
455         maxlen = len;
456     }
457   maxlen += strlen (program);
458   maxlen += strlen (" --in-recursion --verbose --debug --progress");
459   maxlen++;
460   cmdline = malloc (maxlen + 1);
461   if (!cmdline)
462     die ("out of core\n");
463
464   for (idx=0; options[idx]; idx++)
465     {
466       if (verbose)
467         inf ("now running with options '%s'\n", options[idx]);
468       strcpy (cmdline, program);
469       strcat (cmdline, " --in-recursion");
470       if (verbose)
471         strcat (cmdline, " --verbose");
472       if (debug)
473         strcat (cmdline, " --debug");
474       if (with_progress)
475         strcat (cmdline, " --progress");
476       strcat (cmdline, " ");
477       strcat (cmdline, options[idx]);
478       if (system (cmdline))
479         die ("running '%s' failed\n", cmdline);
480     }
481
482   free (cmdline);
483 }
484
485 int
486 main (int argc, char **argv)
487 {
488   int last_argc = -1;
489   int early_rng = 0;
490   int in_recursion = 0;
491   const char *program = NULL;
492
493   if (argc)
494     {
495       program = *argv;
496       argc--; argv++;
497     }
498   else
499     die ("argv[0] missing\n");
500
501   while (argc && last_argc != argc )
502     {
503       last_argc = argc;
504       if (!strcmp (*argv, "--"))
505         {
506           argc--; argv++;
507           break;
508         }
509       else if (!strcmp (*argv, "--help"))
510         {
511           fputs ("usage: random [options]\n", stdout);
512           exit (0);
513         }
514       else if (!strcmp (*argv, "--verbose"))
515         {
516           verbose = 1;
517           argc--; argv++;
518         }
519       else if (!strcmp (*argv, "--debug"))
520         {
521           debug = verbose = 1;
522           argc--; argv++;
523         }
524       else if (!strcmp (*argv, "--progress"))
525         {
526           argc--; argv++;
527           with_progress = 1;
528         }
529       else if (!strcmp (*argv, "--in-recursion"))
530         {
531           in_recursion = 1;
532           argc--; argv++;
533         }
534       else if (!strcmp (*argv, "--early-rng-check"))
535         {
536           early_rng = 1;
537           argc--; argv++;
538         }
539       else if (!strcmp (*argv, "--prefer-standard-rng"))
540         {
541           /* This is anyway the default, but we may want to use it for
542              debugging. */
543           gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_STANDARD);
544           argc--; argv++;
545         }
546       else if (!strcmp (*argv, "--prefer-fips-rng"))
547         {
548           gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_FIPS);
549           argc--; argv++;
550         }
551       else if (!strcmp (*argv, "--prefer-system-rng"))
552         {
553           gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
554           argc--; argv++;
555         }
556     }
557
558 #ifndef HAVE_W32_SYSTEM
559   signal (SIGPIPE, SIG_IGN);
560 #endif
561
562   if (early_rng)
563     check_early_rng_type_switching ();
564
565   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
566   if (!gcry_check_version (GCRYPT_VERSION))
567     die ("version mismatch\n");
568
569   if (with_progress)
570     gcry_set_progress_handler (progress_cb, NULL);
571
572   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
573   if (debug)
574     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
575
576   if (!in_recursion)
577     {
578       check_forking ();
579       check_nonce_forking ();
580       check_close_random_device ();
581     }
582   check_rng_type_switching ();
583
584   if (!in_recursion)
585     run_all_rng_tests (program);
586
587   return 0;
588 }