3 August 2026

A (hopefully not horribly unsafe) CSPRNG based on Feistel networks with a nonlinear kernel in GF(256), as seen in the 4th iteration kcrypt.

  #include <errno.h>
  #include <fcntl.h>
  #include <signal.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <unistd.h>
  #define Fi(n,a...) for (int i = 0; i < n; i++) { a; }
  #define Fq(n,a...) for (int q = 0; q < n; q++) { a; }
  #define _(a...) { return ({ a; }); }
  #define S static
  typedef uint8_t gf;
  typedef struct { gf k1[32], k2[64]; } bk;
  typedef struct { gf k2[64], rk[16][32], p[16][32]; } bc;
  S void wipe(void * p, size_t n) { Fi(n, ((volatile gf *) p)[i] = 0) }
  S gf mul2(gf x)_(  x << 1 ^ (gf) -(x >> 7) & 0x1d)
  S gf eq(gf a, gf b)_((uint32_t) (a ^ b) - 1 >> 8)
  S gf mul(gf a, gf b)_(gf r = 0;  Fi(8,
      r ^= a & (gf) -(b & 1), b >>= 1, a = mul2(a))  r)
  S gf inv(gf x)_(gf x2 = mul(x, x), x3 = mul(x2, x), x6 = mul(x3, x3),
    x12 = mul(x6, x6), y = mul(x12, x3);  Fi(4, y = mul(y, y))
    mul(mul(y, x12), x2))
  S gf mod8(gf x, gf m)_(uint32_t r = 0, t;  Fi(8,
      t = (r += r + (x >> 7 - i & 1)) - m;  r = t + (m & -(t >> 31)))  r)
  S void shuffle(gf p[32], gf key[64], int round) {
    Fi(32, p[i] = i)  Fi(31, int n = 31 - i;
      gf j = mod8(key[n + round * 21 & 63], n + 1), t;
      Fq(32, t = (p[n] ^ p[q]) & eq(q, j);
        p[n] ^= t;  p[q] ^= t))}
  S void permute(gf out[32], gf in[32], gf p[32]) {
    Fq(32, gf x = 0;  Fi(32, x |= in[i] & eq(i, p[q]))  out[q] = x)}
  S void diffuse(gf a[32]) { gf b[32], * x = a, * y = b, * t;  Fq(4,
      Fi(32, y[i] = x[i] ^ mul2(x[i + q * q + q + 1 & 31]))
      t = x;  x = y;  y = t)  wipe(b, sizeof b); }
  S void setup(bc * c, bk * key) {
    gf z[2][32], * s = z[0], * u = z[1];
    Fi(64, c->k2[i] = key->k2[i])  Fi(32, s[i] = key->k1[i])
    Fq(16, shuffle(c->p[q], c->k2, q);  permute(u, s, c->p[q]);
      Fi(32,
        c->rk[q][i] =
          inv(u[i] ^ mul2(u[i + 1 & 31])
            ^ c->k2[i + q * 17 & 63] ^ ((q + 1) * 0x9d + i))
          + c->k2[i + 32 + q * 29 & 63])
      diffuse(c->rk[q]);  Fi(32, s[i] = c->rk[q][i]))
    wipe(z, sizeof z); }
  S void block(gf out[64], gf in[64], bc * c) {
    gf z[4][32], * l = z[0], * r = z[1], * n = z[2], * u = z[3], * t;
    Fi(32, l[i] = in[i];  r[i] = in[32 + i])
    Fq(16, permute(u, r, c->p[q]);
      Fi(32,
        n[i] =
          c->rk[q][i]
            + inv(u[i] ^ mul2(u[i + 1 & 31]) ^ c->k2[i + 32 + q * 11 & 63]))
      diffuse(n);  Fi(32, n[i] ^= l[i])  t = l;  l = r;  r = n;  n = t)
    Fi(32, out[i] = l[i];  out[32 + i] = r[i])
    wipe(z, sizeof z); }
  S int randomize(void * v, size_t left)_(
    gf * p = v;  int fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) return -1;
    while (left) {
      ssize_t n = read(fd, p, left);
      if (n > 0) p += n, left -= n;
      else if (n < 0 && errno == EINTR) continue;
      else {
        int e = n < 0 ? errno : EIO;
        close(fd);  errno = e;  return -1; }
    } close(fd))
  S int putall(void * v, size_t left)_(
    gf * p = v;
    while (left) {
      ssize_t n = write(STDOUT_FILENO, p, left);
      if (n > 0) p += n, left -= n;
      else if (n < 0 && errno == EINTR) continue;
      else if (n < 0 && errno == EPIPE) return 1;
      else { if (!n) errno = EIO;  return -1; } }  0)
  int main(void)_(
    struct { bk key; gf nonce[16]; } seed = {0};
    bc c = {0};  gf in[64] = {0}, out[64] = {0};  int rc = 1, w;
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
      { perror("signal");  goto done; }
    if (randomize(&seed, sizeof seed) < 0)
      { perror("/dev/urandom");  goto done; }
    setup(&c, &seed.key);
    Fi(16, in[i] = seed.nonce[i])  in[63] = 3;
    wipe(&seed, sizeof seed);
    for (uint64_t counter = 0;; counter++) {
      Fi(8, in[16 + i] = counter >> i * 8)
      block(out, in, &c);
      if ((w = putall(out, 64)) > 0)
        { rc = 0;  goto done; }
      if (w < 0)
        { perror("stdout");  goto done; }
      if (counter == UINT64_MAX)
        { fputs("krng: counter exhausted\n", stderr);  goto done; } }
  done:
    wipe(&seed, sizeof seed);  wipe(&c, sizeof c);
    wipe(in, sizeof in);  wipe(out, sizeof out);
    rc)
< back to journal