Files
TOIFAS/code/make_tables.py
T
KiHoLee 3cf3b6c3a7 Re-run the key-length attack sweep and emphasize the main-configuration row
stage_F was missed in the structured-family regeneration, so sec_brute.csv
and sec_sens.csv still carried learned-family results. Re-running it moves
the L=16 brute-force point from 0.7495 to 0.5916, which now agrees with
sec_brute_cmp.csv rather than contradicting it.

make_tables.py bolds the Walsh-Hadamard row the way it already bolds the
proposed and invariant rows, since that family is the main configuration.
2026-08-18 08:48:41 +09:00

118 lines
4.3 KiB
Python

"""Generate the LaTeX rows of every result table from the CSVs, so
that every table in the paper is reproducible from data/ (TIFS mandate).
Prints the tabular bodies; paste into main.tex without edits.
"""
from __future__ import annotations
import csv
import json
import math
from pathlib import Path
DATA = Path(__file__).resolve().parents[1] / "data"
NAME = {
"proposed": r"\textbf{Proposed keyed masking}",
"public_mask": "Public masks",
"perm_key": r"Permutation key~\cite{chen2023shuffling}",
"index_cipher": "Index cipher",
"oma_plain": "OMA (no encryption)",
"random": "Random",
"hadamard": "Walsh-Hadamard",
"learned": "Learned",
"learned_reg": r"Regularized~\eqref{eq:regloss}",
}
RECEIVER = {
"legit": "Legitimate", "oma": "OMA",
"insider": "Insider", "eve": "Outsider",
}
def f3(x: str) -> str:
"""Three decimals, or an em-dash for a value that does not apply."""
try:
v = float(x)
except (TypeError, ValueError):
return "--"
return "--" if math.isnan(v) else f"{v:.3f}"
def cell(x: str, bold: bool) -> str:
s = f3(x)
if s == "--":
return "--"
return rf"$\mathbf{{{s}}}$" if bold else f"${s}$"
def compare_table():
print("% Table: scheme comparison (from sec_compare.csv)")
rows = list(csv.DictReader(open(DATA / "sec_compare.csv")))
order = ["public_mask", "perm_key", "index_cipher", "oma_plain", "proposed"]
rows.sort(key=lambda r: order.index(r["scheme"]))
# stage_E does not jam the orthogonal reference, because the jammer an
# OMA user faces is targeted at public slots rather than mask-matched
# or mask-blind. stage_L measures that case, so the cell comes from
# there instead of being left empty.
jam = {float(r["jsr_db"]): r
for r in csv.DictReader(open(DATA / "sec_jam_cmp.csv"))}
oma_jam = jam[0.0]["oma_targeted"]
for r in rows:
b = r["scheme"] == "proposed"
if r["scheme"] == "oma_plain" and f3(r["jam0_ser"]) == "--":
r["jam0_ser"] = oma_jam
cells = [cell(r[k], b) for k in
("legit_ser", "eve_out", "eve_in", "jam0_ser")]
print(f"{NAME[r['scheme']]} & " + " & ".join(cells) + r" \\")
def maskfam_table():
print("% Table: key families (from sec_maskfam.csv)")
for r in csv.DictReader(open(DATA / "sec_maskfam.csv")):
# the structured family is the main configuration, so its row is
# emphasized the same way the proposed row is in the comparison
b = r["family"] == "hadamard"
cells = [cell(r[k], b) for k in
("legit_ser", "eve_ser", "eve_ones_ser", "mask_xcorr")]
name = NAME[r["family"]]
if b:
name = r"\textbf{" + name + "}"
print(f"{name} & " + " & ".join(cells) + r" \\")
def real_table():
print("% Table: headline recovery (from real_sec_stats.json)")
st = json.loads((DATA / "real_sec_stats.json").read_text())
rec = st["recovery"]
snrs = sorted(rec, key=float)
for key in ("legit", "oma", "insider", "eve"):
cells = " & ".join(f"${rec[s][key]:.3f}$" for s in snrs)
print(f"{RECEIVER[key]} & {cells}" + r" \\")
def refresh_tables():
print("% Table: key refresh (from refresh_summary.csv)")
for r in csv.DictReader(open(DATA / "refresh_summary.csv")):
b = r["scheme"] == "Invariant"
name = r"\textbf{Invariant}" if b else r["scheme"]
f = (lambda t: r"\mathbf{" + t + "}") if b else (lambda t: t)
print(f"{name} & ${f(format(float(r['legit']), '.3f'))}$ & "
f"${f(format(float(r['eve']), '.4f'))}$ & "
f"${f(format(float(r['entropy_bits']), '.1f'))}$~bits" + r" \\")
print()
print("% Table: known plaintext across a refresh (from refresh_kpa.csv)")
rows = {r["n_frames"]: r for r in csv.DictReader(open(DATA / "refresh_kpa.csv"))}
keep = ["2", "8", "64"]
print("Frames used by the attacker & "
+ " & ".join(f"${k}$" for k in keep) + r" \\")
for lbl, key in (("Same block", "ser_same_block"),
("Next block", "ser_next_block")):
print(f"{lbl} & "
+ " & ".join(f"${float(rows[k][key]):.3f}$" for k in keep)
+ r" \\")
if __name__ == "__main__":
compare_table(); print()
maskfam_table(); print()
real_table(); print()
refresh_tables()