arborist/docs/soft-hash-channel-t3-bound.md
russell@unturf.com de997f7be3
docs/T3 bound + tests/anchor_prg: apply fox's testing patterns
Two related cleanups in one commit, both surfaced by reading fox's
test_t3_bound_calculator.py (51 tests for my T3 calculator):

1. Refresh stale §7 numbers in the T3 bound doc
=================================================

fox's test_baseline_matches_section_11_doc docstring (lines
56-61) flagged that my §7.1 worked example said
622.7 / 290.0 / 32.7 bits but the calculator's actual
closed-form output is 625.87 / 292.48 / 33.39. Same drift in
§7.2 (3358 → 3387.72) and §7.3 (247 → 247.14).

The numbers were rounded estimates from when I drafted the doc
before the calculator existed. Refreshed all three §7 numeric
examples to match the calculator's actual output (verified live
via t3_bound_bits()). §3 inline approximation likewise updated
(290 → 292.48). Added a short note pointing readers at the
calculator + tests as the source of truth.

2. Backfill anchor_prg tests with fox's patterns
=================================================

fox's test_t3_bound_calculator.py demonstrated four patterns I'd
missed in my #000035 phi_prg tests:

- **Output prefix invariant** (closure check): phi_prg(h, n+k)[:n]
  ≡ phi_prg(h, n). Streaming-counter invariant — would catch a
  bug where a per-call seed mutation broke determinism across
  dim_h values.
- **Output length monotonicity**: len(phi_prg(h, n)) == n exactly.
  Parametrized over n ∈ {1, 2, 4, 7, 16, 17, 64, 1024}. Catches
  off-by-one in `_expand` truncation.
- **Hand-computed first block**: assert that the first 64 bytes
  of output equal a direct ``hmac.new(seed, h + b'\\x00\\x00
  \\x00\\x00', sha512).digest()``. Pattern from fox's
  test_b1_exact_formula — don't rely on KAT regression alone;
  compute the first-principles math in the test file. Catches
  algorithm drift the KAT (regenerated against a buggy version)
  would miss.
- **Seed-bleed check**: changing the seed must change EVERY output
  position. Probability of false-positive ≈ 64 · 2^-32 ≈ 2^-26;
  none expected in practice.
- **Parametrized invalid-input tests**: collapsed N separate
  ``test_rejects_*`` functions into ``@pytest.mark.parametrize``
  cones (4 wrong-size-hash cases + 3 non-positive-dim_h cases).
  Same coverage, fewer test functions.

Test count: was 20 in test_anchor_prg.py; now 27 (+7 from
parametrize expansion + new patterns). Full suite: 1720 → 1727.

Hygiene
=======
- make test → 1727 passed, 45 skipped.
- make chain-check-shards → 0 across all 7 shards.
- All new tests use ``pytest.importorskip`` already at module top
  (anchor_prg has no extras gate; tests run unconditionally).

Lessons captured
================
The patterns to remember for future calculator/probe-style code:

  1. KAT regression alone isn't enough. Add hand-computed
     formula tests so the math itself is asserted in the test
     file, not just "consistent with a recorded snapshot".
  2. Test monotonicity / closure invariants. They catch
     algorithm drift, sign errors, missing terms.
  3. Parametrize invalid-input tests. One function, N cases.
  4. Test the doc's numbers against the function. Catches
     calibration drift in the doc itself (this commit's
     finding about §7).
  5. CLI subprocess tests for end-to-end. Argparse + main()
     drift the import-only tests miss.
2026-05-10 12:36:13 -04:00

15 KiB
Raw Blame History

T3 per-window covert-channel budget bound

Ticket: #000036 Source analysis: docs/soft-hash-channel-analysis.md Date: 2026-05-10 Status: first-cut formal derivation; awaiting fox + cryptographer review of constants. The framework is the deliverable; the named constants below are conservative-but-loose first estimates that future tightening can replace without changing the call sites of the closed-form bound.


§1. T3 model restatement

From docs/soft-hash-channel-analysis.md §6, threat model T3 (hyperparameter adversary) extends T2 (gradient adversary) with control over:

  • Learning-rate scheduling: choosing the LR at each scheduled decision point from a constrained range (e.g. log-grid of R levels).
  • Batch ordering: permuting batches within an epoch.

Combined with T2's gradient bias (controlling a g-fraction of training-step gradient computations), T3 is a strict superset of T2. The §4 reduction in soft-hash-channel-analysis.md bounds T1

  • T2 channels at the SHA-256 random-oracle baseline assuming φ is independent of parameter geometry; T3 breaks the implicit "published schedule" assumption so the §4 reduction does not directly apply.

M2 mitigation (per-checkpoint anchor nonce, soft-hash-channel analysis §5.3) re-randomizes the anchor every checkpoint. T3's exploitation window is thus bounded to one nonce-window of length W steps before the anchor re-randomizes. This document bounds the bits T3 can steer into C(M_{t+1}) within a single window.

The bound's intended use: operators choose W (the nonce-window length) such that I_window stays below their target residual (e.g. < 1 bit per window means the adversary needs ≥ 2^256 windows to steer C(M) to a specific 256-bit target).


§2. Per-window channel — formal definition

For a single nonce window of length W steps starting at checkpoint t:

I_window := I( A ; C(M_{t+1}) | H_{≤t}, n_t )

where:

  • A is the joint random variable over T3's adversary inputs during the window: A = (g_1, …, g_W, lr_1, …, lr_⌈W/K⌉, π) where g_s is the adversary-controlled gradient signal at step s, lr_d is the LR at decision point d, π is the batch-ordering permutation.
  • C(M_{t+1}) is the SHA-256 hard-hash committed at the next checkpoint.
  • H_{≤t} is the public history before the window opens.
  • n_t is the published per-checkpoint nonce (M2 mitigation).

This is the standard mutual-information upper bound on distinguishing-from-baseline channel capacity. The data processing inequality lets us decompose:

I_window ≤ I( A ; Θ_{t+1} | H_{≤t}, n_t )    (parameter-space proxy)
         + I( Θ_{t+1} ; C(M_{t+1}) | H_{≤t}, n_t )    (φ-mapping)

The second term is bounded at the random-oracle baseline by the §4 reduction (when φ is φ_PRG per #000035, or φ_linear under NO_ALIGNMENT verdict per #000034). So:

I_window ≤ I( A ; Θ_{t+1} | H_{≤t}, n_t ) + (random-baseline bits)

The random-baseline term is constant-bounded; the per-window adversarial capacity is the first term, which we now bound by decomposing across T3's three control surfaces.


§3. C_B1 — gradient-bias bandwidth

Setup. Each step s ∈ {1, …, W}, the adversary controls a g-fraction of gradient computations. The contributed adversarial signal g_s is bounded in norm by g · ‖∇L_max‖ where ‖∇L_max‖ is the per-step gradient-norm cap (gradient clipping, in practice).

Information bound. Apply Fano's inequality to the per-step parameter-shift channel.

For one SGD step with learning rate lr_s:

Δ Θ_s = lr_s · (1 - g) · ∇L_honest + lr_s · g · ∇L_adv

The adversarial component lr_s · g · ∇L_adv is the parameter shift in the adversary's chosen direction. By data-processing-inequality argument:

I( g_s ; Δ Θ_s ) ≤ log₂( ‖adversarial step‖ / ‖noise step‖ + 1 )
                ≤ log₂( g · lr_s · ‖∇L_max‖ / (lr_s · σ_grad) + 1 )
                = log₂( g · ‖∇L_max‖ / σ_grad + 1 )

where σ_grad is the standard deviation of the honest stochastic gradient at this step. The +1 keeps the log finite when the adversarial step is below the noise floor.

Define:

SNR_grad := g · ‖∇L_max‖ / σ_grad

Then per-step gradient-bias capacity is bounded by log₂(SNR_grad + 1) bits.

Per-window. Adversarial signals across steps are information-additive (each step's signal can in principle target a different parameter direction):

C_B1 · g · W · log₂(N_dir)

where the ticket's log₂(N_dir) factor is the per-step bit budget (capped at log₂(SNR_grad + 1) per the Fano bound above, which by data-processing-inequality is much smaller than log₂ of the full direction count 2^256). Replace the ticket sketch's log₂(N_dir) with log₂(SNR_grad + 1):

B1 contribution ≤ C_B1 · g · W · log₂( SNR_grad + 1 )

with C_B1 = 1 (data processing inequality; tight).

For typical deployments g · ‖∇L_max‖ / σ_grad = 0.05 · 1 / 0.1 = 0.5, so log₂(1.5) ≈ 0.5850 bits/step. With W = 10000 and g = 0.05: 1 · 0.05 · 10000 · 0.5850 ≈ 292.48 bits/window.


§4. C_B2 — learning-rate selection

Setup. T3 picks LR from a log-grid of R levels at each LR-decision point. With decision interval K steps, the window of length W contains ⌈W/K⌉ decision points.

Information bound. Each decision contributes at most log₂(R) bits to the channel under uniform prior. By data-processing inequality, the LR choice's effect on Θ_{t+1} is bounded by what the LR can change about gradient steps it applies to — which is at most a multiplicative factor in the parameter-step magnitude.

The categorical-channel capacity of R symbols is log₂(R) bits per decision. The mapping from LR symbol to effective parameter shift is a finite function (each LR level corresponds to a fixed step magnitude), so:

B2 contribution ≤ C_B2 · ⌈W / K⌉ · log₂ R

with C_B2 = 1 (data processing inequality; tight).

For typical deployments K = 100, R = 8, W = 10000: 1 · 100 · log₂(8) = 100 · 3 = 300 bits/window.

Tightening note. C_B2 = 1 assumes every LR choice maps to an information-distinguishable parameter trajectory. In practice many LR choices produce trajectories that converge to similar Θ (the loss surface's basin geometry damps small-LR perturbations). A tighter bound would measure the LR-trajectory diversity empirically; the framework above admits substituting C_B2 < 1 when measured.


§5. C_B3 — batch-order bandwidth

Setup. T3 reorders batches within an epoch. With N_b batches per epoch, the naive bound is log₂(N_b!) bits per epoch — but this drastically overestimates the steering capacity because batch-order effects on final Θ are bounded by gradient noise, not by permutation entropy.

Information bound — the Bottou-Bousquet refinement. Per Bottou-Bousquet (2008), Hardt-Recht-Singer (2016), and the broader random-shuffle SGD analysis:

  • Random-shuffle SGD: order has O(1/√N_b) effect on final Θ distribution. Channel bits ≈ 0.
  • Cyclic-shuffle SGD: order has O(1) effect (consistent bias). Channel bits ≈ O(log₂ N_b) per epoch.
  • Adversarial-order SGD: order can amplify directional bias by at most a factor of √N_b over random-shuffle. Channel bits ≈ log₂( σ_grad · √N_b / ‖∇L_max‖ ) per epoch = 0.5 · log₂(N_b) + log₂(σ_grad / ‖∇L_max‖).

The window of length W contains ⌈W / E⌉ epochs where E = N_b (steps per epoch). Each epoch contributes at most:

log₂( N_b · σ_grad / ‖∇L_max‖ ) / 2  bits  (adversarial-order)

For N_b = 1024, σ_grad/‖∇L_max‖ ≈ 0.1: 0.5 · log₂(102.4) ≈ 3.34 bits/epoch.

B3 contribution ≤ C_B3 · ⌈W / E⌉ · log₂( N_b · σ_grad / ‖∇L_max‖ ) / 2

with C_B3 ≤ 1 (data processing inequality; conjecture is tighter
under the Bottou-Bousquet random-shuffle baseline; see §10).

For W = 10000, E = 1024, the contribution is 1 · 9.77 · 3.34 ≈ 32.6 bits/window — substantially less than the naive log₂(N_b!) bound of ~8000+ bits/epoch.


§6. Combined per-window bound

Combining §3-§5:

I_window ≤ C_B1 · g · W · log₂( SNR_grad + 1 )
         + C_B2 · ⌈W / K⌉ · log₂( R )
         + C_B3 · ⌈W / E⌉ · log₂( N_b · σ_grad / ‖∇L_max‖ ) / 2

with conservative constants C_B1 = C_B2 = C_B3 = 1 (each by data-processing inequality; future work may tighten any of them based on empirical loss-surface measurement).


§7. Numeric examples

§7.1 Small deployment

g = 0.05, ‖∇L_max‖ = 1.0, σ_grad = 0.1
K = 100, R = 8, W = 10000, N_b = 1024, E = 1024
SNR_grad = 0.5

B1 = 1 · 0.05 · 10000 · log₂(1.5)         ≈ 292.48 bits
B2 = 1 · ⌈10000/100⌉ · log₂(8)             = 300.00 bits
B3 = 1 · ⌈10000/1024⌉ · log₂(102.4) / 2   ≈  33.39 bits
total                                       ≈ 625.87 bits/window

(Numbers are the calculator's closed-form output, not rounded estimates; see bench/scripts/t3_bound_calculator.py and the unit tests in tests/test_t3_bound_calculator.py.)

§7.2 Medium deployment (longer window)

g = 0.05, ‖∇L_max‖ = 1.0, σ_grad = 0.1
K = 1000, R = 16, W = 100000, N_b = 8192, E = 8192
SNR_grad = 0.5

B1 = 1 · 0.05 · 100000 · log₂(1.5)         ≈ 2924.81 bits
B2 = 1 · ⌈100000/1000⌉ · log₂(16)           = 400.00 bits
B3 = 1 · ⌈100000/8192⌉ · log₂(819.2) / 2   ≈  62.91 bits
total                                        ≈ 3387.72 bits/window

§7.3 Hardened deployment (lower g, smaller R)

g = 0.01, ‖∇L_max‖ = 1.0, σ_grad = 0.1
K = 100, R = 4, W = 10000, N_b = 1024, E = 1024
SNR_grad = 0.1

B1 = 1 · 0.01 · 10000 · log₂(1.1)          ≈  13.75 bits
B2 = 1 · ⌈10000/100⌉ · log₂(4)              = 200.00 bits
B3 = 1 · ⌈10000/1024⌉ · log₂(102.4) / 2   ≈  33.39 bits
total                                        ≈ 247.14 bits/window

§8. Operator guidance — choosing window length

The operator picks W such that I_window ≤ B_target where B_target is the desired residual.

Target = 1 bit/window (very conservative; adversary needs ≥ 2^256 windows for a specific target):

Solving for W in §6 with the small-deployment constants:

1 ≥ 0.05 · W · 0.58 + (W/100) · 3 + (W/1024) · 3.34 / 2
1 ≥ 0.029 W + 0.030 W + 0.0016 W
1 ≥ 0.061 W
W ≤ 16  steps  (impractical — re-anchor every 16 steps)

Target = 256 bits/window (the SHA-256 hard-hash output size; informational equivalent of a single brute-force preimage attempt per window):

256 ≥ 0.061 W
W ≤ 4196 steps

This is the practical operating range: re-anchor approximately every 4000 SGD steps to keep T3's per-window steerage at the SHA-256 baseline.

Target = 2^16 = 65536 bits/window (much larger window, adversary still needs ~2^240 windows to brute-force):

W ≤ 1.07 × 10^6 steps

This admits week-long training runs between anchor rotations.


§9. Closure of soft-hash-channel-analysis.md §9.3

§9.3 of the analysis posed the open question:

What is the explicit per-window bound on T3 channel capacity under M2?

This document answers it. §6's closed-form bound, parametrized in operator-measurable inputs (g, K, R, W, N_b, σ_grad, ‖∇L_max‖), is the explicit form. Reference: this doc §6.


§10. Open questions + future work

The framework is conservative; tightening any of these refines the bound without invalidating it:

  1. C_B1 below the data-processing limit. The Fano bound log₂(SNR_grad + 1) is a per-step capacity ceiling; tighter bounds are possible if the loss surface has reduced adversary-controllable directions (e.g. Hessian rank deficiency). Empirical measurement via #000034's probe could tighten C_B1 by 1-2 orders of magnitude on typical deployments.
  2. C_B2 below 1. Many LR choices map to similar trajectories; a deployment-specific empirical measurement of LR-trajectory diversity (1-Wasserstein distance between (LR_1, LR_2, … LR_R) final-checkpoint distributions) yields C_B2 < 1.
  3. C_B3 closer to the random-shuffle baseline. If the deployment's SGD is random-shuffle (most modern training is), the adversarial-order bound used here over-estimates by O(√N_b) factor. C_B3 → O(1/√N_b).
  4. Future B4-B5 control surfaces. Adaptive optimizer state manipulation (momentum, second-moment estimates) is not in the §1 model. The framework here generalizes — add new B_i terms as new T3 control surfaces are documented.
  5. Empirical validation. This bound has not been validated against an actual adversarial-training experiment. The acceptance criterion (§5 of the source ticket) explicitly marks empirical validation as out-of-scope; landing the doc establishes the framework operators can plan around. #000034's Phase 1b would feed directly into a future empirical-validation ticket.

§11. Calculator script

The closed-form bound from §6 lands as bench/scripts/t3_bound_calculator.py for operator use:

$ python -m bench.scripts.t3_bound_calculator \
    --gradient-fraction 0.05 \
    --gradient-norm-max 1.0 \
    --gradient-noise-stddev 0.1 \
    --lr-decision-interval 100 \
    --lr-grid-size 8 \
    --window-length 10000 \
    --batches-per-epoch 1024 \
    --steps-per-epoch 1024
{
  "I_window_bits_upper_bound": 625.8716,
  "B1_contribution": 292.4813,
  "B2_contribution": 300.0,
  "B3_contribution": 33.3904,
  "snr_grad": 0.5,
  "decisions_in_window": 100,
  "epochs_in_window": 10,
  "constants": {"C_B1": 1.0, "C_B2": 1.0, "C_B3": 1.0},
  "recommendation": "I_window ≈ 625.9 bits/window EXCEEDS the
                     SHA-256 (256 bit) output size. M2's
                     single-window guarantee is broken at this W.
                     Reduce W (or reduce g / R / increase K) until
                     I_window < 256 bits/window."
}

The above example shows a deployment whose per-window budget exceeds 256 bits — the W of 10000 is too large for a 1-window SHA-256-resistance guarantee. Operators read the calculator output and adjust W (or g, R, K) to tune.


§12. References

  • docs/soft-hash-channel-analysis.md — source analysis (#000018).
  • Ticket #000036 — this document's spec.
  • Ticket #000034 — φ_linear Hessian-alignment probe; informs the C_B1 tightening path.
  • Ticket #000035 — φ_PRG construction; closes the random-oracle modeling gap independent of T3.
  • Bottou & Bousquet (2008), "The Tradeoffs of Large Scale Learning" — gradient-noise / batch-order bound argument.
  • Hardt, Recht & Singer (2016), "Train Faster, Generalize Better: Stability of Stochastic Gradient Descent" — formal stability framework underlying the C_B3 random-shuffle bound.