Skip to content

36 — ML-ADSA Cryptanalysis Review Packet (for independent reviewers)

What this is. A self-contained packet inviting and structuring independent cryptanalysis of ML-ADSA. Machine-checked proofs establish the reductions as modeled; they do not replace human cryptanalysis, which we regard as the decisive gate before any deployment or standardization. This packet tells a reviewer exactly what is claimed, what is proven and how, what is not proven, where to attack, and what would falsify the scheme. We want to be wrong early if we are wrong.

Compiled 2026-06-11. Companion to: spec docs/30, verification dossier docs/31, research paper paper/ml-adsa.md, consistency audit docs/35. Reproduce numbers with formal/count-artifacts.sh.


0. TL;DR for the reviewer

ML-ADSA aggregates N ML-DSA-87 (FIPS-204) signatures on a common message into one byte-exact ML-DSA-87 signature that the unmodified FIPS-204 verifier accepts under a summed key, by summing Fiat–Shamir responses over a shared challenge: z* = Σ zᵢ, t* = Σ tᵢ, c̃* = H(μ ‖ HighBits(Σ wᵢ)). No trusted setup, no trapdoor, no proof system, no aggregator. Security is claimed to reduce to exactly ML-DSA's assumptions (MLWE, SelfTargetMSIS, Module-SIS) at Category 5, in ROM and QROM.

The five things we most want attacked (details in §6): 1. The summed-response forgery surface — does binding c̃* to the full W* really stop an adversary from crafting a verifying (c̃*, z*, h*) from chosen/observed partial contributions? 2. Concurrent / ROS for the interactive commit-reveal variant (the deployed scheme is non-interactive and atomic — see §6.2; the interactive variant is where the genuine open question lives). 3. Deterministic-nonce safety under faults / one-time-discipline failure (key recovery on nonce reuse). 4. The norm budget / cohort cap — is the secure N honestly stated, and is the rejection-free (Construction B) leakage really negligible, not just small? 5. The perfect-HVZK kernel (masking_ok) — the one cryptographic primitive left axiomatic (shared with all ML-DSA proofs); is our use of it sound for the aggregate?


1. Construction (concise; normative spec is docs/30)

Parameters: ML-DSA-87, q=8380417, n=256, (k,ℓ)=(8,7), η=2, τ=60, β=120, γ1=2¹⁹, γ2=(q−1)/32, d=13, ω=75, ζ=1753; pk=2592 B, sig=4627 B; Category 5.

Each signer i has an ordinary ML-DSA key (ρ shared, t_i = A·s1_i + s2_i), A = ExpandA(ρ). To aggregate on common message m (domain/context ctx): 1. each signer derives a deterministic nonce y_i = DeriveNonce(seed_i, C) (PRF; C the content label) and commitment w_i = A·y_i; 2. the public combiner forms W* = Σ w_i, pk* = (ρ, HighBits(Σ t_i)), μ = H(H(pk*) ‖ ctx ‖ m), c̃* = H(μ ‖ HighBits(W*)), c = SampleInBall(c̃*); 3. each signer returns z_i = y_i + c·s1_i (with the FIPS-204 bound/hint discipline); the combiner sets z* = Σ z_i, builds the hint h* so UseHint(h*, A·z* − c·t1*·2^d) = HighBits(W*); 4. output σ* = (c̃*, z*, h*). Verify(pk*, m, σ*) is the unmodified FIPS-204 verifier.

Layers (Construction F): L1 per-content key/nonce refresh (PRF) for many-time security; L2 epoch Merkle key-tree for non-equivocation/accountability; L3 registry + proof-of-possession for rogue-key resistance; a one-time discipline (N1/N2) on (signer, C); a ZKP-free decoy mechanism for signer-set privacy. Two masking regimes: A (bit-zero / per-signer rejection, perfect HVZK, small cohort) and B (wide masks, rejection-free, computational HVZK, the regime used for the N=1000 demos).


2. Threat model & security goals

  • Adversary: chooses messages, sees a signing oracle (classical), controls all but one honest signer (with valid PoP), and — in QROM — has superposition access to the Fiat–Shamir hash. Static corruption.
  • Goals claimed: EUF-CMA and SUF-CMA of the aggregate (forgery = a verifying σ* on an un-queried m with a PoP-valid cohort); equivalence-class hardness (producing any verifying σ* for fixed (pk*, m) is as hard as one ML-DSA forgery); many-time (advantage not degrading beyond the standard multi-instance union with the number of contents); concurrent security; rogue-key resistance; non-equivocation; signer-set privacy (decoys, computational/Module-LWE).
  • Out of scope of the proofs (must be assessed by review/engineering): side-channels and fault injection (deterministic-nonce exposure, shared with EdDSA); the one-time state being correctly enforced; parameter/cohort operational limits.

3. Claims, reductions, and assumptions (what to check)

# Claim Reduction (file:lemma) Bottoms out in
C1 Aggregate correctness (the summed σ* verifies) ml_adsa_identity.v (algebraic identity, N=2/10/100/1000) + Go/CIRCL byte-accept — (functional)
C2 EUF-CMA, tight, no N-loss (Constr. A) ml_adsa_euf.ec : msufcma_uncondadv_mlwe + Pr[STMSIS] MLWE + SelfTargetMSIS, + masking_ok
C3 SUF-CMA ml_adsa_suf.ec : sufcma_uncond MLWE + StrongSIS (STMSIS ∨ M-SIS)
C4 Equivalence-class hardness (ROM+QROM) ml_adsa_euf.ec : equiv_class_guess_bound; ml_adsa_qrom.ec : qrom_equiv_class_uncond = C2/QROM (definitional: hitting the verify-set is forging)
C5 Many-time (refresh) ml_adsa_F_manytime.ec : F_manytime_boundadv_prf + Q·eps_content + PRF security
C6 Rogue-key → single signer ml_adsa_rogue_proof.ec : rogue_reduces_to_single PoP knowledge-soundness (rogue_collapse)
C7 No-new-power → Module-SIS ml_adsa_nnp_proof.ec : new_power_reduces_to_sis extract_or_break
C8 QROM (Constr. A tight; B lossy/derived) ml_adsa_qrom.ec : qrom_eufcma_uncond / qrom_eufcma_lossy; GHHM21 derived in ml_adsa_qrom_ghhm.ec MLWE + QROM-STMSIS + masking_ok; B: + Thm 6.1 distinct-query, Ynontriv
C9 Concurrent (deployed atomic) ml_adsa_euf.ec : concurrent_atomic_uncond (= msufcma_uncond; no open sessions) = C2
C10 Concurrent (interactive variant) ml_adsa_F_concurrent.ecROS-defense core + HVZK hop mechanized; full reduction SUPPLIED A-regularity + commit-binding; needs cryptanalysis
C11 Hiding / signer-set privacy ml_adsa_F_hiding.ec : commit_hiding ≤ 2·adv_mlwe Module-LWE
C12 NTT is a ring isomorphism ml_adsa_ntt.ec (convolution) + ml_adsa_ntt_inv.ec (CRT inversion) — (first principles)
C13 Masking / rounding / Montgomery encoding / NTT loop (CT = DFT) ml_adsa_masking.ec, ml_adsa_rounding.ec, ml_adsa_montgomery.ec, ml_adsa_ntt_ct.ec (ct_step/ct_butterfly/ct_correct) — (first principles; integer arithmetic)

Assumption base (the whole thing): MLWE, Module-SIS, SelfTargetMSIS (ML-DSA's own); a PRF; a collision-resistant/injective hash (SHAKE-256). No ROS, AGM, OMDL, trapdoor, SNARK/STARK, or trusted setup. The aggregate key pk* is itself a bona-fide ML-DSA key (a sum of MLWE samples is an MLWE sample).


4. What is machine-checked vs. what needs human review

Machine-checked (43 prover artifacts, 274 lemmas = 242 EasyCrypt + 32 Coq, 53/53 genuineness, 6 Gobra; formal/count-artifacts.sh). The reductions in §3, the NTT ring-isomorphism and the masking/rounding/Montgomery integer kernels, and 6 Go-code structural theorems (Gobra). Every proof is admit-free and accompanied by a genuineness check (weaken its named primitive → the proof must break).

NOT established by the proofs — for the reviewer: 1. masking_ok (= masking_perfect) — the perfect-HVZK distribution identity sign1 sk m = simsig pk m is an axiom (its consequences are machine-checked; the in-range change-of-variables is proven in ml_adsa_masking.ec, but the full bit-level rejection-sampling identity is the shared-with-all-ML-DSA lattice boundary). Is its use for the aggregate (summed, shared-challenge) sound? 2. The interactive-variant concurrent reduction (C10) — the "secret-free concurrent game ≤ single-session bound" step is the standard FS argument given an unbiasable challenge; not separately mechanized. The deployed scheme avoids this (C9), but if an interactive variant is used, this is open. 3. Soundness of the assumptions themselves at Category 5 (MLWE/STMSIS/M-SIS concrete hardness) — we inherit ML-DSA's parameters; we do not re-derive their security. 4. Side-channel / fault resistance of the deterministic construction. Constant-time posture is now audited and partly machine-backed (docs/34 §2/§2a/§2b): modQ/cabs are branchless; the secret arithmetic path is straight-line; DeriveNonce is rejection-free (Construction B); SampleInBall/ ExpandA time only on public data; and the NTT's data-independent control flow / memory-access pattern is machine-checked (ml_adsa_ntt_imp.ec: every loop bound and index is counter-derived, never value-derived). Residual for review: Construction-A rejection attempt-count (leaks attempts, not the key), %-by-q on exotic ISAs, fault injection on the deterministic nonce (§6.3), and an automated dudect/microarchitectural pass (standardization deliverable). 5. In-place NTT array loop (iterative realization) — the arithmetic kernels (modQ/Montgomery, ml_adsa_montgomery.ec), the full multi-level Cooley–Tukey transform = DFT (ml_adsa_ntt_ct.ec: ct_step, ct_butterfly, ct_correct), and the flat-array factor-tree transform (ml_adsa_ntt_crt.ec: polyL_cat = array low/high split, polyL_bfly = array butterfly write-back, ct_correct's sibling ntt_tree_correct = the flat take/drop/bfly/concat tree computes the per-root evaluation vector for any well-formed twiddle schedule) are now source-proved over Z_q — as are both items that were previously open: (a) the iterative, level-by-level loop computes the same transform as the recursion (forest_step = one length-level; forest_step_inv = one level preserves the transform; forest_loop_correct = once all blocks are size-1 leaves the flat array is ntt_tree t a; i.e. BFS = DFS), and (b) FIPS-204's bit-reversed ζ=1753 schedule satisfies wf (negtree_wf; negtree_computes_eval gives the per-root evaluations for the real schedule). Termination is proved (tdepth_negtree + forest_iter_leaves), so fips_ntt_loop is unconditional: size l (= 8) levels on the FIPS-204 schedule yield the evaluation vector. The absolute-index arithmetic is machine-checked too (jloop_eq: the Go inner loop output[j]=a[j]+z·a[j+m], output[j+m]=a[j]−z·a[j+m] equals the block butterfly; jloop_forest ties it to one forest_step). The bit-reversed precompute is machine-checked too (brv8_twiddle_rec: brv8 = bsrev 8 satisfies negtree's recursion brv8 1 = 2⁷, brv8(2k)=brv8 k/2, brv8(2k+1)=brv8 k/2+2⁷, so zetas[k]=ζ^brv8(k) enumerates the tree's twiddles in BFS order). The sole residual is the literal while-loop syntax / mutable-array representation (an EasyCrypt proc over a Go [256]int64 vs the list-block model) — the same model↔code conformance all of surface B covers, established by the byte-exact KATs against CIRCL + go-qrllib; no algorithmic, index, termination, or twiddle-schedule content is left. (NB: the NTT is implementation-conformance, not a security assumption — no reduction invokes it.)


5. Known non-issues (so you don't spend time here)

These were probed (internally and by an adversarial review pass, docs/35 §6) and are believed resolved or correctly scoped — but re-checking them is welcome: - "Summing responses overflows the norm bound." True worst-case; the construction relies on concentration (Irwin–Hall) and a stated cohort cap; the c·t0* correction is N-independent (it rounds the sum t*, not the sum of roundings), so the earlier "LMFA" cross-term failure does not recur. See §6.4 to attack the concrete bound. - "HighBits(Σ w) ≠ Σ HighBits(w) breaks the hint." The signer computes the hint from the actual summed w* and re-checks UseHint == HighBits(w*) before emitting; correctness is enforced at signing time and byte-validated. (Attack the hint forgery surface in §6.1, not the honest-correctness path.) - "Equivalence-class hardness is circular." It is definitional by design (hitting the verify-set = the EUF-CMA win); we present it as a clarification, not a deep theorem. - "Many-time is not really Q-independent." Correct — the bound is adv_prf + Q·eps_content (negligible, the standard multi-instance union); the leakage-accumulation wall is what refresh removes. - Counts/claims drift. Reconciled and locked to count-artifacts.sh (docs/35).


6. Priority attack surfaces (please probe these)

6.1 The summed-response / shared-challenge forgery surface (HIGHEST PRIORITY)

The crux: a forger sees honest (w_i, z_i) contributions (or chooses adversarial cohort members) and must produce a verifying σ* = (c̃*, z*, h*) for an un-queried m. We claim binding c̃* = H(μ‖HighBits(W*)) to the entire W* collapses this to a single ML-DSA forgery (eq_exact: a verifying forgery relative to the RO is a SelfTargetMSIS witness). Attack ideas to try: malleability of (z*, h*) keeping c̃* fixed; choosing adversarial t_j/w_j to shift pk*/W* favorably (note: pk* is bound via the epoch key-tree + PoP, docs/29 §4.6b); exploiting the hint h* degrees of freedom; sub-aggregate substitution.

6.2 Concurrent / ROS — the interactive variant (C10)

The deployed scheme is non-interactive and atomic (deterministic one-time signing; one content = one challenge = one release), so it has no open sessions to interleave and concurrent security = standard multi-query EUF-CMA (C9, concurrent_atomic_uncond). But any interactive commit-reveal variant lives in the family Drijvers et al. (S&P 2019) showed is not secured by nonce-commitment alone. We claim A-regularity + commit-binding give an unbiasable challenge; we have not mechanized the full concurrent→single-session reduction. Please attack: can a rushing adversary across k interactive sessions solve a ROS/Wagner system despite the unbiasability claim? Is the deployed atomic model truly free of an interleaving surface (e.g., via the gossip "pool" of published w_i, docs/35 §6 / docs/29)?

6.3 Deterministic-nonce safety under faults / state failure

Nonces are PRF(seed, C) (no randomness). Reuse of (seed, C) under two different challenges leaks s1 = (z−z')·(c−c')⁻¹ (the classic attack; and deterministic schemes are fault-attack-prone, cf. Poddebniak et al., Groot-Bruinderink–Pessl). We rely on a durable one-time guard (write-ahead + fsync, onetime_durable.go) + epoch non-equivocation. Please attack: restart/rollback/replay windows; a single equivocating signer or content-key collision inducing cohort-wide reuse; fault models that bypass the guard.

6.4 Norm budget, cohort cap, and Construction-B leakage

For the rejection-free Construction B (σ = 12β = 1440) used in the scaling demos, z* is an Irwin–Hall sum; we claim it stays under γ1−β for the stated cohort and that leakage is negligible (computational HVZK, ml_adsa_regimes.ec: narrow regime → adv_mask, argued ≤ adv_mlwe). Please attack: is the secure N honestly bounded? Is the per-content leakage of the summed z* (whose mean tracks c·s1*) really negligible per fresh key (one-time refresh), or does any cross-content correlation accumulate? Is adv_mask ≤ adv_mlwe justified at these parameters?

6.5 The perfect-HVZK kernel (masking_ok) for the aggregate

masking_ok is the one axiomatic cryptographic primitive (its consequences are machine-checked classically and quantumly; the change-of-variables is proven). It is ML-DSA's own rejection-sampling HVZK property. Please check: is applying it to the summed, shared-challenge response distribution (not a single signer's) sound — does the rejection event correlation under a shared c invalidate the per-coefficient uniformity argument?

6.6 Other surfaces

Rogue-key/PoP soundness (rogue_collapse); epoch-tree non-equivocation binding; decoy hiding reduction (commit_hiding); QROM-B adaptive-reprogramming derivation (ghhm.ec, Thm 6.1 + Ynontriv as standing inputs); the SelfTargetMSIS extraction tightness (eq_exact).

6.7 Public-wᵢ key recoverability and the message-binding firewall (internal pentest #3)

Because non-interactive aggregation must publish the full wᵢ = A·yᵢ (HighBits is non-additive, so the shared challenge c̃* = H(μ‖HighBits(W*)) needs the entire Σwᵢ, not just per-signer high bits), and A is tall (k ≥ ℓ; full column rank per NTT slot), the per-signer nonce is recoverable by F_q linear algebra, yᵢ = A⁺wᵢ — this is not Module-SIS-hidden. Confirmed empirically: recover_nonce_test.go recovers yᵢ exactly on the real ExpandA (256/256 slots, 0 mismatches). With yᵢ and the public zᵢ the one-time s1ᵢ follows, hence the entire per-content aggregate key pk*_C is reconstructable once a content is signed (pentest_test.go Attack 3 mounts the full recovery and forges an arbitrary message under the reconstructed pk*_C with the real SignP).

This is not a break of the deployed scheme, and the reason is two distinct, both load-bearing mitigations — please pressure-test both:

  1. Per-message refresh firewall (cross-content secrecy). Each content uses a fresh, independent PRF-derived key (ContentKeyDerive); recovering a spent content's key gives nothing toward any never-signed content (pentest_test.go Attack 4: signed- vs fresh-content keys are independent at the 1/q noise floor). The recovered material is confined to keys that are already public-after-use (the three-tier secrecy model: permanent / one-time / public). The lattice hardness lives in the clean, un-queried target, not in the exposed transcript — machine-checked in ml_adsa_F_open.ec (deployed_open_uncond: Pr[forge] ≤ adv_prf + Q·(adv_mlwe + STMSIS)).

  2. Message binding (single-purpose pk*_C). The recovered pk*_C lets an attacker forge any message under that one key — so it is only safe if pk*_C is bound to exactly one message. The default consensus path binds the payload into the signed message; the fix (harden_binding.go) additionally binds the payload into the content-key derivation itself (MsgBoundContent / AggregateFBound), making pk*_C a deterministic function of the message: pk*(m0) ≠ pk*(m1) under the same base label. A key recovered for a spent payload is then not the cohort-expected key for any other payload, so the cross-payload forgery never transfers. BindingGuard adds an operational refuse-second-message guard (defense-in-depth on the honest path). Tested: harden_binding_test.go (TestBinding_ClosesRecoveredKeyForgery, TestBindingGuard_RefusesSecondMessage) and the re-pentest pentest_test.go Attack 9 (TestPentest_BoundConstruction_RecoveredKeyDoesNotTransfer).

Please attack: a payload-independent content label (the …Labeled convenience path) leaves pk*_C multi-purpose — is the binding obligation always met by every deployment path that releases a wᵢ? Can a verifier be tricked into accepting a recovered pk* as the cohort key for a payload it was not derived for (e.g. via opening/part-root manipulation, ProvenanceVerifyF)? Does any value derived from the exposed yᵢ/s1ᵢ of a spent key leak across the PRF refresh boundary?

6.8 Internal pentest suite (go-mladsa/pentest_test.go) — 15 mounted attacks

Each attack mounts a concrete adversary and asserts the scheme's response (a finding is logged explicitly, never hidden). The suite is the running, regression-guarded record of what has been tried:

# Attack Result
1 Nonce reuse ⇒ key recovery (primitive s1=(z−z')/(c−c')) succeeds at primitive level ⇒ one-time discipline is load-bearing
2 Force reuse through the protocol (sign a (member,content) twice) defendedOneTimeGuard refuses
3 Recovered-key forgery under a reused pk*_C (payload-independent label) contained — harmless iff C binds the message + one-time (→ §6.7)
4 Cross-content (fresh-message) forgery from a spent key defended — PRF refresh firewall (keys independent at the 1/q floor)
5 Aggregate malleability (tamper z*/h*, keep c̃*) defended — FIPS-204 verifier rejects (SUF)
6 Rogue key with a tampered PoP defendedVerifyPoP/BuildRegistry reject
7 Decoy / weight-0 injection defended — registry weight 0 / FilterRecognized
8 Malformed input (empty/short/long/junk) defended — length guards, no panic, no accept
9 Recovered-key forgery vs the bound construction (full SignP) defendedpk*_C single-purpose; doesn't transfer (→ §6.7)
10 Cross-context replay (ctx="QRL""QRL:bridge"/"ZOND") defendedctx bound into mu
11 Merkle proof forgery (wrong index / tampered path / non-member / leaf-node confusion) defended0x00/0x01 domain separation
12 Metadata tamper (part_root/reg_root/epoch/payload) defended — all bound into the signed message
13 Duplicate-participant weight inflation hardenedAggregateF now dedups by ID (dedupMembersByID)
14 PRF domain-separation collision (F.key vs F.nonce, cross-content) defended — length-prefixed, fixed-width index
15 Sub-aggregate / response splice across cohorts/messages defended — shared-challenge FS binding (not freely mergeable)

Hardening from this round (#13). A duplicated participant entry (caller error or a malicious relay double-listing a member) previously double-counted toward t*, z*, part_root, and the tally if no one-time guard was supplied. AggregateF now canonically sorts and dedups by ID, so [m,m] == [m] — no weight inflation, and a single duplicate entry does not abort the aggregation (no liveness foot-gun).

Known liveness limitation (safety holds; please probe). The per-signer nonce commitment wᵢ = A·yᵢ is published but not separately committed in the epoch key-tree (only the key tᵢ is). In the decentralized combine (CombineFromPublic), a Byzantine participant can therefore publish a deviating wᵢ (e.g. a large nonce) that inflates ‖z*‖ ≥ γ1−β or the hint weight past Ω, forcing the cohort to abstain — a liveness DoS, not a forgery (unforgeability/SUF are unaffected; the standalone AggregateF derives all nonces honestly so the attack lives only in the open-broadcast path). Mitigation is protocol-level: the optimistic/fraud-audit path identifies the non-conforming contributor and re-aggregates without it. Please attack: can a Byzantine wᵢ do worse than force abstention — e.g. bias c* to a forgery-favorable value despite the full-W* binding (§6.1), or evade exclusion?


7. Reproduction

# proofs (29 EasyCrypt/Coq artifacts + tallies)
cd formal && zsh check-all.sh          # → ALL GREEN (28 classical + 5 quantum + 5 Coq)
zsh count-artifacts.sh                  # → 43 artifacts, 274 lemmas (242 EC + 32 Coq), 53/53, 6 Gobra
zsh genuineness.sh                      # → ALL 35 GENUINENESS CHECKS PASS (weaken a primitive ⇒ proof breaks)
cd gobra && zsh run.sh                  # → 6 Gobra theorems, "Gobra found 0 errors"
# implementation conformance (byte-exact vs two independent FIPS-204 verifiers)
cd go-mladsa && go test ./...           # CIRCL + theQRL/go-qrllib byte-accept; KATs; rotation KATs
# live decentralized demos
cmd/mladsa-devnet | mladsa-hieragg | mladsa-epochnet   # order/grouping independence, equivocator exclusion
Toolchains: EasyCrypt (opam switch discus-verified), EasyPQC (easypqc, via ec-quantum.sh), Coq/Rocq, Gobra (Docker ghcr.io/viperproject/gobra). See formal/README.md.


8. Falsification criteria (what would break ML-ADSA)

A reviewer would falsify a core claim by exhibiting any of: - an EUF-CMA / SUF-CMA forgery (a verifying σ* on an un-queried m with PoP-valid cohort) that does not yield an MLWE / SelfTargetMSIS / Module-SIS solution — i.e. a forgery strategy bypassing the reduction; - a concurrent (ROS/Drijvers-style) forgery against the interactive variant under honestly-followed commit-reveal, or a demonstration that the deployed atomic model in fact exposes an interleaving surface; - a nonce-reuse / fault path that recovers s1 without violating the stated one-time discipline; - a recovered-key (public-wᵢ) forgery that transfers across messages despite the message binding of §6.7 — i.e. a pk*_C reconstructed for one signed payload that a verifier accepts as the cohort key for a different payload (or any leakage that crosses the per-content PRF refresh boundary); - a parameter regime where the summed aggregate verifies but leaks the secret beyond ML-DSA's own leakage (breaking the "no assumption weaker than ML-DSA" claim); - a flaw in masking_ok's application to the aggregate (the per-coefficient uniformity failing under the shared challenge); - any proof artifact that does not actually compile, or a genuineness check that passes when it should break.

A finding that an axiom/primitive is unrealistic (e.g., masking_ok, rogue_collapse, Thm 6.1) is equally valuable — those are exactly the trust anchors we want pressure-tested.


9. Submitting findings (responsible disclosure)

This is a defensive publication; cryptanalysis is explicitly invited and credited. Please report: - to the public site's issue channel / the contact in docs/32 (publication & NIST roadmap), with a clear PoC or proof sketch and the affected claim ID (C1–C13) and surface (§6.x); - for a break, please allow coordinated disclosure before public release; for modeling/assumption feedback, open discussion is welcome immediately.

We commit to publishing confirmed findings and corrections in docs/35 (consistency ledger) and the paper's errata, with attribution. The goal is to be wrong early if we are wrong.