The table generator uses the legend form of the scheme name, the system
figure groups blocks with dashed outlines, and the figure PDFs are
rebuilt from the current data.
The lower of two coinciding curves is the wide one, the random-guess
reference is dotted everywhere and never borrows a scheme colour, the
three collection SNRs in the known-plaintext figure differ by face and
width rather than by dash alone, and the legend floor now prints at six
points with an assertion that fails if any figure drags it lower. Both
table generators list the proposal first.
The package was missing every script behind the KM (lrn.) curves and
both learned table rows: exp_learned's driver, the merge that folds the
learned rows into sec_compare.csv and refresh_summary.csv, and the
report that reads the learned numbers back. It was also missing
sec_keylen_perm.csv, so Fig. 3 could not be regenerated at all, and the
two diagnostics that answer why a fixed key beats a learned one here and
where a learned mask would win instead.
The learned artifacts themselves are regenerated. They were trained on
the cross-entropy alone, which drifts to disjoint sparse supports: 99
percent of each key's energy on about six of the 64 entries, so a digit
is decided over a sixth of its period and the key set is a choice of
support rather than a dense direction in R^L. They are now the
regularized keys of Section V-C, and check_consistency asserts which of
the two families the figures draw.
Verified from inside this repository: replot_security.py rebuilds all
seven result figures, make_tables.py reproduces both result tables, and
check_consistency.py passes every check that does not need the
manuscript.
The README now lists what ships. Its run list, layout and figure map had
none of the learned pipeline, named two tables the manuscript renders as
prose, and gave Fig. 3 no data file for its permutation curve.
Fig. 7 varied the marker across collection SNRs of one scheme, taking
shapes that identify other schemes elsewhere; the marker is now the
scheme's and the line style carries the SNR. Adversary labels name the
realization they were run for.
The unjammed reference puts the range at 0.053 to 0.998, over a decade,
so the linear axis and its below-zero legend band are no longer needed.
Minor tick labels are suppressed to keep the left margin clear.
exp_learned.py completes the learned side of the result stages, so every
figure can carry both realizations of keyed masking. real() holds the
structured artifacts aside and restores them, since exp_real_sec writes
fixed file names.
replot_security.py ranks legend handles from one declared order at all
three ax.legend call sites, so entries no longer follow plot-call order
and drift between figures.
exp_learned.py mirrors every structured result stage for the learned key
family at the same SNRs, frame counts and seeds, so Figs. 2, 3, 4 and 7
and Tables IV and VI can carry both realizations of keyed masking.
replot_security.py gains a style registry: colour identifies the scheme
and line style the role, so a curve learned in one figure reads the same
in the next. Previously OMA was grey in two figures and teal in a third,
and blue meant the eavesdropper in one figure and the permutation key in
another.
check_family_enum.py now runs both attacks against both families. The
outsider ranks the L-1 Walsh rows; the insider, holding m_v, ranks the
L-1 products m_v .* m_r, which works because Walsh rows are closed
under the elementwise product and the per-block sign cancels in
m_u .* m_v. Both need a list to rank, and only the structured family
supplies one: the structured family falls at 0.905 from one frame at
10 dB and 0.990 from four, the refresh takes the outsider to 0.000 and
leaves the insider at 0.980, and the learned family gives 0.000
throughout.
exp_full.stage_N sweeps the learned family over the same SNR grid at
the same frame count as stage_A, so Fig. 2 can carry both families and
a reader can see what the key space costs at every SNR rather than at
one point.
check_consistency.py gains four assertions for the key-space
measurements and two for the learned sweep, 82 in all.
README: the assertion count was two rounds stale, and the figure map
omitted family_enum, cov_attack and maskdegen, whose CSVs back quoted
manuscript numbers.
The jammed OMA model now matches the transmit chain the paper
describes, the key-length sweep averages the eavesdropper over eight
substitute-key draws, and verify_math gains the coded-OMA outage
reference, symbolic checks of the three algebraic identities, and the
cross-period remainder of Proposition 2. The README records the energy
and channel conventions.
The ciphertext-only check trained its own L=16 model and printed only a
verdict; it now attacks the main configuration and writes
data/cov_attack.csv, which the manuscript cites.
diag_maskdegen writes data/maskdegen.csv so the claim it supports is
traceable; the legend guard inflates by the marker radius and refuses a
legend that leaves the canvas; one legend size on every figure.
Declared point sizes are now pre-divided by the 0.70 include scale so
the printed sizes are the intended ones, and place_legend scores the
same layout save() enforces.
Every OMA reference takes the L/16 combining gain so the comparison
stays resource matched, four hardcoded copies of the configuration are
replaced by MAIN_D or the main curve, and stage_J's K-by-L Gaussian
draw becomes its exact scalar Beta equivalent.
check_texhealth.py flags control characters and bare macro stubs left
by a shell heredoc, a class of corruption that LaTeX compiles without
complaint. It skips when main.tex is absent, as in this package.
fig_sec_snr now carries an inset with the OMA-to-proposed SER ratio,
since the 1 to 9 percent advantage is invisible across two decades of
log axis, and save() now guards inset overlap as it guards the legend.
check_consistency covers the L=8 crossover, the inset ratio span, the
stated secret sizes, and the Fig. 5 curve coincidence: 29 assertions.
Unconstrained key training converged to disjoint sparse supports: 99
percent of each users key energy sat on three or four of the sixteen
entries, with pairwise disjoint supports and one numerically dead
codebook column. That is an orthogonal slot allocation, so the
superposition collapsed into OMA and the key space was far smaller than
the dense direction the brute-force study assumes.
The main configuration is now the structured Walsh-Hadamard family,
which is dense, exactly orthogonal, unit modulus, and already the best
family in the key-family table. base_keys generalizes to any key length
by truncating the next power-of-two Sylvester order, and the key-length
sweep keeps only lengths where the truncated rows stay exactly
orthogonal, verified numerically.
Also fixes the M-PAM energy normalization in oma_ser_keylen, which used
sqrt(6g/(M^2-1)) where unit average symbol energy gives A^2=3/(M^2-1);
the closed form was 3 dB optimistic and now reproduces a direct Monte
Carlo to 1e-5.
Results move accordingly: the proposal now stays below OMA at every SNR
and reaches 1.52x at key length 64, while the jamming margin falls to
5.5-6.3 dB and the brute-force curve to 0.59 at a million guesses.
check_consistency.py asserts every headline number against its raw CSV,
so a stale quoted value fails loudly instead of surviving a revision. It
skips the manuscript-side assertions when main.tex is absent, which is
the case in this package.
The blind-versus-permutation agreement is 0.002, not 0.0015, and the
resource-matched OMA is undefined below L=16 rather than wherever 16/L
is not an integer.
Resource-match the OMA reference in the key-length sweep (oma_ser_keylen),
which gives it the L/16 combining gain the longer frame allows. The
proposal now passes a resource-matched OMA by 1.27x at L=64 rather than
the 4.3x reported against a fixed-d reference.
Densify the JSR, sensitivity, and brute-force grids so the curves are
smooth, give the index cipher its channel floor instead of error-free
reception, and add the permutation-key known-plaintext attack
(exp_permkpa) so Fig. 7 carries a conventional linear scheme.
Add check_cov_attack.py and check_cov_ceiling.py: a referee raised a
ciphertext-only second-order attack; the exact-population test shows the
received covariance leaks only a sparse rank-deficient subset of the key
Gram and leaves the eavesdropper at the random-guess level.
Dump verify_math.csv, move the superseded V=256 pilot CSVs to data/pilot.
Give the orthogonal-access jammer its own Rayleigh channel in
oma_ser_jammed, matching the convention every simulated scheme already
used. Without it the closed-form curve faced a jammer at full power in
every frame while the Monte Carlo curves faced a fading one, which
inverted the ordering of the comparison.
Measure the outsider error rate for the fixed-key and naive-refresh
cases as well, and emit the two refresh tables from make_tables.py, so
no cell of the paper is hand-typed.
- exclude the all-ones Walsh-Hadamard row and test every key family
against the all-ones guess
- pass the model dimension to the noise scaling so the key-length sweep
runs at a fixed per-dimension SNR
- known-plaintext attack with nested accumulation and common random
numbers, averaged over 40 collections
- key sensitivity and brute-force search extended to the permutation
key and the index cipher
- make_tables regenerates all three result tables from the CSVs
Reproducibility package for the TIFS submission: transmit and receive
core, security stages (eavesdropper, jamming, key families, attack
difficulty, known-plaintext), real BERT token streams, closed-form
verification, and the scripts that regenerate every figure and table
from the released CSVs.