Commit graph

21 commits

Author SHA1 Message Date
DeFiDude
d88a1c0964 implement lxmf delivery announce app data 2026-05-03 15:58:57 -06:00
DeFiDude
354a345531 implement sx1262 reset helper 2026-05-03 13:36:05 -06:00
DeFiDude
2385e3d960 implement lora spi helpers 2026-05-03 13:30:50 -06:00
DeFiDude
e5488d6428 implement lora gpio helpers 2026-05-03 13:19:57 -06:00
DeFiDude
5b8a70c888 implement channel envelopes 2026-05-03 04:03:27 -06:00
DeFiDude
beef415774 implement link request subset 2026-05-03 03:22:45 -06:00
DeFiDude
fd1544f7b2 implement transport path table 2026-05-03 02:54:19 -06:00
DeFiDude
604b2c185b implement qemu flash model 2026-05-03 01:41:23 -06:00
DeFiDude
1e008e9a2d implement announce builder 2026-05-03 01:09:12 -06:00
DeFiDude
2d5345623a destination_hash: add Reticulum destination hash helpers 2026-05-03 00:30:26 -06:00
DeFiDude
c338223429 Bring up TARGET=c6 on Adafruit ESP32-C6 Feather; close milestone 1
- TARGET_C6 register block + linker script (load into 0x4086c410, the same
  upper-half HP_SRAM bootloader region esp-idf uses; loading to 0x40800000
  collides with ROM flash-loader scratch and silently aborts the segment
  copy with "Calculated 0xef stored 0xff").
- USB-Serial/JTAG as the early-bring-up uart_* backend (ADR-0010 refines
  ADR-0004; the C6's mask ROM provides the CDC-ACM stack so we just push
  bytes to a 1-byte FIFO with WR_DONE flush).
- TARGET_C6 paths in clock_init / clock_now_ticks / clock_delay_us /
  uart_init / uart_tx_byte / uart_rx_byte / uart_rx_available.
- clock_init disables TG0 / LP / Super watchdogs (key 0x50D83AA1) so the
  polling main loop survives.
- Makefile image/flash/monitor targets default DIO @ 40 MHz (QIO @ 80 MHz
  hangs on the Feather's factory flash configuration).
- Hardware demo passed end-to-end: KISS-framed Reticulum packet over USB-C
  produces kiss.rx_frame + packet.parsed; short payload produces rejected;
  SHA-256 KAT trigger emits FIPS 180-4 §B.1 vector ba7816bf...20015ad.
- qemu-virt suite still 124/124 green.
2026-05-02 22:26:05 -06:00
DeFiDude
7b4e2e3541 ed25519 scalar arithmetic: sc_reduce + sc_muladd verified (binary long-division mod L; QEMU KATs) 2026-05-02 19:10:09 -06:00
DeFiDude
8f8f4c1841 sha512 family verified (FIPS 180-4 compress + KAT against hashlib.sha512 under QEMU) 2026-05-02 19:00:50 -06:00
DeFiDude
414ee4bf35 x25519_keypair + rng_init + rng_bytes: verified (deterministic-fake CSPRNG, RFC 7748 §6 keypair under QEMU) 2026-05-02 18:34:28 -06:00
DeFiDude
cda7dae845 x25519_field_add + x25519_field_sub: verified (radix 2^25.5, 10-limb donna form)
Field representation locked in per docs/design/x25519-field.md:
10 signed int32 limbs in radix 2^25.5 (curve25519-donna 32-bit form).
Limb bit weights are 2^(25*i + (i+1)/2) — even limbs hold 26 nominal
bits, odd limbs 25, total 255-bit field (mod 2^255-19). Element
storage: 40 bytes (10×int32). The ABI is `void op(int32_t *out, const
int32_t *a, const int32_t *b)` with a0/a1/a2 register passing,
in-place safe.

x25519_field_add and x25519_field_sub are 10 fully-unrolled lw/lw/
add(or sub)/sw triples — straight-line, no branches, no stack frame.
Pointwise; no reduction. Limb growth is left for the multiply step
(or pack/unpack) to absorb.

Verification (all three tiers per ADR-0009):
  * Tier A: X25519FieldOps.cry bridges the asm's limb representation
    to the Integer-valued algebraic spec in X25519.cry. Decodes 10
    signed int32 limbs to the integer they represent. Each asm op's
    post-condition is decode_limbs(out) == op(decode_limbs(a),
    decode_limbs(b)) mod p_25519. SAW discharges the concrete-zero
    KATs (zero±zero==zero, small_add) via z3 in milliseconds.
  * Tier C: per-function angr verifier drives the asm with random
    limb arrays (bounded ±2^25 to avoid int32 overflow), reads back
    the result, decodes to integer, compares against the Python
    oracle. 12 scenarios per op (zeros, edge cases, alias-safe,
    8 random); inputs untouched, all 14 callee-saved regs preserved.
  * Tier B (constant-time): vacuous — straight-line pointwise ops,
    no secret-dependent branches or addresses by construction.

Static checks (test_x25519_field_*.py): symbol present, no
conditional branches, exactly 20 lw + 10 sw + 10 add/sub.

End-to-end ./verify per function: ~1.5 s. Both flip ☐ → ◉.
Verified count: 47 → 49.

Establishes the per-op verification template for the remaining 12
X25519 functions (mul, sq, mul121665, inv, pack, unpack,
decode_scalar, decode_u, cswap, montgomery_ladder, scalar_mult,
keypair). Each follows the same pattern: spec block, .S, .saw with
algebraic post-condition, .py with angr binary equivalence, static
test. Multiplication and squaring will likely hit the same angr
pcode unreliability we saw with the AES Boyar-Peralta circuit; for
those we'll fall back to Tier A + pytest QEMU KAT until macaw-riscv
lands per ADR-0009 §"Tier C path forward".
2026-05-02 15:14:28 -06:00
DeFiDude
edf39b3421 aes_sbox: tested (Boyar-Peralta byte-wise; 256-input KAT vs FIPS 197 Fig 7) 2026-05-02 02:24:24 -06:00
DeFiDude
d04707a1a5 hkdf_extract + hkdf_expand: tested (RFC 5869 A.1/A.2/A.3 pass via 'E'/'X' markers) 2026-05-02 01:41:47 -06:00
DeFiDude
f2da28a758 hmac_sha256: tested (RFC 4231 TC1/2/3/6 pass via 'H' marker in _main)
Also fixes a multi-block sha256_update bug: the .Lwblk loop kept the
block-size constant in caller-saved t0 across the sha256_compress call.
After the first compress, t0 held garbage and the bltu terminator either
re-entered or exited spuriously. KAT integration surfaced it (TC6
hangs); static checks didn't.
2026-05-02 01:36:26 -06:00
DeFiDude
1b5ea42377 sha256_init: tested (KAT IV check; full verify pending toolchain) 2026-05-02 01:01:21 -06:00
DeFiDude
b9e8a1d7a4 tools/check_stack.py: worst-case stack-depth analyzer
Walks every src/**/*.S, parses each function's @stack annotation and
its call/tail/jal outgoing edges, builds a call graph rooted at
_reset, and reports the deepest cumulative stack along any reachable
path. Fails the build if the deepest path exceeds STACK_SIZE from
src/include/config.S.

Cycles are flagged as recursion (recursion is forbidden today; allowing
it needs a separate ADR). Functions reachable from _reset whose .S is
absent are reported as warnings.

Wired into 'make ci': registry → stack → test. Current binary:
deepest path _reset → _main → log_event → log_hex = 48 bytes,
well under STACK_SIZE = 16384.

OQ-4 resolved in docs/open-questions.md.

154 tests, all green.
2026-05-01 23:09:19 -06:00
DeFiDude
36e23ab975 milestone-1: harness, dispatcher, ADR-0008, build chain
Lays the verifier-loop infrastructure that every subsequent function
goes through. Every change is wired to make ci.

Tooling (tools/):
- parse_spec.py validates the ADR-0007 spec block on every .S file
  (required fields, @function ↔ basename, @module ↔ directory, @adrs ↔
  Accepted ADRs, @verify/@tests path existence, unbounded-cycles ↔
  not-required-ct).
- check_registry.py cross-checks FUNCTIONS.md against src/ — every
  registered global has a source file (and vice versa), every
  depends-on resolves, status disagreements raise warnings.

Test harness (tests/harness/):
- log_parser.py parses the ADR-0004 log line format with selectors.
- target.py: Target ABC + NullTarget loopback + Emu/Hw skeletons
  that detect tool availability and raise TargetUnavailable until
  wired (deferred until there's a binary to drive end-to-end).
- oracle.py: pure-Python KISS encode/decode reference + RNS.Packet
  shim for differential testing.
- verify.py + verify_cli.py + ./verify wrapper: the dispatcher.
  Runs spec-validate, pytest, and the @verify-pointed tool
  (kat-only, .saw, .tla, .py); reports pass/fail/skip/not-implemented.

Build chain (toolchain/):
- qemu-virt.ld: DRAM @ 0x80000000, __global_pointer$ anchored,
  16 KiB stack reserved at top. C6 linker script lands with hardware
  bring-up.
- versions.lock pinned to riscv64-elf-binutils 2.46 + qemu 11.0 +
  Python 3.12 + RNS 1.2 (per ADR-0008).

Includes (src/include/):
- psabi.S with the RISC-V ABI register aliases (ADR-0001).
- config.S with capacity constants (stack, ring buffers, KISS frame).
- regs.S with the qemu-virt NS16550A UART addresses; C6 stub with
  .error until populated.

ADR-0008: bundles four decisions surfaced during bring-up:
1. Rename verify/ → proofs/ to free ./verify for the dispatcher.
2. Adopt vanilla riscv64-elf-binutils (homebrew) over the Espressif
   crosstool-NG fork; -march=rv32imac -mabi=ilp32.
3. Log timestamps are 8 hex digits emitted by log_hex(width=8).
4. Spec-block prefix is # (RISC-V GAS line comment), not ;;.

ADR-0006/0007 status lines flagged as partially superseded; ADR-0004
flagged as refined. ADR index updated. parse_spec accepts both ;; and
# prefixes during the transition. open-questions.md tracks OQ-1..OQ-3
as resolved by ADR-0008 and OQ-4 (check_stack.py) deferred.

make ci: 51 tests, all green.
2026-05-01 22:31:38 -06:00