rand: Add ChaCha20-based CSPRNG, among other things

We now have RNG!

- Implement ChaCha20-based cryptographically secure random number generator (CSPRNG) in `src/drivers/rand/random.c` and its header in `src/drivers/rand/random.h`.
- Modify VFS to support directory operations, including opening directories and reading directory entries.
- Update syscall interface to include new syscalls for directory handling: `SYS_OPEN_DIR` and `SYS_READ_ENTRIES`.
- Enhance file creation in EXT2 to allow specifying file modes.
- Refactor VFS file handling to accommodate new flags and modes.
- Update user-space application in `user/include/mlibc/helloworld.c` to demonstrate file operations including `touch`, `ls`, and `cat`.
- Clean up debug print statements in EXT2 file system code.
- Worked on proper error handling and return codes across VFS and syscall implementations.

It's only a small step but we're getting closer to making error codes standardized

We also setup SSE exactly as we should've the first time we introduced it in commit 9a9b91c

We have added it correctly to the user stack trampoline (hopefully), so there shouldn't be any issues now.

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-05-03 00:46:59 -04:00
parent 9a16250a1b
commit ef14a52b49
20 changed files with 716 additions and 79 deletions
+202
View File
@@ -0,0 +1,202 @@
#include "random.h"
#include "fs/vfs.h"
#include "libk/stdio.h"
#include "arch/x86_64/sys/tsc.h"
#include "arch/x86_64/sys/pit.h"
#include "errno.h"
#include <stdbool.h>
#include <string.h>
#include "limine.h"
/* ChaCha20 core (public-domain, 20-round) */
static inline void chacha20_quarterround(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
*a += *b; *d ^= *a; *d = (*d << 16) | (*d >> 16);
*c += *d; *b ^= *c; *b = (*b << 12) | (*b >> 20);
*a += *b; *d ^= *a; *d = (*d << 8) | (*d >> 24);
*c += *d; *b ^= *c; *b = (*b << 7) | (*b >> 25);
}
static void chacha20_block(uint32_t state[16], uint8_t output[64]) {
uint32_t x[16];
for (int i = 0; i < 16; ++i) x[i] = state[i];
for (int i = 0; i < 10; ++i) { /* 10 double-rounds = 20 rounds */
/* column rounds */
chacha20_quarterround(&x[0], &x[4], &x[8], &x[12]);
chacha20_quarterround(&x[1], &x[5], &x[9], &x[13]);
chacha20_quarterround(&x[2], &x[6], &x[10], &x[14]);
chacha20_quarterround(&x[3], &x[7], &x[11], &x[15]);
/* diagonal rounds */
chacha20_quarterround(&x[0], &x[5], &x[10], &x[15]);
chacha20_quarterround(&x[1], &x[6], &x[11], &x[12]);
chacha20_quarterround(&x[2], &x[7], &x[8], &x[13]);
chacha20_quarterround(&x[3], &x[4], &x[9], &x[14]);
}
for (int i = 0; i < 16; ++i) {
x[i] += state[i];
((uint32_t*)output)[i] = x[i];
}
}
/* Global ChaCha20 PRNG state */
static uint8_t rng_key[32];
static uint64_t rng_counter = 0;
static bool rng_seeded = false;
static void rng_generate(uint8_t *buf, size_t len) {
size_t i = 0;
while (len > 0) {
uint32_t state[16];
static const uint32_t sigma[4] = {0x61707865, 0x3320646e, 0x79622d32, 0x6b206574};
state[0] = sigma[0]; state[1] = sigma[1]; state[2] = sigma[2]; state[3] = sigma[3];
for (int j = 0; j < 8; ++j)
state[4 + j] = ((uint32_t*)rng_key)[j];
state[12] = (uint32_t)rng_counter;
state[13] = (uint32_t)(rng_counter >> 32);
state[14] = 0xdeadbeef; /* fixed nonce (PRNG only) */
state[15] = 0xbeefdead;
uint8_t block[64];
chacha20_block(state, block);
size_t copy = (len < 64) ? len : 64;
for (size_t k = 0; k < copy; ++k)
buf[i + k] = block[k];
i += copy;
len -= copy;
rng_counter++;
}
}
static void rng_add_entropy(const uint8_t *data, size_t len) {
if (len == 0 || !data) return;
for (size_t i = 0; i < len; ++i)
rng_key[i % 32] ^= data[i];
/* stir (forward secrecy) */
uint8_t discard[64];
rng_generate(discard, 64);
rng_seeded = true;
}
int random_read(void* buf, size_t len) {
if (len == 0) return 0;
if (!buf) return -EFAULT;
if (!rng_seeded) return -EAGAIN; /* should never happen after init */
rng_generate((uint8_t*)buf, len);
return (int)len;
}
int random_write(const void* buf, size_t len) {
if (len == 0) return 0;
if (!buf) return -EFAULT;
rng_add_entropy((const uint8_t*)buf, len);
return (int)len;
}
int getrandom(void *buf, size_t buflen, unsigned int flags)
{
if (!buf) return -EFAULT;
if (buflen == 0) return 0;
if (flags & GRND_INSECURE) {
// maybe fall back to weaker RNG?
}
if (!rng_seeded) {
if (flags & GRND_NONBLOCK)
return -EAGAIN;
// could block here in the future
return -EAGAIN;
}
rng_generate((uint8_t*)buf, buflen);
return (int)buflen;
}
void random_init(void) {
printf("Initializing ChaCha20-based CSPRNG for /dev/random and /dev/urandom...\n");
uint8_t entropy[64] = {0};
size_t epos = 0;
/* === SEEDING SOURCES === */
/* 1. Limine boot timestamp (wall-clock) */
extern volatile struct limine_date_at_boot_request boot_request;
if (boot_request.response) {
uint64_t ts = boot_request.response->timestamp;
if (epos + sizeof(ts) <= sizeof(entropy)) {
memcpy(entropy + epos, &ts, sizeof(ts));
epos += sizeof(ts);
}
}
/* 2. Multiple TSC samples (high-resolution jitter) */
for (int i = 0; i < 4; ++i) {
uint64_t tsc_val = rdtsc();
if (epos + sizeof(tsc_val) <= sizeof(entropy)) {
memcpy(entropy + epos, &tsc_val, sizeof(tsc_val));
epos += sizeof(tsc_val);
}
}
/* 3. PIT tick counter */
uint64_t pit_ticks = PIT_GetTicks();
if (epos + sizeof(pit_ticks) <= sizeof(entropy)) {
memcpy(entropy + epos, &pit_ticks, sizeof(pit_ticks));
epos += sizeof(pit_ticks);
}
/* 4. RDRAND (hardware TRNG) if present */
{
uint32_t eax, ebx, ecx, edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
if (ecx & (1u << 30)) {
printf("RDRAND detected - using hardware RNG for seeding\n");
for (int i = 0; i < 4 && epos + 8 <= sizeof(entropy); ++i) {
uint64_t rd;
asm volatile("1: rdrand %0\n\tjnc 1b" : "=r"(rd));
memcpy(entropy + epos, &rd, sizeof(rd));
epos += sizeof(rd);
}
}
}
/* 5. Unix seconds from PIT (time-based entropy) */
extern uint64_t g_Unixseconds;
uint64_t unix_sec = g_Unixseconds;
if (epos + sizeof(unix_sec) <= sizeof(entropy)) {
memcpy(entropy + epos, &unix_sec, sizeof(unix_sec));
epos += sizeof(unix_sec);
}
/* 6. Final TSC padding (extra jitter) */
while (epos < 32) {
uint64_t t = rdtsc();
memcpy(entropy + epos, &t, (epos + 8 <= 32) ? 8 : (32 - epos));
epos += (epos + 8 <= 32) ? 8 : (32 - epos);
}
/* Initialise ChaCha20 key from collected entropy */
memcpy(rng_key, entropy, 32);
rng_counter = rdtsc(); /* random starting counter */
rng_seeded = true;
printf("ChaCha20 RNG seeded with %zu bytes of entropy (TSC, PIT, RDRAND, boot time, unix time, etc.)\n", epos);
/* Register both devices (open as "/dev/random" and "/dev/urandom") */
VFS_RegisterCharDev("/dev/random", random_read, random_write);
VFS_RegisterCharDev("/dev/urandom", random_read, random_write);
printf("Registered chardevs: /dev/random and /dev/urandom (ChaCha20 CSPRNG)\n");
}