/*
 * DF-0220 RNG predictability probe.
 *
 * Reads bytes from every kernel RNG interface that the finding claims is
 * vulnerable, plus records the uptime at read time so we can correlate
 * "before first reseed" vs "after".
 *
 *   - /dev/urandom   (read_random unlimited, CSPRNG_UNLIMITED)
 *   - /dev/random    (blocking variant)
 *   - getrandom(2)   (sys_getrandom -> read_random unlimited)
 *   - kern.random    (sysctl -> read_random unlimited)
 *
 * All outputs are printed as lowercase hex so they can be compared byte for
 * byte across independent boots.
 *
 * Build:  cc -O2 -o rand_probe rand_probe.c
 * Run:    ./rand_probe
 */
#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>
#include <time.h>

#define N 64

static void
hexdump(const char *label, const unsigned char *buf, int len)
{
    int i;
    printf("%s (%d bytes):", label, len);
    for (i = 0; i < len; i++) {
        if (i % 16 == 0)
            printf("\n  %04x:", i);
        printf(" %02x", buf[i]);
    }
    printf("\n");
}

static double
uptime_sec(void)
{
    struct timespec ts;
    if (clock_gettime(CLOCK_UPTIME, &ts) == 0)
        return ts.tv_sec + ts.tv_nsec / 1e9;
    struct timeval bt;
    size_t sz = sizeof(bt);
    if (sysctlbyname("kern.boottime", &bt, &sz, NULL, 0) == 0) {
        struct timeval now;
        gettimeofday(&now, NULL);
        return (now.tv_sec - bt.tv_sec) + (now.tv_usec - bt.tv_usec) / 1e6;
    }
    return -1.0;
}

int
main(void)
{
    unsigned char buf[N];
    ssize_t r;
    int fd;

    printf("uptime_at_read = %.6f sec\n", uptime_sec());

    /* /dev/urandom */
    fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) { perror("open /dev/urandom"); } else {
        r = read(fd, buf, N);
        if (r < 0) perror("read urandom");
        else hexdump("/dev/urandom", buf, (int)r);
        close(fd);
    }

    /* /dev/random */
    fd = open("/dev/random", O_RDONLY);
    if (fd < 0) { perror("open /dev/random"); } else {
        r = read(fd, buf, N);
        if (r < 0) perror("read random");
        else hexdump("/dev/random", buf, (int)r);
        close(fd);
    }

    /* getrandom(2) syscall via libc */
    memset(buf, 0xaa, N);
    r = getrandom(buf, N, 0);
    if (r < 0) perror("getrandom");
    else hexdump("getrandom(2)", buf, (int)r);

    /* kern.random sysctl */
    {
        size_t sz = N;
        memset(buf, 0x55, N);
        if (sysctlbyname("kern.random", buf, &sz, NULL, 0) < 0)
            perror("sysctl kern.random");
        else
            hexdump("kern.random sysctl", buf, (int)sz);
    }

    return 0;
}
