/*
 * DF-0220 boot-window scanner (unprivileged).
 *
 * The finding claims an unprivileged user can observe the deterministic
 * pre-reseed csprng output ("zero key window") via /dev/urandom, getrandom(2)
 * and the kern.random sysctl (CTLFLAG_ANYBODY).  This tool samples all three
 * interfaces as hard as possible for a configurable time (default 15 s),
 * counting all-zero 32-byte blocks, which is the degenerate pre-reseed
 * csprng contribution in rand_mode=csprng.  (In the default mixed mode the
 * csprng contribution is XORed with IBAA, so a zero csprng block would NOT
 * show as zero bytes; the manufactured-window experiment in
 * csprng_force_zero.c covers that case with kmem introspection.)
 *
 * Run as the unprivileged user, as early after boot as possible.
 *
 * Build:  cc -O2 -o zero_scan zero_scan.c
 * Run:    ./zero_scan [seconds=15]
 */
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/random.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BLK 32

static double
uptime_sec(void)
{
	struct timespec ts;
	if (clock_gettime(CLOCK_UPTIME, &ts) == 0)
		return ts.tv_sec + ts.tv_nsec / 1e9;
	return -1.0;
}

static int
allzero(const unsigned char *b, int n)
{
	int i;
	for (i = 0; i < n; i++)
		if (b[i])
			return 0;
	return 1;
}

int
main(int argc, char **argv)
{
	double secs = 15.0, t0;
	unsigned char buf[BLK];
	long n_ur = 0, n_gr = 0, n_kr = 0, z_ur = 0, z_gr = 0, z_kr = 0;
	int fd;
	ssize_t r;

	if (argc > 1)
		secs = atof(argv[1]);
	if (secs <= 0)
		secs = 15.0;

	fd = open("/dev/urandom", O_RDONLY);
	if (fd < 0) {
		perror("open /dev/urandom");
		return 1;
	}

	printf("zero_scan: sampling %d-byte blocks for %.1f s "
	    "(uid=%d, uptime_at_start=%.3f)\n",
	    BLK, secs, getuid(), uptime_sec());

	t0 = uptime_sec();
	while (uptime_sec() - t0 < secs) {
		r = read(fd, buf, BLK);
		if (r == BLK) {
			n_ur++;
			if (allzero(buf, BLK))
				z_ur++;
		}
		memset(buf, 0xa5, BLK);
		r = getrandom(buf, BLK, 0);
		if (r == BLK) {
			n_gr++;
			if (allzero(buf, BLK))
				z_gr++;
		}
		{
			size_t sz = BLK;
			memset(buf, 0x5a, BLK);
			if (sysctlbyname("kern.random", buf, &sz, NULL, 0) == 0 &&
			    sz == BLK) {
				n_kr++;
				if (allzero(buf, BLK))
					z_kr++;
			}
		}
		usleep(20000);
	}
	printf("samples: /dev/urandom=%ld getrandom=%ld kern.random=%ld\n",
	    n_ur, n_gr, n_kr);
	printf("all-zero blocks: /dev/urandom=%ld getrandom=%ld kern.random=%ld\n",
	    z_ur, z_gr, z_kr);
	printf("uptime_at_end=%.3f\n", uptime_sec());
	if (z_ur == 0 && z_gr == 0 && z_kr == 0)
		printf("RESULT: NO zero block observed on any interface\n");
	else
		printf("RESULT: ZERO BLOCKS OBSERVED (window caught?)\n");
	return 0;
}
