igneum/docs/analysis/cryptanalysis/report-mixer.md
igneum-labs 56a0bb50e4 adv-mixer: kill ledger (cadical, sibling 07) under main's lease rule; SAT row BLOCKED on the lease
Internal adversarial pass, not an independent review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 19:21:41 +00:00

23 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 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)
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 probes (1024 days x 71 key pairs), exact GF(2) affine-relation kernel at full width, integral cube-sum degree (32 days), the 22 line-index bits, a SAT model of two applications the probes are their own control; the diffusion plant fired affinity violations > 0, dead pairs = 0, key agreements = 0, kernel = 0, degree saturates 1,454,080,000/1,454,080,000 affinity violations, 0 dead word pairs, 0 key-order agreements over 1024 days; kernel 0 at K = 1, 2, 8 on 16 days (full width) and on the address bits; degree >= 16 after 1 application and saturated after 2 on 32 days; address bits clean from K = 2 PASS (BOUND: no composition cheaper than 8x; 9,360 ops per item stand)
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 19: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
3, 4, 5, 6, 7 (queue 15) 4 of 4 each (20729 to 20732) 1025 0 no affine relation at any intermediate count
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 0.5000 0 0 inside band clean
4 0.499998 0 0 3.8 sigma clean
5 0.5000 0 0 4.3 sigma clean (queue 15)
6 0.5000 0 0 4.7 sigma clean (queue 15)
7 0.5000 0 0 3.6 sigma clean (queue 15)
8 0.5000 0 0 3.7 sigma clean (queue 15)

The address bits are clean at every application count from 2 to 8, the full between-reads block.

Address-restricted exact relation search, linrel --addr --apps K --samples 8192 --days 8 (535 columns: 512 input bits, the 22 address bits, the constant):

K Days (20729 to 20736) Rank Kernel Reading
1 8 of 8 535 0 no affine relation to the address bits even after one application
2 8 of 8 535 0 none after two

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 K=1 predictability is incomplete diffusion (holes and strong cells), not a linear leak: no affine relation between the input and the address bits exists even at K=1.

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. 1024 days x 71 pairs x 20,000 trials = 1,454,080,000 pair-trials. Wall 84 s on box 1 at load about 400.

Probe Result over 1024 days and 71 pairs Reading
(a) affinity violations 1,454,080,000 of 1,454,080,000; lowest per-pair violation fraction 1.000000 no adjacent pair of applications is affine on any day
(b) dead word pairs, summed over 1024 days 0 complete word dependency every day
(c) key-order agreements 0 of 1,454,080,000 no key pair commutes on any day

Verdict: the fold bound of the first report holds at every one of the 71 between-application seams, on every one of 1024 consecutive chain days (about 2.8 years of calendar). No fold, no commutation, no separability.

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. 32 consecutive chain days (20729 to 20760), cube dimensions d = 1..16, 2000 placements each: 512 (day, d) rows per K. Wall about 4 min on box 2 at load about 500.

Applications K Rows with every cube-sum nonzero Output bits set per cube-sum (min to max over days) Reading
1 511 of 512 (the one exception: day 20730, d = 4, 1999 of 2000 placements) 156 to 245 of 512 degree >= 16 on every day; the single zero cube-sum in 1,024,000 placements is a chance zero, not a relation (a relation would zero every placement)
2 512 of 512 255.5 to 256.7 of 512 saturated at the random-map value of 256 on every day

Verdict: the degree floor of the first report (>= 16 after one application, saturated after two) holds on 32 consecutive days. No day has a low-degree algebraic form of the applications. Queue 15 added the full 8-application block on 4 days (d = 1..14, 2000 placements): 56 of 56 rows at fraction 1.0000, 255.6 to 256.4 bits set, saturated.

Queue 15, the per-K fill and a cross-box replication (box 2)

Queue file 15 re-ran the 1024-day fold-sweep first (its header slice carried that line over from file 11), which makes a replication on the other box: 1,454,080,000 of 1,454,080,000 affinity violations, 0 dead word pairs, 0 key-order agreements, identical to box 1 (log fold-sweep-1024d-box2-replication.log). Then linrel at K = 3..7, integral at K = 8 and lineindex at K = 5..8, all folded into the tables above. Wall 2 min 52 s at load about 500.

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). KILLED at 20:20 BST under main's rule that every hand-started sweep stops and re-queues through lease pool: cadical ran 1,749 s wall on one thread (162 MB peak) and reached neither SAT nor UNSAT, which was the expected outcome inside the hour: the instance is a preimage-shaped search on a 2^512 space, so a cap-out bounds solver reach only and is no evidence either way. The CNF stays on box 2 at /srv/builds/_adv-adv-mixer/logs/adv-mixer-commute-20729.cnf (6.8 MB) for a re-queue through lease pool once that subcommand exists (at 20:21 BST lease --help offered lease cores and lease status only). Status BLOCKED on the lease, not on the model.

Q1 consolidated verdict (internal adversarial pass, not an independent review)

Across every probe this lane ran at full 32-bit width with the real day constants, no composition of the 8 keyed applications between two cache reads costs less than 8 times one application.

Candidate shortcut from the brief Evidence Status
The multiply layer folds or commutes across applications (only rk changes) adjacent-pair affinity violated on 1,454,080,000 of 1,454,080,000 trials over 1024 days and all 71 seams; key-order commutation 0 of 1,454,080,000 ruled out at the probe's reach
An exact linear or affine relation across the composition GF(2) kernel 0 (rank 1025 of 1025) at K = 1, 2 and 8 on 16 days; kernel 0 on the 22 address bits at K = 1 and 2 ruled out exactly (chance survivor 2^-7167)
A low-degree algebraic form to batch the applications cube-sums nonzero at every d up to 16 after one application, saturated at 256 of 512 bits after two, on 32 days ruled out below degree 16; above 16 is owed
A predictable line index that lets a chip prefetch the read early address bits predictable only after 1 application (incomplete diffusion, no linear leak); clean from K = 2 at most 1 of 8 applications hides behind the memory latency
An exact commutation witness (SAT model) 105,652-variable, 361,188-clause instance; cadical 3.0.1 running under a 1 h cap pending; a timeout bounds solver reach only

Priced against the chip model: 0 ops saved per item; 9,360 ops per item stand. What a longer pass would add: an exact algebraic-degree measurement above 16 (the cube test stops at 2^16 points) and a solver run long enough to decide the commutation instance; neither would move the fold or the affine bound, which are exact or exhaustive at their reach. The probes cover 1024 consecutive chain days from genesis, about 2.8 years of the public calendar.

Sibling queue files run by this lane

Per main's rule (claim the next unclaimed sibling file in name order once the own queue is empty), this lane claimed and ran the following. The results belong to the owning lane and are not interpreted here.

File Owner Claimed Run on Result
07-adv-mixer-3-days.sh adv-mixer-3 19:54 BST box 1, nice 10, cores 8-95 KILLED 20:20 BST under main's rule after 6 of 56 steps (index day 20730 k = 2, 3, 4, 8; sac day 20730 k = 2, 3; every one "uniform within the band" or clean per the owner's grep); my claim released so the owner or any lane re-queues it through the lease; the owner's logs stay in /srv/builds/_adv-mixer-3/logs

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

| Q1 deepening queue 11 fold-sweep 1024 days | box 1 | 84 s | | Q1 deepening queue 12 integral 32 days | box 2 | 4 min 21 s | | Q1 deepening queue 13 lineindex + addr linrel | box 1 | 77 s | | Q1 deepening queue 14 linrel + cnf | box 2 | 10 s | | Q1 deepening queue 15 per-K fill + fold-sweep replication | box 2 | 2 min 52 s | | cadical on the commutation CNF | box 2 | one thread, capped at 1 h | | Sibling 07-adv-mixer-3-days.sh (owner adv-mixer-3) | box 1 | running |

Total about 0.6 box-hours of the 8-hour first-results budget (ask line 16), plus up to 1 core-hour of cadical. Pod-hours 0. Both boxes ran at load 240 to 555 on 96 cores during the deepening (sibling sweeps, not builds), so wall times above are under heavy sharing.

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
20:20 main: every hand-started sweep is killed by its pid file now and re-queued through lease pool; release builds and the class v5 suites outrank sweeps killed cadical (box 2, 29 min lost, no result) and sibling 07 (box 1, 6 of 56 steps done, claim released); nothing started since; lease pool absent at 20:21, so the re-queue waits on it

Kill ledger (main's rule, 20:20 BST)

Process Box pid-file Killed (UTC) Lost Re-queue
cadical on adv-mixer-commute-20729.cnf 2 sat-commute-20729.log.pid 19:20:41Z 1,749 s of a 3,600 s cap, no SAT or UNSAT through lease pool when it exists; the CNF is kept
07-adv-mixer-3-days.sh (owner adv-mixer-3) 1 queue-07-adv-mixer-3-days.sh.log.pid 19:20:47Z 50 of 56 steps unrun; the 6 done are in the owner's logs claim released; owner or next free lane

Both kills were the wrapper pid from the pid file plus its descendants (one cadical, one adv-mixer-3), TERM then KILL; no process of this lane remained on either box afterwards.