igneum/docs/analysis/cryptanalysis/report-mixer.md
igneum-labs d10eee9f34 adv-mixer: Q1 deepening rows (linrel 16 days x 3 K, lineindex K1-3, CNF model); sweeps running
Internal adversarial pass, not an independent review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 18:52:16 +00:00

15 KiB

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 017e703764 (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)
Lane adv-mixer, narrowed by main (19:2x BST) to the ALGEBRAIC STRUCTURE of M_r (Q1); Q2 is adv-mixer-3, Q3 is adv-mixer-2
Branch adv-mixer; working HEAD d8eea5f7 (merge of build/master 04c4d9bc)
Byte-identity after the merge, git diff --quiet 017e70376489251e18564c0abce7e466e606c8b3 HEAD -- igneum-pow prints IDENTICAL: the whole crate is the frozen object. The branch was first cut from stale master 3f0afcd5 whose igneum-pow differed in 6 non-mixer files; those pre-merge runs were re-run on the merged tree and match (see below)
Harness attack-adv-mixer (merged) sha256 179b77a5bb1e2158004d4044de6d6622cf649ce6e8cc5e69623053e849651274
Harness attack-f4 census (stale, mixer-identical) sha256 d51df6caab338b0165485c3ce6d6379c0af7180bf2b72fc0a1531beabeaf2d22
Boxes igneum-build-2 (box 2) and 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)
Clock plan pushed 19:09 BST; first results in this report 19:40 BST (ask line 00:00 BST); times are TZ=Europe/London

Status board

Q Lane Method Known-failed shape Gate Result (numbers) Status
Q1 algebraic structure adv-mixer (this) fold probe + integral cube-sum degree test the probes are their own control affinity violations > 0, dead pairs = 0, key agreements = 0; degree saturates 1e6/1e6 affinity violations; 0/256 dead word pairs; 0/1e6 key-order agreements; algebraic degree >= 16 after 1 application, saturated (256/512 bits per cube-sum) after 2 PASS (BOUND: no shortcut, 8x stands)
Q2 round margin adv-mixer-3 (courtesy run here) diffusion avalanche census K=1..8, 2e6 states diffusion --plant weak (fired) full diffusion at K distinguisher reaches K=1 only; K=2..8 clean (0 holes, 0 strong, worst 4.5 to 4.9 sigma) BOUND (handed to adv-mixer-3)
Q3 weak draws adv-mixer-2 (courtesy run here) f4 census over 2^24 days plant alleq/mul1/mul1all/rc0/rcrk0 (all fired) any class >= 1.1x on a non-negligible fraction M1 cost 197 to 263, mean 231.1; best day a 1.17x smaller FPGA multiply datapath, 1 day in 2^24; 0 days DSP or wall-time gain; ROT-all-equal never seen BOUND+tail (handed to adv-mixer-2)

Q1: the algebraic structure of M_r (BOUND: no composition cheaper than 8x)

The question. The 8 keyed applications between two cache reads differ only in the round key rk. Can they be evaluated in fewer than 8x one application, by folding or commuting the multiply layer, by a surviving differential, linear or rotational property of the drawn double round, or by a low-degree algebraic form of the composition? Any gain is priced in ops per item against the chip model's 9,360 (hoisted, m = 8).

Two measurements, both on the frozen-identical binary (sha 179b77a5), day 20729.

Fold probe

Command (box 1):

nice -n 10 attack-adv-mixer fold --day 20729 --trials 1000000
Probe Result Reading
(a) GF(2) affinity of the 2-application map g 1,000,000 of 1,000,000 quadruples violate g(a)+g(b)+g(c)+g(a+b+c)=g(0) g is maximally far from affine; the two multiply layers do not fold through the double round
(b) dead (in_word, out_word) pairs over the full 8-application block 0 of 256 every output word depends on every input word; no separability to split on
(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 or commuted

Integral (algebraic-degree) cube-sum test

Command (box 1):

nice -n 10 attack-adv-mixer integral --day 20729 --apps 1 --dmax 16 --placements 4000 --threads 48
nice -n 10 attack-adv-mixer integral --day 20729 --apps 2 --dmax 16 --placements 4000 --threads 48

For each cube dimension d the harness XOR-sums the output over all 2^d flips of d random input bits, over 4000 random placements. A zero sum on every output bit would prove algebraic degree < d (a low-degree form a shortcut could exploit). The result:

Applications d = 1..16 Cube-sum nonzero fraction Mean output bits set per cube-sum Reading
1 every d 1.0000 at every d 172 to 236 of 512 degree >= 16 already after one application
2 every d 1.0000 at every d about 256 of 512 (half) saturated: two applications look like a random high-degree map up to degree 16

Verdict for Q1. No composition cheaper than 8x was found. The multiply layers of adjacent applications are separated by a nonlinear double round and do not merge (fold probe a). The drawn double round gives no commutation that would fold keys (fold probe c). The word-dependency is complete at 8 applications (fold probe b). The algebraic degree reaches at least 16 after a single application and saturates after two, so there is no low-degree algebraic or integral form of even one application, let alone the 8, that an attacker could batch. The 8 keyed applications cost 8x on this evidence. Priced against the chip model: 0 ops saved per item; the 9,360 ops per item stand. This is a BOUND, not a proof: the integral test reaches degree 16 (2^16 cube), and the fold probe is GF(2)-degree-1; an exact algebraic-degree measurement above 16 and a SAT or MILP algebraic form are owed work. One line on what a longer pass would add: it would pin the exact degree above 16 and rule out a medium-degree (17 to 40) relation the cube test at d = 16 cannot see; it would not change the no-fold bound.

Q2: the round margin (courtesy run; lane adv-mixer-3)

Command (box 2), per K in 1..8:

nice -n 10 attack-adv-mixer diffusion --day 20729 --apps K --states 2000000 --threads 32

Log: docs/analysis/cryptanalysis/logs/adv-mixer/diffusion-K1-8.log (copied off the box). Census band at 2e6 states is 8 sigma = 0.00283, so a per-cell bias below 0.28 percent is invisible; quoted with the result.

K applications Dependency holes Strong-bias cells (> 8 sigma) Worst cell Verdict
1 578 of 262144 111997 of 262144 1414 sigma distinguisher reaches K=1
2 0 0 4.8 sigma clean
3 0 0 4.6 sigma clean
4 0 0 4.7 sigma clean
5 0 0 4.9 sigma clean
6 0 0 4.6 sigma clean
7 0 0 4.5 sigma clean
8 0 0 4.8 sigma clean

The plant fired: the degenerate day (MUL all 1, RC all 0, ROT all 16) at K=1 gave 7894 holes and 236269 strong cells, worst 223.6 sigma. Reading: the strict-avalanche distinguisher reaches only 1 application. Full diffusion at 2 applications. Margin against the 8 between reads: 6 applications (8 minus 2). Margin against the 72 per item: 70. This is an avalanche census, not a trail search; a bias below 0.28 percent at K=2 is invisible. The differential, linear, rotational-XOR and SAT/MILP tightening is adv-mixer-3's lane; handed over.

Q3: weak parameter draws (courtesy run; lane adv-mixer-2)

Command (box 2):

nice -n 10 taskset -c 64-95 attack-f4 census --from 20729 --count 16777216 --threads 32

16,777,216 days (2^24), 4.4 s. Log: docs/analysis/cryptanalysis/logs/adv-mixer/census-2pow24.log (copied off the box).

The M1 metric is the per-day FPGA LUT datapath adder count, 64 + sum(NAF(MUL_i) - 1). Convention-free facts over 2^24 days:

Quantity Value
M1 cost range 197 (day 4819563) to 263 (day 15262713)
M1 cost mean, sd 231.1, 6.19
Best attacker day multiply datapath 197/231.1 = 0.853 of mean, a 1.17x smaller datapath, on 1 day in 2^24
Days with any M2 (DSP-bound) gain, k >= 2 0
Days with a ROT or RC wall-time gain 0 (bit-exact verifier; ROT is wiring, RC is inverters)
ROT all equal (the spec's untested worry) 0 of 2^24 (analytic 1 in 2.751e10 days)
MUL any = 1, MUL two equal, RC any = 0 0, 0, 0

Open cross-check for the Q3 lane owner: the census computes the over-1.1x count against its own measured median 231 (5476 days, 3.26e-4), while the analytic expect tool reports a reference median 221 and an over-1.1x fraction near 8.6e-7 (about 14.5 days in 2^24). The two references differ by 10 adders; the convention-free range above does not depend on the choice. Reconciling the median is handed to adv-mixer-2. Either way the best day is a 1.17x FPGA-datapath gain the chip model does not credit as hash rate (the chip is bound by the 8 cache reads and the fixed op count, not by one day's multiply-adder count), and a weak day is a public calendar.

Q1 deepening (from 20:4x BST): more days, all key pairs, exact relations, the address bits, a SAT model

Main's re-scope asked for the probes at full 32-bit width over more days and more rk pairs, a SAT model of two keyed applications for an algebraic relation, and a relation search in the s[0] line-index bits. Binary sha256 bbfab24075923083f0c9c8c4372a6dd22257c245bac75ba6fadbd60ab64ec803 (frozen-identical tree, same bytes on both boxes). Queue files tools/attack/adv-mixer/queue/11..14, claimed in /srv/builds/_adv/mixer/claims. Logs under /srv/builds/_adv-adv-mixer/logs on each box, copied into docs/analysis/cryptanalysis/logs/adv-mixer when done.

Exact affine-relation search at full width (linrel), queue 14, box 2

Command: attack-adv-mixer linrel --day 20729 --apps K --samples 8192 --days 16 for K in 1, 2, 8. Each sample is a GF(2) row over 1025 columns (512 input bits, 512 output bits, the constant). Rank 1025 means no exact affine relation a.x XOR b.y = c exists between the input and output bits of K applications; a chance survivor has odds 2^-(8192-1025) = 2^-7167.

Applications K Days (20729 to 20744) Rank Kernel dimension Reading
1 16 of 16 1025 0 no affine relation after one application
2 16 of 16 1025 0 no affine relation after two
8 16 of 16 1025 0 no affine relation across the full between-reads block

This is the decidable algebraic probe the brief's "algebraic form" question needs: at full width with the real day constants there is no linear or affine structure a chip could use to batch or predict the 8 applications.

The line-index bits of s[0] (lineindex), queue 13, box 1

Command: attack-adv-mixer lineindex --day 20729 --apps K --states 1000000 --threads 44. The 22 address bits (s[0] AND 2^22-1) against each of the 512 input bits; band 8 sigma = 0.004 at 1e6 states.

K Mean address-bit flip Holes / 11264 Strong cells / 11264 Worst cell Verdict
1 0.4275 70 6904 1000 sigma FINDING: address bits predictable after one application
2 0.500007 0 0 3.9 sigma clean
3 clean 0 0 inside band clean
4 running RUNNING

Reading for the chip: the line index a read depends on is not predictable before the second of the 8 applications completes, so a prefetch cannot hide more than 1 of the 8 applications behind the memory latency. The address-restricted exact relation search (linrel --addr, 8 days, K = 1 and 2) is in the same queue file and lands here.

Fold probes over 1024 days and all 71 adjacent key pairs (fold-sweep), queue 11, box 1

Command: attack-adv-mixer fold-sweep --day 20729 --days 1024 --trials 20000 --threads 44: probes (a) and (c) on every adjacent application pair (i, i+1) for i in 0..70 and probe (b) on the full block, per day. RUNNING; the row lands here.

Integral degree test over 32 days (integral), queue 12, box 2

Command: attack-adv-mixer integral --day 20729 --apps K --dmax 16 --placements 2000 --days 32 for K = 1, 2. RUNNING; the row lands here.

SAT model of two keyed applications (cnf), queue 14 then a solve on box 2

Command: attack-adv-mixer cnf --day 20729 --out .../adv-mixer-commute-20729.cnf. Bit-exact Tseitin encoding of M(M(x,rk1),rk2) and M(M(x,rk2),rk1) on a shared 512-variable input with the two outputs constrained equal: 105,652 variables, 361,188 clauses, 6.8 MB. SAT = a state on which the two key orders commute; UNSAT = a proof over all 2^512 states that fold probe (c) holds exactly. No solver reaches the box from crates.io (HTTP 403), so the harness marks the solve BLOCKED; the sibling lane adv-mixer-3 has cadical 3.0.1 built from source on box 2, and that binary is running on this instance under a one-hour cap at nice 10 (log sat-commute-20729.log). RUNNING; the row lands here. A timeout is the honest expected outcome: the instance is a preimage-shaped search on a 2^512 space, and a plain timeout is a bound on solver reach, not evidence either way.

Confirmation across the stale and frozen trees

The branch was first cut from stale master 3f0afcd5. Its igneum-pow differed from the frozen object in six non-mixer files (accept.rs, emit.rs, generator.rs, packcheck.rs, tests/mixer.rs, tests/recheck.rs); memhard.rs, seed.rs, bind.rs, derive.rs and the Cargo pin were byte-identical. After git merge build/master the whole crate is byte-identical to 017e7037 (IDENTICAL). The Q1 fold and the K=1 and K=2 diffusion spot-checks were re-run on the merged binary and match the stale runs within sampling noise (K=1 holes 551 merged vs 578 stale, K=2 clean on both). So the stale-tree Q2 and Q3 numbers coincide with the frozen object. The self-check asserts the spec 1.8.4 genesis ROT, MUL and RC vectors on every run.

Box-hours and pod-hours spent

No GPU pods used: pod-hours 0. All CPU, nice 10, both boxes.

Step Box Wall
Builds (adv-mixer x2, f4 x2) box 1 and 2 about 1.5 min total
f4 five plant firings box 2 about 3 min
f4 census 2^24 box 2 4.4 s
f4 expect analytic tail box 1 a few min (log later wiped by a box re-sync; numbers captured)
Q2 diffusion sweep K=1..8, 2e6 states box 2 11 min
Q1 fold + integral + spot-checks box 1 1 min 44 s

Total about 0.4 box-hours of the 8-hour first-results budget (ask line 16). Pod-hours 0.

Rule-change timeline (so the box-hours stay honest)

Time (BST) Change Effect on this lane
19:1x both boxes, nice 10, drop the single band adopted; runs moved off a single 32-core band
19:2x three-lane re-scope: this lane is Q1 only Q2 and Q3 kept as courtesy runs, attributed
19:3x merge build/master, re-prove igneum-pow identical done, IDENTICAL; pre-merge runs re-run and matched
19:3x drop SIGSTOP yield, run on cores 8-95 only no yield was ever added; run-box.sh band set to 8-95; direct nohup runs at nice 10