Radek e07030d81e Rewrite README with change log and full requirements
Replace README.md with a single comprehensive doc. Add a "What Changed"
section mapping each improvement to its commit (DRBG fix, health checks,
cleanup, HTTP service, auth, tests, docs). Expand Requirements into
software, system libraries, hardware, kernel-RNG, and container
subsections. Keep the existing usage, HTTP service, container, remote
consumption, and tests sections.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-02 13:10:38 +01:00
2026-03-11 15:41:06 +00:00

Entropy-RNG: Camera-Based Random Number Generator

This project uses a live video feed from a network camera (e.g. an Axis M1013) to generate cryptographically secure random numbers. The camera is pointed at a high-contrast, ever-changing scene (e.g. ceiling and lamps) so the pixel data carries genuine entropy. The output can be used locally (feeding the kernel RNG via rngd) or served over HTTP so remote systems can pull entropy to seed their own pools.


What Changed

This section records the rewrite from the original scripts to the current pipeline. Each bullet maps to a commit in git log.

Core RNG fix (the important one)

The original code computed a SHA-256 of the camera frame into a variable named entropy and then discarded it, drawing instead from the OS CSPRNG via secrets.randbits(). The camera contributed nothing — the output was just relabeled /dev/urandom.

  • Replaced with a NIST SP 800-90A HMAC-DRBG (SHA-256) seeded from the frame hash. The camera now genuinely contributes entropy.
  • The DRBG is reseeded every frame, so output changes even when camera noise is modest.
  • Added --interval for loop rate control, --out for writing raw bytes to a FIFO, mutual exclusion of --loop/--single, and bit-multiple validation.

Health checks

  • Added health.py with FIPS 140-2 continuous tests (monobit, runs, long-run) on the raw JPEG bytes before hashing.
  • A frozen, all-identical, or degraded frame is rejected and never seeds the DRBG.
  • Runs thresholds are doubled because we count 0-runs and 1-runs together.
  • --no-health disables the checks for debugging.

Cleanup

  • Removed entropy-rng-cam.py and entropy-rng-opencv.py, which were superseded by entropy.py and carried the same unused-entropy bug.
  • Removed the empty 0-byte file.
  • Added requirements.txt with pinned dependencies.

HTTP service + container deployment

  • Added api.py — a FastAPI app that runs the camera producer in a background thread and serves the latest entropy blob over HTTP.
  • Added Dockerfile for container builds, with a healthcheck against /healthz.
  • All config is via environment variables for containers.

Auth (simple by design)

  • Single shared secret via X-API-Key header, controlled by ENTROPY_API_KEY.
  • Empty key means no auth, so you can start open and lock down by setting one env var.
  • The API layer is where HMAC-signed blobs or mTLS plug in later — no architecture change needed.

Tests

  • Added test_entropy.py — 16 tests covering the DRBG, health checks, a bit-balance smoke test, and API auth gating. All pass without a camera (the producer is stubbed).

Docs

  • Rewrote this README and notes.md to match the new pipeline and commands.

How It Works

  1. Capture frame — fetch a single MJPEG frame from the camera via OpenCV.
  2. Health check — the raw JPEG bytes are run through FIPS 140-2 continuous tests (monobit, runs, long-run). A frozen or degraded frame is rejected before use.
  3. Extract entropy — the frame is SHA-256 hashed to a 32-byte digest.
  4. DRBG — the digest seeds a NIST SP 800-90A HMAC-DRBG (SHA-256), which generates the output bytes. The camera genuinely contributes entropy; the output is not just relabeled OS randomness.
  5. Emit — hex to stdout, or raw bytes to a file/FIFO (--out).

Requirements

Software

  • Python 3.10+
  • Python packages: opencv-python, fastapi, uvicorn, pytest

System libraries (Debian/Ubuntu)

OpenCV needs runtime libraries that are not pip-installed:

sudo apt-get install libgl1 libglib2.0-0

Install Python dependencies

pip install -r requirements.txt

Hardware

  • An IP webcam serving an MJPEG stream over HTTP (e.g. Axis M1013 at http://192.168.0.200/mjpg/video.mjpg).
  • The camera must be pointed at a dynamic, high-contrast scene. A static frame will fail health checks and be rejected.

For the kernel-RNG flow

  • Linux with rngd installed (rng-tools package).
  • sudo privileges to run rngd.

For container deployment

  • Docker (or any OCI-compatible runtime) for local containers.
  • Kubernetes for orchestrated deployment (optional).

Local Usage

Single number

python3 entropy.py --bit 256

Loop, writing raw bytes to a FIFO for rngd

mkfifo /tmp/entropy.fifo
python3 entropy.py --loop --bit 512 --out /tmp/entropy.fifo &
sudo rngd -f -W 90% -x rdrand -x jitter -x pkcs11 -x rtlsdr \
  -O namedpipe:path:/tmp/entropy.fifo -O namedpipe:timeout:2

Flags

Flag Default Description
--url http://192.168.0.200/mjpg/video.mjpg Camera MJPEG stream URL
--bit 256 Bit length of each random number (multiple of 8)
--single N 1 Generate N numbers and exit
--loop off Run forever
--interval 1.0 Seconds between iterations in --loop
--out PATH stdout Append raw bytes to a file/FIFO instead of printing hex
--no-health off Disable FIPS 140-2 health checks

--loop and --single are mutually exclusive.


HTTP Service (for remote systems)

api.py runs the camera producer in a background thread and serves the latest entropy blob over HTTP, so other systems can pull entropy to seed their own pools.

Run locally

uvicorn api:app --host 0.0.0.0 --port 8000

Endpoints

Method Path Auth Returns
GET / none service info
GET /healthz none {status: ok} (liveness probe)
GET /entropy X-API-Key if configured {hex, bits, ts}

Configuration (env vars)

Var Default Description
ENTROPY_CAMERA_URL http://192.168.0.200/mjpg/video.mjpg Camera MJPEG URL
ENTROPY_BITS 256 Bits per blob
ENTROPY_INTERVAL 1.0 Seconds between frames
ENTROPY_NO_HEALTH 0 1 disables health checks
ENTROPY_API_KEY empty Shared secret for /entropy; empty = no auth

Container Deployment

Build and run

docker build -t entropy-rng .
docker run -d -p 8000:8000 \
  -e ENTROPY_CAMERA_URL=http://192.168.0.200/mjpg/video.mjpg \
  -e ENTROPY_API_KEY=changeme \
  entropy-rng

The container has a healthcheck against /healthz. The camera must be reachable from the container's network.

Kubernetes notes

  • Replicas: 1. Multiple pods hitting the same camera produce correlated output. Pin to a single replica.
  • Camera reachability is the real constraint. Schedule the pod on a node that can reach the camera (use nodeSelector or a labeled node), or expose the camera via a Service.
  • Store ENTROPY_API_KEY in a Secret and mount as an env var.
  • Use an Ingress or Service for TLS termination in front of uvicorn.

Consuming Remotely (seed another system's pool)

A remote Linux box can pull a blob and feed its kernel RNG via rngd:

curl -s -H "X-API-Key: changeme" https://entropy-svc.internal/entropy \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['hex'])" \
  | xxd -r -p > /tmp/entropy.fifo
sudo rngd -f -W 90% -x rdrand -x jitter -x pkcs11 -x rtlsdr \
  -O namedpipe:path:/tmp/entropy.fifo -O namedpipe:timeout:2

Tests

pytest -q

Covers the HMAC-DRBG (determinism, reseed, length, reseed limit), health checks (random passes, frozen/all-ones/short/long-run rejected), a bit-balance smoke test, and the API endpoints including auth gating. Tests stub the camera, so they run without hardware.


Notes

  • Point the camera at a dynamic scene. A static frame will fail health checks and be rejected.
  • The DRBG is reseeded from each frame, so output changes even if the camera noise is modest.
  • Auth is a single shared secret for now. When you need per-client keys, rotate to HMAC-signed blobs or mTLS — the API layer is where that plugs in.
  • The 256-bits-per-frame entropy credit is conservative. If you want a defensible number for your specific camera, run ent or rngtest on a captured sample.
S
Description
Generate entropy from a MJPEG stream
Readme
51 KiB
Languages
Python 95.3%
Dockerfile 4.7%