Rewrite README to reflect the actual pipeline (frame -> health check -> SHA-256 -> HMAC-DRBG -> emit), document all entropy.py flags, the HTTP service endpoints and env vars, container build/run, Kubernetes notes, and the remote-consumption rngd flow. Fix the notes.md typos and align its commands with the new --out flag. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
145 lines
4.8 KiB
Markdown
145 lines
4.8 KiB
Markdown
# 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.
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
- Python 3.10+
|
|
- `opencv-python`, `fastapi`, `uvicorn`, `pytest`
|
|
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
On Debian/Ubuntu you also need OpenCV runtime libs: `apt-get install libgl1 libglib2.0-0`.
|
|
|
|
---
|
|
|
|
## Local Usage
|
|
|
|
### Single number
|
|
|
|
```bash
|
|
python3 entropy.py --bit 256
|
|
```
|
|
|
|
### Loop, writing raw bytes to a FIFO for `rngd`
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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`:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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.
|