blob: 10bf6467f380127f2e119a3642fedcac6ca91ec3 [file] [log] [blame]
/* random.c - part of the Libgcrypt test suite.
Copyright (C) 2005 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifndef HAVE_W32_SYSTEM
# include <signal.h>
# include <unistd.h>
# include <sys/wait.h>
#endif
#include "../src/gcrypt-int.h"
#define PGM "random"
static int verbose;
static int debug;
static int with_progress;
static void
die (const char *format, ...)
{
va_list arg_ptr;
va_start (arg_ptr, format);
fputs ( PGM ": ", stderr);
vfprintf (stderr, format, arg_ptr);
va_end (arg_ptr);
exit (1);
}
static void
inf (const char *format, ...)
{
va_list arg_ptr;
va_start (arg_ptr, format);
fputs ( PGM ": ", stderr);
vfprintf (stderr, format, arg_ptr);
va_end (arg_ptr);
}
static void
print_hex (const char *text, const void *buf, size_t n)
{
const unsigned char *p = buf;
inf ("%s", text);
for (; n; n--, p++)
fprintf (stderr, "%02X", *p);
putc ('\n', stderr);
}
static void
progress_cb (void *cb_data, const char *what, int printchar,
int current, int total)
{
(void)cb_data;
inf ("progress (%s %c %d %d)\n", what, printchar, current, total);
fflush (stderr);
}
static int
writen (int fd, const void *buf, size_t nbytes)
{
size_t nleft = nbytes;
int nwritten;
while (nleft > 0)
{
nwritten = write (fd, buf, nleft);
if (nwritten < 0)
{
if (errno == EINTR)
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
buf = (const char*)buf + nwritten;
}
return 0;
}
static int
readn (int fd, void *buf, size_t buflen, size_t *ret_nread)
{
size_t nleft = buflen;
int nread;
while ( nleft > 0 )
{
nread = read ( fd, buf, nleft );
if (nread < 0)
{
if (nread == EINTR)
nread = 0;
else
return -1;
}
else if (!nread)
break; /* EOF */
nleft -= nread;
buf = (char*)buf + nread;
}
if (ret_nread)
*ret_nread = buflen - nleft;
return 0;
}
/* Check that forking won't return the same random. */
static void
check_forking (void)
{
#ifdef HAVE_W32_SYSTEM
if (verbose)
inf ("check_forking skipped: not applicable on Windows\n");
#else /*!HAVE_W32_SYSTEM*/
pid_t pid;
int rp[2];
int i, status;
size_t nread;
char tmp1[16], tmp1c[16], tmp1p[16];
if (verbose)
inf ("checking that a fork won't cause the same random output\n");
/* We better make sure that the RNG has been initialzied. */
gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
if (verbose)
print_hex ("initial random: ", tmp1, sizeof tmp1);
if (pipe (rp) == -1)
die ("pipe failed: %s\n", strerror (errno));
pid = fork ();
if (pid == (pid_t)(-1))
die ("fork failed: %s\n", strerror (errno));
if (!pid)
{
gcry_randomize (tmp1c, sizeof tmp1c, GCRY_STRONG_RANDOM);
if (writen (rp[1], tmp1c, sizeof tmp1c))
die ("write failed: %s\n", strerror (errno));
if (verbose)
{
print_hex (" child random: ", tmp1c, sizeof tmp1c);
fflush (stdout);
}
_exit (0);
}
gcry_randomize (tmp1p, sizeof tmp1p, GCRY_STRONG_RANDOM);
if (verbose)
print_hex (" parent random: ", tmp1p, sizeof tmp1p);
close (rp[1]);
if (readn (rp[0], tmp1c, sizeof tmp1c, &nread))
die ("read failed: %s\n", strerror (errno));
if (nread != sizeof tmp1c)
die ("read too short\n");
while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
;
if (i != (pid_t)(-1)
&& WIFEXITED (status) && !WEXITSTATUS (status))
;
else
die ("child failed\n");
if (!memcmp (tmp1p, tmp1c, sizeof tmp1c))
die ("parent and child got the same random number\n");
#endif /*!HAVE_W32_SYSTEM*/
}
/* Check that forking won't return the same nonce. */
static void
check_nonce_forking (void)
{
#ifdef HAVE_W32_SYSTEM
if (verbose)
inf ("check_nonce_forking skipped: not applicable on Windows\n");
#else /*!HAVE_W32_SYSTEM*/
pid_t pid;
int rp[2];
int i, status;
size_t nread;
char nonce1[10], nonce1c[10], nonce1p[10];
if (verbose)
inf ("checking that a fork won't cause the same nonce output\n");
/* We won't get the same nonce back if we never initialized the
nonce subsystem, thus we get one nonce here and forget about
it. */
gcry_create_nonce (nonce1, sizeof nonce1);
if (verbose)
print_hex ("initial nonce: ", nonce1, sizeof nonce1);
if (pipe (rp) == -1)
die ("pipe failed: %s\n", strerror (errno));
pid = fork ();
if (pid == (pid_t)(-1))
die ("fork failed: %s\n", strerror (errno));
if (!pid)
{
gcry_create_nonce (nonce1c, sizeof nonce1c);
if (writen (rp[1], nonce1c, sizeof nonce1c))
die ("write failed: %s\n", strerror (errno));
if (verbose)
{
print_hex (" child nonce: ", nonce1c, sizeof nonce1c);
fflush (stdout);
}
_exit (0);
}
gcry_create_nonce (nonce1p, sizeof nonce1p);
if (verbose)
print_hex (" parent nonce: ", nonce1p, sizeof nonce1p);
close (rp[1]);
if (readn (rp[0], nonce1c, sizeof nonce1c, &nread))
die ("read failed: %s\n", strerror (errno));
if (nread != sizeof nonce1c)
die ("read too short\n");
while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
;
if (i != (pid_t)(-1)
&& WIFEXITED (status) && !WEXITSTATUS (status))
;
else
die ("child failed\n");
if (!memcmp (nonce1p, nonce1c, sizeof nonce1c))
die ("parent and child got the same nonce\n");
#endif /*!HAVE_W32_SYSTEM*/
}
/* Check that a closed random device os re-opened if needed. */
static void
check_close_random_device (void)
{
#ifdef HAVE_W32_SYSTEM
if (verbose)
inf ("check_close_random_device skipped: not applicable on Windows\n");
#else /*!HAVE_W32_SYSTEM*/
pid_t pid;
int i, status;
char buf[4];
if (verbose)
inf ("checking that close_random_device works\n");
gcry_randomize (buf, sizeof buf, GCRY_STRONG_RANDOM);
if (verbose)
print_hex ("parent random: ", buf, sizeof buf);
pid = fork ();
if (pid == (pid_t)(-1))
die ("fork failed: %s\n", strerror (errno));
if (!pid)
{
gcry_control (GCRYCTL_CLOSE_RANDOM_DEVICE, 0);
/* The next call will re-open the device. */
gcry_randomize (buf, sizeof buf, GCRY_STRONG_RANDOM);
if (verbose)
{
print_hex ("child random : ", buf, sizeof buf);
fflush (stdout);
}
_exit (0);
}
while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
;
if (i != (pid_t)(-1)
&& WIFEXITED (status) && !WEXITSTATUS (status))
;
else
die ("child failed\n");
#endif /*!HAVE_W32_SYSTEM*/
}
static int
rng_type (void)
{
int rngtype;
if (gcry_control (GCRYCTL_GET_CURRENT_RNG_TYPE, &rngtype))
die ("retrieving RNG type failed\n");
return rngtype;
}
static void
check_rng_type_switching (void)
{
int rngtype, initial;
char tmp1[4];
if (verbose)
inf ("checking whether RNG type switching works\n");
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
initial = rngtype;
gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
if (debug)
print_hex (" sample: ", tmp1, sizeof tmp1);
if (rngtype != rng_type ())
die ("RNG type unexpectedly changed\n");
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
if (rngtype != initial)
die ("switching to System RNG unexpectedly succeeded\n");
gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
if (debug)
print_hex (" sample: ", tmp1, sizeof tmp1);
if (rngtype != rng_type ())
die ("RNG type unexpectedly changed\n");
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_FIPS);
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
if (rngtype != initial)
die ("switching to FIPS RNG unexpectedly succeeded\n");
gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
if (debug)
print_hex (" sample: ", tmp1, sizeof tmp1);
if (rngtype != rng_type ())
die ("RNG type unexpectedly changed\n");
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_STANDARD);
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
if (rngtype != GCRY_RNG_TYPE_STANDARD)
die ("switching to standard RNG failed\n");
gcry_randomize (tmp1, sizeof tmp1, GCRY_STRONG_RANDOM);
if (debug)
print_hex (" sample: ", tmp1, sizeof tmp1);
if (rngtype != rng_type ())
die ("RNG type unexpectedly changed\n");
}
static void
check_early_rng_type_switching (void)
{
int rngtype, initial;
if (verbose)
inf ("checking whether RNG type switching works in the early stage\n");
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
initial = rngtype;
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
if (initial >= GCRY_RNG_TYPE_SYSTEM && rngtype != GCRY_RNG_TYPE_SYSTEM)
die ("switching to System RNG failed\n");
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_FIPS);
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
if (initial >= GCRY_RNG_TYPE_FIPS && rngtype != GCRY_RNG_TYPE_FIPS)
die ("switching to FIPS RNG failed\n");
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_STANDARD);
rngtype = rng_type ();
if (debug)
inf ("rng type: %d\n", rngtype);
if (rngtype != GCRY_RNG_TYPE_STANDARD)
die ("switching to standard RNG failed\n");
}
/* Because we want to check initialization behaviour, we need to
fork/exec this program with several command line arguments. We use
system, so that these tests work also on Windows. */
static void
run_all_rng_tests (const char *program)
{
static const char *options[] = {
"--early-rng-check",
"--early-rng-check --prefer-standard-rng",
"--early-rng-check --prefer-fips-rng",
"--early-rng-check --prefer-system-rng",
"--prefer-standard-rng",
"--prefer-fips-rng",
"--prefer-system-rng",
NULL
};
int idx;
size_t len, maxlen;
char *cmdline;
maxlen = 0;
for (idx=0; options[idx]; idx++)
{
len = strlen (options[idx]);
if (len > maxlen)
maxlen = len;
}
maxlen += strlen (program);
maxlen += strlen (" --in-recursion --verbose --debug --progress");
maxlen++;
cmdline = malloc (maxlen + 1);
if (!cmdline)
die ("out of core\n");
for (idx=0; options[idx]; idx++)
{
if (verbose)
inf ("now running with options '%s'\n", options[idx]);
strcpy (cmdline, program);
strcat (cmdline, " --in-recursion");
if (verbose)
strcat (cmdline, " --verbose");
if (debug)
strcat (cmdline, " --debug");
if (with_progress)
strcat (cmdline, " --progress");
strcat (cmdline, " ");
strcat (cmdline, options[idx]);
if (system (cmdline))
die ("running '%s' failed\n", cmdline);
}
free (cmdline);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
int early_rng = 0;
int in_recursion = 0;
const char *program = NULL;
if (argc)
{
program = *argv;
argc--; argv++;
}
else
die ("argv[0] missing\n");
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: random [options]\n", stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
debug = verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--progress"))
{
argc--; argv++;
with_progress = 1;
}
else if (!strcmp (*argv, "--in-recursion"))
{
in_recursion = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--early-rng-check"))
{
early_rng = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--prefer-standard-rng"))
{
/* This is anyway the default, but we may want to use it for
debugging. */
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_STANDARD);
argc--; argv++;
}
else if (!strcmp (*argv, "--prefer-fips-rng"))
{
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_FIPS);
argc--; argv++;
}
else if (!strcmp (*argv, "--prefer-system-rng"))
{
gcry_control (GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
argc--; argv++;
}
}
#ifndef HAVE_W32_SYSTEM
signal (SIGPIPE, SIG_IGN);
#endif
if (early_rng)
check_early_rng_type_switching ();
gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
if (!gcry_check_version (GCRYPT_VERSION))
die ("version mismatch\n");
if (with_progress)
gcry_set_progress_handler (progress_cb, NULL);
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
if (debug)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
if (!in_recursion)
{
check_forking ();
check_nonce_forking ();
check_close_random_device ();
}
check_rng_type_switching ();
if (!in_recursion)
run_all_rng_tests (program);
return 0;
}