adv-mixer: report with Q3 census (BOUND+tail), Q1 fold (BOUND), Q2 running

Internal adversarial pass, not an independent review. Q3 census over 2^24 day
keys: largest per-day M1 FPGA-datapath gain 1.1726x on one day, 3.26e-4 of days
over 1.1x, zero days with any DSP or wall-time gain, ROT-all-equal never seen.
Q1 fold probe: 1e6/1e6 affinity violations, 0 dead word pairs, 0 key-order
agreements, so no cheap composition of the 8 keyed applications. Q2 diffusion
sweep and the f4 analytic tail are running; numbers land as they finish.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 18:24:11 +00:00
parent 029e5219d6
commit 5bddb289c3

View file

@ -0,0 +1,158 @@
# Report: adversarial cryptanalysis of the mixer M_r
Internal adversarial pass, not an independent review.
The label "internal adversarial pass, not an independent review" applies to every sentence here that could be
quoted in public. This is such a pass. It is not an outside review.
## Header
| Field | Value |
|---|---|
| Target commit | 017e70376489251e18564c0abce7e466e606c8b3 (class v4 sub-version 3, object byte 7) |
| Target | the mixer M_r (spec 01 section 1.8.4), 8 keyed applications between reads, 72 per item, class v4 (m = 8) |
| Branch | adv-mixer, from build/master |
| Byte-identity | memhard.rs, seed.rs, bind.rs, derive.rs, Cargo.toml, Cargo.lock are byte-identical to the frozen commit; accept.rs, emit.rs, generator.rs, packcheck.rs and the two mixer/recheck test files differ and are NOT M_r (plan section 0) |
| Harness attack-adv-mixer | sha256 a01bf61016a8bda530468f081442131f1bff4a7b6401dd84cbcde83c78bb48f3 (box 2 release) |
| Harness attack-f4 census | sha256 d51df6caab338b0165485c3ce6d6379c0af7180bf2b72fc0a1531beabeaf2d22 (box 2 release) |
| Box | igneum-build-2 (box 2), nice 10; the f4 expect tail on igneum-build-1 (box 1), nice 10 |
| Toolchain | rustc 1.99.0 both sides |
| Day under test | chain day index 20729 (genesis, 3 October 2026), bind::day_bytes(20729) |
## Status board
| Q | Method | Known-failed shape | Gate | Result (numbers) | Status |
|---|---|---|---|---|---|
| Q3 weak draws | f4 census over 2^24 days, M1/M2 datapath cost | plant alleq/mul1/mul1all/rc0/rcrk0 (all fired) | any class >= 1.1x on a non-negligible fraction | largest per-day M1 gain 1.1726x on 1 day in 2^24; 5476 days (3.26e-4) over 1.1x on the generous M1 metric; 0 days with any M2 DSP gain; 0 ops from ROT or RC on any day; ROT-all-equal never occurred | FINDING (tail), BOUNDED |
| Q2 round margin | adv-mixer diffusion census, K = 1..8, 2e6 states | diffusion --plant weak (fired: 7894 holes, 236269 strong cells at K=1) | full diffusion (no hole, no strong bias at 8 sigma) at K | running | RUNNING |
| Q1 shortcut | adv-mixer fold probe, 1e6 trials | the probes are their own control | affinity violations > 0, dead pairs = 0, key agreements = 0 | 1e6/1e6 affinity violations, 0 of 256 dead word pairs, 0 of 1e6 key-order agreements | PASS (BOUND: no fold) |
| Q4 other | watched while the above ran | n/a | n/a | fixed round keys and MUL/RC reuse in item init are covered by Q3 classes; nothing further yet | RUNNING |
## Q3: weak parameter draws (BOUND with a measured tail)
Command (box 2):
```
nice -n 10 taskset -c 64-95 attack-f4 census --from 20729 --count 16777216 --threads 32
```
Seed: MixParams::with_shape(seed_words_from_bytes(bind::day_bytes(d)), Shape::for_class(&V4_CLASS)) for
consecutive chain day indices d from 20729. No external seed. 16,777,216 days (2^24), 4.4 s wall.
Log: /srv/builds/igneum-wt-adv-mixer/adv/census-20261007T181830Z.log on box 2.
The gain metric M1 is the per-day LUT datapath: an FPGA bitstream synthesised for one day, the only per-day
attacker that exists. A constant XOR folds into the next LUT, a constant rotation is routing, a 32-bit add or XOR
is one adder-equivalent, a multiply by a constant is NAF(MUL) - 1 adders. M1 cost per application is
64 + sum(NAF(MUL_i) - 1). The 64 is the 8 quarter rounds' adds and xors. Gain is the census median over the
day's cost. This is the generous bound: real single-constant multipliers beat NAF and a DSP multiply is
value-independent.
Headline numbers:
| Quantity | Value |
|---|---|
| M1 cost mean | 231.11 adders per application (sd 6.19) |
| M1 cost median | 231 |
| M1 cost min (best attacker day) | 197, day 4819563 |
| M1 cost max | 263, day 15262713 |
| Largest M1 gain | 1.1726x (231 / 197), 1 day in 2^24 |
| Days with M1 gain over 1.1x (cost < 210) | 5476 of 16,777,216 = 3.26e-4 (about 1 in 3064 days) |
| Days with any M2 DSP-bound gain, k >= 2 | 0 |
| Days with a ROT or RC wall-time gain | 0 (a bit-exact verifier never skips an application; ROT is wiring, RC is inverters) |
What this means for the honest miner and the verifier. The M1 gain is the adder count of an FPGA datapath
synthesised for one calendar day. It is not a wall-time shortcut against the honest GPU and not a skipped
application at the bit-exact verifier. The chip model prices the mixer at 9,360 ops per item hoisted; a 1.17x cut
in the multiply layer's adder count on the single best day in 2^24 does not move the chip rows, which are bound
by the 8 cache reads and the fixed op count, not by the FPGA adder count of one day. Over 2^24 days (about 45,900
years of calendar days) the worst day hands a per-day FPGA a 1.17x smaller multiply datapath, once.
The spec's open worry (1.8.4: a ROT draw of eight equal values is possible and untested). ROT all equal did NOT
occur in 2^24 days. The analytic rate is 1 in 2.751e10 days (about 1 day in 75 million years). The worst realized
ROT concentration is 2 days with all eight ROT in {1,2,30,31}, at M1 gain 1.0645x.
Class table, the members that matter (full table in the log):
| Class | Count / 2^24 | Fraction | Analytic expected | Worst day: M1 cost, gain |
|---|---|---|---|---|
| ROT all equal | 0 | 0 | 3.64e-11 | none |
| ROT distinct <= 3 | 534 | 3.18e-5 | 3.07e-5 | day 11482247: 208, 1.1106x |
| ROT max multiplicity >= 4 | 35631 | 2.12e-3 | 2.35e-3 | day 9506389: 206, 1.1214x |
| ROT any pair sums to 32 | 10022037 | 5.97e-1 | 6.01e-1 | day 4819563: 197, 1.1726x |
| ROT all 8 in {1,2,30,31} | 2 | 1.19e-7 | 7.68e-8 | day 14330190: 217, 1.0645x |
| MUL any = 1 | 0 | 0 | 7.45e-9 | none |
| MUL any popcount <= 4 | 612 | 3.65e-5 | 3.72e-5 | day 7275755: 200, 1.1550x |
| MUL any NAF weight <= 2 | 4 | 2.38e-7 | (expect run) | day 7786546: 221, M2 1.067x |
| MUL any NAF weight <= 3 | 216 | 1.29e-5 | (expect run) | day 332924: 207, M2 1.067x |
| MUL two equal | 0 | 0 | 5.59e-8 | none |
| MUL M2 k >= 2 (gain >= 1.14x) | 0 | 0 | (expect run) | none |
| RC any = 0 | 0 | 0 | 3.73e-9 | none |
| RC + rk = 0 for any of 72 keys | 10 | 5.96e-7 | 2.68e-7 | day 3194363: 218, 1.0596x |
Every counted fraction tracks the analytic expectation (the draw is unbiased). The M2 DSP metric found zero days
with two or more low-NAF multiplies, so no day frees a second DSP block. The analytic expect run (box 1) is
cross-checking the NAF-weight tail; its numbers land in this section when it finishes.
Verdict for Q3. A measured weak-day tail exists on the generous M1 FPGA-adder metric: 3.26e-4 of days beat 1.1x,
the single best day reaches 1.1726x. It is bounded and small, zero on the DSP metric and zero on any wall-time
metric, and the spec's untested ROT-all-equal case never occurs and is astronomically rare. A weak day is a
public calendar, so an FPGA attacker could target day 4819563 of the chain for a 1.17x smaller multiply datapath,
which the chip model does not credit as a hash-rate gain. This is a FINDING in that the tail is nonzero, a BOUND
in that the worst case is 1.17x on a metric that does not move the honest or verifier cost.
## Q2: the round margin (RUNNING)
Command (box 2), per K in 1..8:
```
nice -n 10 attack-adv-mixer diffusion --day 20729 --apps K --states 2000000 --threads 32
```
Seed: probe states from the harness SplitMix64 seeded per thread from the day index; mixer params the real day
20729 draw. Log: /srv/builds/igneum-wt-adv-mixer/adv/diffusion-20261007T181948Z.log on box 2.
The census band at 2e6 states is 8 sigma = 0.00566, so a per-cell bias below 0.57 percent is invisible; this
limit is quoted with every row. The plant fired: the degenerate day (MUL all 1, RC all 0, ROT all 16) at K = 1
gave 7894 dependency holes and 236269 strong-bias cells of 262144, mean flip 0.1739, worst cell 223.6 sigma. A
real day must clear to zero holes and zero strong cells to call the distinguisher out at that K. Results land here
per K as the sweep runs.
## Q1: the structural shortcut (BOUND, no fold)
Command (box 2):
```
attack-adv-mixer fold --day 20729 --trials 1000000
```
Seed: harness SplitMix64 seeded from the day index; mixer params the real day 20729 draw.
| Probe | Result | Reading |
|---|---|---|
| (a) GF(2) affinity of the 2-application map | 1,000,000 of 1,000,000 quadruples violate affinity | the two multiply layers do not fold through the double round; the map is far from affine |
| (b) dead (in_word, out_word) pairs over the full 8-application block | 0 of 256 | every output word depends on every input word after 8 applications |
| (c) key-order agreements M(M(.,rk1),rk2) == M(M(.,rk2),rk1) | 0 of 1,000,000 | key order matters; the 8 round keys cannot be folded |
Verdict for Q1. On these probes the 8 keyed applications show no cheap composition. The multiply layers of
adjacent applications are separated by a nonlinear double round, so they do not merge (candidate 1 fails). The
drawn double round gives no surviving commutation that would fold keys (candidate 3 fails). The word-dependency
is complete at 8 applications (no separability). This bounds the cheapest folds. It does not rule out a high-order
algebraic or integral distinguisher below the probe's reach; that is owed work (plan section 7). No gain is
priced against the 9,360 ops per item: the 8 applications cost 8x on this evidence.
## Q4: anything else (RUNNING)
Two structural notes, both covered by Q3 classes. The 72 round keys are fixed multiples of 0x9E3779B9, not drawn,
so a bad round key is the same every day; the RC + rk = 0 class counts the one interaction (10 days in 2^24). The
item init s[8+i] = t*MUL[i] + RC[i] reuses MUL and RC, so a MUL[i] = 1 would collapse an init word to t + RC[i];
MUL any = 1 occurred on 0 days. Nothing further found yet.
## Box-hours spent (so far)
| Step | Box | Wall | Slot-hours |
|---|---|---|---|
| Build adv-mixer | box 2 | 21 s | 0.006 |
| Build f4-weakday | box 2 | 4 s | 0.001 |
| adv-mixer diffusion plant K=1 (50k) | box 2 | 5 s | 0.001 |
| adv-mixer fold 1e6 | box 2 | ~6 s | 0.002 |
| f4 five plant firings | box 2 | ~3 min | 0.05 |
| f4 census 2^24 | box 2 | 4.4 s | 0.001 |
| Build f4 on box 1 | box 1 | 39 s | 0.011 |
| Q2 diffusion sweep K=1..8 | box 2 | running | tracked at end |
| f4 expect tail | box 1 | running | tracked at end |
Spent before the two running sweeps: about 0.08 box-hours of the 8-hour first-results budget.