Replaces the deterministic-fake CSPRNG with the real ESP32-C6 hardware
RNG when built for hardware. The qemu-virt path is unchanged — qemu has
no RNG model, so the existing sha256(seed || counter) deterministic-
fake stays the back-end for the emulator (and continues to satisfy
KAT-style tests of higher-level crypto that rely on reproducible RNG
output, e.g. the x25519_keypair / ed25519_keypair test vectors).
The C6 path is a leaf: read LPPERI_RNG_DATA (0x600B2808) in 32-bit
chunks, emit 4 bytes per read, settle ~64 CPU cycles before the next
read (esp-idf hw_random.c uses ~16 APB cycles, we are 4× generous at
80 MHz CPU / 20 MHz APB). Without WiFi/BLE the RNG is hardware-seeded
once per boot but not continuously reseeded by the analog noise source —
adequate as a milestone-2 RNG, replaced by an HMAC-DRBG (NIST SP 800-90A)
seeded from this raw source as separate hardening work.
3 new hardware tests in tests/hardware/test_rng.py assert the entropy
shape: successive calls differ, output isn't all-zero or all-0xff, and
a 255-byte sample hits >32 distinct byte values. Together they catch
the obvious failure modes (peripheral mis-clocked, register reading
flash padding, PRNG stuck on one state).
`pytest --hardware tests/hardware/`: 12 passed in 6.4 s (was 9 before
this commit).
Six more on-silicon KAT round-trips covering the most distinct crypto
code paths:
- SHA-512 (FIPS 180-4 §C.1 "abc"): proves the 64-bit (lo, hi) register-
pair arithmetic the RV32IMAC core synthesizes for the 80-round
transform.
- AES-256 single-block encrypt (FIPS 197 §C.3): proves the
Boyar-Peralta sbox + GF(2^8) MixColumns chain runs on real silicon
(the path angr's pcode RV32IMC engine miscompiles 27% of the time —
on hardware it just works).
- X25519 scalar_mult (RFC 7748 §5.2 vector 1): proves the 10-limb
radix-2^25.5 field arithmetic with the M-extension mul/mulh paired
multiplies.
- Ed25519 sign + verify + tamper-rejection (RFC 8032 §7.1 test 1):
proves the entire signature-scheme composition (SHA-512 +
scalarmult + point_compress + sc_reduce + sc_muladd) works end-to-
end on the C6.
Adds _send_until() helper to handle slow ops where the kiss_rx_frame
log line lands immediately but the result line takes several seconds —
read_lines() returns on the first idle gap, so without an event-aware
wait the Ed25519 tests would race the chip's scalarmult.
`pytest --hardware tests/hardware/`: 9 passed in 5.3 s. Each KAT is
bit-for-bit equal to its qemu-virt + Cryptol/SAW + pyca/cryptography
counterpart.
Closes the milestone-1 §harness deliverable's third target (the abstract
spec listed Emu/Hw/Oracle; only the Oracle and Emu targets had
implementations until now).
- HwTarget.start/stop/write/read against pyserial + esptool. Class-level
flash cache keyed on (port, image-bin path, mtime) so back-to-back
tests share the ~2 s reflash; per-test cost is just the DTR/RTS reset
pulse and a boot-drain to the `boot\tready` marker.
- BuildArtifacts grew an `image_bin` field; build('c6') now also runs
`make image` so the .image.bin and .elf stay in lockstep when a test
forces a rebuild.
- TargetConfig grew image_bin / flash_chip / auto_flash / boot_ready_timeout
fields; defaults match the Adafruit Feather (chip=esp32c6, port comes
from --hardware-port / RATSPEAK_HW_PORT, default /dev/cu.usbmodem4101).
- conftest.py adds --hardware (and RATSPEAK_HW=1 env) opt-in; tests
marked @pytest.mark.hardware skip cleanly without the flag.
- tests/hardware/test_basic_demo.py — three round-trip tests on real
silicon: HEADER_1 packet → packet.parsed; short payload → packet.rejected;
'S'+'abc' → SHA-256 KAT bit-for-bit equal to FIPS 180-4 §B.1.
- 442 pytest pass without --hardware (3 hardware skipped); 445 pass
with --hardware in ~28 s.